From ae3b0533345a4c4ab622e2e871358df8dfb762b9 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 18 Jan 2019 13:10:02 +0000 Subject: [PATCH 001/347] Initial version --- Hadrons/Modules.hpp | 7 +++++-- Hadrons/modules.inc | 14 ++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 787fecea..3e611907 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -21,18 +21,18 @@ #include #include #include +#include #include #include #include -#include #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -45,6 +45,9 @@ #include #include #include +#include +#include +#include #include #include #include diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 88cfea88..efede8ec 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -26,14 +26,14 @@ modules_cc =\ Modules/MSolver/LocalCoherenceLanczos.cc \ Modules/MGauge/StoutSmearing.cc \ Modules/MGauge/Unit.cc \ - Modules/MGauge/Electrify.cc \ Modules/MGauge/UnitEm.cc \ Modules/MGauge/StochEm.cc \ Modules/MGauge/Random.cc \ + Modules/MGauge/Electrify.cc \ Modules/MGauge/FundtoHirep.cc \ Modules/MGauge/GaugeFix.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ Modules/MUtilities/RandomVectors.cc \ Modules/MUtilities/TestSeqGamma.cc \ Modules/MUtilities/PrecisionCast.cc \ @@ -43,6 +43,9 @@ modules_cc =\ Modules/MScalar/VPCounterTerms.cc \ Modules/MScalar/ChargedProp.cc \ Modules/MScalar/ScalarVP.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MDistil/LapEvec.cc \ + Modules/MDistil/PerambLight.cc \ Modules/MNPR/Amputate.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ @@ -94,18 +97,18 @@ modules_hpp =\ Modules/MSink/Point.hpp \ Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ Modules/MSolver/Guesser.hpp \ Modules/MSolver/RBPrecCG.hpp \ Modules/MSolver/A2AVectors.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ Modules/MGauge/UnitEm.hpp \ Modules/MGauge/StoutSmearing.hpp \ Modules/MGauge/Unit.hpp \ - Modules/MGauge/Electrify.hpp \ Modules/MGauge/Random.hpp \ Modules/MGauge/GaugeFix.hpp \ Modules/MGauge/FundtoHirep.hpp \ Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ Modules/MUtilities/PrecisionCast.hpp \ @@ -118,6 +121,9 @@ modules_hpp =\ Modules/MScalar/ScalarVP.hpp \ Modules/MScalar/Scalar.hpp \ Modules/MScalar/ChargedProp.hpp \ + Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/PerambLight.hpp \ Modules/MNPR/Bilinear.hpp \ Modules/MNPR/Amputate.hpp \ Modules/MNPR/FourQuark.hpp \ From b821dde0200dc211c375c14522dbda8d263721e0 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 18 Jan 2019 13:14:28 +0000 Subject: [PATCH 002/347] Initial version --- Hadrons/Modules/MDistil/DistilVectors.cc | 7 ++ Hadrons/Modules/MDistil/DistilVectors.hpp | 85 ++++++++++++++++ Hadrons/Modules/MDistil/LapEvec.cc | 37 +++++++ Hadrons/Modules/MDistil/LapEvec.hpp | 114 ++++++++++++++++++++++ Hadrons/Modules/MDistil/PerambLight.cc | 7 ++ Hadrons/Modules/MDistil/PerambLight.hpp | 85 ++++++++++++++++ 6 files changed, 335 insertions(+) create mode 100644 Hadrons/Modules/MDistil/DistilVectors.cc create mode 100644 Hadrons/Modules/MDistil/DistilVectors.hpp create mode 100644 Hadrons/Modules/MDistil/LapEvec.cc create mode 100644 Hadrons/Modules/MDistil/LapEvec.hpp create mode 100644 Hadrons/Modules/MDistil/PerambLight.cc create mode 100644 Hadrons/Modules/MDistil/PerambLight.hpp diff --git a/Hadrons/Modules/MDistil/DistilVectors.cc b/Hadrons/Modules/MDistil/DistilVectors.cc new file mode 100644 index 00000000..3f04a18b --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilVectors.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TDistilVectors; diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp new file mode 100644 index 00000000..d5008170 --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -0,0 +1,85 @@ +#ifndef Hadrons_MDistil_DistilVectors_hpp_ +#define Hadrons_MDistil_DistilVectors_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * DistilVectors * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class DistilVectorsPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilVectorsPar, + unsigned int, i); +}; + +template +class TDistilVectors: public Module +{ +public: + // constructor + TDistilVectors(const std::string name); + // destructor + virtual ~TDistilVectors(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(DistilVectors, TDistilVectors, MDistil); + +/****************************************************************************** + * TDistilVectors implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TDistilVectors::TDistilVectors(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TDistilVectors::getInput(void) +{ + std::vector in; + + return in; +} + +template +std::vector TDistilVectors::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TDistilVectors::setup(void) +{ + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TDistilVectors::execute(void) +{ + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_DistilVectors_hpp_ diff --git a/Hadrons/Modules/MDistil/LapEvec.cc b/Hadrons/Modules/MDistil/LapEvec.cc new file mode 100644 index 00000000..488eb48c --- /dev/null +++ b/Hadrons/Modules/MDistil/LapEvec.cc @@ -0,0 +1,37 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/LapEvec.cc + + Copyright (C) 2015-2019 + + *** MM making a change on Tesseract ** + Author: Felix Erben + Author: Michael Marshall + + 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 + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TLapEvec; diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp new file mode 100644 index 00000000..f0c93607 --- /dev/null +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -0,0 +1,114 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/LapEvec.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + +#ifndef Hadrons_MDistil_LapEvec_hpp_ +#define Hadrons_MDistil_LapEvec_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * LapEvec * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class LapEvecPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, + unsigned int, i); +}; + +template +class TLapEvec: public Module +{ +public: + // constructor + TLapEvec(const std::string name); + // destructor + virtual ~TLapEvec(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); + +/****************************************************************************** + * TLapEvec implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TLapEvec::TLapEvec(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TLapEvec::getInput(void) +{ + std::vector in; + + return in; +} + +template +std::vector TLapEvec::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TLapEvec::setup(void) +{ + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TLapEvec::execute(void) +{ + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_LapEvec_hpp_ diff --git a/Hadrons/Modules/MDistil/PerambLight.cc b/Hadrons/Modules/MDistil/PerambLight.cc new file mode 100644 index 00000000..bcb21d71 --- /dev/null +++ b/Hadrons/Modules/MDistil/PerambLight.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::Tperambulator_l; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp new file mode 100644 index 00000000..91f776a0 --- /dev/null +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -0,0 +1,85 @@ +#ifndef Hadrons_MDistil_perambulator_l_hpp_ +#define Hadrons_MDistil_perambulator_l_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * perambulator_l * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class perambulator_lPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(perambulator_lPar, + unsigned int, i); +}; + +template +class Tperambulator_l: public Module +{ +public: + // constructor + Tperambulator_l(const std::string name); + // destructor + virtual ~Tperambulator_l(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(perambulator_l, Tperambulator_l, MDistil); + +/****************************************************************************** + * Tperambulator_l implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +Tperambulator_l::Tperambulator_l(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector Tperambulator_l::getInput(void) +{ + std::vector in; + + return in; +} + +template +std::vector Tperambulator_l::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void Tperambulator_l::setup(void) +{ + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void Tperambulator_l::execute(void) +{ + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_perambulator_l_hpp_ From 2568504821e3ab8dc083c5fb74ba0467154d681f Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 18 Jan 2019 13:23:03 +0000 Subject: [PATCH 003/347] small change --- Hadrons/Modules/MDistil/DistilVectors.hpp | 93 +++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index d5008170..40df28e0 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -7,6 +7,99 @@ BEGIN_HADRONS_NAMESPACE +/****************************************************************************** + * * Class to generate Distillation $\varrho$ and $\varphi$ vectors * + * ******************************************************************************/ +/* + * template + * class DistilVectorsFromPerambulator + * { + * public: + * FERM_TYPE_ALIASES(FImpl,); + * SOLVER_TYPE_ALIASES(FImpl,); + * public: + * DistilVectorsFromPerambulator(FMat &action); + * virtual ~DistilVectorsFromPerambulator(void) = default; + * void makeRho(FermionField &rhoOut, + * const FermionField &evec, const Complex &noises); + * void makePhi(FermionField &phiOut, + * const FermionField &evec, const SpinVector &Perambulators); + * private: + * GridBase *fGrid_, *frbGrid_, *gGrid_; + * bool is5d_; + * FermionField src_o_, sol_e_, sol_o_, tmp_, tmp5_; + * }; + * + * */ + + +/****************************************************************************** + * * * A2AVectorsSchurDiagTwo template implementation * + * * ******************************************************************************/ +/* + * + * template + * A2AVectorsSchurDiagTwo::A2AVectorsSchurDiagTwo(FMat &action) + * : action_(action) + * , fGrid_(action_.FermionGrid()) + * , frbGrid_(action_.FermionRedBlackGrid()) + * , gGrid_(action_.GaugeGrid()) + * , src_o_(frbGrid_) + * , sol_e_(frbGrid_) + * , sol_o_(frbGrid_) + * , tmp_(frbGrid_) + * , tmp5_(fGrid_) + * , op_(action_) + * {} + * + * template + * void A2AVectorsSchurDiagTwo::makeRho(FermionField &rhoOut, const FermionField &evec, const Complex &noises) + * { + * + * LatticeSpinColourVector tmp2(grid4d); + * LatticeSpinColourVector tmp3d(grid3d); + * LatticeColourVector tmp3d_nospin(grid3d); + * LatticeColourVector tmp_nospin(grid4d); + * + + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + if(full_tdil) dt=tsrc; //TODO: this works for now, as longs as tsrc=0, but will crash otherwise! + for (int ds = 0; ds < Ns; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + sources_tsrc[vecindex] = zero; + tmp3d_nospin = zero; + for (int it = dt; it < Nt; it += TI){ + if( it >= Ntfirst && it < Ntfirst + Ntlocal ) { + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced + tmp3d_nospin = eig[it].evec[ik] * noises[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + tmp3d=zero; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=zero; +#ifdef USE_LOCAL_SLICES + InsertSliceLocal(tmp3d,tmp2,0,it-Ntfirst,Grid::QCD::Tdir); +#else + InsertSlice(tmp3d,tmp2,it,Grid::QCD::Tdir); +#endif + sources_tsrc[vecindex] += tmp2; + } + } + } + } + } + } + } + } + + +} + +*/ + + + /****************************************************************************** * DistilVectors * ******************************************************************************/ From 2343e621e6ddfb2a356f53dd0178b16a6703c169 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 18 Jan 2019 13:32:27 +0000 Subject: [PATCH 004/347] Bananas --- Hadrons/Modules/MDistil/LapEvec.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index f0c93607..f52f76f9 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -38,6 +38,7 @@ BEGIN_HADRONS_NAMESPACE /****************************************************************************** * LapEvec * + ***** TEST ***** ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) From f0f1ba0307ff1bee0b0972d241aeb16298eff87b Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 18 Jan 2019 15:58:10 +0000 Subject: [PATCH 005/347] uses evec4d now --- Hadrons/Modules/MDistil/DistilVectors.hpp | 100 +++++++++++----------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 40df28e0..272380d1 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -8,59 +8,60 @@ BEGIN_HADRONS_NAMESPACE /****************************************************************************** - * * Class to generate Distillation $\varrho$ and $\varphi$ vectors * - * ******************************************************************************/ + * Class to generate Distillation $\varrho$ and $\varphi$ vectors * + ******************************************************************************/ /* - * template - * class DistilVectorsFromPerambulator - * { - * public: - * FERM_TYPE_ALIASES(FImpl,); - * SOLVER_TYPE_ALIASES(FImpl,); - * public: - * DistilVectorsFromPerambulator(FMat &action); - * virtual ~DistilVectorsFromPerambulator(void) = default; - * void makeRho(FermionField &rhoOut, - * const FermionField &evec, const Complex &noises); - * void makePhi(FermionField &phiOut, - * const FermionField &evec, const SpinVector &Perambulators); - * private: - * GridBase *fGrid_, *frbGrid_, *gGrid_; - * bool is5d_; - * FermionField src_o_, sol_e_, sol_o_, tmp_, tmp5_; - * }; - * - * */ + template + class DistilVectorsFromPerambulator + { + public: + FERM_TYPE_ALIASES(FImpl,); + SOLVER_TYPE_ALIASES(FImpl,); + public: + DistilVectorsFromPerambulator(FMat &action); + virtual ~DistilVectorsFromPerambulator(void) = default; + void makeRho(FermionField &rhoOut, + const FermionField &evec, const Complex &noises); + void makePhi(FermionField &phiOut, + const FermionField &evec, const SpinVector &Perambulators); + private: + GridBase *fGrid_, *frbGrid_, *gGrid_; + bool is5d_; + FermionField src_o_, sol_e_, sol_o_, tmp_, tmp5_; + }; + +*/ /****************************************************************************** - * * * A2AVectorsSchurDiagTwo template implementation * - * * ******************************************************************************/ + * A2AVectorsSchurDiagTwo template implementation * + ******************************************************************************/ /* - * - * template - * A2AVectorsSchurDiagTwo::A2AVectorsSchurDiagTwo(FMat &action) - * : action_(action) - * , fGrid_(action_.FermionGrid()) - * , frbGrid_(action_.FermionRedBlackGrid()) - * , gGrid_(action_.GaugeGrid()) - * , src_o_(frbGrid_) - * , sol_e_(frbGrid_) - * , sol_o_(frbGrid_) - * , tmp_(frbGrid_) - * , tmp5_(fGrid_) - * , op_(action_) - * {} - * - * template - * void A2AVectorsSchurDiagTwo::makeRho(FermionField &rhoOut, const FermionField &evec, const Complex &noises) - * { - * - * LatticeSpinColourVector tmp2(grid4d); - * LatticeSpinColourVector tmp3d(grid3d); - * LatticeColourVector tmp3d_nospin(grid3d); - * LatticeColourVector tmp_nospin(grid4d); - * + +template +A2AVectorsSchurDiagTwo::A2AVectorsSchurDiagTwo(FMat &action) +: action_(action) +, fGrid_(action_.FermionGrid()) +, frbGrid_(action_.FermionRedBlackGrid()) +, gGrid_(action_.GaugeGrid()) +, src_o_(frbGrid_) +, sol_e_(frbGrid_) +, sol_o_(frbGrid_) +, tmp_(frbGrid_) +, tmp5_(fGrid_) +, op_(action_) +{} + +template +void A2AVectorsSchurDiagTwo::makeRho(FermionField &rhoOut, const FermionField &evec, const Complex &noises) +{ + + LatticeSpinColourVector tmp2(grid4d); + LatticeSpinColourVector tmp3d(grid3d); + LatticeColourVector tmp3d_nospin(grid3d); + LatticeColourVector tmp_nospin(grid4d); + LatticeColourVector evec3d(grid3d); + for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { @@ -74,7 +75,8 @@ BEGIN_HADRONS_NAMESPACE if( it >= Ntfirst && it < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced - tmp3d_nospin = eig[it].evec[ik] * noises[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + ExtractSliceLocal(evec3d,eig4d.evec[ik],0,it,3); + tmp3d_nospin = evec3d * noises[inoise][it][ik]()(is)(); //noises do not have to be a spin vector tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; From ced30b61e2d0dce10d121ec26c25c5bddde54cf8 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 18 Jan 2019 16:38:13 +0000 Subject: [PATCH 006/347] added phi vectors - still commented out and does not compile otherwise --- Hadrons/Modules/MDistil/DistilVectors.hpp | 44 ++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 272380d1..59e38009 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -34,12 +34,12 @@ BEGIN_HADRONS_NAMESPACE /****************************************************************************** - * A2AVectorsSchurDiagTwo template implementation * + * DistilVectorsFromPerambulator template implementation * ******************************************************************************/ /* template -A2AVectorsSchurDiagTwo::A2AVectorsSchurDiagTwo(FMat &action) +DistilVectorsFromPerambulator::DistilVectorsFromPerambulator(FMat &action) : action_(action) , fGrid_(action_.FermionGrid()) , frbGrid_(action_.FermionRedBlackGrid()) @@ -53,7 +53,7 @@ A2AVectorsSchurDiagTwo::A2AVectorsSchurDiagTwo(FMat &action) {} template -void A2AVectorsSchurDiagTwo::makeRho(FermionField &rhoOut, const FermionField &evec, const Complex &noises) +void DistilVectorsFromPerambulator::makeRho(FermionField &rhoOut, const FermionField &evec, const Complex &noises) { LatticeSpinColourVector tmp2(grid4d); @@ -62,7 +62,7 @@ void A2AVectorsSchurDiagTwo::makeRho(FermionField &rhoOut, const FermionF LatticeColourVector tmp_nospin(grid4d); LatticeColourVector evec3d(grid3d); - + int vecindex; for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { @@ -98,6 +98,42 @@ void A2AVectorsSchurDiagTwo::makeRho(FermionField &rhoOut, const FermionF } +template +void DistilVectorsFromPerambulator::makePhi(FermionField &rhoOut, const FermionField &evec, const SpinVector &perambulator) +{ + + LatticeSpinColourVector tmp2(grid4d); + LatticeSpinColourVector tmp3d(grid3d); + LatticeColourVector tmp3d_nospin(grid3d); + LatticeColourVector tmp_nospin(grid4d); + LatticeColourVector evec3d(grid3d); + + int vecindex; + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < Ns; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + sinks_tsrc[vecindex] = zero; + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + sink_tslice=zero; + for (int ivec = 0; ivec < nvec; ivec++) { + ExtractSliceLocal(evec3d,eig4d.evec[ivec],0,t,3); + sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); + } +#ifdef USE_LOCAL_SLICES + InsertSliceLocal(sink_tslice,sinks_tsrc[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); +#else + InsertSlice(sink_tslice,sinks_tsrc[vecindex],t,Grid::QCD::Tdir); +#endif + } + } + } + } + } + +} + */ From 0ff410ae1910427c0b4b0bf1cc71c4ea5b8f270a Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 18 Jan 2019 17:47:41 +0000 Subject: [PATCH 007/347] copied perambulato code into PerambLight.hpp --- Hadrons/Modules/MDistil/PerambLight.hpp | 100 +++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 91f776a0..6fe5be72 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -68,6 +68,43 @@ std::vector Tperambulator_l::getOutput(void) template void Tperambulator_l::setup(void) { +/* + std::cout << "Compute perambulator from timeslice " << tsrc << std::endl; + + LatticeSpinColourVector dist_source(grid4d); + LatticeSpinColourVector tmp2(grid4d); + LatticeSpinColourVector tmp3d(grid3d); + LatticeColourVector tmp3d_nospin(grid3d); + LatticeColourVector evec3d(grid3d); + LatticeColourVector tmp_nospin(grid4d); + + LatticeColourVector result_tmp(grid3d); + + LatticeSpinVector peramb_tmp(grid4d); + LatticeFermion result(grid4d); result=zero; //Fermion = SpinColourVector!!! + LatticeFermion result_single_component(grid4d); result_single_component=zero; //Fermion = SpinColourVector!!! + LatticeColourVector result_nospin(grid4d); result_nospin=zero; //Fermion = SpinColourVector!!! + LatticeColourVector result_3d(grid3d); result_3d=zero; //Fermion = SpinColourVector!!! + LatticeFermion result_test(grid3d); result_test=zero; //Fermion = SpinColourVector!!! + + + Real mass=SPar.mass; // TODO Infile + Real M5 =SPar.M5; // TODO Infile + std::cout << "init RBG " << std::endl; + GridRedBlackCartesian RBGrid(grid4d); + std::cout << "init RBG done" << std::endl; + + GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(DPar.Ls,grid4d); + GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(DPar.Ls,grid4d); + + typedef DomainWallFermionR FermionAction; + + FermionAction Dop(Umu,*FGrid,*FrbGrid,*grid4d,RBGrid,mass,M5); + + MdagMLinearOperator HermOp(Dop); + ConjugateGradient CG(SPar.CGPrecision,SPar.MaxIterations); + SchurRedBlackDiagMooeeSolve SchurSolver(CG); +*/ } @@ -75,7 +112,68 @@ void Tperambulator_l::setup(void) template void Tperambulator_l::execute(void) { - +/* + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + if(full_tdil) dt=tsrc; //this works for now, as longs as tsrc=0, but will crash otherwise! + for (int ds = 0; ds < Ns; ds++) { + std::cout << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; + dist_source = zero; + tmp3d_nospin = zero; + evec3d = zero; + for (int it = dt; it < Nt; it += TI){ + if( it >= Ntfirst && it < Ntfirst + Ntlocal ) { + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced + std::cout << "LapH source vector from noise " << it << " and dilution component (d_k,d_t,d_alpha) : (" << ik << ","<< is << ")" << std::endl; + ExtractSliceLocal(evec3d,eig4d.evec[ik],0,it,3); + tmp3d_nospin = evec3d * noises[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + tmp3d=zero; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=zero; +#ifdef USE_LOCAL_SLICES + InsertSliceLocal(tmp3d,tmp2,0,it-Ntfirst,Grid::QCD::Tdir); +#else + InsertSlice(tmp3d,tmp2,it,Grid::QCD::Tdir); +#endif + dist_source += tmp2; + } + } + } + } + std::cout << "Inversion for noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; + result=zero; + LatticeFermion src5(FGrid); + LatticeFermion sol5(FGrid); + Dop.ImportPhysicalFermionSource(dist_source,src5); + SchurSolver(Dop,src5,sol5); + Dop.ExportPhysicalFermionSolution(sol5,result); //These are the meson sinks + if (compute_current_sink) + current_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; + std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; + for (int is = 0; is < Ns; is++) { + result_nospin = peekSpin(result,is); + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { +#ifdef USE_LOCAL_SLICES + ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); +#else + ExtractSlice(result_3d,result_nospin,t,3); +#endif + for (int ivec = 0; ivec < nvec; ivec++) { + ExtractSliceLocal(evec3d,eig4d.evec[ivec],0,t,3); + pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); + } + } + } + } + } + std::cout << "perambulator done" << std::endl; + perambulator.SliceShare( grid3d, grid4d ); + + // THIS IS WHERE WE WANT TO SAVE THE PERAMBULATORS TO DISK + perambulator.WriteTemporary(std::string(pszPerambPack)); +*/ } END_MODULE_NAMESPACE From c93a43f1583e88ab1bf2dd454645bb236b59fe77 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 21 Jan 2019 10:39:28 +0000 Subject: [PATCH 008/347] Added test program --- tests/hadrons/Test_hadrons_distil.cc | 253 +++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 tests/hadrons/Test_hadrons_distil.cc diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc new file mode 100644 index 00000000..cb20b03e --- /dev/null +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -0,0 +1,253 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Tests/Hadrons/Test_hadrons_distil.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 +#include + +using namespace Grid; +using namespace Hadrons; + +// This is copied from the free propagator test +// Just used as an example - will be deleted + +void free_prop(Application &application) +{ + std::vector flavour = {"h"}; //{"l", "s", "c1", "c2", "c3"}; + std::vector mass = {.2}; //{.01, .04, .2 , .25 , .3 }; + std::vector lepton_flavour = {"mu"}; + std::vector lepton_mass = {.2}; + + unsigned int nt = GridDefaultLatt()[Tp]; + + // global parameters + Application::GlobalPar globalPar; + globalPar.trajCounter.start = 1500; + globalPar.trajCounter.end = 1520; + globalPar.trajCounter.step = 20; + globalPar.runId = "test"; + application.setPar(globalPar); + // gauge field + application.createModule("gauge"); + // unit gauge field for lepton + application.createModule("free_gauge"); + // pt source + MSource::Point::Par ptPar; + ptPar.position = "0 0 0 0"; + application.createModule("pt", ptPar); + // sink + MSink::Point::Par sinkPar; + sinkPar.mom = "0 0 0"; + application.createModule("sink", sinkPar); + + // set fermion boundary conditions to be periodic space, antiperiodic time. + std::string boundary = "1 1 1 -1"; + + + //Propagators from FFT and Feynman rules + for (unsigned int i = 0; i < lepton_mass.size(); ++i) + { + //DWF actions + MAction::DWF::Par actionPar_lep; + actionPar_lep.gauge = "free_gauge"; + actionPar_lep.Ls = 8; + actionPar_lep.M5 = 1.8; + actionPar_lep.mass = lepton_mass[i]; + actionPar_lep.boundary = boundary; + application.createModule("free_DWF_" + lepton_flavour[i], actionPar_lep); + + //DWF free propagators + MFermion::FreeProp::Par freePar; + freePar.source = "pt"; + freePar.action = "free_DWF_" + lepton_flavour[i]; + freePar.twist = "0 0 0 0.5"; + freePar.mass = lepton_mass[i]; + application.createModule("Lpt_" + lepton_flavour[i], + freePar); + + //Wilson actions + MAction::Wilson::Par actionPar_lep_W; + actionPar_lep_W.gauge = "free_gauge"; + actionPar_lep_W.mass = lepton_mass[i]; + actionPar_lep_W.boundary = boundary; + application.createModule("free_W_" + lepton_flavour[i], actionPar_lep_W); + + //Wilson free propagators + MFermion::FreeProp::Par freePar_W; + freePar_W.source = "pt"; + freePar_W.action = "free_W_" + lepton_flavour[i]; + freePar_W.twist = "0 0 0 0.5"; + freePar_W.mass = lepton_mass[i]; + application.createModule("W_Lpt_" + lepton_flavour[i], + freePar_W); + + + } + + + + //Propagators from inversion + for (unsigned int i = 0; i < flavour.size(); ++i) + { + //DWF actions + MAction::DWF::Par actionPar; + actionPar.gauge = "gauge"; + actionPar.Ls = 8; + actionPar.M5 = 1.8; + actionPar.mass = mass[i]; + actionPar.boundary = boundary; + application.createModule("DWF_" + flavour[i], actionPar); + + // solvers + MSolver::RBPrecCG::Par solverPar; + solverPar.action = "DWF_" + flavour[i]; + solverPar.residual = 1.0e-8; + solverPar.maxIteration = 10000; + application.createModule("CG_" + flavour[i], + solverPar); + + //DWF propagators + MFermion::GaugeProp::Par quarkPar; + quarkPar.solver = "CG_" + flavour[i]; + quarkPar.source = "pt"; + application.createModule("Qpt_" + flavour[i], + quarkPar); + + + + //Wilson actions + MAction::Wilson::Par actionPar_W; + actionPar_W.gauge = "gauge"; + actionPar_W.mass = mass[i]; + actionPar_W.boundary = boundary; + application.createModule("W_" + flavour[i], actionPar_W); + + + // solvers + MSolver::RBPrecCG::Par solverPar_W; + solverPar_W.action = "W_" + flavour[i]; + solverPar_W.residual = 1.0e-8; + solverPar_W.maxIteration = 10000; + application.createModule("W_CG_" + flavour[i], + solverPar_W); + + //Wilson propagators + MFermion::GaugeProp::Par quarkPar_W; + quarkPar_W.solver = "W_CG_" + flavour[i]; + quarkPar_W.source = "pt"; + application.createModule("W_Qpt_" + flavour[i], + quarkPar_W); + + } + + + //2pt contraction for Propagators from FFT and Feynman rules + for (unsigned int i = 0; i < lepton_flavour.size(); ++i) + for (unsigned int j = i; j < lepton_flavour.size(); ++j) + { + //2pt function contraction DWF + MContraction::Meson::Par freemesPar; + freemesPar.output = "2pt_free/DWF_L_pt_" + lepton_flavour[i] + lepton_flavour[j]; + freemesPar.q1 = "Lpt_" + lepton_flavour[i]; + freemesPar.q2 = "Lpt_" + lepton_flavour[j]; + freemesPar.gammas = "(Gamma5 Gamma5)"; + freemesPar.sink = "sink"; + application.createModule("meson_L_pt_" + + lepton_flavour[i] + lepton_flavour[j], + freemesPar); + + //2pt function contraction Wilson + MContraction::Meson::Par freemesPar_W; + freemesPar_W.output = "2pt_free/W_L_pt_" + lepton_flavour[i] + lepton_flavour[j]; + freemesPar_W.q1 = "W_Lpt_" + lepton_flavour[i]; + freemesPar_W.q2 = "W_Lpt_" + lepton_flavour[j]; + freemesPar_W.gammas = "(Gamma5 Gamma5)"; + freemesPar_W.sink = "sink"; + application.createModule("W_meson_L_pt_" + + lepton_flavour[i] + lepton_flavour[j], + freemesPar_W); + + } + + //2pt contraction for Propagators from inverion + for (unsigned int i = 0; i < flavour.size(); ++i) + for (unsigned int j = i; j < flavour.size(); ++j) + { + //2pt function contraction DWF + MContraction::Meson::Par mesPar; + mesPar.output = "2pt_free/DWF_pt_" + flavour[i] + flavour[j]; + mesPar.q1 = "Qpt_" + flavour[i]; + mesPar.q2 = "Qpt_" + flavour[j]; + mesPar.gammas = "(Gamma5 Gamma5)"; + mesPar.sink = "sink"; + application.createModule("meson_pt_" + + flavour[i] + flavour[j], + mesPar); + + + //2pt function contraction Wilson + MContraction::Meson::Par mesPar_W; + mesPar_W.output = "2pt_free/W_pt_" + flavour[i] + flavour[j]; + mesPar_W.q1 = "W_Qpt_" + flavour[i]; + mesPar_W.q2 = "W_Qpt_" + flavour[j]; + mesPar_W.gammas = "(Gamma5 Gamma5)"; + mesPar_W.sink = "sink"; + application.createModule("W_meson_pt_" + + flavour[i] + flavour[j], + mesPar_W); + + } +} + +int main(int argc, char *argv[]) +{ + // initialization ////////////////////////////////////////////////////////// + Grid_init(&argc, &argv); + HadronsLogError.Active(GridLogError.isActive()); + HadronsLogWarning.Active(GridLogWarning.isActive()); + HadronsLogMessage.Active(GridLogMessage.isActive()); + HadronsLogIterative.Active(GridLogIterative.isActive()); + HadronsLogDebug.Active(GridLogDebug.isActive()); + LOG(Message) << "Grid initialized" << std::endl; + + // run setup /////////////////////////////////////////////////////////////// + Application application; + + // For now perform free propagator test - replace this with distillation test(s) + free_prop( application ); + + // execution + application.saveParameterFile("test_hadrons_distil.xml"); + application.run(); + + // epilogue + LOG(Message) << "Grid is finalizing now" << std::endl; + Grid_finalize(); + + return EXIT_SUCCESS; +} From b8c106f320cd007afce0bfc8e09eefc62805e8b8 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 21 Jan 2019 16:04:18 +0000 Subject: [PATCH 009/347] working on DistilVectors, initialisation done and compiles --- Hadrons/Modules/MDistil/DistilVectors.hpp | 163 +++++++++++++++++++++- 1 file changed, 156 insertions(+), 7 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 59e38009..e9966f9d 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -4,8 +4,96 @@ #include #include #include +#include +#include +#include +#include BEGIN_HADRONS_NAMESPACE +/* +template +class Perambulator : Serializable{ + // TODO: The next line makes friends across all combinations + // (not much of a problem given all public anyway ...) + // FYI, the bug here was that I forgot that the friend is templated + template friend std::ostream & operator<<(std::ostream &os, const Perambulator& p); +protected: +public: + GRID_SERIALIZABLE_CLASS_MEMBERS( Perambulator, + std::string, ID, // Allows owner to specialise + std::string, Provenance, // For info only + std::vector, dimensions, + std::vector, perambulator, + // Following items are redundant, but useful + int, nd, // Number of dimensions + size_t, NumElements); // Number of elements +protected: + // Constructor common code + inline void ConstructCommon(const int * Dimensions) { + assert(nd > 0); + dimensions.resize(nd); + NumElements = 1; + for(int i = 0 ; i < nd ; i++) { + assert(Dimensions[i] > 0); + NumElements *= (size_t) Dimensions[i]; + dimensions[i] = Dimensions[i]; + } + //const LatticeObj perambulatorDefault; + perambulator.resize(NumElements);//,perambulatorDefault); + } +public: + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions) + : nd {(int) Dimensions.size()} { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions, const std::string sID) + : nd {(int) Dimensions.size()}, ID(sID) { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions, const std::string sID, const std::string sProvenance) + : nd {(int) Dimensions.size()}, ID(sID), Provenance(sProvenance) { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as individual parameters + // FYI: The caller is free to ignore the names and use the indices however they see fit + inline Perambulator(int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) { + int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; + nd = sizeof(Dimensions)/sizeof(Dimensions[0]); + while( nd > 1 && Dimensions[nd-1] == 1 ) + nd--; + ConstructCommon( Dimensions ); + } + + inline LatticeObj & operator()(size_t count, const int * Coord) { + assert( count == nd ); + assert( Coord ); + size_t idx = 0; + // C memory order (???) + for( int d = 0 ; d < nd ; d++ ) { + assert( Coord[d] < dimensions[d] ); + idx *= (size_t) dimensions[d]; + idx += (size_t) Coord[d]; + } + return perambulator[idx]; + } + + inline LatticeObj & operator()(const std::vector Coord) { + return operator()(Coord.size(), &Coord[0]); + } + + inline LatticeObj & operator()(int idxNoise, int idxEvec=0, int idxTime=0, int idxSpin=0, int I_k=0, int I_t=0, int I_s=0) { + int MyIndex[]={idxNoise,idxEvec,idxTime,idxSpin,I_k,I_t,I_s}; + int i = sizeof(MyIndex)/sizeof(MyIndex[0]); + assert( i >= nd ); + while( i > nd ) + assert(MyIndex[--i] == 0); + return operator()(i, MyIndex); + } +}; +*/ /****************************************************************************** * Class to generate Distillation $\varrho$ and $\varphi$ vectors * @@ -36,8 +124,8 @@ BEGIN_HADRONS_NAMESPACE /****************************************************************************** * DistilVectorsFromPerambulator template implementation * ******************************************************************************/ -/* +/* template DistilVectorsFromPerambulator::DistilVectorsFromPerambulator(FMat &action) : action_(action) @@ -55,7 +143,21 @@ DistilVectorsFromPerambulator::DistilVectorsFromPerambulator(FMat &action template void DistilVectorsFromPerambulator::makeRho(FermionField &rhoOut, const FermionField &evec, const Complex &noises) { + // INPUT::::::::: + Grid::Hadrons::EigenPack eig4d; + std::vector > > noises; + // OUTPUT:::::::: + std::vector sources_tsrc; + { + LatticeSpinColourVector vecModel(grid4d); + sources_tsrc.resize(nnoise*LI*Ns*Nt_inv,vecModel); + } + + + + + // TEMPORARY::::::::: LatticeSpinColourVector tmp2(grid4d); LatticeSpinColourVector tmp3d(grid3d); LatticeColourVector tmp3d_nospin(grid3d); @@ -102,6 +204,21 @@ template void DistilVectorsFromPerambulator::makePhi(FermionField &rhoOut, const FermionField &evec, const SpinVector &perambulator) { + // INPUT::::::::: + Perambulator perambulator; + Grid::Hadrons::EigenPack eig4d; + + // OUTPUT:::::::: + std::vector sinks_tsrc; + { + LatticeSpinColourVector vecModel(grid4d); + sinks_tsrc.resize(nnoise*LI*Ns*Nt_inv,vecModel); + } + + + + + // TEMPORARY::::::::: LatticeSpinColourVector tmp2(grid4d); LatticeSpinColourVector tmp3d(grid3d); LatticeColourVector tmp3d_nospin(grid3d); @@ -134,8 +251,8 @@ void DistilVectorsFromPerambulator::makePhi(FermionField &rhoOut, const F } -*/ +*/ /****************************************************************************** @@ -147,12 +264,17 @@ class DistilVectorsPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(DistilVectorsPar, - unsigned int, i); + std::string, noise, + std::string, perambulator, + std::string, eigenPack, + bool, multiFile); }; template class TDistilVectors: public Module { +public: + FERM_TYPE_ALIASES(FImpl,); public: // constructor TDistilVectors(const std::string name); @@ -183,14 +305,18 @@ template std::vector TDistilVectors::getInput(void) { std::vector in; - + + in.push_back(par().noise); + in.push_back(par().perambulator); + in.push_back(par().eigenPack); + return in; } template std::vector TDistilVectors::getOutput(void) { - std::vector out = {getName()}; + std::vector out = {getName() + "_rho", getName() + "_phi"}; return out; } @@ -199,14 +325,37 @@ std::vector TDistilVectors::getOutput(void) template void TDistilVectors::setup(void) { - + auto &noise = envGet(std::vector>>, par().noise); + + envCreate(std::vector, getName() + "_rho", 1, + noise.size(), envGetGrid(FermionField)); + envCreate(std::vector, getName() + "_phi", 1, + noise.size(), envGetGrid(FermionField)); + + + envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(env().getGrid())); + envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(env().getGrid())); + //GridCartesian * const grid3d; + //ExtractSlice(grid3d,env().getGrid(),0,3); + //envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + /*LatticeSpinColourVector tmp3d(grid3d); + LatticeColourVector tmp3d_nospin(grid3d); + LatticeColourVector evec3d(grid3d); + */ } // execution /////////////////////////////////////////////////////////////////// template void TDistilVectors::execute(void) { - + + auto &noise = envGet(std::vector>>, par().noise); + auto &perambulator = envGet(std::vector, par().perambulator); + //auto &perambulator = envGet(Perambulator, par().noise); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + auto &rho = envGet(std::vector, getName() + "_rho"); + auto &phi = envGet(std::vector, getName() + "_phi"); + } END_MODULE_NAMESPACE From 79d533550d71994bea88af7448d2dfab1855b01c Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 21 Jan 2019 16:45:31 +0000 Subject: [PATCH 010/347] continued on DistilVectors.hpp --- Hadrons/Modules/MDistil/DistilVectors.hpp | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index e9966f9d..6615217d 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -356,6 +356,51 @@ void TDistilVectors::execute(void) auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); + + envGetTmp(LatticeSpinColourVector, tmp2); + /*LatticeSpinColourVector tmp2(grid4d); + LatticeSpinColourVector tmp3d(grid3d); + LatticeColourVector tmp3d_nospin(grid3d); + LatticeColourVector tmp_nospin(grid4d); + LatticeColourVector evec3d(grid3d);*/ + + int tsrc=0; + int nnoise=1; + int LI=6; + int Ns=4; + int Nt_inv=1; + int Nt=64; + int nvec=6; + bool full_tdil=true; + + int vecindex; + /* for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + if(full_tdil) dt=tsrc; //TODO: this works for now, as longs as tsrc=0, but will crash otherwise! + for (int ds = 0; ds < Ns; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + sources_tsrc[vecindex] = zero; + tmp3d_nospin = zero; + for (int it = dt; it < Nt; it += TI){ + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced + ExtractSliceLocal(evec3d,eig4d.evec[ik],0,it,3); + tmp3d_nospin = evec3d * noises[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + tmp3d=zero; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=zero; + InsertSlice(tmp3d,tmp2,it,Grid::QCD::Tdir); + rho[vecindex] += tmp2; + } + } + } + } + } + } + } +*/ + } END_MODULE_NAMESPACE From 81bb361299b08bc21f6920ac42a853276c2ab5fe Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 22 Jan 2019 13:19:39 +0000 Subject: [PATCH 011/347] Test program ready --- Hadrons/Modules/MDistil/LapEvec.hpp | 23 ++++- tests/hadrons/Test_hadrons_distil.cc | 123 ++++++++++++++++++++++++--- 2 files changed, 133 insertions(+), 13 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index f52f76f9..fc1fae61 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -36,17 +36,34 @@ BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MDistil) + /****************************************************************************** * LapEvec * ***** TEST ***** ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) + class LapEvecPar: Serializable { public: - GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, - unsigned int, i); + GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, + // StoutParameters, + int, steps, + double, parm, + // ChebyshevParameters, + int, PolyOrder, + double, alpha, + double, beta, + // LanczosParameters, + int, Nstart, + int, Nvec, + int, Nk, + int, Nm, // Not currently used + int, Np, + int, MaxIt, + int, MinRes, + double, resid); }; template diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index cb20b03e..1d16b086 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -33,8 +33,10 @@ using namespace Grid; using namespace Hadrons; +///////////////////////////////////////////////////////////// // This is copied from the free propagator test // Just used as an example - will be deleted +///////////////////////////////////////////////////////////// void free_prop(Application &application) { @@ -105,12 +107,8 @@ void free_prop(Application &application) freePar_W.mass = lepton_mass[i]; application.createModule("W_Lpt_" + lepton_flavour[i], freePar_W); - - } - - - + //Propagators from inversion for (unsigned int i = 0; i < flavour.size(); ++i) { @@ -138,8 +136,6 @@ void free_prop(Application &application) application.createModule("Qpt_" + flavour[i], quarkPar); - - //Wilson actions MAction::Wilson::Par actionPar_W; actionPar_W.gauge = "gauge"; @@ -147,7 +143,6 @@ void free_prop(Application &application) actionPar_W.boundary = boundary; application.createModule("W_" + flavour[i], actionPar_W); - // solvers MSolver::RBPrecCG::Par solverPar_W; solverPar_W.action = "W_" + flavour[i]; @@ -220,12 +215,108 @@ void free_prop(Application &application) application.createModule("W_meson_pt_" + flavour[i] + flavour[j], mesPar_W); - } } +///////////////////////////////////////////////////////////// +// Test creation of laplacian eigenvectors +///////////////////////////////////////////////////////////// + +void test_LapEvec(Application &application) +{ + const unsigned int nt = GridDefaultLatt()[Tp]; + + // global parameters + Application::GlobalPar globalPar; + globalPar.trajCounter.start = 1500; + globalPar.trajCounter.end = 1520; + globalPar.trajCounter.step = 20; + globalPar.runId = "test"; + application.setPar(globalPar); + // gauge field + application.createModule("gauge"); + // Now make an instance of the LapEvec object + application.createModule("LapEvecInstance"); +} + +///////////////////////////////////////////////////////////// +// Felix, this is your test here +///////////////////////////////////////////////////////////// + +void test_FelixRenameMe(Application &application) +{ + const unsigned int nt = GridDefaultLatt()[Tp]; + + // global parameters + Application::GlobalPar globalPar; + globalPar.trajCounter.start = 1500; + globalPar.trajCounter.end = 1520; + globalPar.trajCounter.step = 20; + globalPar.runId = "test"; + application.setPar(globalPar); + // gauge field + application.createModule("gauge"); + // Now make an instance of the LapEvec object + application.createModule("DistilVectorsInstance"); +} + +bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) +{ + if( bGobbleWhiteSpace ) + while( std::isspace(static_cast(*pstr)) ) + pstr++; + const char * p = pstr; + bool bMinus = false; + char c = * p++; + if( c == '+' ) + c = * p++; + else if( c == '-' ) { + bMinus = true; + c = * p++; + } + int n = c - '0'; + if( n < 0 || n > 9 ) + return false; + while( * p >= '0' && * p <= '9' ) { + n = n * 10 + ( * p ) - '0'; + p++; + } + if( bMinus ) + n *= -1; + ri = n; + pstr = p; + return true; +} + int main(int argc, char *argv[]) { + // Decode command-line parameters. 1st one is which test to run + int iTestNum = 2; + + for(int i = 1 ; i < argc ; i++ ) { + std::cout << "argv[" << i << "]=\"" << argv[i] << "\"" << std::endl; + const char * p = argv[i]; + if( * p == '/' || * p == '-' ) { + p++; + char c = * p++; + switch(toupper(c)) { + case 'T': + if( bNumber( iTestNum, p ) ) { + std::cout << "Test " << iTestNum << " requested"; + if( * p ) + std::cout << " (ignoring trailer \"" << p << "\")"; + std::cout << std::endl; + } + else + std::cout << "Invalid test \"" << &argv[i][2] << "\"" << std::endl; + break; + default: + std::cout << "Ignoring switch \"" << &argv[i][1] << "\"" << std::endl; + break; + } + } + } + // initialization ////////////////////////////////////////////////////////// Grid_init(&argc, &argv); HadronsLogError.Active(GridLogError.isActive()); @@ -239,7 +330,19 @@ int main(int argc, char *argv[]) Application application; // For now perform free propagator test - replace this with distillation test(s) - free_prop( application ); + LOG(Message) << "====== Creating xml for test " << iTestNum << " ======" << std::endl; + switch(iTestNum) { + case 0: + free_prop( application ); + break; + case 1: + test_LapEvec( application ); + break; + default: // 2 + test_FelixRenameMe( application ); + break; + } + LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; // execution application.saveParameterFile("test_hadrons_distil.xml"); From b1c27a141dcc6deff75217b397092b80f1a13fcb Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 22 Jan 2019 13:27:51 +0000 Subject: [PATCH 012/347] DistilVectors complete and compiling - not tested at all! --- Hadrons/Modules/MDistil/DistilVectors.hpp | 103 +++++++++++++++------- 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 6615217d..1c2b92ba 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -10,7 +10,7 @@ #include BEGIN_HADRONS_NAMESPACE -/* + template class Perambulator : Serializable{ // TODO: The next line makes friends across all combinations @@ -93,7 +93,7 @@ public: return operator()(i, MyIndex); } }; -*/ + /****************************************************************************** * Class to generate Distillation $\varrho$ and $\varphi$ vectors * @@ -332,16 +332,25 @@ void TDistilVectors::setup(void) envCreate(std::vector, getName() + "_phi", 1, noise.size(), envGetGrid(FermionField)); - - envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(env().getGrid())); - envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(env().getGrid())); - //GridCartesian * const grid3d; - //ExtractSlice(grid3d,env().getGrid(),0,3); - //envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - /*LatticeSpinColourVector tmp3d(grid3d); - LatticeColourVector tmp3d_nospin(grid3d); - LatticeColourVector evec3d(grid3d); - */ + + GridCartesian * grid4d = env().getGrid(); + std::vector latt_size = GridDefaultLatt(); + std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); + std::vector mpi_layout = GridDefaultMpi(); + std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); + latt_size[Nd-1] = 1; + simd_layout_3.push_back( 1 ); + mpi_layout[Nd-1] = 1; + GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + + + envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeSpinColourVector, "sink4d",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); } // execution /////////////////////////////////////////////////////////////////// @@ -350,19 +359,25 @@ void TDistilVectors::execute(void) { auto &noise = envGet(std::vector>>, par().noise); - auto &perambulator = envGet(std::vector, par().perambulator); - //auto &perambulator = envGet(Perambulator, par().noise); + //auto &perambulator = envGet(std::vector, par().perambulator); + auto &perambulator = envGet(Perambulator, par().noise); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); envGetTmp(LatticeSpinColourVector, tmp2); - /*LatticeSpinColourVector tmp2(grid4d); - LatticeSpinColourVector tmp3d(grid3d); - LatticeColourVector tmp3d_nospin(grid3d); - LatticeColourVector tmp_nospin(grid4d); - LatticeColourVector evec3d(grid3d);*/ + envGetTmp(LatticeColourVector, tmp_nospin); + envGetTmp(LatticeSpinColourVector, tmp3d); + envGetTmp(LatticeColourVector, tmp3d_nospin); + envGetTmp(LatticeSpinColourVector, sink_tslice); + envGetTmp(LatticeSpinColourVector, sink4d); + envGetTmp(LatticeColourVector, evec3d); + + GridCartesian * grid4d = env().getGrid(); + + int Ntlocal = grid4d->LocalDimensions()[3]; + int Ntfirst = grid4d->LocalStarts()[3]; int tsrc=0; int nnoise=1; @@ -370,28 +385,31 @@ void TDistilVectors::execute(void) int Ns=4; int Nt_inv=1; int Nt=64; + int TI=64; int nvec=6; bool full_tdil=true; int vecindex; - /* for (int inoise = 0; inoise < nnoise; inoise++) { + for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { if(full_tdil) dt=tsrc; //TODO: this works for now, as longs as tsrc=0, but will crash otherwise! for (int ds = 0; ds < Ns; ds++) { vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; - sources_tsrc[vecindex] = zero; + rho[vecindex] = zero; tmp3d_nospin = zero; for (int it = dt; it < Nt; it += TI){ - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced - ExtractSliceLocal(evec3d,eig4d.evec[ik],0,it,3); - tmp3d_nospin = evec3d * noises[inoise][it][ik]()(is)(); //noises do not have to be a spin vector - tmp3d=zero; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=zero; - InsertSlice(tmp3d,tmp2,it,Grid::QCD::Tdir); - rho[vecindex] += tmp2; + if( it >= Ntfirst && it < Ntfirst + Ntlocal ) { + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced + ExtractSliceLocal(evec3d,epack.evec[ik],0,it,3); + tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + tmp3d=zero; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=zero; + InsertSliceLocal(tmp3d,tmp2,0,it-Ntfirst,Grid::QCD::Tdir); + rho[vecindex] += tmp2; + } } } } @@ -399,7 +417,30 @@ void TDistilVectors::execute(void) } } } -*/ + + + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < Ns; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + phi[vecindex] = zero; + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + sink_tslice=zero; + for (int ivec = 0; ivec < nvec; ivec++) { + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + // sink_tslice += evec3d * perambulator[inoise + nnoise * dk + nnoise * LI * dt + nnoise*LI*Nt_inv * t + nnoise*LI*Nt_inv*Nt* ivec]()(ds)(); + sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); + } + //InsertSliceLocal(sink_tslice,sink4d,0,t-Ntfirst,Grid::QCD::Tdir); + //phi[vecindex]+=sink4d; //DIFFERENT TYPES???? + InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); + } + } + } + } + } + } From 46b05aa9c539c0df95fc0891e4b8587eae490423 Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 22 Jan 2019 13:48:44 +0000 Subject: [PATCH 013/347] cleaned up, deleted commented out old code --- Hadrons/Modules/MDistil/DistilVectors.hpp | 163 ---------------------- 1 file changed, 163 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 1c2b92ba..c5a387bf 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -95,166 +95,6 @@ public: }; -/****************************************************************************** - * Class to generate Distillation $\varrho$ and $\varphi$ vectors * - ******************************************************************************/ -/* - template - class DistilVectorsFromPerambulator - { - public: - FERM_TYPE_ALIASES(FImpl,); - SOLVER_TYPE_ALIASES(FImpl,); - public: - DistilVectorsFromPerambulator(FMat &action); - virtual ~DistilVectorsFromPerambulator(void) = default; - void makeRho(FermionField &rhoOut, - const FermionField &evec, const Complex &noises); - void makePhi(FermionField &phiOut, - const FermionField &evec, const SpinVector &Perambulators); - private: - GridBase *fGrid_, *frbGrid_, *gGrid_; - bool is5d_; - FermionField src_o_, sol_e_, sol_o_, tmp_, tmp5_; - }; - -*/ - - -/****************************************************************************** - * DistilVectorsFromPerambulator template implementation * - ******************************************************************************/ - -/* -template -DistilVectorsFromPerambulator::DistilVectorsFromPerambulator(FMat &action) -: action_(action) -, fGrid_(action_.FermionGrid()) -, frbGrid_(action_.FermionRedBlackGrid()) -, gGrid_(action_.GaugeGrid()) -, src_o_(frbGrid_) -, sol_e_(frbGrid_) -, sol_o_(frbGrid_) -, tmp_(frbGrid_) -, tmp5_(fGrid_) -, op_(action_) -{} - -template -void DistilVectorsFromPerambulator::makeRho(FermionField &rhoOut, const FermionField &evec, const Complex &noises) -{ - // INPUT::::::::: - Grid::Hadrons::EigenPack eig4d; - std::vector > > noises; - - // OUTPUT:::::::: - std::vector sources_tsrc; - { - LatticeSpinColourVector vecModel(grid4d); - sources_tsrc.resize(nnoise*LI*Ns*Nt_inv,vecModel); - } - - - - - // TEMPORARY::::::::: - LatticeSpinColourVector tmp2(grid4d); - LatticeSpinColourVector tmp3d(grid3d); - LatticeColourVector tmp3d_nospin(grid3d); - LatticeColourVector tmp_nospin(grid4d); - LatticeColourVector evec3d(grid3d); - - int vecindex; - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - if(full_tdil) dt=tsrc; //TODO: this works for now, as longs as tsrc=0, but will crash otherwise! - for (int ds = 0; ds < Ns; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; - sources_tsrc[vecindex] = zero; - tmp3d_nospin = zero; - for (int it = dt; it < Nt; it += TI){ - if( it >= Ntfirst && it < Ntfirst + Ntlocal ) { - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced - ExtractSliceLocal(evec3d,eig4d.evec[ik],0,it,3); - tmp3d_nospin = evec3d * noises[inoise][it][ik]()(is)(); //noises do not have to be a spin vector - tmp3d=zero; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=zero; -#ifdef USE_LOCAL_SLICES - InsertSliceLocal(tmp3d,tmp2,0,it-Ntfirst,Grid::QCD::Tdir); -#else - InsertSlice(tmp3d,tmp2,it,Grid::QCD::Tdir); -#endif - sources_tsrc[vecindex] += tmp2; - } - } - } - } - } - } - } - } - - -} - -template -void DistilVectorsFromPerambulator::makePhi(FermionField &rhoOut, const FermionField &evec, const SpinVector &perambulator) -{ - - // INPUT::::::::: - Perambulator perambulator; - Grid::Hadrons::EigenPack eig4d; - - // OUTPUT:::::::: - std::vector sinks_tsrc; - { - LatticeSpinColourVector vecModel(grid4d); - sinks_tsrc.resize(nnoise*LI*Ns*Nt_inv,vecModel); - } - - - - - // TEMPORARY::::::::: - LatticeSpinColourVector tmp2(grid4d); - LatticeSpinColourVector tmp3d(grid3d); - LatticeColourVector tmp3d_nospin(grid3d); - LatticeColourVector tmp_nospin(grid4d); - LatticeColourVector evec3d(grid3d); - - int vecindex; - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < Ns; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; - sinks_tsrc[vecindex] = zero; - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - sink_tslice=zero; - for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(evec3d,eig4d.evec[ivec],0,t,3); - sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); - } -#ifdef USE_LOCAL_SLICES - InsertSliceLocal(sink_tslice,sinks_tsrc[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); -#else - InsertSlice(sink_tslice,sinks_tsrc[vecindex],t,Grid::QCD::Tdir); -#endif - } - } - } - } - } - -} - - -*/ - - /****************************************************************************** * DistilVectors * ******************************************************************************/ @@ -429,11 +269,8 @@ void TDistilVectors::execute(void) sink_tslice=zero; for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); - // sink_tslice += evec3d * perambulator[inoise + nnoise * dk + nnoise * LI * dt + nnoise*LI*Nt_inv * t + nnoise*LI*Nt_inv*Nt* ivec]()(ds)(); sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); } - //InsertSliceLocal(sink_tslice,sink4d,0,t-Ntfirst,Grid::QCD::Tdir); - //phi[vecindex]+=sink4d; //DIFFERENT TYPES???? InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); } } From 0a82fae45cd3c57db5e135fdff5b86d9d55d60db Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 22 Jan 2019 15:06:45 +0000 Subject: [PATCH 014/347] moved perambulator definition to shared header file --- Hadrons/DistilVectors.hpp | 124 ++++++++++++++++++++++ Hadrons/Modules/MDistil/DistilVectors.hpp | 84 +-------------- Hadrons/Modules/MDistil/PerambLight.hpp | 90 +++++++--------- 3 files changed, 161 insertions(+), 137 deletions(-) create mode 100644 Hadrons/DistilVectors.hpp diff --git a/Hadrons/DistilVectors.hpp b/Hadrons/DistilVectors.hpp new file mode 100644 index 00000000..54a45f04 --- /dev/null +++ b/Hadrons/DistilVectors.hpp @@ -0,0 +1,124 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/A2AVectors.hpp + +Copyright (C) 2015-2018 + +Author: Antonin Portelli +Author: fionnoh + +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 */ +#ifndef Distil_Vectors_hpp_ +#define Distil_Vectors_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +template +class Perambulator : Serializable{ + // TODO: The next line makes friends across all combinations + // (not much of a problem given all public anyway ...) + // FYI, the bug here was that I forgot that the friend is templated + template friend std::ostream & operator<<(std::ostream &os, const Perambulator& p); +protected: +public: + GRID_SERIALIZABLE_CLASS_MEMBERS( Perambulator, + std::string, ID, // Allows owner to specialise + std::string, Provenance, // For info only + std::vector, dimensions, + std::vector, perambulator, + // Following items are redundant, but useful + int, nd, // Number of dimensions + size_t, NumElements); // Number of elements +protected: + // Constructor common code + inline void ConstructCommon(const int * Dimensions) { + assert(nd > 0); + dimensions.resize(nd); + NumElements = 1; + for(int i = 0 ; i < nd ; i++) { + assert(Dimensions[i] > 0); + NumElements *= (size_t) Dimensions[i]; + dimensions[i] = Dimensions[i]; + } + //const LatticeObj perambulatorDefault; + perambulator.resize(NumElements);//,perambulatorDefault); + } +public: + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions) + : nd {(int) Dimensions.size()} { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions, const std::string sID) + : nd {(int) Dimensions.size()}, ID(sID) { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions, const std::string sID, const std::string sProvenance) + : nd {(int) Dimensions.size()}, ID(sID), Provenance(sProvenance) { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as individual parameters + // FYI: The caller is free to ignore the names and use the indices however they see fit + inline Perambulator(int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) { + int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; + nd = sizeof(Dimensions)/sizeof(Dimensions[0]); + while( nd > 1 && Dimensions[nd-1] == 1 ) + nd--; + ConstructCommon( Dimensions ); + } + + inline LatticeObj & operator()(size_t count, const int * Coord) { + assert( count == nd ); + assert( Coord ); + size_t idx = 0; + // C memory order (???) + for( int d = 0 ; d < nd ; d++ ) { + assert( Coord[d] < dimensions[d] ); + idx *= (size_t) dimensions[d]; + idx += (size_t) Coord[d]; + } + return perambulator[idx]; + } + + inline LatticeObj & operator()(const std::vector Coord) { + return operator()(Coord.size(), &Coord[0]); + } + + inline LatticeObj & operator()(int idxNoise, int idxEvec=0, int idxTime=0, int idxSpin=0, int I_k=0, int I_t=0, int I_s=0) { + int MyIndex[]={idxNoise,idxEvec,idxTime,idxSpin,I_k,I_t,I_s}; + int i = sizeof(MyIndex)/sizeof(MyIndex[0]); + assert( i >= nd ); + while( i > nd ) + assert(MyIndex[--i] == 0); + return operator()(i, MyIndex); + } +}; + + +END_HADRONS_NAMESPACE + +#endif // Distil_Vectors_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index c5a387bf..49e6196e 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -8,92 +8,10 @@ #include #include #include +#include BEGIN_HADRONS_NAMESPACE -template -class Perambulator : Serializable{ - // TODO: The next line makes friends across all combinations - // (not much of a problem given all public anyway ...) - // FYI, the bug here was that I forgot that the friend is templated - template friend std::ostream & operator<<(std::ostream &os, const Perambulator& p); -protected: -public: - GRID_SERIALIZABLE_CLASS_MEMBERS( Perambulator, - std::string, ID, // Allows owner to specialise - std::string, Provenance, // For info only - std::vector, dimensions, - std::vector, perambulator, - // Following items are redundant, but useful - int, nd, // Number of dimensions - size_t, NumElements); // Number of elements -protected: - // Constructor common code - inline void ConstructCommon(const int * Dimensions) { - assert(nd > 0); - dimensions.resize(nd); - NumElements = 1; - for(int i = 0 ; i < nd ; i++) { - assert(Dimensions[i] > 0); - NumElements *= (size_t) Dimensions[i]; - dimensions[i] = Dimensions[i]; - } - //const LatticeObj perambulatorDefault; - perambulator.resize(NumElements);//,perambulatorDefault); - } -public: - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions) - : nd {(int) Dimensions.size()} { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions, const std::string sID) - : nd {(int) Dimensions.size()}, ID(sID) { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions, const std::string sID, const std::string sProvenance) - : nd {(int) Dimensions.size()}, ID(sID), Provenance(sProvenance) { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as individual parameters - // FYI: The caller is free to ignore the names and use the indices however they see fit - inline Perambulator(int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) { - int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; - nd = sizeof(Dimensions)/sizeof(Dimensions[0]); - while( nd > 1 && Dimensions[nd-1] == 1 ) - nd--; - ConstructCommon( Dimensions ); - } - - inline LatticeObj & operator()(size_t count, const int * Coord) { - assert( count == nd ); - assert( Coord ); - size_t idx = 0; - // C memory order (???) - for( int d = 0 ; d < nd ; d++ ) { - assert( Coord[d] < dimensions[d] ); - idx *= (size_t) dimensions[d]; - idx += (size_t) Coord[d]; - } - return perambulator[idx]; - } - - inline LatticeObj & operator()(const std::vector Coord) { - return operator()(Coord.size(), &Coord[0]); - } - - inline LatticeObj & operator()(int idxNoise, int idxEvec=0, int idxTime=0, int idxSpin=0, int I_k=0, int I_t=0, int I_s=0) { - int MyIndex[]={idxNoise,idxEvec,idxTime,idxSpin,I_k,I_t,I_s}; - int i = sizeof(MyIndex)/sizeof(MyIndex[0]); - assert( i >= nd ); - while( i > nd ) - assert(MyIndex[--i] == 0); - return operator()(i, MyIndex); - } -}; - /****************************************************************************** * DistilVectors * diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 6fe5be72..51d565cb 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -1,32 +1,40 @@ -#ifndef Hadrons_MDistil_perambulator_l_hpp_ -#define Hadrons_MDistil_perambulator_l_hpp_ +#ifndef Hadrons_MDistil_PerambLight_hpp_ +#define Hadrons_MDistil_PerambLight_hpp_ #include #include #include +#include +#include +#include +#include +#include BEGIN_HADRONS_NAMESPACE + /****************************************************************************** - * perambulator_l * + * PerambLight * ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) -class perambulator_lPar: Serializable +class PerambLightPar: Serializable { public: - GRID_SERIALIZABLE_CLASS_MEMBERS(perambulator_lPar, - unsigned int, i); + GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, + std::string, noise, + std::string, eigenPack, + bool, multiFile); }; template -class Tperambulator_l: public Module +class TPerambLight: public Module { public: // constructor - Tperambulator_l(const std::string name); + TPerambLight(const std::string name); // destructor - virtual ~Tperambulator_l(void) {}; + virtual ~TPerambLight(void) {}; // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); @@ -36,81 +44,55 @@ public: virtual void execute(void); }; -MODULE_REGISTER_TMP(perambulator_l, Tperambulator_l, MDistil); +MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); /****************************************************************************** - * Tperambulator_l implementation * + * TPerambLight implementation * ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -Tperambulator_l::Tperambulator_l(const std::string name) -: Module(name) +TPerambLight::TPerambLight(const std::string name) +: Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template -std::vector Tperambulator_l::getInput(void) +std::vector TPerambLight::getInput(void) { std::vector in; + + in.push_back(par().noise); + in.push_back(par().eigenPack); return in; } template -std::vector Tperambulator_l::getOutput(void) +std::vector TPerambLight::getOutput(void) { - std::vector out = {getName()}; + std::vector out = {getName() + "_perambulator_light"}; return out; } // setup /////////////////////////////////////////////////////////////////////// template -void Tperambulator_l::setup(void) +void TPerambLight::setup(void) { -/* - std::cout << "Compute perambulator from timeslice " << tsrc << std::endl; - LatticeSpinColourVector dist_source(grid4d); - LatticeSpinColourVector tmp2(grid4d); - LatticeSpinColourVector tmp3d(grid3d); - LatticeColourVector tmp3d_nospin(grid3d); - LatticeColourVector evec3d(grid3d); - LatticeColourVector tmp_nospin(grid4d); + auto &noise = envGet(std::vector>>, par().noise); + + int nvec = 6; + int Nt=64; - LatticeColourVector result_tmp(grid3d); + envCreate(Perambulator, getName() + "_perambulator_light", 1, + noise.size() *nvec*Nt); - LatticeSpinVector peramb_tmp(grid4d); - LatticeFermion result(grid4d); result=zero; //Fermion = SpinColourVector!!! - LatticeFermion result_single_component(grid4d); result_single_component=zero; //Fermion = SpinColourVector!!! - LatticeColourVector result_nospin(grid4d); result_nospin=zero; //Fermion = SpinColourVector!!! - LatticeColourVector result_3d(grid3d); result_3d=zero; //Fermion = SpinColourVector!!! - LatticeFermion result_test(grid3d); result_test=zero; //Fermion = SpinColourVector!!! - - - Real mass=SPar.mass; // TODO Infile - Real M5 =SPar.M5; // TODO Infile - std::cout << "init RBG " << std::endl; - GridRedBlackCartesian RBGrid(grid4d); - std::cout << "init RBG done" << std::endl; - - GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(DPar.Ls,grid4d); - GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(DPar.Ls,grid4d); - - typedef DomainWallFermionR FermionAction; - - FermionAction Dop(Umu,*FGrid,*FrbGrid,*grid4d,RBGrid,mass,M5); - - MdagMLinearOperator HermOp(Dop); - ConjugateGradient CG(SPar.CGPrecision,SPar.MaxIterations); - SchurRedBlackDiagMooeeSolve SchurSolver(CG); -*/ - } // execution /////////////////////////////////////////////////////////////////// template -void Tperambulator_l::execute(void) +void TPerambLight::execute(void) { /* for (int inoise = 0; inoise < nnoise; inoise++) { @@ -180,4 +162,4 @@ END_MODULE_NAMESPACE END_HADRONS_NAMESPACE -#endif // Hadrons_MDistil_perambulator_l_hpp_ +#endif // Hadrons_MDistil_PerambLight_hpp_ From f45d2d5dccfdfd299b75ed2d7bb3313e87003bfe Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 22 Jan 2019 15:52:26 +0000 Subject: [PATCH 015/347] perambLight done, but SliceShare and Write does not work yet --- Hadrons/DistilVectors.hpp | 60 +++++++++++ Hadrons/Modules/MDistil/DistilVectors.hpp | 5 +- Hadrons/Modules/MDistil/PerambLight.hpp | 118 ++++++++++++++++++---- 3 files changed, 162 insertions(+), 21 deletions(-) diff --git a/Hadrons/DistilVectors.hpp b/Hadrons/DistilVectors.hpp index 54a45f04..c26421e8 100644 --- a/Hadrons/DistilVectors.hpp +++ b/Hadrons/DistilVectors.hpp @@ -32,6 +32,8 @@ See the full license in the file "LICENSE" in the top level distribution directo #include #include #include +#include "Grid/lattice/Lattice_peekpoke.h" +#include BEGIN_HADRONS_NAMESPACE @@ -117,7 +119,65 @@ public: return operator()(i, MyIndex); } }; + /* +#define BEGIN_GRID_NAMESPACE namespace Grid { +BEGIN_GRID_NAMESPACE +void CartesianCommunicatorCandidate::SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Buffer, int BufferSize ) +{ + // Work out which dimension is the spread-out dimension + assert(gridLowDim); + assert(gridHighDim); + const int iNumDims{(const int)gridHighDim->_gdimensions.size()}; + assert(iNumDims == gridLowDim->_gdimensions.size()); + int dimSpreadOut = -1; + std::vector coor(iNumDims); + for( int i = 0 ; i < iNumDims ; i++ ) { + coor[i] = gridHighDim->_processor_coor[i]; + if( gridLowDim->_gdimensions[i] != gridHighDim->_gdimensions[i] ) { + assert( dimSpreadOut == -1 ); + assert( gridLowDim->_processors[i] == 1 ); // easiest assumption to make for now + dimSpreadOut = i; + } + } + if( dimSpreadOut != -1 && gridHighDim->_processors[dimSpreadOut] != gridLowDim->_processors[dimSpreadOut] ) { + // Make sure the same number of data elements exist on each slice + const int NumSlices{gridHighDim->_processors[dimSpreadOut] / gridLowDim->_processors[dimSpreadOut]}; + assert(gridHighDim->_processors[dimSpreadOut] == gridLowDim->_processors[dimSpreadOut] * NumSlices); + const int SliceSize{BufferSize/NumSlices}; + CCC_DEBUG_DUMP(Buffer, NumSlices, SliceSize); + assert(BufferSize == SliceSize * NumSlices); +#ifndef USE_LOCAL_SLICES + assert(0); // Can't do this without MPI (should really test whether MPI is defined) +#else + const auto MyRank{gridHighDim->ThisRank()}; + std::vector reqs(0); + int MySlice{coor[dimSpreadOut]}; + char * const _buffer{(char *)Buffer}; + char * const MyData{_buffer + MySlice * SliceSize}; + for(int i = 1; i < NumSlices ; i++ ){ + int SendSlice = ( MySlice + i ) % NumSlices; + int RecvSlice = ( MySlice - i + NumSlices ) % NumSlices; + char * const RecvData{_buffer + RecvSlice * SliceSize}; + coor[dimSpreadOut] = SendSlice; + const auto SendRank{gridHighDim->RankFromProcessorCoor(coor)}; + coor[dimSpreadOut] = RecvSlice; + const auto RecvRank{gridHighDim->RankFromProcessorCoor(coor)}; + std::cout << GridLogMessage << "Send slice " << MySlice << " (" << MyRank << ") to " << SendSlice << " (" << SendRank + << "), receive slice from " << RecvSlice << " (" << RecvRank << ")" << std::endl; + gridHighDim->SendToRecvFromBegin(reqs,MyData,SendRank,RecvData,RecvRank,SliceSize); + //memcpy(RecvData,MyData,SliceSize); // Debug + } + gridHighDim->SendToRecvFromComplete(reqs); + std::cout << GridLogMessage << "Slice data shared." << std::endl; + CCC_DEBUG_DUMP(Buffer, NumSlices, SliceSize); +#endif + } +} + +#define END_GRID_NAMESPACE } + +*/ END_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 49e6196e..9715752c 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -107,7 +107,6 @@ void TDistilVectors::setup(void) envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeSpinColourVector, "sink4d",1,LatticeSpinColourVector(grid4d)); envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); } @@ -117,8 +116,7 @@ void TDistilVectors::execute(void) { auto &noise = envGet(std::vector>>, par().noise); - //auto &perambulator = envGet(std::vector, par().perambulator); - auto &perambulator = envGet(Perambulator, par().noise); + auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); @@ -129,7 +127,6 @@ void TDistilVectors::execute(void) envGetTmp(LatticeSpinColourVector, tmp3d); envGetTmp(LatticeColourVector, tmp3d_nospin); envGetTmp(LatticeSpinColourVector, sink_tslice); - envGetTmp(LatticeSpinColourVector, sink4d); envGetTmp(LatticeColourVector, evec3d); GridCartesian * grid4d = env().getGrid(); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 51d565cb..59d80646 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -88,13 +88,103 @@ void TPerambLight::setup(void) envCreate(Perambulator, getName() + "_perambulator_light", 1, noise.size() *nvec*Nt); + GridCartesian * grid4d = env().getGrid(); + std::vector latt_size = GridDefaultLatt(); + std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); + std::vector mpi_layout = GridDefaultMpi(); + std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); + latt_size[Nd-1] = 1; + simd_layout_3.push_back( 1 ); + mpi_layout[Nd-1] = 1; + GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + + envTmp(LatticeSpinColourVector, "dist_source",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "result",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "result_single_component",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeColourVector, "result_nospin",1,LatticeColourVector(grid4d)); + envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeSpinVector, "peramb_tmp",1,LatticeSpinVector(grid4d)); + } // execution /////////////////////////////////////////////////////////////////// template void TPerambLight::execute(void) { -/* + + auto &noise = envGet(std::vector>>, par().noise); + auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + + GridCartesian * grid4d = env().getGrid(); + std::vector latt_size = GridDefaultLatt(); + std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); + std::vector mpi_layout = GridDefaultMpi(); + std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); + latt_size[Nd-1] = 1; + simd_layout_3.push_back( 1 ); + mpi_layout[Nd-1] = 1; + GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + + LatticeGaugeField Umu(grid4d); + FieldMetaData header; + std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); + std::cout << GridLogMessage << "Loading NERSC configuration from '" << fileName << "'" << std::endl; + NerscIO::readConfiguration(Umu, header, fileName); + std::cout << GridLogMessage << "reading done." << std::endl; + + envGetTmp(LatticeSpinColourVector, dist_source); + envGetTmp(LatticeSpinColourVector, tmp2); + envGetTmp(LatticeSpinColourVector, result); + envGetTmp(LatticeSpinColourVector, result_single_component); + envGetTmp(LatticeColourVector, result_nospin); + envGetTmp(LatticeColourVector, tmp_nospin); + envGetTmp(LatticeSpinColourVector, tmp3d); + envGetTmp(LatticeColourVector, tmp3d_nospin); + envGetTmp(LatticeColourVector, result_3d); + envGetTmp(LatticeColourVector, evec3d); + envGetTmp(LatticeSpinVector, peramb_tmp); + + int Ntlocal = grid4d->LocalDimensions()[3]; + int Ntfirst = grid4d->LocalStarts()[3]; + + int tsrc=0; + int nnoise=1; + int LI=6; + int Ns=4; + int Nt_inv=1; + int Nt=64; + int TI=64; + int nvec=6; + bool full_tdil=true; + + Real mass=0.005; // TODO Infile + Real M5 =1.8; // TODO Infile + std::cout << "init RBG " << std::endl; + GridRedBlackCartesian RBGrid(grid4d); + std::cout << "init RBG done" << std::endl; + + int Ls=16; + + double CGPrecision = 10e-8; + int MaxIterations = 10000; + + GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,grid4d); + GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,grid4d); + + typedef DomainWallFermionR FermionAction; + + FermionAction Dop(Umu,*FGrid,*FrbGrid,*grid4d,RBGrid,mass,M5); + + MdagMLinearOperator HermOp(Dop); + ConjugateGradient CG(CGPrecision,MaxIterations); + SchurRedBlackDiagMooeeSolve SchurSolver(CG); + for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { @@ -109,16 +199,12 @@ void TPerambLight::execute(void) for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced std::cout << "LapH source vector from noise " << it << " and dilution component (d_k,d_t,d_alpha) : (" << ik << ","<< is << ")" << std::endl; - ExtractSliceLocal(evec3d,eig4d.evec[ik],0,it,3); - tmp3d_nospin = evec3d * noises[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + ExtractSliceLocal(evec3d,epack.evec[ik],0,it,3); + tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; -#ifdef USE_LOCAL_SLICES InsertSliceLocal(tmp3d,tmp2,0,it-Ntfirst,Grid::QCD::Tdir); -#else - InsertSlice(tmp3d,tmp2,it,Grid::QCD::Tdir); -#endif dist_source += tmp2; } } @@ -131,31 +217,29 @@ void TPerambLight::execute(void) Dop.ImportPhysicalFermionSource(dist_source,src5); SchurSolver(Dop,src5,sol5); Dop.ExportPhysicalFermionSolution(sol5,result); //These are the meson sinks - if (compute_current_sink) - current_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; + //if (compute_current_sink) + // current_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; for (int is = 0; is < Ns; is++) { result_nospin = peekSpin(result,is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { -#ifdef USE_LOCAL_SLICES ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); -#else - ExtractSlice(result_3d,result_nospin,t,3); -#endif for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(evec3d,eig4d.evec[ivec],0,t,3); + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); } } } } } + } +} std::cout << "perambulator done" << std::endl; - perambulator.SliceShare( grid3d, grid4d ); + //perambulator.SliceShare( grid3d, grid4d ); // THIS IS WHERE WE WANT TO SAVE THE PERAMBULATORS TO DISK - perambulator.WriteTemporary(std::string(pszPerambPack)); -*/ + //perambulator.WriteTemporary(std::string("perambulators/file")); + } END_MODULE_NAMESPACE From 09fa821510ceefcc9d5ebd44994ec591f89c0d86 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 22 Jan 2019 17:59:55 +0000 Subject: [PATCH 016/347] Added remaining methods to Permabulator --- Hadrons/DistilVectors.hpp | 184 ----------- Hadrons/Modules/MDistil/Distil.hpp | 372 ++++++++++++++++++++++ Hadrons/Modules/MDistil/DistilVectors.hpp | 4 +- Hadrons/Modules/MDistil/LapEvec.hpp | 3 + Hadrons/Modules/MDistil/PerambLight.cc | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 8 +- 6 files changed, 384 insertions(+), 189 deletions(-) delete mode 100644 Hadrons/DistilVectors.hpp create mode 100644 Hadrons/Modules/MDistil/Distil.hpp diff --git a/Hadrons/DistilVectors.hpp b/Hadrons/DistilVectors.hpp deleted file mode 100644 index c26421e8..00000000 --- a/Hadrons/DistilVectors.hpp +++ /dev/null @@ -1,184 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: Hadrons/A2AVectors.hpp - -Copyright (C) 2015-2018 - -Author: Antonin Portelli -Author: fionnoh - -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 */ -#ifndef Distil_Vectors_hpp_ -#define Distil_Vectors_hpp_ - -#include -#include -#include -#include "Grid/lattice/Lattice_peekpoke.h" -#include - -BEGIN_HADRONS_NAMESPACE - -template -class Perambulator : Serializable{ - // TODO: The next line makes friends across all combinations - // (not much of a problem given all public anyway ...) - // FYI, the bug here was that I forgot that the friend is templated - template friend std::ostream & operator<<(std::ostream &os, const Perambulator& p); -protected: -public: - GRID_SERIALIZABLE_CLASS_MEMBERS( Perambulator, - std::string, ID, // Allows owner to specialise - std::string, Provenance, // For info only - std::vector, dimensions, - std::vector, perambulator, - // Following items are redundant, but useful - int, nd, // Number of dimensions - size_t, NumElements); // Number of elements -protected: - // Constructor common code - inline void ConstructCommon(const int * Dimensions) { - assert(nd > 0); - dimensions.resize(nd); - NumElements = 1; - for(int i = 0 ; i < nd ; i++) { - assert(Dimensions[i] > 0); - NumElements *= (size_t) Dimensions[i]; - dimensions[i] = Dimensions[i]; - } - //const LatticeObj perambulatorDefault; - perambulator.resize(NumElements);//,perambulatorDefault); - } -public: - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions) - : nd {(int) Dimensions.size()} { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions, const std::string sID) - : nd {(int) Dimensions.size()}, ID(sID) { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions, const std::string sID, const std::string sProvenance) - : nd {(int) Dimensions.size()}, ID(sID), Provenance(sProvenance) { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as individual parameters - // FYI: The caller is free to ignore the names and use the indices however they see fit - inline Perambulator(int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) { - int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; - nd = sizeof(Dimensions)/sizeof(Dimensions[0]); - while( nd > 1 && Dimensions[nd-1] == 1 ) - nd--; - ConstructCommon( Dimensions ); - } - - inline LatticeObj & operator()(size_t count, const int * Coord) { - assert( count == nd ); - assert( Coord ); - size_t idx = 0; - // C memory order (???) - for( int d = 0 ; d < nd ; d++ ) { - assert( Coord[d] < dimensions[d] ); - idx *= (size_t) dimensions[d]; - idx += (size_t) Coord[d]; - } - return perambulator[idx]; - } - - inline LatticeObj & operator()(const std::vector Coord) { - return operator()(Coord.size(), &Coord[0]); - } - - inline LatticeObj & operator()(int idxNoise, int idxEvec=0, int idxTime=0, int idxSpin=0, int I_k=0, int I_t=0, int I_s=0) { - int MyIndex[]={idxNoise,idxEvec,idxTime,idxSpin,I_k,I_t,I_s}; - int i = sizeof(MyIndex)/sizeof(MyIndex[0]); - assert( i >= nd ); - while( i > nd ) - assert(MyIndex[--i] == 0); - return operator()(i, MyIndex); - } -}; - /* -#define BEGIN_GRID_NAMESPACE namespace Grid { -BEGIN_GRID_NAMESPACE - -void CartesianCommunicatorCandidate::SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Buffer, int BufferSize ) -{ - // Work out which dimension is the spread-out dimension - assert(gridLowDim); - assert(gridHighDim); - const int iNumDims{(const int)gridHighDim->_gdimensions.size()}; - assert(iNumDims == gridLowDim->_gdimensions.size()); - int dimSpreadOut = -1; - std::vector coor(iNumDims); - for( int i = 0 ; i < iNumDims ; i++ ) { - coor[i] = gridHighDim->_processor_coor[i]; - if( gridLowDim->_gdimensions[i] != gridHighDim->_gdimensions[i] ) { - assert( dimSpreadOut == -1 ); - assert( gridLowDim->_processors[i] == 1 ); // easiest assumption to make for now - dimSpreadOut = i; - } - } - if( dimSpreadOut != -1 && gridHighDim->_processors[dimSpreadOut] != gridLowDim->_processors[dimSpreadOut] ) { - // Make sure the same number of data elements exist on each slice - const int NumSlices{gridHighDim->_processors[dimSpreadOut] / gridLowDim->_processors[dimSpreadOut]}; - assert(gridHighDim->_processors[dimSpreadOut] == gridLowDim->_processors[dimSpreadOut] * NumSlices); - const int SliceSize{BufferSize/NumSlices}; - CCC_DEBUG_DUMP(Buffer, NumSlices, SliceSize); - assert(BufferSize == SliceSize * NumSlices); -#ifndef USE_LOCAL_SLICES - assert(0); // Can't do this without MPI (should really test whether MPI is defined) -#else - const auto MyRank{gridHighDim->ThisRank()}; - std::vector reqs(0); - int MySlice{coor[dimSpreadOut]}; - char * const _buffer{(char *)Buffer}; - char * const MyData{_buffer + MySlice * SliceSize}; - for(int i = 1; i < NumSlices ; i++ ){ - int SendSlice = ( MySlice + i ) % NumSlices; - int RecvSlice = ( MySlice - i + NumSlices ) % NumSlices; - char * const RecvData{_buffer + RecvSlice * SliceSize}; - coor[dimSpreadOut] = SendSlice; - const auto SendRank{gridHighDim->RankFromProcessorCoor(coor)}; - coor[dimSpreadOut] = RecvSlice; - const auto RecvRank{gridHighDim->RankFromProcessorCoor(coor)}; - std::cout << GridLogMessage << "Send slice " << MySlice << " (" << MyRank << ") to " << SendSlice << " (" << SendRank - << "), receive slice from " << RecvSlice << " (" << RecvRank << ")" << std::endl; - gridHighDim->SendToRecvFromBegin(reqs,MyData,SendRank,RecvData,RecvRank,SliceSize); - //memcpy(RecvData,MyData,SliceSize); // Debug - } - gridHighDim->SendToRecvFromComplete(reqs); - std::cout << GridLogMessage << "Slice data shared." << std::endl; - CCC_DEBUG_DUMP(Buffer, NumSlices, SliceSize); -#endif - } -} - -#define END_GRID_NAMESPACE } - -*/ - -END_HADRONS_NAMESPACE - -#endif // Distil_Vectors_hpp_ diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp new file mode 100644 index 00000000..3e86651c --- /dev/null +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -0,0 +1,372 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/Distil.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + +#ifndef Hadrons_MDistil_Distil_hpp_ +#define Hadrons_MDistil_Distil_hpp_ + +#include +#include +#include + +/****************************************************************************** + This potentially belongs in CartesianCommunicator + ******************************************************************************/ + +BEGIN_MODULE_NAMESPACE(Grid) +inline void SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Buffer, int BufferSize ) +{ + // Work out which dimension is the spread-out dimension + assert(gridLowDim); + assert(gridHighDim); + const int iNumDims{(const int)gridHighDim->_gdimensions.size()}; + assert(iNumDims == gridLowDim->_gdimensions.size()); + int dimSpreadOut = -1; + std::vector coor(iNumDims); + for( int i = 0 ; i < iNumDims ; i++ ) { + coor[i] = gridHighDim->_processor_coor[i]; + if( gridLowDim->_gdimensions[i] != gridHighDim->_gdimensions[i] ) { + assert( dimSpreadOut == -1 ); + assert( gridLowDim->_processors[i] == 1 ); // easiest assumption to make for now + dimSpreadOut = i; + } + } + if( dimSpreadOut != -1 && gridHighDim->_processors[dimSpreadOut] != gridLowDim->_processors[dimSpreadOut] ) { + // Make sure the same number of data elements exist on each slice + const int NumSlices{gridHighDim->_processors[dimSpreadOut] / gridLowDim->_processors[dimSpreadOut]}; + assert(gridHighDim->_processors[dimSpreadOut] == gridLowDim->_processors[dimSpreadOut] * NumSlices); + const int SliceSize{BufferSize/NumSlices}; + //CCC_DEBUG_DUMP(Buffer, NumSlices, SliceSize); + assert(BufferSize == SliceSize * NumSlices); +//#ifndef USE_LOCAL_SLICES +// assert(0); // Can't do this without MPI (should really test whether MPI is defined) +//#else + const auto MyRank{gridHighDim->ThisRank()}; + std::vector reqs(0); + int MySlice{coor[dimSpreadOut]}; + char * const _buffer{(char *)Buffer}; + char * const MyData{_buffer + MySlice * SliceSize}; + for(int i = 1; i < NumSlices ; i++ ){ + int SendSlice = ( MySlice + i ) % NumSlices; + int RecvSlice = ( MySlice - i + NumSlices ) % NumSlices; + char * const RecvData{_buffer + RecvSlice * SliceSize}; + coor[dimSpreadOut] = SendSlice; + const auto SendRank{gridHighDim->RankFromProcessorCoor(coor)}; + coor[dimSpreadOut] = RecvSlice; + const auto RecvRank{gridHighDim->RankFromProcessorCoor(coor)}; + std::cout << GridLogMessage << "Send slice " << MySlice << " (" << MyRank << ") to " << SendSlice << " (" << SendRank + << "), receive slice from " << RecvSlice << " (" << RecvRank << ")" << std::endl; + gridHighDim->SendToRecvFromBegin(reqs,MyData,SendRank,RecvData,RecvRank,SliceSize); + //memcpy(RecvData,MyData,SliceSize); // Debug + } + gridHighDim->SendToRecvFromComplete(reqs); + std::cout << GridLogMessage << "Slice data shared." << std::endl; + //CCC_DEBUG_DUMP(Buffer, NumSlices, SliceSize); +//#endif + } +} +END_MODULE_NAMESPACE // Grid + +/****************************************************************************** + Common elements for distillation + ******************************************************************************/ + +BEGIN_HADRONS_NAMESPACE + +BEGIN_MODULE_NAMESPACE(MDistil) + +/****************************************************************************** + Perambulator object + ******************************************************************************/ + +template +class Perambulator : Serializable{ + // TODO: The next line makes friends across all combinations + // (not much of a problem given all public anyway ...) + // FYI, the bug here was that I forgot that the friend is templated + template friend std::ostream & operator<<(std::ostream &os, const Perambulator& p); +protected: +public: + GRID_SERIALIZABLE_CLASS_MEMBERS( Perambulator, + std::string, ID, // Allows owner to specialise + std::string, Provenance, // For info only + std::vector, dimensions, + std::vector, perambulator, + // Following items are redundant, but useful + int, nd, // Number of dimensions + size_t, NumElements); // Number of elements +protected: + // Constructor common code + inline void ConstructCommon(const int * Dimensions) { + assert(nd > 0); + dimensions.resize(nd); + NumElements = 1; + for(int i = 0 ; i < nd ; i++) { + assert(Dimensions[i] > 0); + NumElements *= (size_t) Dimensions[i]; + dimensions[i] = Dimensions[i]; + } + //const LatticeObj perambulatorDefault; + perambulator.resize(NumElements);//,perambulatorDefault); + } +public: + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions) + : nd {(int) Dimensions.size()} { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions, const std::string sID) + : nd {(int) Dimensions.size()}, ID(sID) { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as std::vector + inline Perambulator(const std::vector & Dimensions, const std::string sID, const std::string sProvenance) + : nd {(int) Dimensions.size()}, ID(sID), Provenance(sProvenance) { + ConstructCommon( &Dimensions[0] ); } + + // Constructor with dimensions passed as individual parameters + // FYI: The caller is free to ignore the names and use the indices however they see fit + inline Perambulator(int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) { + int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; + nd = sizeof(Dimensions)/sizeof(Dimensions[0]); + while( nd > 1 && Dimensions[nd-1] == 1 ) + nd--; + ConstructCommon( Dimensions ); + } + + inline Perambulator(const std::string sID, int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) : ID{sID} { + int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; + nd = sizeof(Dimensions)/sizeof(Dimensions[0]); + while( nd > 1 && Dimensions[nd-1] == 1 ) + nd--; + ConstructCommon( Dimensions ); + } + + inline Perambulator(const std::string sID, const std::string sProvenance, int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) : ID{sID}, Provenance{sProvenance} { + int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; + nd = sizeof(Dimensions)/sizeof(Dimensions[0]); + while( nd > 1 && Dimensions[nd-1] == 1 ) + nd--; + ConstructCommon( Dimensions ); + } + + inline LatticeObj & operator()(size_t count, const int * Coord) { + assert( count == nd ); + assert( Coord ); + size_t idx = 0; + // C memory order (???) + for( int d = 0 ; d < nd ; d++ ) { + assert( Coord[d] < dimensions[d] ); + idx *= (size_t) dimensions[d]; + idx += (size_t) Coord[d]; + } + return perambulator[idx]; + } + + inline LatticeObj & operator()(const std::vector Coord) { + return operator()(Coord.size(), &Coord[0]); + } + + inline LatticeObj & operator()(int idxNoise, int idxEvec=0, int idxTime=0, int idxSpin=0, int I_k=0, int I_t=0, int I_s=0) { + int MyIndex[]={idxNoise,idxEvec,idxTime,idxSpin,I_k,I_t,I_s}; + int i = sizeof(MyIndex)/sizeof(MyIndex[0]); + assert( i >= nd ); + while( i > nd ) + assert(MyIndex[--i] == 0); + return operator()(i, MyIndex); + } + + // Share data for timeslices we calculated with other nodes + inline void SliceShare( GridCartesian * gridLowDim, GridCartesian * gridHighDim ) { + Grid::SliceShare( gridLowDim, gridHighDim, &perambulator[0], + (int) perambulator.size() * sizeof(perambulator[0]) ); + } + + /************************************************************************************* + + Write/Read perambulator to/from disk + + Temporary version - keep the code running until such time as correct format written + + *************************************************************************************/ + + inline void WriteTemporary(const std::string filename) const + { + std::cout << GridLogMessage << "Writing perambulator ID \"" << ID << "\" to " << filename << std::endl; + BinaryWriter myPhDThesis( filename + ".tmp" ); + write( myPhDThesis, "Perambulator", *this ); + } + + inline bool ReadTemporary(const std::string filename) + { + std::string _filename{filename}; + _filename.append( ".tmp" ); + bool bReturnValue = false; + std::fstream f; + f.open(_filename,std::ios_base::in); + if( !f.is_open() ) + std::cout << GridLogMessage << "Cached perambulator file " << _filename << " does not exist" << std::endl; + else { + f.close(); + bReturnValue = true; + auto MyID{ID}; + std::cout << GridLogMessage << "Reading perambulator ID \"" << ID << "\" from " << _filename << std::endl; + BinaryReader reader( _filename ); + read( reader, "Perambulator", *this ); + std::cout << GridLogMessage << "Perambulator ID read from " << _filename << " was \"" << ID << "\"" << std::endl; + assert(MyID == ID); + } + return bReturnValue; + } + + /************************************************************************************* + + Write perambulator to disk + TODO 1. Ensure precision on disk can be specified independently of in memory + 2. Validate format with Peter, Antonin et al + 3. This object "works" for small lattii (lattices), + BUT, the final block is written as XML, and while write is fast, read is painfully slow + i.e. on a 24^3 x 64 lattice, I abandoned the perambulator read after 1 hour on Tesseract + + *************************************************************************************/ + + inline void WritePoorly(LatticeGaugeField &field, const std::string filename) + { + std::cout << GridLogMessage << "Writing perambulator ID \"" << ID << "\" to " << filename << std::endl; + assert(nd>=2); // Really should be a little bigger + GridBase * gridHighDim = field._grid; + //ScidacWriterPerambulator binWriter(gridHighDim->IsBoss()); + ScidacWriter binWriter(gridHighDim->IsBoss()); + //makeFileDir(filename, gridHighDim); // Assume this makes directory ... but why pass it the grid? + binWriter.open(filename); + // Write the header + { + XmlWriter xmlWriter("", "perambulatorPar"); + xmlWriter.pushXmlString("" + ID + ""); + xmlWriter.pushXmlString("" + Provenance + ""); + // TODO add all the perambulator parameters here + binWriter.writeLimeObject(1, 1, xmlWriter, "parameters", SCIDAC_FILE_XML); + std::cout << GridLogMessage << "Perambulator header written" << std::endl; + } + // Now write the local portion of the Perambulator + { + //binWriter.writeScidacPerambulatorRecord(field, *this); + //std::cout << GridLogMessage << "Perambulator body written" << std::endl; + } + { + //////////////////////////////////////// + // fill the Grid header + //////////////////////////////////////// + FieldMetaData header; + scidacRecord _scidacRecord; + scidacFile _scidacFile; + ScidacMetaData(field,header,_scidacRecord,_scidacFile); + + ////////////////////////////////////////////// + // Fill the Lime file record by record + ////////////////////////////////////////////// + constexpr auto precision = std::numeric_limits::digits10; + binWriter.writeLimeObject(1,0,header ,std::string("FieldMetaData"),std::string(GRID_FORMAT)); // Open message + binWriter.writeLimeObject(0, 0, _scidacRecord, _scidacRecord.SerialisableClassName(), + std::string(SCIDAC_PRIVATE_RECORD_XML)); + binWriter.writeLimeObject(0,1,*this,this->SerialisableClassName(),std::string("Perambulator"),precision); + } + } + + /************************************************************************************* + + Read perambulator from disk + TODO 1. Ensure precision on disk can be specified independently of in memory + 2. Validate format with Peter + 3. Abandoning for now because of synchronisation during write. + Object small enough to send to root and write from there + + *************************************************************************************/ + + struct PerambHeader{ + std::string ID, Provenance; + }; + + inline bool ReadPoorly(LatticeGaugeField &field, const std::string filename) + { + assert(nd>=2); // Really should be a little bigger + bool bReturnValue = false; + std::fstream f; + f.open(filename,std::ios_base::in); + if( !f.is_open() ) + std::cout << GridLogMessage << "Cached perambulator file " << filename << " does not exist" << std::endl; + else { + f.close(); + ScidacReader binReader; + binReader.open(filename); + PerambHeader header; + // Read the header + { + std::string recordXml; + std::cout << GridLogMessage << "Reading perambulator header from " << filename << std::endl; + binReader.readLimeObject(recordXml, SCIDAC_FILE_XML); + XmlReader xmlReader(recordXml, true, "perambulatorPar"); + xmlReader.push("perambulatorPar"); + //xmlReader.readCurrentSubtree(header.ID); + xmlReader.readDefault("ID",header.ID); + std::cout << GridLogMessage << "Perambulator ID=" << header.ID << std::endl; + //xmlReader.nextElement(); + //xmlReader.readCurrentSubtree(header.Provenance); + xmlReader.readDefault("Provenance",header.Provenance); + std::cout << GridLogMessage << "Perambulator Provenance=" << header.Provenance << std::endl; + assert( header.ID == ID ); + bReturnValue = true; + } + // Now read the Perambulator + { + //////////////////////////////////////// + // fill the Grid header + //////////////////////////////////////// + FieldMetaData header; + scidacRecord _scidacRecord; + + ////////////////////////////////////////////// + // Fill the Lime file record by record + ////////////////////////////////////////////// + binReader.readLimeObject(header ,std::string("FieldMetaData"),std::string(GRID_FORMAT)); // Open message + binReader.readLimeObject( _scidacRecord, _scidacRecord.SerialisableClassName(), + std::string(SCIDAC_PRIVATE_RECORD_XML)); + binReader.readLimeObject(*this,this->SerialisableClassName(),std::string("Perambulator")); + } + // TODO Add validation that the field matches what we read in + } + return bReturnValue; + } +}; + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_Distil_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 9715752c..6f71fba9 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -8,7 +8,9 @@ #include #include #include -#include + +// These are members of Distillation +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index fc1fae61..06be0bbc 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -34,6 +34,9 @@ #include #include +// These are members of Distillation +#include + BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) diff --git a/Hadrons/Modules/MDistil/PerambLight.cc b/Hadrons/Modules/MDistil/PerambLight.cc index bcb21d71..36dc97af 100644 --- a/Hadrons/Modules/MDistil/PerambLight.cc +++ b/Hadrons/Modules/MDistil/PerambLight.cc @@ -4,4 +4,4 @@ using namespace Grid; using namespace Hadrons; using namespace MDistil; -template class Grid::Hadrons::MDistil::Tperambulator_l; +template class Grid::Hadrons::MDistil::TPerambLight; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 59d80646..997c125b 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -8,7 +8,9 @@ #include #include #include -#include + +// These are members of Distillation +#include BEGIN_HADRONS_NAMESPACE @@ -235,10 +237,10 @@ void TPerambLight::execute(void) } } std::cout << "perambulator done" << std::endl; - //perambulator.SliceShare( grid3d, grid4d ); + perambulator.SliceShare( grid3d, grid4d ); // THIS IS WHERE WE WANT TO SAVE THE PERAMBULATORS TO DISK - //perambulator.WriteTemporary(std::string("perambulators/file")); + perambulator.WriteTemporary(std::string("perambulators/file")); } From be5605931c45034626d8b587c255124d4597bfe4 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 23 Jan 2019 10:51:09 +0000 Subject: [PATCH 017/347] merge --- Hadrons/Modules/MDistil/PerambLight.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.cc b/Hadrons/Modules/MDistil/PerambLight.cc index bcb21d71..36dc97af 100644 --- a/Hadrons/Modules/MDistil/PerambLight.cc +++ b/Hadrons/Modules/MDistil/PerambLight.cc @@ -4,4 +4,4 @@ using namespace Grid; using namespace Hadrons; using namespace MDistil; -template class Grid::Hadrons::MDistil::Tperambulator_l; +template class Grid::Hadrons::MDistil::TPerambLight; From 4cc2ebc9e45fcd600df878fe90036580c1ebf675 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 23 Jan 2019 11:26:07 +0000 Subject: [PATCH 018/347] moved hard-coded parameters to module input --- Hadrons/Modules/MDistil/PerambLight.hpp | 46 +++++++++++++++++-------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 997c125b..774882d4 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -26,7 +26,21 @@ public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, std::string, noise, std::string, eigenPack, - bool, multiFile); + bool, multiFile, + int, tsrc, + int, nnoise, + int, LI, + int, SI, + int, TI, + int, nvec, + int, Ns, + int, Nt, + int, Nt_inv, + Real, mass, + Real, M5, + int, Ls, + double, CGPrecision, + int, MaxIterations); }; template @@ -155,26 +169,28 @@ void TPerambLight::execute(void) int Ntlocal = grid4d->LocalDimensions()[3]; int Ntfirst = grid4d->LocalStarts()[3]; - int tsrc=0; - int nnoise=1; - int LI=6; - int Ns=4; - int Nt_inv=1; - int Nt=64; - int TI=64; - int nvec=6; - bool full_tdil=true; + + int tsrc=par().tsrc; + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int Nt_inv=par().Nt_inv; + int Nt=par().Nt; + int TI=par().TI; + int nvec=par().nvec; + + bool full_tdil=(TI==Nt); - Real mass=0.005; // TODO Infile - Real M5 =1.8; // TODO Infile + Real mass=par().mass; // TODO Infile + Real M5 =par().M5; // TODO Infile std::cout << "init RBG " << std::endl; GridRedBlackCartesian RBGrid(grid4d); std::cout << "init RBG done" << std::endl; - int Ls=16; + int Ls=par().Ls; - double CGPrecision = 10e-8; - int MaxIterations = 10000; + double CGPrecision = par().CGPrecision; + int MaxIterations = par().MaxIterations; GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,grid4d); GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,grid4d); From d7908c33de7ba7e8a4505998331abe0f278372ad Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 23 Jan 2019 11:32:53 +0000 Subject: [PATCH 019/347] moved hard-coded parameters in DistilVectors to module input --- Hadrons/Modules/MDistil/DistilVectors.hpp | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 6f71fba9..2aa0d65a 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -27,7 +27,16 @@ public: std::string, noise, std::string, perambulator, std::string, eigenPack, - bool, multiFile); + bool, multiFile, + int, tsrc, + int, nnoise, + int, LI, + int, SI, + int, TI, + int, nvec, + int, Ns, + int, Nt, + int, Nt_inv); }; template @@ -136,15 +145,16 @@ void TDistilVectors::execute(void) int Ntlocal = grid4d->LocalDimensions()[3]; int Ntfirst = grid4d->LocalStarts()[3]; - int tsrc=0; - int nnoise=1; - int LI=6; - int Ns=4; - int Nt_inv=1; - int Nt=64; - int TI=64; - int nvec=6; - bool full_tdil=true; + int tsrc=par().tsrc; + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int Nt_inv=par().Nt_inv; + int Nt=par().Nt; + int TI=par().TI; + int nvec=par().nvec; + + bool full_tdil=(TI==Nt); int vecindex; for (int inoise = 0; inoise < nnoise; inoise++) { From 2756f16a5ea38cbd13e7e520f2bf13fdfad35597 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 23 Jan 2019 12:49:20 +0000 Subject: [PATCH 020/347] created test prog for perambs --- tests/hadrons/Test_hadrons_distil.cc | 46 +++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 1d16b086..3e411a58 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -240,10 +240,10 @@ void test_LapEvec(Application &application) } ///////////////////////////////////////////////////////////// -// Felix, this is your test here +// Perambulators ///////////////////////////////////////////////////////////// -void test_FelixRenameMe(Application &application) +void test_Perambulators(Application &application) { const unsigned int nt = GridDefaultLatt()[Tp]; @@ -257,7 +257,41 @@ void test_FelixRenameMe(Application &application) // gauge field application.createModule("gauge"); // Now make an instance of the LapEvec object - application.createModule("DistilVectorsInstance"); + application.createModule("PerambulatorsInstance"); +} +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +void test_DistilVectors(Application &application) +{ + const unsigned int nt = GridDefaultLatt()[Tp]; + + // global parameters + Application::GlobalPar globalPar; + globalPar.trajCounter.start = 1500; + globalPar.trajCounter.end = 1520; + globalPar.trajCounter.step = 20; + globalPar.runId = "test"; + application.setPar(globalPar); + // Module parameters + MDistil::DistilVectors::Par DistilPar; + DistilPar.noise="noise"; + DistilPar.perambulator="perambulator"; + DistilPar.eigenPack="ePack"; + DistilPar.tsrc = 0; + DistilPar.nnoise = 1; + DistilPar.LI=6; + DistilPar.SI=4; + DistilPar.TI=64; + DistilPar.nvec=6; + DistilPar.Ns=4; + DistilPar.Nt=64; + DistilPar.Nt_inv=1; + // gauge field + application.createModule("gauge"); + // Now make an instance of the LapEvec object + application.createModule("DistilVectorsInstance",DistilPar); } bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) @@ -331,7 +365,9 @@ int main(int argc, char *argv[]) // For now perform free propagator test - replace this with distillation test(s) LOG(Message) << "====== Creating xml for test " << iTestNum << " ======" << std::endl; - switch(iTestNum) { + const unsigned int nt = GridDefaultLatt()[Tp]; + + switch(iTestNum) { case 0: free_prop( application ); break; @@ -339,7 +375,7 @@ int main(int argc, char *argv[]) test_LapEvec( application ); break; default: // 2 - test_FelixRenameMe( application ); + test_DistilVectors( application ); break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From 3d3e8f4f9fe79d3089c6566e7fc89dd635886b63 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 23 Jan 2019 12:59:55 +0000 Subject: [PATCH 021/347] Structured objects passed into LapEvec --- Hadrons/Modules/MDistil/LapEvec.hpp | 83 +++++++++++++++++++++++----- tests/hadrons/Test_hadrons_distil.cc | 5 +- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 06be0bbc..aac71ea3 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -42,23 +42,30 @@ BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** - * LapEvec * - ***** TEST ***** + + Laplacian eigenvectors - parameters + ******************************************************************************/ - -class LapEvecPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, - // StoutParameters, +struct StoutParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(StoutParameters, int, steps, - double, parm, - // ChebyshevParameters, + double, parm) + StoutParameters() = default; + template StoutParameters(Reader& Reader){read(Reader,"StoutSmearing",*this);} +}; + +struct ChebyshevParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(ChebyshevParameters, int, PolyOrder, double, alpha, - double, beta, - // LanczosParameters, + double, beta) + ChebyshevParameters() = default; + template ChebyshevParameters(Reader& Reader){read(Reader,"Chebyshev",*this);} +}; + +struct LanczosParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters, int, Nstart, int, Nvec, int, Nk, @@ -66,9 +73,51 @@ public: int, Np, int, MaxIt, int, MinRes, - double, resid); + double, resid) + LanczosParameters() = default; + template LanczosParameters(Reader& Reader){read(Reader,"Lanczos",*this);} }; +struct DistilParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, TI, + int, LI, + int, Nnoise, + int, Ls, // For makeFiveDimGrid + int, tSrc) + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} +}; + +struct SolverParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(SolverParameters, + double, CGPrecision, + int, MaxIterations, + double, mass, + double, M5) + SolverParameters() = default; + template SolverParameters(Reader& Reader){read(Reader,"Solver",*this);} +}; + +// These are the actual parameters passed to the module during construction + +class LapEvecPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, + StoutParameters, Stout, + ChebyshevParameters, Cheby, + LanczosParameters, Lanczos, + DistilParameters, Distil, + SolverParameters, Solver); +}; + +/****************************************************************************** + + Laplacian eigenvectors - Module (class) definition + + ******************************************************************************/ + template class TLapEvec: public Module { @@ -118,14 +167,18 @@ std::vector TLapEvec::getOutput(void) template void TLapEvec::setup(void) { - + LOG(Message) << "setup() : start" << std::endl; + LOG(Message) << "Stout.steps=" << par().Stout.steps << ", Stout.parm=" << par().Stout.parm << std::endl; + LOG(Message) << "setup() : end" << std::endl; } // execution /////////////////////////////////////////////////////////////////// template void TLapEvec::execute(void) { - + LOG(Message) << "execute() : start" << std::endl; + LOG(Message) << "Stout.steps=" << par().Stout.steps << ", Stout.parm=" << par().Stout.parm << std::endl; + LOG(Message) << "execute() : end" << std::endl; } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 1d16b086..276a69f7 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -236,7 +236,10 @@ void test_LapEvec(Application &application) // gauge field application.createModule("gauge"); // Now make an instance of the LapEvec object - application.createModule("LapEvecInstance"); + MDistil::LapEvecPar levPar; + levPar.Stout.steps = 173; + levPar.Stout.parm = -9.87654321; + application.createModule("LapEvec",levPar); } ///////////////////////////////////////////////////////////// From a6ab742fdbb5ef7f744281f9fb4f3c4f276b19f1 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 23 Jan 2019 13:58:20 +0000 Subject: [PATCH 022/347] added perambs to test --- Hadrons/Modules/MDistil/DistilVectors.hpp | 6 ++- Hadrons/Modules/MDistil/PerambLight.hpp | 53 ++++++++++++++++++----- tests/hadrons/Test_hadrons_distil.cc | 51 +++++++++++++++------- 3 files changed, 81 insertions(+), 29 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 2aa0d65a..de8f6199 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -126,7 +126,8 @@ template void TDistilVectors::execute(void) { - auto &noise = envGet(std::vector>>, par().noise); + //auto &noise = envGet(std::vector>>, par().noise); + auto &noise = envGet(std::vector, par().noise); auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); @@ -170,7 +171,8 @@ void TDistilVectors::execute(void) for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced ExtractSliceLocal(evec3d,epack.evec[ik],0,it,3); - tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + tmp3d_nospin = evec3d * noise[inoise + nnoise*(it + Nt*(ik+nvec*is))]; + //tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 774882d4..dbac54ca 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -24,7 +24,6 @@ class PerambLightPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, - std::string, noise, std::string, eigenPack, bool, multiFile, int, tsrc, @@ -77,7 +76,7 @@ std::vector TPerambLight::getInput(void) { std::vector in; - in.push_back(par().noise); + //in.push_back(par().noise); in.push_back(par().eigenPack); return in; @@ -86,7 +85,7 @@ std::vector TPerambLight::getInput(void) template std::vector TPerambLight::getOutput(void) { - std::vector out = {getName() + "_perambulator_light"}; + std::vector out = {getName() + "_perambulator_light",getName() + "_noise"}; return out; } @@ -96,13 +95,19 @@ template void TPerambLight::setup(void) { - auto &noise = envGet(std::vector>>, par().noise); + // auto &noise = envGet(std::vector>>, par().noise); - int nvec = 6; - int Nt=64; - - envCreate(Perambulator, getName() + "_perambulator_light", 1, - noise.size() *nvec*Nt); + int LI=par().LI; + int SI=par().SI; + int TI=par().TI; + int nnoise=par().nnoise; + int Nt=par().Nt; + int nvec=par().nvec; + + envCreate(Perambulator, getName() + "_perambulator_light", 1, + LI*SI*TI*nnoise*nvec*Nt); + envCreate(std::vector, getName() + "_noise", 1, + nvec*Ns*Nt*nnoise); GridCartesian * grid4d = env().getGrid(); std::vector latt_size = GridDefaultLatt(); @@ -133,7 +138,8 @@ template void TPerambLight::execute(void) { - auto &noise = envGet(std::vector>>, par().noise); + //auto &noise = envGet(std::vector>>, par().noise); + auto &noise = envGet(std::vector, getName() + "_noise"); auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); @@ -180,6 +186,30 @@ void TPerambLight::execute(void) int nvec=par().nvec; bool full_tdil=(TI==Nt); + bool exact_distillation = (full_tdil && LI==nvec); + + //Create Noises + //std::cout << pszGaugeConfigFile << std::endl; + //GridSerialRNG sRNG; sRNG.SeedUniqueString(std::string(pszGaugeConfigFile)); + GridSerialRNG sRNG; sRNG.SeedUniqueString("unique_string"); + Real rn; + + for (int inoise=0;inoise 0) - (rn-0.5 < 0); //TODO: This could be 0 if rn==0.5!! + //noises[inoise][t][ivec]()(is)() = (rn-0.5 > 0) - (rn-0.5 < 0); //TODO: This could be 0 if rn==0.5!! + } + } + } + } + } Real mass=par().mass; // TODO Infile Real M5 =par().M5; // TODO Infile @@ -218,7 +248,8 @@ void TPerambLight::execute(void) for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced std::cout << "LapH source vector from noise " << it << " and dilution component (d_k,d_t,d_alpha) : (" << ik << ","<< is << ")" << std::endl; ExtractSliceLocal(evec3d,epack.evec[ik],0,it,3); - tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + tmp3d_nospin = evec3d * noise[inoise + nnoise*(it + Nt*(ik+nvec*is))]; + //tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 3e411a58..17044563 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -274,24 +274,43 @@ void test_DistilVectors(Application &application) globalPar.trajCounter.step = 20; globalPar.runId = "test"; application.setPar(globalPar); - // Module parameters - MDistil::DistilVectors::Par DistilPar; - DistilPar.noise="noise"; - DistilPar.perambulator="perambulator"; - DistilPar.eigenPack="ePack"; - DistilPar.tsrc = 0; - DistilPar.nnoise = 1; - DistilPar.LI=6; - DistilPar.SI=4; - DistilPar.TI=64; - DistilPar.nvec=6; - DistilPar.Ns=4; - DistilPar.Nt=64; - DistilPar.Nt_inv=1; + // PerambLight parameters + MDistil::PerambLight::Par PerambPar; + PerambPar.eigenPack="ePack"; + PerambPar.tsrc = 0; + PerambPar.nnoise = 1; + PerambPar.LI=6; + PerambPar.SI=4; + PerambPar.TI=64; + PerambPar.nvec=6; + PerambPar.Ns=4; + PerambPar.Nt=64; + PerambPar.Nt_inv=1; + PerambPar.mass=0.005; + PerambPar.M5=1.8; + PerambPar.Ls=16; + PerambPar.CGPrecision=1e-8; + PerambPar.MaxIterations=10000; + // DistilVectors parameters + MDistil::DistilVectors::Par DistilVecPar; + DistilVecPar.noise="noise"; + DistilVecPar.perambulator="perambulator"; + DistilVecPar.eigenPack="ePack"; + DistilVecPar.tsrc = 0; + DistilVecPar.nnoise = 1; + DistilVecPar.LI=6; + DistilVecPar.SI=4; + DistilVecPar.TI=64; + DistilVecPar.nvec=6; + DistilVecPar.Ns=4; + DistilVecPar.Nt=64; + DistilVecPar.Nt_inv=1; // gauge field application.createModule("gauge"); - // Now make an instance of the LapEvec object - application.createModule("DistilVectorsInstance",DistilPar); + // Now make an instance of the Perambulator object + application.createModule("PerambulatorsInstance",PerambPar); + // Now make an instance of the DistilVectors object + application.createModule("DistilVectorsInstance",DistilVecPar); } bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) From 7c5a06f6d0bc34894f144790ab0f7275d2dd6aa5 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 23 Jan 2019 15:19:51 +0000 Subject: [PATCH 023/347] Trying to work out why LapEvec constructor not being called --- Hadrons/Modules/MDistil/Distil.hpp | 18 +++++++++++++++ Hadrons/Modules/MDistil/LapEvec.hpp | 33 ++++++++++++++++++++++++---- tests/hadrons/Test_hadrons_distil.cc | 14 ++++++------ 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 3e86651c..38672dfb 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -100,6 +100,24 @@ BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) +/****************************************************************************** + Make a lower dimensional grid + ******************************************************************************/ + +inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) +{ + int nd{static_cast(gridHD->_ndimension)}; + std::vector latt_size = gridHD->_fdimensions; + latt_size[nd-1] = 1; + + std::vector simd_layout = GridDefaultSimd(nd-1, vComplex::Nsimd()); + simd_layout.push_back( 1 ); + + std::vector mpi_layout = gridHD->_processors; + mpi_layout[nd-1] = 1; + return new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); +} + /****************************************************************************** Perambulator object ******************************************************************************/ diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index aac71ea3..62062f03 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -123,9 +123,10 @@ class TLapEvec: public Module { public: // constructor + TLapEvec(); TLapEvec(const std::string name); // destructor - virtual ~TLapEvec(void) {}; + virtual ~TLapEvec(void); // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); @@ -133,6 +134,8 @@ public: virtual void setup(void); // execution virtual void execute(void); +public: + GridCartesian * gridLD = nullptr; }; MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); @@ -142,9 +145,25 @@ MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TLapEvec::TLapEvec(const std::string name) -: Module(name) -{} +TLapEvec::TLapEvec(const std::string name) : Module(name) +{ + // NB: This constructor isn't used!!! + LOG(Message) << "TLapEvec constructor : TLapEvec::TLapEvec(const std::string name)" << std::endl; + LOG(Message) << "TLapEvec constructor : Setting gridLD=nullptr" << std::endl; + gridLD = nullptr; +} + +// destructor ///////////////////////////////////////////////////////////////// +template +TLapEvec::~TLapEvec() +{ + if( gridLD != nullptr ) { + LOG(Message) << "Destroying lower dimensional grid" << std::endl; + delete gridLD; + } + else + LOG(Message) << "Lower dimensional grid was never created" << std::endl; +} // dependencies/products /////////////////////////////////////////////////////// template @@ -169,6 +188,12 @@ void TLapEvec::setup(void) { LOG(Message) << "setup() : start" << std::endl; LOG(Message) << "Stout.steps=" << par().Stout.steps << ", Stout.parm=" << par().Stout.parm << std::endl; + if( gridLD ) { + LOG(Message) << "Didn't expect to need to destroy gridLD here!" << std::endl; + delete gridLD; + } + LOG(Message) << "Creating lower dimensional grid" << std::endl; + gridLD = MakeLowerDimGrid( env().getGrid() ); LOG(Message) << "setup() : end" << std::endl; } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index a2d13c63..93796f94 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -236,10 +236,10 @@ void test_LapEvec(Application &application) // gauge field application.createModule("gauge"); // Now make an instance of the LapEvec object - MDistil::LapEvecPar levPar; - levPar.Stout.steps = 173; - levPar.Stout.parm = -9.87654321; - application.createModule("LapEvec",levPar); + MDistil::LapEvecPar par; + par.Stout.steps = 173; + par.Stout.parm = -9.87654321; + application.createModule("LapEvec",par); } ///////////////////////////////////////////////////////////// @@ -252,9 +252,9 @@ void test_Perambulators(Application &application) // global parameters Application::GlobalPar globalPar; - globalPar.trajCounter.start = 1500; - globalPar.trajCounter.end = 1520; - globalPar.trajCounter.step = 20; + globalPar.trajCounter.start = 3000; + globalPar.trajCounter.end = 3040; + globalPar.trajCounter.step = 40; globalPar.runId = "test"; application.setPar(globalPar); // gauge field From b45586e81c6584c264f3be4fb89fb6a8c0b812a2 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 23 Jan 2019 21:17:56 +0000 Subject: [PATCH 024/347] Discovered bug root cause. setup() is called multiple times. Now ready to copy-paste the LapEvec code --- Hadrons/Modules/MDistil/Distil.hpp | 5 +- Hadrons/Modules/MDistil/LapEvec.hpp | 84 +++++++++++++++++----------- tests/hadrons/Test_hadrons_distil.cc | 6 +- 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 38672dfb..74e35085 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -106,6 +106,7 @@ BEGIN_MODULE_NAMESPACE(MDistil) inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) { + //LOG(Message) << "MakeLowerDimGrid() begin" << std::endl; int nd{static_cast(gridHD->_ndimension)}; std::vector latt_size = gridHD->_fdimensions; latt_size[nd-1] = 1; @@ -115,7 +116,9 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) std::vector mpi_layout = gridHD->_processors; mpi_layout[nd-1] = 1; - return new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); + GridCartesian * gridLD = new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); + //LOG(Message) << "MakeLowerDimGrid() end" << std::endl; + return gridLD; } /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 62062f03..910c4315 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -33,6 +33,7 @@ #include #include #include +#include // These are members of Distillation #include @@ -105,6 +106,7 @@ class LapEvecPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, + std::string, gauge, StoutParameters, Stout, ChebyshevParameters, Cheby, LanczosParameters, Lanczos, @@ -122,54 +124,57 @@ template class TLapEvec: public Module { public: - // constructor - TLapEvec(); - TLapEvec(const std::string name); - // destructor - virtual ~TLapEvec(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); + GAUGE_TYPE_ALIASES(FImpl,); + typedef std::vector > DistilEP; + public: - GridCartesian * gridLD = nullptr; + // constructor + TLapEvec(const std::string name); + // destructor + virtual ~TLapEvec(void); + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +protected: + // These variables are created in setup() and freed in Cleanup() + GridCartesian * gridLD; // Owned by me, so I must delete it + GridCartesian * gridHD; // Owned by environment (so I won't delete it) + int Nx, Ny, Nz, Nt; + +protected: + void Cleanup(void); }; MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); /****************************************************************************** - * TLapEvec implementation * + TLapEvec implementation ******************************************************************************/ + // constructor ///////////////////////////////////////////////////////////////// template -TLapEvec::TLapEvec(const std::string name) : Module(name) +TLapEvec::TLapEvec(const std::string name) : gridLD{nullptr}, Module(name) { - // NB: This constructor isn't used!!! LOG(Message) << "TLapEvec constructor : TLapEvec::TLapEvec(const std::string name)" << std::endl; - LOG(Message) << "TLapEvec constructor : Setting gridLD=nullptr" << std::endl; - gridLD = nullptr; + LOG(Message) << "this=" << this << ", gridLD=" << gridLD << std::endl; } // destructor ///////////////////////////////////////////////////////////////// template TLapEvec::~TLapEvec() { - if( gridLD != nullptr ) { - LOG(Message) << "Destroying lower dimensional grid" << std::endl; - delete gridLD; - } - else - LOG(Message) << "Lower dimensional grid was never created" << std::endl; + Cleanup(); } // dependencies/products /////////////////////////////////////////////////////// template std::vector TLapEvec::getInput(void) { - std::vector in; + std::vector in = {par().gauge}; return in; } @@ -186,15 +191,30 @@ std::vector TLapEvec::getOutput(void) template void TLapEvec::setup(void) { - LOG(Message) << "setup() : start" << std::endl; - LOG(Message) << "Stout.steps=" << par().Stout.steps << ", Stout.parm=" << par().Stout.parm << std::endl; - if( gridLD ) { - LOG(Message) << "Didn't expect to need to destroy gridLD here!" << std::endl; + Cleanup(); + Environment & e{env()}; + gridHD = e.getGrid(); + gridLD = MakeLowerDimGrid( gridHD ); + Nx = gridHD->_gdimensions[Xdir]; + Ny = gridHD->_gdimensions[Ydir]; + Nz = gridHD->_gdimensions[Zdir]; + Nt = gridHD->_gdimensions[Tdir]; + // Temporaries + envTmpLat(GaugeField, "Umu"); + envTmpLat(GaugeField, "Umu_stout"); + envTmpLat(GaugeField, "Umu_smear"); + // Output objects + envCreate(DistilEP, getName(), 1, Nt); +} + +// clean up any temporaries created by setup (that aren't stored in the environment) +template +void TLapEvec::Cleanup(void) +{ + if( gridLD != nullptr ) { delete gridLD; + gridLD = nullptr; } - LOG(Message) << "Creating lower dimensional grid" << std::endl; - gridLD = MakeLowerDimGrid( env().getGrid() ); - LOG(Message) << "setup() : end" << std::endl; } // execution /////////////////////////////////////////////////////////////////// diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 7bd5e857..17d89331 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -224,8 +224,7 @@ void free_prop(Application &application) void test_LapEvec(Application &application) { - const unsigned int nt = GridDefaultLatt()[Tp]; - + const char szGaugeName[] = "gauge"; // global parameters Application::GlobalPar globalPar; globalPar.trajCounter.start = 1500; @@ -234,11 +233,12 @@ void test_LapEvec(Application &application) globalPar.runId = "test"; application.setPar(globalPar); // gauge field - application.createModule("gauge"); + application.createModule(szGaugeName); // Now make an instance of the LapEvec object MDistil::LapEvecPar par; par.Stout.steps = 173; par.Stout.parm = -9.87654321; + par.gauge = szGaugeName; application.createModule("LapEvec",par); } From 00b0f75b0d9add932110ed2edaf5fc1770edc00b Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 24 Jan 2019 12:44:06 +0000 Subject: [PATCH 025/347] Eigenvectors created. Still need to correctly set parameters for test. --- Hadrons/Modules/MDistil/Distil.hpp | 105 +++++++++++++++++++ Hadrons/Modules/MDistil/LapEvec.hpp | 149 +++++++++++++++++++++++++-- tests/hadrons/Test_hadrons_distil.cc | 21 +++- 3 files changed, 262 insertions(+), 13 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 74e35085..ec502544 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -90,6 +90,73 @@ inline void SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Bu //#endif } } + +/************************************************************************************* + + -Grad^2 (Peardon, 2009, pg 2, equation 3) + Field Type of field the operator will be applied to + GaugeField Gauge field the operator will smear using + + TODO CANDIDATE for integration into laplacian operator + should just require adding number of dimensions to act on to constructor, + where the default=all dimensions, but we could specify 3 spatial dimensions + + *************************************************************************************/ + +template +class LinOpPeardonNabla : public LinearOperatorBase, public LinearFunction { + typedef typename GaugeField::vector_type vCoeff_t; +protected: // I don't really mind if _gf is messed with ... so make this public? + //GaugeField & _gf; + int nd; // number of spatial dimensions + std::vector > > U; +public: + // Construct this operator given a gauge field and the number of dimensions it should act on + LinOpPeardonNabla( GaugeField& gf, int dimSpatial = Grid::QCD::Tdir ) : /*_gf(gf),*/ nd{dimSpatial} { + assert(dimSpatial>=1); + for( int mu = 0 ; mu < nd ; mu++ ) + U.push_back(PeekIndex(gf,mu)); + } + + // Apply this operator to "in", return result in "out" + void operator()(const Field& in, Field& out) { + assert( nd <= in._grid->Nd() ); + conformable( in, out ); + out = ( ( Real ) ( 2 * nd ) ) * in; + Field _tmp(in._grid); + typedef typename GaugeField::vector_type vCoeff_t; + //Lattice > U(in._grid); + for( int mu = 0 ; mu < nd ; mu++ ) { + //U = PeekIndex(_gf,mu); + out -= U[mu] * Cshift( in, mu, 1); + _tmp = adj( U[mu] ) * in; + out -= Cshift(_tmp,mu,-1); + } + } + + void OpDiag (const Field &in, Field &out) { assert(0); }; + void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }; + void Op (const Field &in, Field &out) { assert(0); }; + void AdjOp (const Field &in, Field &out) { assert(0); }; + void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2) { assert(0); }; + void HermOp(const Field &in, Field &out) { operator()(in,out); }; +}; + +template +class LinOpPeardonNablaHerm : public LinearFunction { +public: + OperatorFunction & _poly; + LinearOperatorBase &_Linop; + + LinOpPeardonNablaHerm(OperatorFunction & poly,LinearOperatorBase& linop) : _poly(poly), _Linop(linop) { + } + + void operator()(const Field& in, Field& out) { + _poly(_Linop,in,out); + } +}; + + END_MODULE_NAMESPACE // Grid /****************************************************************************** @@ -100,6 +167,9 @@ BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) +typedef Grid::Hadrons::EigenPack DistilEP; +typedef std::vector > > DistilNoises; + /****************************************************************************** Make a lower dimensional grid ******************************************************************************/ @@ -386,6 +456,41 @@ public: } }; +/************************************************************************************* + + Rotate eigenvectors into our phase convention + First component of first eigenvector is real and positive + + *************************************************************************************/ + +inline void RotateEigen(std::vector & evec) +{ + ColourVector cv0; + auto grid = evec[0]._grid; + std::vector siteFirst(grid->Nd(),0); + peekSite(cv0, evec[0], siteFirst); + auto & cplx0 = cv0()()(0); + if( std::imag(cplx0) == 0 ) + std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; + else { + const auto cplx0_mag{std::abs(cplx0)}; + const auto phase{std::conj(cplx0 / cplx0_mag)}; + std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (std::arg(phase) / 3.14159265) << " pi" << std::endl; + { + // TODO: Only really needed on the master slice + for( int k = 0 ; k < evec.size() ; k++ ) + evec[k] *= phase; + if(grid->IsBoss()){ + for( int c = 0 ; c < Nc ; c++ ) + cv0()()(c) *= phase; + cplx0.imag(0); // This assumes phase convention is real, positive (so I get rid of rounding error) + //pokeSite(cv0, evec[0], siteFirst); + pokeLocalSite(cv0, evec[0], siteFirst); + } + } + } +} + END_MODULE_NAMESPACE END_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 910c4315..c06cc300 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -107,6 +107,7 @@ class LapEvecPar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, std::string, gauge, + std::string, EigenPackName, StoutParameters, Stout, ChebyshevParameters, Cheby, LanczosParameters, Lanczos, @@ -125,7 +126,6 @@ class TLapEvec: public Module { public: GAUGE_TYPE_ALIASES(FImpl,); - typedef std::vector > DistilEP; public: // constructor @@ -155,6 +155,8 @@ MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); TLapEvec implementation ******************************************************************************/ +//constexpr char szEigenPackSuffix[] = "_eigenPack"; + // constructor ///////////////////////////////////////////////////////////////// template TLapEvec::TLapEvec(const std::string name) : gridLD{nullptr}, Module(name) @@ -182,7 +184,7 @@ std::vector TLapEvec::getInput(void) template std::vector TLapEvec::getOutput(void) { - std::vector out = {getName()}; + std::vector out = {getName()}; // This is the higher dimensional eigenpack return out; } @@ -195,16 +197,19 @@ void TLapEvec::setup(void) Environment & e{env()}; gridHD = e.getGrid(); gridLD = MakeLowerDimGrid( gridHD ); - Nx = gridHD->_gdimensions[Xdir]; - Ny = gridHD->_gdimensions[Ydir]; - Nz = gridHD->_gdimensions[Zdir]; - Nt = gridHD->_gdimensions[Tdir]; + Nx = gridHD->_fdimensions[Xdir]; + Ny = gridHD->_fdimensions[Ydir]; + Nz = gridHD->_fdimensions[Zdir]; + Nt = gridHD->_fdimensions[Tdir]; // Temporaries envTmpLat(GaugeField, "Umu"); envTmpLat(GaugeField, "Umu_stout"); envTmpLat(GaugeField, "Umu_smear"); + envTmp(LatticeGaugeField, "UmuNoTime",1,LatticeGaugeField(gridLD)); + envTmp(LatticeColourVector, "src",1,LatticeColourVector(gridLD)); + envTmp(std::vector, "eig",1,std::vector(Nt)); // Output objects - envCreate(DistilEP, getName(), 1, Nt); + envCreate(DistilEP, getName(), 1, par().Lanczos.Nvec, gridHD ); } // clean up any temporaries created by setup (that aren't stored in the environment) @@ -215,14 +220,142 @@ void TLapEvec::Cleanup(void) delete gridLD; gridLD = nullptr; } + gridHD = nullptr; } +/****************************************************************************** + Calculate low-mode eigenvalues of the Laplacian + ******************************************************************************/ + // execution /////////////////////////////////////////////////////////////////// template void TLapEvec::execute(void) { LOG(Message) << "execute() : start" << std::endl; - LOG(Message) << "Stout.steps=" << par().Stout.steps << ", Stout.parm=" << par().Stout.parm << std::endl; + + // Alii for parameters + const int &TI{par().Distil.TI}; + const int &LI{par().Distil.LI}; + const int &nnoise{par().Distil.Nnoise}; + const int &tsrc{par().Distil.tSrc}; + const LanczosParameters &LPar{par().Lanczos}; + const int &nvec{LPar.Nvec}; + const bool exact_distillation{TI==Nt && LI==nvec}; + const bool full_tdil{TI==Nt}; + const int &Nt_inv{full_tdil ? 1 : TI}; + const ChebyshevParameters &ChebPar{par().Cheby}; + + // Assertions on the parameters we read + assert(TI>1); + assert(LI>1); + if(exact_distillation) + assert(nnoise==1); + else + assert(nnoise>1); + + // Stout smearing + envGetTmp(GaugeField, Umu); + envGetTmp(GaugeField, Umu_smear); + LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu) << std::endl; + { + envGetTmp(GaugeField, Umu_stout); + const int &Steps{par().Stout.steps}; + Smear_Stout LS(par().Stout.parm); + for (int i = 0; i < Steps; i++) { + LS.smear(Umu_stout, Umu_smear); + Umu_smear = Umu_stout; + } + } + LOG(Message) << "Smeared plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; + + // For debugging only, write logging output to a local file + std::ofstream * ll = nullptr; + const int rank{gridHD->ThisRank()}; + if((0)) { // debug to a local log file + std::string filename{"Local_"}; + filename.append(std::to_string(rank)); + filename.append(".log"); + ll = new std::ofstream(filename); + } + + //////////////////////////////////////////////////////////////////////// + // Invert Peardon Nabla operator separately on each time-slice + //////////////////////////////////////////////////////////////////////// + + std::string sEigenPackName(par().EigenPackName); + bool bReturnValue = true; + auto & eig4d = envGet(DistilEP, getName() ); + eig4d.resize(nvec,gridHD); + envGetTmp(std::vector, eig); // Eigenpack for each timeslice + envGetTmp(LatticeGaugeField, UmuNoTime); // Gauge field without time dimension + envGetTmp(LatticeColourVector, src); + const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; + const int Ntfirst{gridHD->LocalStarts()[Tdir]}; + for(int t=Ntfirst;bReturnValue && t PeardonNabla(UmuNoTime); + std::cout << "Chebyshev preconditioning to order " << ChebPar.PolyOrder + << " with parameters (alpha,beta) = (" << ChebPar.alpha << "," << ChebPar.beta << ")" << std::endl; + Chebyshev Cheb(ChebPar.alpha,ChebPar.beta,ChebPar.PolyOrder); + + //from Test_Cheby.cc + if ( ((0)) && Ntfirst == 0 && t==0) { + std::ofstream of("cheby_" + std::to_string(ChebPar.alpha) + "_" + std::to_string(ChebPar.beta) + "_" + std::to_string(ChebPar.PolyOrder)); + Cheb.csv(of); + } + + // Construct source vector according to Test_dwf_compressed_lanczos.cc + src=11.0; + RealD nn = norm2(src); + nn = Grid::sqrt(nn); + src = src * (1.0/nn); + + GridLogIRL.Active(1); + LinOpPeardonNablaHerm PeardonNablaCheby(Cheb,PeardonNabla); + ImplicitlyRestartedLanczos IRL(PeardonNablaCheby,PeardonNabla,LPar.Nvec,LPar.Nk,LPar.Nk+LPar.Np,LPar.resid,LPar.MaxIt); + int Nconv = 0; + + if(ll) *ll << t << " : Before IRL.calc()" << std::endl; + IRL.calc(eig[t].eval,eig[t].evec,src,Nconv); + if(ll) *ll << t << " : After IRL.calc()" << std::endl; + if( Nconv < LPar.Nvec ) { + bReturnValue = false; + if(ll) *ll << t << " : Convergence error : Only " << Nconv << " converged!" << std::endl; + } else { + if( Nconv > LPar.Nvec ) + eig[t].resize( LPar.Nvec, gridLD ); + std::cout << GridLogMessage << "Timeslice " << t << " has " << eig[t].eval.size() << " eigenvalues and " << eig[t].evec.size() << " eigenvectors." << std::endl; + + // Now rotate the eigenvectors into our phase convention + RotateEigen( eig[t].evec ); + + // Write the eigenvectors and eigenvalues to disk + //std::cout << GridLogMessage << "Writing eigenvalues/vectors to " << pszEigenPack << std::endl; + eig[t].record.operatorXml="Michael"; + eig[t].record.solverXml="Felix"; + eig[t].write(sEigenPackName,false,t); + //std::cout << GridLogMessage << "Written eigenvectors" << std::endl; + } + for (int i=0;i(szGaugeName); // Now make an instance of the LapEvec object - MDistil::LapEvecPar par; - par.Stout.steps = 173; - par.Stout.parm = -9.87654321; - par.gauge = szGaugeName; - application.createModule("LapEvec",par); + MDistil::LapEvecPar p; + p.gauge = szGaugeName; + p.EigenPackName = "ePack"; + p.Distil.TI = 8; + p.Distil.LI = 3; + p.Distil.Nnoise = 2; + p.Distil.tSrc = 0; + p.Stout.steps = 3; + p.Stout.parm = 0.2; + p.Cheby.PolyOrder = 11; + p.Cheby.alpha = 0.3; + p.Cheby.beta = 12.5; + p.Lanczos.Nvec = 5; + p.Lanczos.Nk = 6; + p.Lanczos.Np = 2; + application.createModule("LapEvec",p); } ///////////////////////////////////////////////////////////// From cf85f0388d9d6683b1fea7b1293eb0a61568cd52 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 24 Jan 2019 13:26:05 +0000 Subject: [PATCH 026/347] Still debugging eigenvector parameters --- Hadrons/Modules/MDistil/LapEvec.hpp | 42 ++++++++++++++++++++++++---- tests/hadrons/Test_hadrons_distil.cc | 4 ++- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index c06cc300..54a1b028 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -67,13 +67,13 @@ struct ChebyshevParameters: Serializable { struct LanczosParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters, - int, Nstart, + //int, Nstart, int, Nvec, int, Nk, - int, Nm, // Not currently used + //int, Nm, // Not currently used int, Np, int, MaxIt, - int, MinRes, + //int, MinRes, double, resid) LanczosParameters() = default; template LanczosParameters(Reader& Reader){read(Reader,"Lanczos",*this);} @@ -202,7 +202,7 @@ void TLapEvec::setup(void) Nz = gridHD->_fdimensions[Zdir]; Nt = gridHD->_fdimensions[Tdir]; // Temporaries - envTmpLat(GaugeField, "Umu"); + //envTmpLat(GaugeField, "Umu"); envTmpLat(GaugeField, "Umu_stout"); envTmpLat(GaugeField, "Umu_smear"); envTmp(LatticeGaugeField, "UmuNoTime",1,LatticeGaugeField(gridLD)); @@ -253,9 +253,41 @@ void TLapEvec::execute(void) else assert(nnoise>1); + // Debugging only + //envGetTmp(GaugeField, Umu); + auto &Umu = envGet(GaugeField, par().gauge); + if((1)) { + const std::vector seeds({1, 2, 3, 4, 5}); + GridParallelRNG pRNG4d(gridHD); + pRNG4d.SeedFixedIntegers(seeds); + std::cout << GridLogMessage << "now hot config" << std::endl; + SU::HotConfiguration(pRNG4d, Umu); + std::cout << GridLogMessage << "hot cfg done." << std::endl; + + // Set up the SAME gauge field on every time plane + // int Nt = grid4d->gDimensions()[Tdir]; + Grid_unquiesce_nodes(); + + auto Usft = Umu; + Lattice > coor(gridHD); + LatticeCoordinate(coor,Tdir); + for(int t=1;t 7,0,1,2,3,4,5,6 t=1 + // 0,0,2,3,4,5,6,7 6,7,0,1,2,3,4,5 t=2 + // 0,0,0,3,4,5,6,7 5,6,7,0,1,2,3,4 t=3 + //... + + Usft = Cshift(Usft,Tdir,-1); + Umu = where(coor==t,Usft,Umu); + } + // std::cout << "Umu is "<::avgPlaquette(Umu) << std::endl; { envGetTmp(GaugeField, Umu_stout); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index e63a02e7..50d594c9 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -233,7 +233,7 @@ void test_LapEvec(Application &application) globalPar.runId = "test"; application.setPar(globalPar); // gauge field - application.createModule(szGaugeName); + application.createModule(szGaugeName); // Now make an instance of the LapEvec object MDistil::LapEvecPar p; p.gauge = szGaugeName; @@ -250,6 +250,8 @@ void test_LapEvec(Application &application) p.Lanczos.Nvec = 5; p.Lanczos.Nk = 6; p.Lanczos.Np = 2; + p.Lanczos.MaxIt = 1000; + p.Lanczos.resid = 1e-2; application.createModule("LapEvec",p); } From a4c1ab6147ec174a91ad830a3c1f386b77a42960 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 24 Jan 2019 16:12:19 +0000 Subject: [PATCH 027/347] all modules linked in test prog --- tests/hadrons/Test_hadrons_distil.cc | 37 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 50d594c9..6d24b929 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -237,7 +237,7 @@ void test_LapEvec(Application &application) // Now make an instance of the LapEvec object MDistil::LapEvecPar p; p.gauge = szGaugeName; - p.EigenPackName = "ePack"; + //p.EigenPackName = "ePack"; p.Distil.TI = 8; p.Distil.LI = 3; p.Distil.Nnoise = 2; @@ -283,6 +283,7 @@ void test_DistilVectors(Application &application) { const unsigned int nt = GridDefaultLatt()[Tp]; + const char szGaugeName[] = "gauge"; // global parameters Application::GlobalPar globalPar; globalPar.trajCounter.start = 1500; @@ -290,9 +291,30 @@ void test_DistilVectors(Application &application) globalPar.trajCounter.step = 20; globalPar.runId = "test"; application.setPar(globalPar); + // gauge field + application.createModule(szGaugeName); + // Now make an instance of the LapEvec object + MDistil::LapEvecPar p; + p.gauge = szGaugeName; + //p.EigenPackName = "eigenPack"; + p.Distil.TI = 8; + p.Distil.LI = 3; + p.Distil.Nnoise = 2; + p.Distil.tSrc = 0; + p.Stout.steps = 3; + p.Stout.parm = 0.2; + p.Cheby.PolyOrder = 11; + p.Cheby.alpha = 0.3; + p.Cheby.beta = 12.5; + p.Lanczos.Nvec = 5; + p.Lanczos.Nk = 6; + p.Lanczos.Np = 2; + p.Lanczos.MaxIt = 1000; + p.Lanczos.resid = 1e-2; + application.createModule("LapEvec",p); // PerambLight parameters MDistil::PerambLight::Par PerambPar; - PerambPar.eigenPack="ePack"; + PerambPar.eigenPack="LapEvec_eigenPack"; PerambPar.tsrc = 0; PerambPar.nnoise = 1; PerambPar.LI=6; @@ -307,11 +329,12 @@ void test_DistilVectors(Application &application) PerambPar.Ls=16; PerambPar.CGPrecision=1e-8; PerambPar.MaxIterations=10000; + application.createModule("Peramb",PerambPar); // DistilVectors parameters MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="noise"; - DistilVecPar.perambulator="perambulator"; - DistilVecPar.eigenPack="ePack"; + DistilVecPar.noise="Peramb_noise"; + DistilVecPar.perambulator="Peramb_perambulator_light"; + DistilVecPar.eigenPack="LapEvec_eigenPack"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; DistilVecPar.LI=6; @@ -322,9 +345,7 @@ void test_DistilVectors(Application &application) DistilVecPar.Nt=64; DistilVecPar.Nt_inv=1; // gauge field - application.createModule("gauge"); - // Now make an instance of the Perambulator object - application.createModule("PerambulatorsInstance",PerambPar); + //application.createModule("gauge"); // Now make an instance of the DistilVectors object application.createModule("DistilVectorsInstance",DistilVecPar); } From dfb7fb1d9f848caa786c7fc80cea9fed2a925af4 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 24 Jan 2019 16:30:13 +0000 Subject: [PATCH 028/347] LapEvec test works on --grid 4.4.4.8 --- Hadrons/Modules/MDistil/LapEvec.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 54a1b028..a86af902 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -323,6 +323,8 @@ void TLapEvec::execute(void) envGetTmp(LatticeColourVector, src); const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; const int Ntfirst{gridHD->LocalStarts()[Tdir]}; + const char DefaultOperatorXml[] = "Michael"; + const char DefaultsolverXml[] = "Felix"; for(int t=Ntfirst;bReturnValue && t::execute(void) } } + // Now write out the 4d eigenvectors + eig4d.record.operatorXml = DefaultOperatorXml; + eig4d.record.solverXml = DefaultsolverXml; + eig4d.write(sEigenPackName,false); + // Close the local debugging log file if( ll ) { *ll << " Returning " << bReturnValue << std::endl; From 577cdf1d72b846c685ca983e35cb3b56ba64d81d Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 24 Jan 2019 18:50:18 +0000 Subject: [PATCH 029/347] Simplified tests --- Hadrons/Modules/MDistil/LapEvec.hpp | 107 ++++++----- tests/hadrons/Test_hadrons_distil.cc | 267 +++------------------------ 2 files changed, 78 insertions(+), 296 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index a86af902..7496accf 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -79,17 +79,17 @@ struct LanczosParameters: Serializable { template LanczosParameters(Reader& Reader){read(Reader,"Lanczos",*this);} }; -struct DistilParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, - int, TI, - int, LI, - int, Nnoise, - int, Ls, // For makeFiveDimGrid - int, tSrc) - DistilParameters() = default; - template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} -}; - +/*struct DistilParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters + ,int, TI + ,int, LI + ,int, Nnoise + ,int, tSrc + )//,int, Ls) // For makeFiveDimGrid + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} + }; + struct SolverParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(SolverParameters, double, CGPrecision, @@ -98,21 +98,21 @@ struct SolverParameters: Serializable { double, M5) SolverParameters() = default; template SolverParameters(Reader& Reader){read(Reader,"Solver",*this);} -}; +};*/ // These are the actual parameters passed to the module during construction class LapEvecPar: Serializable { public: - GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, - std::string, gauge, - std::string, EigenPackName, - StoutParameters, Stout, - ChebyshevParameters, Cheby, - LanczosParameters, Lanczos, - DistilParameters, Distil, - SolverParameters, Solver); + GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar + ,std::string, gauge + //,std::string, EigenPackName + ,StoutParameters, Stout + ,ChebyshevParameters, Cheby + ,LanczosParameters, Lanczos + //,DistilParameters, Distil + )//,SolverParameters, Solver) }; /****************************************************************************** @@ -231,69 +231,69 @@ void TLapEvec::Cleanup(void) template void TLapEvec::execute(void) { - LOG(Message) << "execute() : start" << std::endl; + LOG(Message) << "execute() : start for " << getName() << std::endl; // Alii for parameters - const int &TI{par().Distil.TI}; - const int &LI{par().Distil.LI}; - const int &nnoise{par().Distil.Nnoise}; - const int &tsrc{par().Distil.tSrc}; - const LanczosParameters &LPar{par().Lanczos}; - const int &nvec{LPar.Nvec}; - const bool exact_distillation{TI==Nt && LI==nvec}; - const bool full_tdil{TI==Nt}; - const int &Nt_inv{full_tdil ? 1 : TI}; + //const int &TI{par().Distil.TI}; + //const int &LI{par().Distil.LI}; + //const int &nnoise{par().Distil.Nnoise}; + //const int &tsrc{par().Distil.tSrc}; const ChebyshevParameters &ChebPar{par().Cheby}; + const LanczosParameters &LPar{par().Lanczos}; + const int &nvec{LPar.Nvec}; + //const bool exact_distillation{TI==Nt && LI==nvec}; + //const bool full_tdil{TI==Nt}; + //const int &Nt_inv{full_tdil ? 1 : TI}; // Assertions on the parameters we read - assert(TI>1); - assert(LI>1); - if(exact_distillation) - assert(nnoise==1); - else - assert(nnoise>1); + //assert(TI>1); + //assert(LI>1); + //if(exact_distillation) + //assert(nnoise==1); + //else + //assert(nnoise>1); // Debugging only //envGetTmp(GaugeField, Umu); auto &Umu = envGet(GaugeField, par().gauge); - if((1)) { - const std::vector seeds({1, 2, 3, 4, 5}); - GridParallelRNG pRNG4d(gridHD); - pRNG4d.SeedFixedIntegers(seeds); - std::cout << GridLogMessage << "now hot config" << std::endl; - SU::HotConfiguration(pRNG4d, Umu); - std::cout << GridLogMessage << "hot cfg done." << std::endl; + envGetTmp(GaugeField, Umu_smear); + if((0)) { + //const std::vector seeds({1, 2, 3, 4, 5}); + //GridParallelRNG pRNG4d(gridHD); + //pRNG4d.SeedFixedIntegers(seeds); + //std::cout << GridLogMessage << "now hot config" << std::endl; + //SU::HotConfiguration(pRNG4d, Umu); + //std::cout << GridLogMessage << "hot cfg done." << std::endl; // Set up the SAME gauge field on every time plane // int Nt = grid4d->gDimensions()[Tdir]; Grid_unquiesce_nodes(); - auto Usft = Umu; + Umu_smear = Umu; Lattice > coor(gridHD); LatticeCoordinate(coor,Tdir); for(int t=1;t 7,0,1,2,3,4,5,6 t=1 // 0,0,2,3,4,5,6,7 6,7,0,1,2,3,4,5 t=2 // 0,0,0,3,4,5,6,7 5,6,7,0,1,2,3,4 t=3 //... - Usft = Cshift(Usft,Tdir,-1); - Umu = where(coor==t,Usft,Umu); + Umu_smear = Cshift(Umu_smear,Tdir,-1); + Umu = where(coor==t,Umu_smear,Umu); } // std::cout << "Umu is "<::avgPlaquette(Umu) << std::endl; { + const StoutParameters &Stout{par().Stout}; envGetTmp(GaugeField, Umu_stout); - const int &Steps{par().Stout.steps}; - Smear_Stout LS(par().Stout.parm); - for (int i = 0; i < Steps; i++) { + Smear_Stout LS(Stout.parm); + for (int i = 0; i < Stout.steps; i++) { LS.smear(Umu_stout, Umu_smear); Umu_smear = Umu_stout; } @@ -314,10 +314,9 @@ void TLapEvec::execute(void) // Invert Peardon Nabla operator separately on each time-slice //////////////////////////////////////////////////////////////////////// - std::string sEigenPackName(par().EigenPackName); + std::string sEigenPackName(getName()); bool bReturnValue = true; auto & eig4d = envGet(DistilEP, getName() ); - eig4d.resize(nvec,gridHD); envGetTmp(std::vector, eig); // Eigenpack for each timeslice envGetTmp(LatticeGaugeField, UmuNoTime); // Gauge field without time dimension envGetTmp(LatticeColourVector, src); @@ -330,9 +329,7 @@ void TLapEvec::execute(void) std::cout << GridLogMessage << " Compute eigenpack, Timeslice = " << t << std::endl; std::cout << GridLogMessage << "------------------------------------------------------------" << std::endl; - LOG(Message) << "eig.size()=" << eig.size() << std::endl; eig[t].resize(LPar.Nk+LPar.Np,gridLD); - LOG(Message) << "After eig[t].resize" << std::endl; // Construct smearing operator ExtractSliceLocal(UmuNoTime,Umu_smear,0,t-Ntfirst,Grid::QCD::Tdir); // switch to 3d/4d objects diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 6d24b929..999f79f7 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -34,19 +34,11 @@ using namespace Grid; using namespace Hadrons; ///////////////////////////////////////////////////////////// -// This is copied from the free propagator test -// Just used as an example - will be deleted +// Test creation of laplacian eigenvectors ///////////////////////////////////////////////////////////// -void free_prop(Application &application) +void test_Global(Application &application) { - std::vector flavour = {"h"}; //{"l", "s", "c1", "c2", "c3"}; - std::vector mass = {.2}; //{.01, .04, .2 , .25 , .3 }; - std::vector lepton_flavour = {"mu"}; - std::vector lepton_mass = {.2}; - - unsigned int nt = GridDefaultLatt()[Tp]; - // global parameters Application::GlobalPar globalPar; globalPar.trajCounter.start = 1500; @@ -54,168 +46,6 @@ void free_prop(Application &application) globalPar.trajCounter.step = 20; globalPar.runId = "test"; application.setPar(globalPar); - // gauge field - application.createModule("gauge"); - // unit gauge field for lepton - application.createModule("free_gauge"); - // pt source - MSource::Point::Par ptPar; - ptPar.position = "0 0 0 0"; - application.createModule("pt", ptPar); - // sink - MSink::Point::Par sinkPar; - sinkPar.mom = "0 0 0"; - application.createModule("sink", sinkPar); - - // set fermion boundary conditions to be periodic space, antiperiodic time. - std::string boundary = "1 1 1 -1"; - - - //Propagators from FFT and Feynman rules - for (unsigned int i = 0; i < lepton_mass.size(); ++i) - { - //DWF actions - MAction::DWF::Par actionPar_lep; - actionPar_lep.gauge = "free_gauge"; - actionPar_lep.Ls = 8; - actionPar_lep.M5 = 1.8; - actionPar_lep.mass = lepton_mass[i]; - actionPar_lep.boundary = boundary; - application.createModule("free_DWF_" + lepton_flavour[i], actionPar_lep); - - //DWF free propagators - MFermion::FreeProp::Par freePar; - freePar.source = "pt"; - freePar.action = "free_DWF_" + lepton_flavour[i]; - freePar.twist = "0 0 0 0.5"; - freePar.mass = lepton_mass[i]; - application.createModule("Lpt_" + lepton_flavour[i], - freePar); - - //Wilson actions - MAction::Wilson::Par actionPar_lep_W; - actionPar_lep_W.gauge = "free_gauge"; - actionPar_lep_W.mass = lepton_mass[i]; - actionPar_lep_W.boundary = boundary; - application.createModule("free_W_" + lepton_flavour[i], actionPar_lep_W); - - //Wilson free propagators - MFermion::FreeProp::Par freePar_W; - freePar_W.source = "pt"; - freePar_W.action = "free_W_" + lepton_flavour[i]; - freePar_W.twist = "0 0 0 0.5"; - freePar_W.mass = lepton_mass[i]; - application.createModule("W_Lpt_" + lepton_flavour[i], - freePar_W); - } - - //Propagators from inversion - for (unsigned int i = 0; i < flavour.size(); ++i) - { - //DWF actions - MAction::DWF::Par actionPar; - actionPar.gauge = "gauge"; - actionPar.Ls = 8; - actionPar.M5 = 1.8; - actionPar.mass = mass[i]; - actionPar.boundary = boundary; - application.createModule("DWF_" + flavour[i], actionPar); - - // solvers - MSolver::RBPrecCG::Par solverPar; - solverPar.action = "DWF_" + flavour[i]; - solverPar.residual = 1.0e-8; - solverPar.maxIteration = 10000; - application.createModule("CG_" + flavour[i], - solverPar); - - //DWF propagators - MFermion::GaugeProp::Par quarkPar; - quarkPar.solver = "CG_" + flavour[i]; - quarkPar.source = "pt"; - application.createModule("Qpt_" + flavour[i], - quarkPar); - - //Wilson actions - MAction::Wilson::Par actionPar_W; - actionPar_W.gauge = "gauge"; - actionPar_W.mass = mass[i]; - actionPar_W.boundary = boundary; - application.createModule("W_" + flavour[i], actionPar_W); - - // solvers - MSolver::RBPrecCG::Par solverPar_W; - solverPar_W.action = "W_" + flavour[i]; - solverPar_W.residual = 1.0e-8; - solverPar_W.maxIteration = 10000; - application.createModule("W_CG_" + flavour[i], - solverPar_W); - - //Wilson propagators - MFermion::GaugeProp::Par quarkPar_W; - quarkPar_W.solver = "W_CG_" + flavour[i]; - quarkPar_W.source = "pt"; - application.createModule("W_Qpt_" + flavour[i], - quarkPar_W); - - } - - - //2pt contraction for Propagators from FFT and Feynman rules - for (unsigned int i = 0; i < lepton_flavour.size(); ++i) - for (unsigned int j = i; j < lepton_flavour.size(); ++j) - { - //2pt function contraction DWF - MContraction::Meson::Par freemesPar; - freemesPar.output = "2pt_free/DWF_L_pt_" + lepton_flavour[i] + lepton_flavour[j]; - freemesPar.q1 = "Lpt_" + lepton_flavour[i]; - freemesPar.q2 = "Lpt_" + lepton_flavour[j]; - freemesPar.gammas = "(Gamma5 Gamma5)"; - freemesPar.sink = "sink"; - application.createModule("meson_L_pt_" - + lepton_flavour[i] + lepton_flavour[j], - freemesPar); - - //2pt function contraction Wilson - MContraction::Meson::Par freemesPar_W; - freemesPar_W.output = "2pt_free/W_L_pt_" + lepton_flavour[i] + lepton_flavour[j]; - freemesPar_W.q1 = "W_Lpt_" + lepton_flavour[i]; - freemesPar_W.q2 = "W_Lpt_" + lepton_flavour[j]; - freemesPar_W.gammas = "(Gamma5 Gamma5)"; - freemesPar_W.sink = "sink"; - application.createModule("W_meson_L_pt_" - + lepton_flavour[i] + lepton_flavour[j], - freemesPar_W); - - } - - //2pt contraction for Propagators from inverion - for (unsigned int i = 0; i < flavour.size(); ++i) - for (unsigned int j = i; j < flavour.size(); ++j) - { - //2pt function contraction DWF - MContraction::Meson::Par mesPar; - mesPar.output = "2pt_free/DWF_pt_" + flavour[i] + flavour[j]; - mesPar.q1 = "Qpt_" + flavour[i]; - mesPar.q2 = "Qpt_" + flavour[j]; - mesPar.gammas = "(Gamma5 Gamma5)"; - mesPar.sink = "sink"; - application.createModule("meson_pt_" - + flavour[i] + flavour[j], - mesPar); - - - //2pt function contraction Wilson - MContraction::Meson::Par mesPar_W; - mesPar_W.output = "2pt_free/W_pt_" + flavour[i] + flavour[j]; - mesPar_W.q1 = "W_Qpt_" + flavour[i]; - mesPar_W.q2 = "W_Qpt_" + flavour[j]; - mesPar_W.gammas = "(Gamma5 Gamma5)"; - mesPar_W.sink = "sink"; - application.createModule("W_meson_pt_" - + flavour[i] + flavour[j], - mesPar_W); - } } ///////////////////////////////////////////////////////////// @@ -225,23 +55,16 @@ void free_prop(Application &application) void test_LapEvec(Application &application) { const char szGaugeName[] = "gauge"; - // global parameters - Application::GlobalPar globalPar; - globalPar.trajCounter.start = 1500; - globalPar.trajCounter.end = 1520; - globalPar.trajCounter.step = 20; - globalPar.runId = "test"; - application.setPar(globalPar); // gauge field application.createModule(szGaugeName); // Now make an instance of the LapEvec object MDistil::LapEvecPar p; p.gauge = szGaugeName; //p.EigenPackName = "ePack"; - p.Distil.TI = 8; - p.Distil.LI = 3; - p.Distil.Nnoise = 2; - p.Distil.tSrc = 0; + //p.Distil.TI = 8; + //p.Distil.LI = 3; + //p.Distil.Nnoise = 2; + //p.Distil.tSrc = 0; p.Stout.steps = 3; p.Stout.parm = 0.2; p.Cheby.PolyOrder = 11; @@ -261,60 +84,9 @@ void test_LapEvec(Application &application) void test_Perambulators(Application &application) { - const unsigned int nt = GridDefaultLatt()[Tp]; - - // global parameters - Application::GlobalPar globalPar; - globalPar.trajCounter.start = 3000; - globalPar.trajCounter.end = 3040; - globalPar.trajCounter.step = 40; - globalPar.runId = "test"; - application.setPar(globalPar); - // gauge field - application.createModule("gauge"); - // Now make an instance of the LapEvec object - application.createModule("PerambulatorsInstance"); -} -///////////////////////////////////////////////////////////// -// DistilVectors -///////////////////////////////////////////////////////////// - -void test_DistilVectors(Application &application) -{ - const unsigned int nt = GridDefaultLatt()[Tp]; - - const char szGaugeName[] = "gauge"; - // global parameters - Application::GlobalPar globalPar; - globalPar.trajCounter.start = 1500; - globalPar.trajCounter.end = 1520; - globalPar.trajCounter.step = 20; - globalPar.runId = "test"; - application.setPar(globalPar); - // gauge field - application.createModule(szGaugeName); - // Now make an instance of the LapEvec object - MDistil::LapEvecPar p; - p.gauge = szGaugeName; - //p.EigenPackName = "eigenPack"; - p.Distil.TI = 8; - p.Distil.LI = 3; - p.Distil.Nnoise = 2; - p.Distil.tSrc = 0; - p.Stout.steps = 3; - p.Stout.parm = 0.2; - p.Cheby.PolyOrder = 11; - p.Cheby.alpha = 0.3; - p.Cheby.beta = 12.5; - p.Lanczos.Nvec = 5; - p.Lanczos.Nk = 6; - p.Lanczos.Np = 2; - p.Lanczos.MaxIt = 1000; - p.Lanczos.resid = 1e-2; - application.createModule("LapEvec",p); // PerambLight parameters MDistil::PerambLight::Par PerambPar; - PerambPar.eigenPack="LapEvec_eigenPack"; + PerambPar.eigenPack="LapEvec"; PerambPar.tsrc = 0; PerambPar.nnoise = 1; PerambPar.LI=6; @@ -330,6 +102,13 @@ void test_DistilVectors(Application &application) PerambPar.CGPrecision=1e-8; PerambPar.MaxIterations=10000; application.createModule("Peramb",PerambPar); +} +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +void test_DistilVectors(Application &application) +{ // DistilVectors parameters MDistil::DistilVectors::Par DistilVecPar; DistilVecPar.noise="Peramb_noise"; @@ -421,16 +200,22 @@ int main(int argc, char *argv[]) // For now perform free propagator test - replace this with distillation test(s) LOG(Message) << "====== Creating xml for test " << iTestNum << " ======" << std::endl; - const unsigned int nt = GridDefaultLatt()[Tp]; + //const unsigned int nt = GridDefaultLatt()[Tp]; - switch(iTestNum) { - case 0: - free_prop( application ); - break; + switch(iTestNum) { case 1: + test_Global( application ); test_LapEvec( application ); break; - default: // 2 + case 2: + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + break; + default: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); test_DistilVectors( application ); break; } From 2568f5b925411fd660c4b38b28de016c39f66138 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 25 Jan 2019 12:37:18 +0000 Subject: [PATCH 030/347] bugfix in prambLight --- Hadrons/Modules/MDistil/PerambLight.hpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index dbac54ca..ced85ccf 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -155,10 +155,19 @@ void TPerambLight::execute(void) LatticeGaugeField Umu(grid4d); FieldMetaData header; - std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); - std::cout << GridLogMessage << "Loading NERSC configuration from '" << fileName << "'" << std::endl; - NerscIO::readConfiguration(Umu, header, fileName); - std::cout << GridLogMessage << "reading done." << std::endl; + if((1)){ + const std::vector seeds({1, 2, 3, 4, 5}); + GridParallelRNG pRNG4d(grid4d); + pRNG4d.SeedFixedIntegers(seeds); + std::cout << GridLogMessage << "now hot config" << std::endl; + SU::HotConfiguration(pRNG4d, Umu); + std::cout << GridLogMessage << "hot cfg done." << std::endl; + } else { + std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); + std::cout << GridLogMessage << "Loading NERSC configuration from '" << fileName << "'" << std::endl; + NerscIO::readConfiguration(Umu, header, fileName); + std::cout << GridLogMessage << "reading done." << std::endl; + } envGetTmp(LatticeSpinColourVector, dist_source); envGetTmp(LatticeSpinColourVector, tmp2); @@ -246,7 +255,6 @@ void TPerambLight::execute(void) if( it >= Ntfirst && it < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced - std::cout << "LapH source vector from noise " << it << " and dilution component (d_k,d_t,d_alpha) : (" << ik << ","<< is << ")" << std::endl; ExtractSliceLocal(evec3d,epack.evec[ik],0,it,3); tmp3d_nospin = evec3d * noise[inoise + nnoise*(it + Nt*(ik+nvec*is))]; //tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector @@ -274,8 +282,11 @@ void TPerambLight::execute(void) for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); for (int ivec = 0; ivec < nvec; ivec++) { + std::cout << "1" << std::endl; ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + std::cout << "2" << std::endl; pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); + std::cout << "3" << std::endl; } } } From 7496da0987ae84558b9005a19c8956748b8d6d3b Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 25 Jan 2019 13:08:56 +0000 Subject: [PATCH 031/347] bugfix in prambLight --- Hadrons/Modules/MDistil/PerambLight.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index ced85ccf..f1d2c921 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -102,10 +102,11 @@ void TPerambLight::setup(void) int TI=par().TI; int nnoise=par().nnoise; int Nt=par().Nt; + int Nt_inv=par().Nt_inv; int nvec=par().nvec; envCreate(Perambulator, getName() + "_perambulator_light", 1, - LI*SI*TI*nnoise*nvec*Nt); + Nt,nvec,LI,nnoise,Nt_inv,SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Ns*Nt*nnoise); @@ -278,8 +279,10 @@ void TPerambLight::execute(void) // current_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; for (int is = 0; is < Ns; is++) { + std::cout << "is" << is << std::endl; result_nospin = peekSpin(result,is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + std::cout << "t" << t << std::endl; ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); for (int ivec = 0; ivec < nvec; ivec++) { std::cout << "1" << std::endl; From d15bf4b8e1a1f755a9f3b998871ad583e20c0cc8 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 25 Jan 2019 13:26:48 +0000 Subject: [PATCH 032/347] Added trajectory number to output file --- Hadrons/Modules/MDistil/Distil.hpp | 1 + Hadrons/Modules/MDistil/LapEvec.hpp | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index ec502544..a6e87d57 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -36,6 +36,7 @@ /****************************************************************************** This potentially belongs in CartesianCommunicator + Turns out I don't actually need this when running inside hadrons ******************************************************************************/ BEGIN_MODULE_NAMESPACE(Grid) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 7496accf..1a0b2b00 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -315,6 +315,8 @@ void TLapEvec::execute(void) //////////////////////////////////////////////////////////////////////// std::string sEigenPackName(getName()); + sEigenPackName.append("_"); + sEigenPackName.append(std::to_string(vm().getTrajectory())); bool bReturnValue = true; auto & eig4d = envGet(DistilEP, getName() ); envGetTmp(std::vector, eig); // Eigenpack for each timeslice @@ -369,12 +371,14 @@ void TLapEvec::execute(void) // Now rotate the eigenvectors into our phase convention RotateEigen( eig[t].evec ); - // Write the eigenvectors and eigenvalues to disk - //std::cout << GridLogMessage << "Writing eigenvalues/vectors to " << pszEigenPack << std::endl; - eig[t].record.operatorXml = DefaultOperatorXml; - eig[t].record.solverXml = DefaultsolverXml; - eig[t].write(sEigenPackName,false,t); - //std::cout << GridLogMessage << "Written eigenvectors" << std::endl; + if((1)) { // Debugging only + // Write the eigenvectors and eigenvalues to disk + //std::cout << GridLogMessage << "Writing eigenvalues/vectors to " << pszEigenPack << std::endl; + eig[t].record.operatorXml = DefaultOperatorXml; + eig[t].record.solverXml = DefaultsolverXml; + eig[t].write(sEigenPackName,false,t); + //std::cout << GridLogMessage << "Written eigenvectors" << std::endl; + } } for (int i=0;i Date: Fri, 25 Jan 2019 13:44:19 +0000 Subject: [PATCH 033/347] test works up to perambulators now --- Hadrons/Modules/MDistil/PerambLight.hpp | 5 ----- tests/hadrons/Test_hadrons_distil.cc | 9 +++------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index f1d2c921..76637233 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -279,17 +279,12 @@ void TPerambLight::execute(void) // current_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; for (int is = 0; is < Ns; is++) { - std::cout << "is" << is << std::endl; result_nospin = peekSpin(result,is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - std::cout << "t" << t << std::endl; ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); for (int ivec = 0; ivec < nvec; ivec++) { - std::cout << "1" << std::endl; ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); - std::cout << "2" << std::endl; pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); - std::cout << "3" << std::endl; } } } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 999f79f7..ffb642a0 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -89,10 +89,10 @@ void test_Perambulators(Application &application) PerambPar.eigenPack="LapEvec"; PerambPar.tsrc = 0; PerambPar.nnoise = 1; - PerambPar.LI=6; + PerambPar.LI=5; PerambPar.SI=4; PerambPar.TI=64; - PerambPar.nvec=6; + PerambPar.nvec=5; PerambPar.Ns=4; PerambPar.Nt=64; PerambPar.Nt_inv=1; @@ -113,7 +113,7 @@ void test_DistilVectors(Application &application) MDistil::DistilVectors::Par DistilVecPar; DistilVecPar.noise="Peramb_noise"; DistilVecPar.perambulator="Peramb_perambulator_light"; - DistilVecPar.eigenPack="LapEvec_eigenPack"; + DistilVecPar.eigenPack="LapEvec"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; DistilVecPar.LI=6; @@ -123,9 +123,6 @@ void test_DistilVectors(Application &application) DistilVecPar.Ns=4; DistilVecPar.Nt=64; DistilVecPar.Nt_inv=1; - // gauge field - //application.createModule("gauge"); - // Now make an instance of the DistilVectors object application.createModule("DistilVectorsInstance",DistilVecPar); } From 3438dde8dfc8ba1ef7b19b8d6dd3164835fa9469 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 25 Jan 2019 15:19:18 +0000 Subject: [PATCH 034/347] test prog now computes everything up to meson fields --- tests/hadrons/Test_hadrons_distil.cc | 30 ++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index ffb642a0..ba88ceb0 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -116,14 +116,29 @@ void test_DistilVectors(Application &application) DistilVecPar.eigenPack="LapEvec"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; - DistilVecPar.LI=6; + DistilVecPar.LI=5; DistilVecPar.SI=4; DistilVecPar.TI=64; - DistilVecPar.nvec=6; + DistilVecPar.nvec=5; DistilVecPar.Ns=4; DistilVecPar.Nt=64; DistilVecPar.Nt_inv=1; - application.createModule("DistilVectorsInstance",DistilVecPar); + application.createModule("DistilVecs",DistilVecPar); +} +///////////////////////////////////////////////////////////// +// MesonFields +///////////////////////////////////////////////////////////// + +void test_MesonField(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_phi"; + A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.output="DistilFields"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + application.createModule("DistilMesonField",A2AMesonFieldPar); } bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) @@ -209,12 +224,19 @@ int main(int argc, char *argv[]) test_LapEvec( application ); test_Perambulators( application ); break; - default: // 3 + case 3: // 3 test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); break; + case 4: // 4 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_MesonField( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From 84fe36d0844072d170a8b1be08fc0709a9f67fec Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 25 Jan 2019 17:26:43 +0000 Subject: [PATCH 035/347] meson functions work until to be saved --- tests/hadrons/Test_hadrons_distil.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index ba88ceb0..013683db 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -138,6 +138,8 @@ void test_MesonField(Application &application) A2AMesonFieldPar.output="DistilFields"; A2AMesonFieldPar.gammas="all"; A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=4; + A2AMesonFieldPar.block=16; application.createModule("DistilMesonField",A2AMesonFieldPar); } From 9f6f77646004ce39d720d950c288de6eb5dbe2a6 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 25 Jan 2019 19:14:22 +0000 Subject: [PATCH 036/347] ensured there is a default test to run --- tests/hadrons/Test_hadrons_distil.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 013683db..9cbce26e 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -174,7 +174,7 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) int main(int argc, char *argv[]) { // Decode command-line parameters. 1st one is which test to run - int iTestNum = 2; + int iTestNum = 4; for(int i = 1 ; i < argc ; i++ ) { std::cout << "argv[" << i << "]=\"" << argv[i] << "\"" << std::endl; @@ -232,7 +232,7 @@ int main(int argc, char *argv[]) test_Perambulators( application ); test_DistilVectors( application ); break; - case 4: // 4 + default: // 4 test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); From 33d8fb2dd9358e79e5f0eaf5c5495a333809819d Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 25 Jan 2019 19:21:12 +0000 Subject: [PATCH 037/347] Default --- tests/hadrons/Test_hadrons_distil.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 9cbce26e..a399af39 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -174,7 +174,7 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) int main(int argc, char *argv[]) { // Decode command-line parameters. 1st one is which test to run - int iTestNum = 4; + int iTestNum = -1; for(int i = 1 ; i < argc ; i++ ) { std::cout << "argv[" << i << "]=\"" << argv[i] << "\"" << std::endl; From 5580b3a7d1f7c2c86670dec70a9741788de88521 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 28 Jan 2019 12:24:47 +0000 Subject: [PATCH 038/347] bugfix in DistilVectors --- Hadrons/Modules/MDistil/DistilVectors.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index de8f6199..d8199184 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -94,7 +94,8 @@ std::vector TDistilVectors::getOutput(void) template void TDistilVectors::setup(void) { - auto &noise = envGet(std::vector>>, par().noise); + //auto &noise = envGet(std::vector>>, par().noise); + auto &noise = envGet(std::vector, par().noise); envCreate(std::vector, getName() + "_rho", 1, noise.size(), envGetGrid(FermionField)); @@ -128,7 +129,7 @@ void TDistilVectors::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); - auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); + auto &perambulator = envGet(Perambulator, par().perambulator); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); From c7ceff6a214ee845a099583c08e744c163d01406 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 28 Jan 2019 12:28:35 +0000 Subject: [PATCH 039/347] Switched to Gauge field (GIMPL) --- Hadrons/Modules/MDistil/LapEvec.cc | 2 +- Hadrons/Modules/MDistil/LapEvec.hpp | 52 ++++++++++++++--------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.cc b/Hadrons/Modules/MDistil/LapEvec.cc index 488eb48c..019af23c 100644 --- a/Hadrons/Modules/MDistil/LapEvec.cc +++ b/Hadrons/Modules/MDistil/LapEvec.cc @@ -34,4 +34,4 @@ using namespace Grid; using namespace Hadrons; using namespace MDistil; -template class Grid::Hadrons::MDistil::TLapEvec; +template class Grid::Hadrons::MDistil::TLapEvec; diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 1a0b2b00..5254b02a 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -121,11 +121,11 @@ public: ******************************************************************************/ -template +template class TLapEvec: public Module { public: - GAUGE_TYPE_ALIASES(FImpl,); + GAUGE_TYPE_ALIASES(GImpl,); public: // constructor @@ -149,7 +149,7 @@ protected: void Cleanup(void); }; -MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); +MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); /****************************************************************************** TLapEvec implementation @@ -158,31 +158,31 @@ MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); //constexpr char szEigenPackSuffix[] = "_eigenPack"; // constructor ///////////////////////////////////////////////////////////////// -template -TLapEvec::TLapEvec(const std::string name) : gridLD{nullptr}, Module(name) +template +TLapEvec::TLapEvec(const std::string name) : gridLD{nullptr}, Module(name) { - LOG(Message) << "TLapEvec constructor : TLapEvec::TLapEvec(const std::string name)" << std::endl; + LOG(Message) << "TLapEvec constructor : TLapEvec::TLapEvec(const std::string name)" << std::endl; LOG(Message) << "this=" << this << ", gridLD=" << gridLD << std::endl; } // destructor ///////////////////////////////////////////////////////////////// -template -TLapEvec::~TLapEvec() +template +TLapEvec::~TLapEvec() { Cleanup(); } // dependencies/products /////////////////////////////////////////////////////// -template -std::vector TLapEvec::getInput(void) +template +std::vector TLapEvec::getInput(void) { std::vector in = {par().gauge}; return in; } -template -std::vector TLapEvec::getOutput(void) +template +std::vector TLapEvec::getOutput(void) { std::vector out = {getName()}; // This is the higher dimensional eigenpack @@ -190,8 +190,8 @@ std::vector TLapEvec::getOutput(void) } // setup /////////////////////////////////////////////////////////////////////// -template -void TLapEvec::setup(void) +template +void TLapEvec::setup(void) { Cleanup(); Environment & e{env()}; @@ -213,8 +213,8 @@ void TLapEvec::setup(void) } // clean up any temporaries created by setup (that aren't stored in the environment) -template -void TLapEvec::Cleanup(void) +template +void TLapEvec::Cleanup(void) { if( gridLD != nullptr ) { delete gridLD; @@ -228,8 +228,8 @@ void TLapEvec::Cleanup(void) ******************************************************************************/ // execution /////////////////////////////////////////////////////////////////// -template -void TLapEvec::execute(void) +template +void TLapEvec::execute(void) { LOG(Message) << "execute() : start for " << getName() << std::endl; @@ -258,17 +258,15 @@ void TLapEvec::execute(void) auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); if((0)) { - //const std::vector seeds({1, 2, 3, 4, 5}); - //GridParallelRNG pRNG4d(gridHD); - //pRNG4d.SeedFixedIntegers(seeds); - //std::cout << GridLogMessage << "now hot config" << std::endl; - //SU::HotConfiguration(pRNG4d, Umu); - //std::cout << GridLogMessage << "hot cfg done." << std::endl; + const std::vector seeds({1, 2, 3, 4, 5}); + GridParallelRNG pRNG4d(gridHD); + pRNG4d.SeedFixedIntegers(seeds); + std::cout << GridLogMessage << "now hot config" << std::endl; + SU::HotConfiguration(pRNG4d, Umu); + std::cout << GridLogMessage << "hot cfg done." << std::endl; // Set up the SAME gauge field on every time plane - // int Nt = grid4d->gDimensions()[Tdir]; Grid_unquiesce_nodes(); - Umu_smear = Umu; Lattice > coor(gridHD); LatticeCoordinate(coor,Tdir); @@ -376,7 +374,7 @@ void TLapEvec::execute(void) //std::cout << GridLogMessage << "Writing eigenvalues/vectors to " << pszEigenPack << std::endl; eig[t].record.operatorXml = DefaultOperatorXml; eig[t].record.solverXml = DefaultsolverXml; - eig[t].write(sEigenPackName,false,t); + eig[t].write("DistilEigen",false,t); //std::cout << GridLogMessage << "Written eigenvectors" << std::endl; } } From d8831fe9258919c12d1629118dbadd56a6db0752 Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 29 Jan 2019 13:40:26 +0000 Subject: [PATCH 040/347] changed parameters to match Test_Distil --- tests/hadrons/Test_hadrons_distil.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index a399af39..47939090 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -91,10 +91,10 @@ void test_Perambulators(Application &application) PerambPar.nnoise = 1; PerambPar.LI=5; PerambPar.SI=4; - PerambPar.TI=64; + PerambPar.TI=8; PerambPar.nvec=5; PerambPar.Ns=4; - PerambPar.Nt=64; + PerambPar.Nt=8; PerambPar.Nt_inv=1; PerambPar.mass=0.005; PerambPar.M5=1.8; @@ -118,10 +118,10 @@ void test_DistilVectors(Application &application) DistilVecPar.nnoise = 1; DistilVecPar.LI=5; DistilVecPar.SI=4; - DistilVecPar.TI=64; + DistilVecPar.TI=8; DistilVecPar.nvec=5; DistilVecPar.Ns=4; - DistilVecPar.Nt=64; + DistilVecPar.Nt=8; DistilVecPar.Nt_inv=1; application.createModule("DistilVecs",DistilVecPar); } From 67a3d7aeeda9fcfe25ffa2af494388e1a5fc743b Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 29 Jan 2019 16:24:59 +0000 Subject: [PATCH 041/347] added debug output, perambulators now agree up to 8 digits --- Hadrons/Modules/MDistil/LapEvec.hpp | 3 ++- Hadrons/Modules/MDistil/PerambLight.hpp | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 5254b02a..b42ee4fd 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -257,7 +257,7 @@ void TLapEvec::execute(void) //envGetTmp(GaugeField, Umu); auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); - if((0)) { + if((1)) { const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(gridHD); pRNG4d.SeedFixedIntegers(seeds); @@ -381,6 +381,7 @@ void TLapEvec::execute(void) for (int i=0;i::execute(void) std::cout << GridLogMessage << "now hot config" << std::endl; SU::HotConfiguration(pRNG4d, Umu); std::cout << GridLogMessage << "hot cfg done." << std::endl; + + // Set up the SAME gauge field on every time plane + // int Nt = grid4d->gDimensions()[Tdir]; + Grid_unquiesce_nodes(); + + auto Usft = Umu; + Lattice > coor(grid4d); + LatticeCoordinate(coor,Tdir); + for(int t=1;t 7,0,1,2,3,4,5,6 t=1 + // 0,0,2,3,4,5,6,7 6,7,0,1,2,3,4,5 t=2 + // 0,0,0,3,4,5,6,7 5,6,7,0,1,2,3,4 t=3 + //... + + Usft = Cshift(Usft,Tdir,-1); + Umu = where(coor==t,Usft,Umu); + } } else { std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); std::cout << GridLogMessage << "Loading NERSC configuration from '" << fileName << "'" << std::endl; @@ -285,6 +304,7 @@ void TPerambLight::execute(void) for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); + std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; } } } From c3273eff20b72cc66b74c98dd89e0835d9cc3da2 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 30 Jan 2019 11:20:22 +0000 Subject: [PATCH 042/347] agreement up to laph vectors --- Hadrons/Modules/MDistil/DistilVectors.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index d8199184..52afcd81 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -183,6 +183,11 @@ void TDistilVectors::execute(void) } } } + SpinColourVector scv0; + std::vector siteFirst(grid4d->Nd(),0); + peekSite(scv0, rho[vecindex], siteFirst); + auto & cplx0 = scv0()(0)(0); + std::cout << "site0[rho(vecindex = " << vecindex << ")] = " << cplx0 << std::endl; } } } @@ -203,6 +208,11 @@ void TDistilVectors::execute(void) } InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); } + SpinColourVector scv0; + std::vector siteFirst(grid4d->Nd(),0); + peekSite(scv0, phi[vecindex], siteFirst); + auto & cplx0 = scv0()(0)(0); + std::cout << "site0[phi(vecindex = " << vecindex << ")] = " << cplx0 << std::endl; } } } From 7b66197534a954d743a5fe7ae0f2d903537f3c69 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 30 Jan 2019 18:03:34 +0000 Subject: [PATCH 043/347] meson fields are now the same --- Hadrons/Modules/MDistil/DistilVectors.hpp | 13 ++++++++++--- tests/hadrons/Test_hadrons_distil.cc | 7 ++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 52afcd81..caba41de 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -96,11 +96,16 @@ void TDistilVectors::setup(void) { //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); - + + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int Nt_inv=par().Nt_inv; + envCreate(std::vector, getName() + "_rho", 1, - noise.size(), envGetGrid(FermionField)); + nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); envCreate(std::vector, getName() + "_phi", 1, - noise.size(), envGetGrid(FermionField)); + nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); GridCartesian * grid4d = env().getGrid(); @@ -218,6 +223,8 @@ void TDistilVectors::execute(void) } } + std::cout << "size rho" << rho.size() << std::endl; + std::cout << "size phi" << phi.size() << std::endl; } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 47939090..34df6ce7 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -134,12 +134,13 @@ void test_MesonField(Application &application) // DistilVectors parameters MContraction::A2AMesonField::Par A2AMesonFieldPar; A2AMesonFieldPar.left="DistilVecs_phi"; - A2AMesonFieldPar.right="DistilVecs_rho"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_phi"; A2AMesonFieldPar.output="DistilFields"; A2AMesonFieldPar.gammas="all"; A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=4; - A2AMesonFieldPar.block=16; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; application.createModule("DistilMesonField",A2AMesonFieldPar); } From f7e4661ca0e2534438c372265d1cf3b366402653 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 30 Jan 2019 21:16:09 +0000 Subject: [PATCH 044/347] Fixed grid3d leak in PerambLight --- Hadrons/Modules/MDistil/LapEvec.hpp | 3 +- Hadrons/Modules/MDistil/PerambLight.hpp | 56 ++++++++++++++++++------- tests/hadrons/Test_hadrons_distil.cc | 50 ++++++++++++++++++++++ 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index b42ee4fd..5750ef95 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -144,9 +144,8 @@ protected: GridCartesian * gridLD; // Owned by me, so I must delete it GridCartesian * gridHD; // Owned by environment (so I won't delete it) int Nx, Ny, Nz, Nt; - protected: - void Cleanup(void); + virtual void Cleanup(void); }; MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 500b1bb3..532c007f 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -49,7 +49,7 @@ public: // constructor TPerambLight(const std::string name); // destructor - virtual ~TPerambLight(void) {}; + virtual ~TPerambLight(void); // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); @@ -57,6 +57,13 @@ public: virtual void setup(void); // execution virtual void execute(void); +protected: + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) + +protected: + virtual void Cleanup(void); }; MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); @@ -67,9 +74,16 @@ MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); // constructor ///////////////////////////////////////////////////////////////// template TPerambLight::TPerambLight(const std::string name) -: Module(name) +: grid3d{nullptr}, grid4d{nullptr}, Module(name) {} +// destructor +template +TPerambLight::~TPerambLight(void) +{ + Cleanup(); +}; + // dependencies/products /////////////////////////////////////////////////////// template std::vector TPerambLight::getInput(void) @@ -94,31 +108,32 @@ std::vector TPerambLight::getOutput(void) template void TPerambLight::setup(void) { + Cleanup(); // auto &noise = envGet(std::vector>>, par().noise); - int LI=par().LI; - int SI=par().SI; - int TI=par().TI; - int nnoise=par().nnoise; - int Nt=par().Nt; - int Nt_inv=par().Nt_inv; - int nvec=par().nvec; + const int LI{par().LI}; + const int SI{par().SI}; + //const int TI{par().TI}; + const int nnoise{par().nnoise}; + const int Nt{par().Nt}; + const int Nt_inv{par().Nt_inv}; + const int nvec{par().nvec}; envCreate(Perambulator, getName() + "_perambulator_light", 1, Nt,nvec,LI,nnoise,Nt_inv,SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Ns*Nt*nnoise); - GridCartesian * grid4d = env().getGrid(); - std::vector latt_size = GridDefaultLatt(); + grid4d = env().getGrid(); + /*std::vector latt_size = GridDefaultLatt(); std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); std::vector mpi_layout = GridDefaultMpi(); std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); latt_size[Nd-1] = 1; simd_layout_3.push_back( 1 ); - mpi_layout[Nd-1] = 1; - GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + mpi_layout[Nd-1] = 1;*/ + grid3d = MakeLowerDimGrid(grid4d);//new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); envTmp(LatticeSpinColourVector, "dist_source",1,LatticeSpinColourVector(grid4d)); envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); @@ -134,6 +149,17 @@ void TPerambLight::setup(void) } +// clean up any temporaries created by setup (that aren't stored in the environment) +template +void TPerambLight::Cleanup(void) +{ + if( grid3d != nullptr ) { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; +} + // execution /////////////////////////////////////////////////////////////////// template void TPerambLight::execute(void) @@ -144,7 +170,7 @@ void TPerambLight::execute(void) auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - GridCartesian * grid4d = env().getGrid(); + /*GridCartesian * grid4d = env().getGrid(); std::vector latt_size = GridDefaultLatt(); std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); std::vector mpi_layout = GridDefaultMpi(); @@ -152,7 +178,7 @@ void TPerambLight::execute(void) latt_size[Nd-1] = 1; simd_layout_3.push_back( 1 ); mpi_layout[Nd-1] = 1; - GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d);*/ LatticeGaugeField Umu(grid4d); FieldMetaData header; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 47939090..82a6e3d0 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -171,8 +171,58 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) return true; } +#ifdef DEBUG + +typedef Eigen::Tensor MyTensor; + +bool DebugEigenTest() +{ + MyTensor x(2,3,4); + const MyTensor::Index s{x.size()}; + std::cout << "x.size() = " << s << std::endl; + std::cout << "x.NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; + std::cout << "x.NumIndices = " << x.NumIndices << std::endl; + const MyTensor::Dimensions & d{x.dimensions()}; + std::cout << "d.size() = " << d.size() << std::endl; + std::cout << "Dimensions are "; + for(auto i : d ) std::cout << "[" << i << "]"; + std::cout << std::endl; + MyTensor::Index SizeCalculated{1}; + std::cout << "Dimensions again"; + for(int i=0 ; i < d.size() ; i++ ) { + std::cout << " : [" << i << "]=" << d[i]; + SizeCalculated *= d[i]; + } + std::cout << std::endl; + std::cout << "SizeCalculated = " << SizeCalculated << std::endl;\ + assert( SizeCalculated == s ); + // Initialise + assert( d.size() == 3 ); + for( int i = 0 ; i < d[0] ; i++ ) + for( int j = 0 ; j < d[1] ; j++ ) + for( int k = 0 ; k < d[2] ; k++ ) { + x(i,j,k) = std::complex(SizeCalculated, SizeCalculated); + SizeCalculated--; + } + // Show raw data + std::cout << "Data follow : " << std::endl; + Complex * p = x.data(); + for( auto i = 0 ; i < s ; i++ ) { + if( i ) std::cout << ", "; + std::cout << "x.data()[" << i << "]=" << * p++; + } + std::cout << std::endl; + return true; +} +#endif + int main(int argc, char *argv[]) { +#ifdef DEBUG + // Debug only - test of Eigen::Tensor + //if( DebugEigenTest() ) return 0; +#endif + // Decode command-line parameters. 1st one is which test to run int iTestNum = -1; From 7e74f7bec47818ce4873f754f5dbaaf0955d05fc Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 31 Jan 2019 11:35:05 +0000 Subject: [PATCH 045/347] tsrc != 0 now works --- Hadrons/Modules/MDistil/DistilVectors.hpp | 11 ++++++----- Hadrons/Modules/MDistil/PerambLight.hpp | 12 ++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index caba41de..492c9755 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -164,25 +164,26 @@ void TDistilVectors::execute(void) bool full_tdil=(TI==Nt); int vecindex; + int t_inv; for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - if(full_tdil) dt=tsrc; //TODO: this works for now, as longs as tsrc=0, but will crash otherwise! for (int ds = 0; ds < Ns; ds++) { vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; rho[vecindex] = zero; tmp3d_nospin = zero; for (int it = dt; it < Nt; it += TI){ - if( it >= Ntfirst && it < Ntfirst + Ntlocal ) { + if (full_tdil) t_inv = tsrc; else t_inv = it; + if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced - ExtractSliceLocal(evec3d,epack.evec[ik],0,it,3); - tmp3d_nospin = evec3d * noise[inoise + nnoise*(it + Nt*(ik+nvec*is))]; + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); + tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; //tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; - InsertSliceLocal(tmp3d,tmp2,0,it-Ntfirst,Grid::QCD::Tdir); + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); rho[vecindex] += tmp2; } } diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 532c007f..7ab7372a 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -288,26 +288,26 @@ void TPerambLight::execute(void) ConjugateGradient CG(CGPrecision,MaxIterations); SchurRedBlackDiagMooeeSolve SchurSolver(CG); + int t_inv; for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - if(full_tdil) dt=tsrc; //this works for now, as longs as tsrc=0, but will crash otherwise! for (int ds = 0; ds < Ns; ds++) { std::cout << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; dist_source = zero; tmp3d_nospin = zero; evec3d = zero; for (int it = dt; it < Nt; it += TI){ - if( it >= Ntfirst && it < Ntfirst + Ntlocal ) { + if (full_tdil) t_inv = tsrc; else t_inv = it; + if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced - ExtractSliceLocal(evec3d,epack.evec[ik],0,it,3); - tmp3d_nospin = evec3d * noise[inoise + nnoise*(it + Nt*(ik+nvec*is))]; - //tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); + tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; - InsertSliceLocal(tmp3d,tmp2,0,it-Ntfirst,Grid::QCD::Tdir); + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); dist_source += tmp2; } } From d7b9ed199dd09a73ed09b4a017039814b19bc947 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 31 Jan 2019 12:24:32 +0000 Subject: [PATCH 046/347] PerambLight fixes --- Hadrons/Modules/MDistil/LapEvec.hpp | 23 --- Hadrons/Modules/MDistil/PerambLight.hpp | 210 ++++++++++++------------ tests/hadrons/Test_hadrons_distil.cc | 24 +-- 3 files changed, 115 insertions(+), 142 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 5750ef95..e5b1f63c 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -79,27 +79,6 @@ struct LanczosParameters: Serializable { template LanczosParameters(Reader& Reader){read(Reader,"Lanczos",*this);} }; -/*struct DistilParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters - ,int, TI - ,int, LI - ,int, Nnoise - ,int, tSrc - )//,int, Ls) // For makeFiveDimGrid - DistilParameters() = default; - template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} - }; - -struct SolverParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(SolverParameters, - double, CGPrecision, - int, MaxIterations, - double, mass, - double, M5) - SolverParameters() = default; - template SolverParameters(Reader& Reader){read(Reader,"Solver",*this);} -};*/ - // These are the actual parameters passed to the module during construction class LapEvecPar: Serializable @@ -126,8 +105,6 @@ class TLapEvec: public Module { public: GAUGE_TYPE_ALIASES(GImpl,); - -public: // constructor TLapEvec(const std::string name); // destructor diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 532c007f..208d9789 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -20,36 +20,51 @@ BEGIN_HADRONS_NAMESPACE ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) +struct DistilParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, TI, + int, LI, + int, nnoise, + int, tsrc, + int, SI, + int, Ns, + int, Nt, + int, Nt_inv) + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} +}; + +struct SolverParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(SolverParameters, + double, CGPrecision, + int, MaxIterations, + double, mass, + double, M5) + SolverParameters() = default; + template SolverParameters(Reader& Reader){read(Reader,"Solver",*this);} +}; + class PerambLightPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, std::string, eigenPack, bool, multiFile, - int, tsrc, - int, nnoise, - int, LI, - int, SI, - int, TI, - int, nvec, - int, Ns, - int, Nt, - int, Nt_inv, - Real, mass, - Real, M5, - int, Ls, - double, CGPrecision, - int, MaxIterations); + int, nvec, + int, Ls, // For makeFiveDimGrid + DistilParameters, Distil, + SolverParameters, Solver); }; -template +template class TPerambLight: public Module { public: + GAUGE_TYPE_ALIASES(GImpl,); // constructor TPerambLight(const std::string name); // destructor - virtual ~TPerambLight(void); + virtual ~TPerambLight(void); // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); @@ -58,12 +73,12 @@ public: // execution virtual void execute(void); protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) - + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) + protected: - virtual void Cleanup(void); + virtual void Cleanup(void); }; MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); @@ -72,21 +87,21 @@ MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); * TPerambLight implementation * ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// -template -TPerambLight::TPerambLight(const std::string name) +template +TPerambLight::TPerambLight(const std::string name) : grid3d{nullptr}, grid4d{nullptr}, Module(name) {} // destructor -template -TPerambLight::~TPerambLight(void) +template +TPerambLight::~TPerambLight(void) { Cleanup(); }; // dependencies/products /////////////////////////////////////////////////////// -template -std::vector TPerambLight::getInput(void) +template +std::vector TPerambLight::getInput(void) { std::vector in; @@ -96,8 +111,8 @@ std::vector TPerambLight::getInput(void) return in; } -template -std::vector TPerambLight::getOutput(void) +template +std::vector TPerambLight::getOutput(void) { std::vector out = {getName() + "_perambulator_light",getName() + "_noise"}; @@ -105,53 +120,40 @@ std::vector TPerambLight::getOutput(void) } // setup /////////////////////////////////////////////////////////////////////// -template -void TPerambLight::setup(void) +template +void TPerambLight::setup(void) { - Cleanup(); + Cleanup(); - // auto &noise = envGet(std::vector>>, par().noise); - - const int LI{par().LI}; - const int SI{par().SI}; - //const int TI{par().TI}; - const int nnoise{par().nnoise}; - const int Nt{par().Nt}; - const int Nt_inv{par().Nt_inv}; - const int nvec{par().nvec}; - - envCreate(Perambulator, getName() + "_perambulator_light", 1, - Nt,nvec,LI,nnoise,Nt_inv,SI); - envCreate(std::vector, getName() + "_noise", 1, - nvec*Ns*Nt*nnoise); + // auto &noise = envGet(std::vector>>, par().noise); + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; - grid4d = env().getGrid(); - /*std::vector latt_size = GridDefaultLatt(); - std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); - std::vector mpi_layout = GridDefaultMpi(); - std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); - latt_size[Nd-1] = 1; - simd_layout_3.push_back( 1 ); - mpi_layout[Nd-1] = 1;*/ - grid3d = MakeLowerDimGrid(grid4d);//new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + envCreate(Perambulator, getName() + "_perambulator_light", 1, + Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); + envCreate(std::vector, getName() + "_noise", 1, + nvec*Distil.Ns*Distil.Nt*Distil.nnoise); - envTmp(LatticeSpinColourVector, "dist_source",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "result",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "result_single_component",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeColourVector, "result_nospin",1,LatticeColourVector(grid4d)); - envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); - envTmp(LatticeSpinVector, "peramb_tmp",1,LatticeSpinVector(grid4d)); + grid4d = env().getGrid(); + grid3d = MakeLowerDimGrid(grid4d);//new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + envTmpLat(GaugeField, "Umu"); + envTmpLat(LatticeSpinColourVector, "dist_source"); + envTmpLat(LatticeSpinColourVector, "tmp2"); + envTmpLat(LatticeSpinColourVector, "result"); + //envTmpLat(LatticeSpinColourVector, "result_single_component"); + envTmpLat(LatticeColourVector, "result_nospin"); + //envTmpLat(LatticeColourVector, "tmp_nospin"); + //envTmpLat(LatticeSpinVector, "peramb_tmp"); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); } // clean up any temporaries created by setup (that aren't stored in the environment) -template -void TPerambLight::Cleanup(void) +template +void TPerambLight::Cleanup(void) { if( grid3d != nullptr ) { delete grid3d; @@ -161,26 +163,30 @@ void TPerambLight::Cleanup(void) } // execution /////////////////////////////////////////////////////////////////// -template -void TPerambLight::execute(void) +template +void TPerambLight::execute(void) { + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; + const SolverParameters & Solver{par().Solver}; + const int LI{Distil.LI}; + //const int SI{Distil.SI}; + const int TI{Distil.TI}; + const int nnoise{Distil.nnoise}; + const int Nt{Distil.Nt}; + const int Nt_inv{Distil.Nt_inv}; + const int tsrc{Distil.tsrc}; + const int Ns{Distil.Ns}; + + const bool full_tdil{TI==Nt}; + const bool exact_distillation{full_tdil && LI==nvec}; //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, getName() + "_noise"); auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - /*GridCartesian * grid4d = env().getGrid(); - std::vector latt_size = GridDefaultLatt(); - std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); - std::vector mpi_layout = GridDefaultMpi(); - std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); - latt_size[Nd-1] = 1; - simd_layout_3.push_back( 1 ); - mpi_layout[Nd-1] = 1; - GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d);*/ - - LatticeGaugeField Umu(grid4d); + envGetTmp(GaugeField, Umu); FieldMetaData header; if((1)){ const std::vector seeds({1, 2, 3, 4, 5}); @@ -197,7 +203,7 @@ void TPerambLight::execute(void) auto Usft = Umu; Lattice > coor(grid4d); LatticeCoordinate(coor,Tdir); - for(int t=1;t 7,0,1,2,3,4,5,6 t=1 @@ -218,30 +224,17 @@ void TPerambLight::execute(void) envGetTmp(LatticeSpinColourVector, dist_source); envGetTmp(LatticeSpinColourVector, tmp2); envGetTmp(LatticeSpinColourVector, result); - envGetTmp(LatticeSpinColourVector, result_single_component); + //envGetTmp(LatticeSpinColourVector, result_single_component); envGetTmp(LatticeColourVector, result_nospin); - envGetTmp(LatticeColourVector, tmp_nospin); + //envGetTmp(LatticeColourVector, tmp_nospin); + //envGetTmp(LatticeSpinVector, peramb_tmp); envGetTmp(LatticeSpinColourVector, tmp3d); envGetTmp(LatticeColourVector, tmp3d_nospin); envGetTmp(LatticeColourVector, result_3d); envGetTmp(LatticeColourVector, evec3d); - envGetTmp(LatticeSpinVector, peramb_tmp); - int Ntlocal = grid4d->LocalDimensions()[3]; - int Ntfirst = grid4d->LocalStarts()[3]; - - - int tsrc=par().tsrc; - int nnoise=par().nnoise; - int LI=par().LI; - int Ns=par().Ns; - int Nt_inv=par().Nt_inv; - int Nt=par().Nt; - int TI=par().TI; - int nvec=par().nvec; - - bool full_tdil=(TI==Nt); - bool exact_distillation = (full_tdil && LI==nvec); + const int Ntlocal{grid4d->LocalDimensions()[3]}; + const int Ntfirst{grid4d->LocalStarts()[3]}; //Create Noises //std::cout << pszGaugeConfigFile << std::endl; @@ -266,17 +259,16 @@ void TPerambLight::execute(void) } } - Real mass=par().mass; // TODO Infile - Real M5 =par().M5; // TODO Infile + const Real mass{Solver.mass}; + const Real M5 {Solver.M5}; std::cout << "init RBG " << std::endl; GridRedBlackCartesian RBGrid(grid4d); std::cout << "init RBG done" << std::endl; - int Ls=par().Ls; - - double CGPrecision = par().CGPrecision; - int MaxIterations = par().MaxIterations; - + const int Ls{par().Ls}; + const double CGPrecision{Solver.CGPrecision}; + const int MaxIterations {Solver.MaxIterations}; + { GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,grid4d); GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,grid4d); @@ -338,6 +330,10 @@ void TPerambLight::execute(void) } } } + // Kill our 5 dimensional grid (avoid leaks). Should really declare these objects temporary + delete FrbGrid; + delete FGrid; + } std::cout << "perambulator done" << std::endl; perambulator.SliceShare( grid3d, grid4d ); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index d0308d74..c3759a05 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -87,20 +87,20 @@ void test_Perambulators(Application &application) // PerambLight parameters MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; - PerambPar.tsrc = 0; - PerambPar.nnoise = 1; - PerambPar.LI=5; - PerambPar.SI=4; - PerambPar.TI=8; + PerambPar.Distil.tsrc = 0; + PerambPar.Distil.nnoise = 1; + PerambPar.Distil.LI=5; + PerambPar.Distil.SI=4; + PerambPar.Distil.TI=8; PerambPar.nvec=5; - PerambPar.Ns=4; - PerambPar.Nt=8; - PerambPar.Nt_inv=1; - PerambPar.mass=0.005; - PerambPar.M5=1.8; + PerambPar.Distil.Ns=4; + PerambPar.Distil.Nt=8; + PerambPar.Distil.Nt_inv=1; + PerambPar.Solver.mass=0.005; + PerambPar.Solver.M5=1.8; PerambPar.Ls=16; - PerambPar.CGPrecision=1e-8; - PerambPar.MaxIterations=10000; + PerambPar.Solver.CGPrecision=1e-8; + PerambPar.Solver.MaxIterations=10000; application.createModule("Peramb",PerambPar); } ///////////////////////////////////////////////////////////// From 48b6f7e6ad6d7a76ec2149bc8e76924dd4fa5aeb Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 31 Jan 2019 12:37:00 +0000 Subject: [PATCH 047/347] Changed PerambLight to PerambLight --- Hadrons/Modules/MDistil/PerambLight.cc | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.cc b/Hadrons/Modules/MDistil/PerambLight.cc index 36dc97af..89f98191 100644 --- a/Hadrons/Modules/MDistil/PerambLight.cc +++ b/Hadrons/Modules/MDistil/PerambLight.cc @@ -4,4 +4,4 @@ using namespace Grid; using namespace Hadrons; using namespace MDistil; -template class Grid::Hadrons::MDistil::TPerambLight; +template class Grid::Hadrons::MDistil::TPerambLight; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 180800e5..ffdf8726 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -81,7 +81,7 @@ protected: virtual void Cleanup(void); }; -MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); +MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); /****************************************************************************** * TPerambLight implementation * From 3d31113337b8dc64ab81b2d97921db348cd9e483 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 31 Jan 2019 13:01:16 +0000 Subject: [PATCH 048/347] added test t5 to compute meson fields of different quarks. Different nvec are allowed. --- tests/hadrons/Test_hadrons_distil.cc | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index d0308d74..c3fb7cbf 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -125,6 +125,49 @@ void test_DistilVectors(Application &application) DistilVecPar.Nt_inv=1; application.createModule("DistilVecs",DistilVecPar); } +void test_PerambulatorsS(Application &application) +{ + // PerambLight parameters + MDistil::PerambLight::Par PerambPar; + PerambPar.eigenPack="LapEvec"; + PerambPar.tsrc = 0; + PerambPar.nnoise = 1; + PerambPar.LI=3; + PerambPar.SI=4; + PerambPar.TI=8; + PerambPar.nvec=3; + PerambPar.Ns=4; + PerambPar.Nt=8; + PerambPar.Nt_inv=1; + PerambPar.mass=0.04; //strange mass??? + PerambPar.M5=1.8; + PerambPar.Ls=16; + PerambPar.CGPrecision=1e-8; + PerambPar.MaxIterations=10000; + application.createModule("PerambS",PerambPar); +} +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +void test_DistilVectorsS(Application &application) +{ + // DistilVectors parameters + MDistil::DistilVectors::Par DistilVecPar; + DistilVecPar.noise="PerambS_noise"; + DistilVecPar.perambulator="PerambS_perambulator_light"; + DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.tsrc = 0; + DistilVecPar.nnoise = 1; + DistilVecPar.LI=3; + DistilVecPar.SI=4; + DistilVecPar.TI=8; + DistilVecPar.nvec=3; + DistilVecPar.Ns=4; + DistilVecPar.Nt=8; + DistilVecPar.Nt_inv=1; + application.createModule("DistilVecsS",DistilVecPar); +} ///////////////////////////////////////////////////////////// // MesonFields ///////////////////////////////////////////////////////////// @@ -143,6 +186,24 @@ void test_MesonField(Application &application) A2AMesonFieldPar.block=4; application.createModule("DistilMesonField",A2AMesonFieldPar); } +///////////////////////////////////////////////////////////// +// MesonFields +///////////////////////////////////////////////////////////// + +void test_MesonFieldSL(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecsS_phi"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_phi"; + A2AMesonFieldPar.output="DistilFields"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonField",A2AMesonFieldPar); +} bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { @@ -290,6 +351,15 @@ int main(int argc, char *argv[]) test_DistilVectors( application ); test_MesonField( application ); break; + case 5: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_PerambulatorsS( application ); + test_DistilVectorsS( application ); + test_MesonFieldSL( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From d7dc61774677595942aefded42ad5cfdf154a3ce Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 31 Jan 2019 15:06:52 +0000 Subject: [PATCH 049/347] Switched perambulator to sue Eigen::Tensor (file write temporarily excluded) --- Hadrons/Modules/MDistil/Distil.hpp | 265 +--------------------- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 7 +- 3 files changed, 18 insertions(+), 256 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index a6e87d57..d1b1a747 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -179,7 +179,7 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) { //LOG(Message) << "MakeLowerDimGrid() begin" << std::endl; int nd{static_cast(gridHD->_ndimension)}; - std::vector latt_size = gridHD->_fdimensions; + std::vector latt_size = gridHD->_gdimensions; latt_size[nd-1] = 1; std::vector simd_layout = GridDefaultSimd(nd-1, vComplex::Nsimd()); @@ -196,264 +196,23 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) Perambulator object ******************************************************************************/ -template -class Perambulator : Serializable{ - // TODO: The next line makes friends across all combinations - // (not much of a problem given all public anyway ...) - // FYI, the bug here was that I forgot that the friend is templated - template friend std::ostream & operator<<(std::ostream &os, const Perambulator& p); -protected: +template +class Perambulator : public Eigen::Tensor +{ public: - GRID_SERIALIZABLE_CLASS_MEMBERS( Perambulator, - std::string, ID, // Allows owner to specialise - std::string, Provenance, // For info only - std::vector, dimensions, - std::vector, perambulator, - // Following items are redundant, but useful - int, nd, // Number of dimensions - size_t, NumElements); // Number of elements -protected: - // Constructor common code - inline void ConstructCommon(const int * Dimensions) { - assert(nd > 0); - dimensions.resize(nd); - NumElements = 1; - for(int i = 0 ; i < nd ; i++) { - assert(Dimensions[i] > 0); - NumElements *= (size_t) Dimensions[i]; - dimensions[i] = Dimensions[i]; - } - //const LatticeObj perambulatorDefault; - perambulator.resize(NumElements);//,perambulatorDefault); - } -public: - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions) - : nd {(int) Dimensions.size()} { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions, const std::string sID) - : nd {(int) Dimensions.size()}, ID(sID) { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as std::vector - inline Perambulator(const std::vector & Dimensions, const std::string sID, const std::string sProvenance) - : nd {(int) Dimensions.size()}, ID(sID), Provenance(sProvenance) { - ConstructCommon( &Dimensions[0] ); } - - // Constructor with dimensions passed as individual parameters - // FYI: The caller is free to ignore the names and use the indices however they see fit - inline Perambulator(int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) { - int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; - nd = sizeof(Dimensions)/sizeof(Dimensions[0]); - while( nd > 1 && Dimensions[nd-1] == 1 ) - nd--; - ConstructCommon( Dimensions ); + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Perambulator(Eigen::Index firstDimension, IndexTypes... otherDimensions) + : Eigen::Tensor(firstDimension, otherDimensions...) + { + // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. + EIGEN_STATIC_ASSERT(sizeof...(otherDimensions) + 1 == NumIndices_, YOU_MADE_A_PROGRAMMING_MISTAKE) } - inline Perambulator(const std::string sID, int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) : ID{sID} { - int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; - nd = sizeof(Dimensions)/sizeof(Dimensions[0]); - while( nd > 1 && Dimensions[nd-1] == 1 ) - nd--; - ConstructCommon( Dimensions ); - } - - inline Perambulator(const std::string sID, const std::string sProvenance, int NumNoise, int NumEvec=1, int NumTime=1, int NumSpin=1, int I_k=1, int I_t=1, int I_s=1) : ID{sID}, Provenance{sProvenance} { - int Dimensions[]={NumNoise,NumEvec,NumTime,NumSpin,I_k,I_t,I_s}; - nd = sizeof(Dimensions)/sizeof(Dimensions[0]); - while( nd > 1 && Dimensions[nd-1] == 1 ) - nd--; - ConstructCommon( Dimensions ); - } - - inline LatticeObj & operator()(size_t count, const int * Coord) { - assert( count == nd ); - assert( Coord ); - size_t idx = 0; - // C memory order (???) - for( int d = 0 ; d < nd ; d++ ) { - assert( Coord[d] < dimensions[d] ); - idx *= (size_t) dimensions[d]; - idx += (size_t) Coord[d]; - } - return perambulator[idx]; - } - - inline LatticeObj & operator()(const std::vector Coord) { - return operator()(Coord.size(), &Coord[0]); - } - - inline LatticeObj & operator()(int idxNoise, int idxEvec=0, int idxTime=0, int idxSpin=0, int I_k=0, int I_t=0, int I_s=0) { - int MyIndex[]={idxNoise,idxEvec,idxTime,idxSpin,I_k,I_t,I_s}; - int i = sizeof(MyIndex)/sizeof(MyIndex[0]); - assert( i >= nd ); - while( i > nd ) - assert(MyIndex[--i] == 0); - return operator()(i, MyIndex); - } + inline void WriteTemporary(const std::string &FileName){} // Share data for timeslices we calculated with other nodes inline void SliceShare( GridCartesian * gridLowDim, GridCartesian * gridHighDim ) { - Grid::SliceShare( gridLowDim, gridHighDim, &perambulator[0], - (int) perambulator.size() * sizeof(perambulator[0]) ); - } - - /************************************************************************************* - - Write/Read perambulator to/from disk - - Temporary version - keep the code running until such time as correct format written - - *************************************************************************************/ - - inline void WriteTemporary(const std::string filename) const - { - std::cout << GridLogMessage << "Writing perambulator ID \"" << ID << "\" to " << filename << std::endl; - BinaryWriter myPhDThesis( filename + ".tmp" ); - write( myPhDThesis, "Perambulator", *this ); - } - - inline bool ReadTemporary(const std::string filename) - { - std::string _filename{filename}; - _filename.append( ".tmp" ); - bool bReturnValue = false; - std::fstream f; - f.open(_filename,std::ios_base::in); - if( !f.is_open() ) - std::cout << GridLogMessage << "Cached perambulator file " << _filename << " does not exist" << std::endl; - else { - f.close(); - bReturnValue = true; - auto MyID{ID}; - std::cout << GridLogMessage << "Reading perambulator ID \"" << ID << "\" from " << _filename << std::endl; - BinaryReader reader( _filename ); - read( reader, "Perambulator", *this ); - std::cout << GridLogMessage << "Perambulator ID read from " << _filename << " was \"" << ID << "\"" << std::endl; - assert(MyID == ID); - } - return bReturnValue; - } - - /************************************************************************************* - - Write perambulator to disk - TODO 1. Ensure precision on disk can be specified independently of in memory - 2. Validate format with Peter, Antonin et al - 3. This object "works" for small lattii (lattices), - BUT, the final block is written as XML, and while write is fast, read is painfully slow - i.e. on a 24^3 x 64 lattice, I abandoned the perambulator read after 1 hour on Tesseract - - *************************************************************************************/ - - inline void WritePoorly(LatticeGaugeField &field, const std::string filename) - { - std::cout << GridLogMessage << "Writing perambulator ID \"" << ID << "\" to " << filename << std::endl; - assert(nd>=2); // Really should be a little bigger - GridBase * gridHighDim = field._grid; - //ScidacWriterPerambulator binWriter(gridHighDim->IsBoss()); - ScidacWriter binWriter(gridHighDim->IsBoss()); - //makeFileDir(filename, gridHighDim); // Assume this makes directory ... but why pass it the grid? - binWriter.open(filename); - // Write the header - { - XmlWriter xmlWriter("", "perambulatorPar"); - xmlWriter.pushXmlString("" + ID + ""); - xmlWriter.pushXmlString("" + Provenance + ""); - // TODO add all the perambulator parameters here - binWriter.writeLimeObject(1, 1, xmlWriter, "parameters", SCIDAC_FILE_XML); - std::cout << GridLogMessage << "Perambulator header written" << std::endl; - } - // Now write the local portion of the Perambulator - { - //binWriter.writeScidacPerambulatorRecord(field, *this); - //std::cout << GridLogMessage << "Perambulator body written" << std::endl; - } - { - //////////////////////////////////////// - // fill the Grid header - //////////////////////////////////////// - FieldMetaData header; - scidacRecord _scidacRecord; - scidacFile _scidacFile; - ScidacMetaData(field,header,_scidacRecord,_scidacFile); - - ////////////////////////////////////////////// - // Fill the Lime file record by record - ////////////////////////////////////////////// - constexpr auto precision = std::numeric_limits::digits10; - binWriter.writeLimeObject(1,0,header ,std::string("FieldMetaData"),std::string(GRID_FORMAT)); // Open message - binWriter.writeLimeObject(0, 0, _scidacRecord, _scidacRecord.SerialisableClassName(), - std::string(SCIDAC_PRIVATE_RECORD_XML)); - binWriter.writeLimeObject(0,1,*this,this->SerialisableClassName(),std::string("Perambulator"),precision); - } - } - - /************************************************************************************* - - Read perambulator from disk - TODO 1. Ensure precision on disk can be specified independently of in memory - 2. Validate format with Peter - 3. Abandoning for now because of synchronisation during write. - Object small enough to send to root and write from there - - *************************************************************************************/ - - struct PerambHeader{ - std::string ID, Provenance; - }; - - inline bool ReadPoorly(LatticeGaugeField &field, const std::string filename) - { - assert(nd>=2); // Really should be a little bigger - bool bReturnValue = false; - std::fstream f; - f.open(filename,std::ios_base::in); - if( !f.is_open() ) - std::cout << GridLogMessage << "Cached perambulator file " << filename << " does not exist" << std::endl; - else { - f.close(); - ScidacReader binReader; - binReader.open(filename); - PerambHeader header; - // Read the header - { - std::string recordXml; - std::cout << GridLogMessage << "Reading perambulator header from " << filename << std::endl; - binReader.readLimeObject(recordXml, SCIDAC_FILE_XML); - XmlReader xmlReader(recordXml, true, "perambulatorPar"); - xmlReader.push("perambulatorPar"); - //xmlReader.readCurrentSubtree(header.ID); - xmlReader.readDefault("ID",header.ID); - std::cout << GridLogMessage << "Perambulator ID=" << header.ID << std::endl; - //xmlReader.nextElement(); - //xmlReader.readCurrentSubtree(header.Provenance); - xmlReader.readDefault("Provenance",header.Provenance); - std::cout << GridLogMessage << "Perambulator Provenance=" << header.Provenance << std::endl; - assert( header.ID == ID ); - bReturnValue = true; - } - // Now read the Perambulator - { - //////////////////////////////////////// - // fill the Grid header - //////////////////////////////////////// - FieldMetaData header; - scidacRecord _scidacRecord; - - ////////////////////////////////////////////// - // Fill the Lime file record by record - ////////////////////////////////////////////// - binReader.readLimeObject(header ,std::string("FieldMetaData"),std::string(GRID_FORMAT)); // Open message - binReader.readLimeObject( _scidacRecord, _scidacRecord.SerialisableClassName(), - std::string(SCIDAC_PRIVATE_RECORD_XML)); - binReader.readLimeObject(*this,this->SerialisableClassName(),std::string("Perambulator")); - } - // TODO Add validation that the field matches what we read in - } - return bReturnValue; + //Grid::SliceShare( gridLowDim, gridHighDim, data(), (int) (size() * sizeof(Scalar_))); } }; diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 492c9755..c33eb3b4 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -134,7 +134,7 @@ void TDistilVectors::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); - auto &perambulator = envGet(Perambulator, par().perambulator); + auto &perambulator = *env().template getObject >(par().perambulator); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index ffdf8726..63bfd20e 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -129,7 +129,8 @@ void TPerambLight::setup(void) const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; - envCreate(Perambulator, getName() + "_perambulator_light", 1, + //envCreate(Perambulator, getName() + "_perambulator_light", 1, + env().template createObject >(getName() + "_perambulator_light", Environment::Storage::object, 1, Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); @@ -183,7 +184,9 @@ void TPerambLight::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, getName() + "_noise"); - auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); + auto &perambulator = //envGet(Perambulator, + *env().template getObject >( + getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); envGetTmp(GaugeField, Umu); From b6b267fd4bd87bfc868e1d428cb01dc24effde1a Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 31 Jan 2019 15:11:12 +0000 Subject: [PATCH 050/347] Fixed new test parameters --- tests/hadrons/Test_hadrons_distil.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 79315832..5fedd812 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -130,20 +130,20 @@ void test_PerambulatorsS(Application &application) // PerambLight parameters MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; - PerambPar.tsrc = 0; - PerambPar.nnoise = 1; - PerambPar.LI=3; - PerambPar.SI=4; - PerambPar.TI=8; + PerambPar.Distil.tsrc = 0; + PerambPar.Distil.nnoise = 1; + PerambPar.Distil.LI=3; + PerambPar.Distil.SI=4; + PerambPar.Distil.TI=8; PerambPar.nvec=3; - PerambPar.Ns=4; - PerambPar.Nt=8; - PerambPar.Nt_inv=1; - PerambPar.mass=0.04; //strange mass??? - PerambPar.M5=1.8; + PerambPar.Distil.Ns=4; + PerambPar.Distil.Nt=8; + PerambPar.Distil.Nt_inv=1; + PerambPar.Solver.mass=0.04; //strange mass??? + PerambPar.Solver.M5=1.8; PerambPar.Ls=16; - PerambPar.CGPrecision=1e-8; - PerambPar.MaxIterations=10000; + PerambPar.Solver.CGPrecision=1e-8; + PerambPar.Solver.MaxIterations=10000; application.createModule("PerambS",PerambPar); } ///////////////////////////////////////////////////////////// From 7cc13f48d5d974815772851418b512b43cbbbded Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 31 Jan 2019 16:54:11 +0000 Subject: [PATCH 051/347] added some TODO comments; needs discussion --- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index c33eb3b4..635e312a 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -156,7 +156,7 @@ void TDistilVectors::execute(void) int nnoise=par().nnoise; int LI=par().LI; int Ns=par().Ns; - int Nt_inv=par().Nt_inv; + int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI int Nt=par().Nt; int TI=par().TI; int nvec=par().nvec; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 63bfd20e..7426ff9b 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -105,7 +105,6 @@ std::vector TPerambLight::getInput(void) { std::vector in; - //in.push_back(par().noise); in.push_back(par().eigenPack); return in; @@ -175,7 +174,7 @@ void TPerambLight::execute(void) const int TI{Distil.TI}; const int nnoise{Distil.nnoise}; const int Nt{Distil.Nt}; - const int Nt_inv{Distil.Nt_inv}; + const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; const int tsrc{Distil.tsrc}; const int Ns{Distil.Ns}; @@ -242,7 +241,7 @@ void TPerambLight::execute(void) //Create Noises //std::cout << pszGaugeConfigFile << std::endl; //GridSerialRNG sRNG; sRNG.SeedUniqueString(std::string(pszGaugeConfigFile)); - GridSerialRNG sRNG; sRNG.SeedUniqueString("unique_string"); + GridSerialRNG sRNG; sRNG.SeedUniqueString("unique_string"); // TODO: Proper unique string. Include quark mass, gauge field? Maybe also nvec, but in a way that more nvec would only add noises, not change all of them??? Real rn; for (int inoise=0;inoise::execute(void) if (full_tdil) t_inv = tsrc; else t_inv = it; if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced + for (int is = ds; is < Ns; is += Ns){ // TODO: Also allow non-full spin dilution (re-define exact_distillation?) ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; tmp3d=zero; @@ -315,6 +314,7 @@ void TPerambLight::execute(void) Dop.ImportPhysicalFermionSource(dist_source,src5); SchurSolver(Dop,src5,sol5); Dop.ExportPhysicalFermionSolution(sol5,result); //These are the meson sinks + // TODO: Can we inherit something from MContraction to compute the fourier-transformed sinks??? //if (compute_current_sink) // current_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; From a9848becb008534f75855a6ac9f92ccc0cb9405e Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 1 Feb 2019 13:23:42 +0000 Subject: [PATCH 052/347] unsmeared sinks can now be computed - new test program available --- Hadrons/Modules/MDistil/PerambLight.cc | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 49 ++++++++++++++----------- tests/hadrons/Test_hadrons_distil.cc | 37 +++++++++++++++---- 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.cc b/Hadrons/Modules/MDistil/PerambLight.cc index 89f98191..36dc97af 100644 --- a/Hadrons/Modules/MDistil/PerambLight.cc +++ b/Hadrons/Modules/MDistil/PerambLight.cc @@ -4,4 +4,4 @@ using namespace Grid; using namespace Hadrons; using namespace MDistil; -template class Grid::Hadrons::MDistil::TPerambLight; +template class Grid::Hadrons::MDistil::TPerambLight; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 7426ff9b..c87fdfd3 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -56,11 +56,11 @@ public: SolverParameters, Solver); }; -template +template class TPerambLight: public Module { public: - GAUGE_TYPE_ALIASES(GImpl,); + FERM_TYPE_ALIASES(FImpl,); // constructor TPerambLight(const std::string name); // destructor @@ -81,27 +81,27 @@ protected: virtual void Cleanup(void); }; -MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); +MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); /****************************************************************************** * TPerambLight implementation * ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// -template -TPerambLight::TPerambLight(const std::string name) +template +TPerambLight::TPerambLight(const std::string name) : grid3d{nullptr}, grid4d{nullptr}, Module(name) {} // destructor -template -TPerambLight::~TPerambLight(void) +template +TPerambLight::~TPerambLight(void) { Cleanup(); }; // dependencies/products /////////////////////////////////////////////////////// -template -std::vector TPerambLight::getInput(void) +template +std::vector TPerambLight::getInput(void) { std::vector in; @@ -110,29 +110,35 @@ std::vector TPerambLight::getInput(void) return in; } -template -std::vector TPerambLight::getOutput(void) +template +std::vector TPerambLight::getOutput(void) { - std::vector out = {getName() + "_perambulator_light",getName() + "_noise"}; + std::vector out = {getName() + "_perambulator_light",getName() + "_noise",getName() + "_unsmeared_sink"}; return out; } // setup /////////////////////////////////////////////////////////////////////// -template -void TPerambLight::setup(void) +template +void TPerambLight::setup(void) { Cleanup(); // auto &noise = envGet(std::vector>>, par().noise); const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; + const int LI{Distil.LI}; + const int nnoise{Distil.nnoise}; + const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; + const int Ns{Distil.Ns}; //envCreate(Perambulator, getName() + "_perambulator_light", 1, env().template createObject >(getName() + "_perambulator_light", Environment::Storage::object, 1, Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); + envCreate(std::vector, getName() + "_unsmeared_sink", 1, + nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d);//new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); @@ -152,8 +158,8 @@ void TPerambLight::setup(void) } // clean up any temporaries created by setup (that aren't stored in the environment) -template -void TPerambLight::Cleanup(void) +template +void TPerambLight::Cleanup(void) { if( grid3d != nullptr ) { delete grid3d; @@ -163,8 +169,8 @@ void TPerambLight::Cleanup(void) } // execution /////////////////////////////////////////////////////////////////// -template -void TPerambLight::execute(void) +template +void TPerambLight::execute(void) { const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; @@ -187,6 +193,7 @@ void TPerambLight::execute(void) *env().template getObject >( getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); envGetTmp(GaugeField, Umu); FieldMetaData header; @@ -315,8 +322,8 @@ void TPerambLight::execute(void) SchurSolver(Dop,src5,sol5); Dop.ExportPhysicalFermionSolution(sol5,result); //These are the meson sinks // TODO: Can we inherit something from MContraction to compute the fourier-transformed sinks??? - //if (compute_current_sink) - // current_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; + if ((1)) // comment out if unsmeared sink is too large??? + unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; for (int is = 0; is < Ns; is++) { result_nospin = peekSpin(result,is); @@ -341,7 +348,7 @@ void TPerambLight::execute(void) perambulator.SliceShare( grid3d, grid4d ); // THIS IS WHERE WE WANT TO SAVE THE PERAMBULATORS TO DISK - perambulator.WriteTemporary(std::string("perambulators/file")); + perambulator.WriteTemporary(std::string("perambulators/file")); // TODO: Specify the file name in the xml } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 5fedd812..1a7170cc 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -169,22 +169,21 @@ void test_DistilVectorsS(Application &application) application.createModule("DistilVecsS",DistilVecPar); } ///////////////////////////////////////////////////////////// -// MesonFields +// MesonSink ///////////////////////////////////////////////////////////// -void test_MesonField(Application &application) +void test_MesonSink(Application &application) { // DistilVectors parameters MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_phi"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_phi"; + A2AMesonFieldPar.left="Peramb_unsmeared_sink"; + A2AMesonFieldPar.right="Peramb_unsmeared_sink"; A2AMesonFieldPar.output="DistilFields"; A2AMesonFieldPar.gammas="all"; A2AMesonFieldPar.mom={"0 0 0"}; A2AMesonFieldPar.cacheBlock=2; A2AMesonFieldPar.block=4; - application.createModule("DistilMesonField",A2AMesonFieldPar); + application.createModule("DistilMesonSink",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// // MesonFields @@ -197,7 +196,25 @@ void test_MesonFieldSL(Application &application) A2AMesonFieldPar.left="DistilVecsS_phi"; //A2AMesonFieldPar.right="DistilVecs_rho"; A2AMesonFieldPar.right="DistilVecs_phi"; - A2AMesonFieldPar.output="DistilFields"; + A2AMesonFieldPar.output="DistilFieldsS"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldS",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// MesonFields +///////////////////////////////////////////////////////////// + +void test_MesonField(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_phi"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_phi"; + A2AMesonFieldPar.output="MesonSinks"; A2AMesonFieldPar.gammas="all"; A2AMesonFieldPar.mom={"0 0 0"}; A2AMesonFieldPar.cacheBlock=2; @@ -360,6 +377,12 @@ int main(int argc, char *argv[]) test_DistilVectorsS( application ); test_MesonFieldSL( application ); break; + case 6: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_MesonSink( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From f7b90a0c14fe0d2c122b587b79b0c58c191b9dd7 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 1 Feb 2019 15:20:35 +0000 Subject: [PATCH 053/347] Added index names to perambulator --- Hadrons/Modules/MDistil/Distil.hpp | 13 +++++++--- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 24 +++++++++++------- tests/hadrons/Test_hadrons_distil.cc | 30 +++++++++++++++++------ 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index d1b1a747..c46c89c3 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -34,6 +34,10 @@ #include #include +#ifndef COMMA +#define COMMA , +#endif + /****************************************************************************** This potentially belongs in CartesianCommunicator Turns out I don't actually need this when running inside hadrons @@ -199,10 +203,13 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) template class Perambulator : public Eigen::Tensor { +protected: +public: + std::array IndexNames; public: template - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Perambulator(Eigen::Index firstDimension, IndexTypes... otherDimensions) - : Eigen::Tensor(firstDimension, otherDimensions...) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Perambulator(std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) + : IndexNames{IndexNames_}, Eigen::Tensor(firstDimension, otherDimensions...) { // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. EIGEN_STATIC_ASSERT(sizeof...(otherDimensions) + 1 == NumIndices_, YOU_MADE_A_PROGRAMMING_MISTAKE) @@ -212,7 +219,7 @@ public: // Share data for timeslices we calculated with other nodes inline void SliceShare( GridCartesian * gridLowDim, GridCartesian * gridHighDim ) { - //Grid::SliceShare( gridLowDim, gridHighDim, data(), (int) (size() * sizeof(Scalar_))); + Grid::SliceShare( gridLowDim, gridHighDim, this->data(), (int) (this->size() * sizeof(Scalar_))); } }; diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 635e312a..c5526bc5 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -134,7 +134,7 @@ void TDistilVectors::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); - auto &perambulator = *env().template getObject >(par().perambulator); + auto &perambulator = envGet(Perambulator, par().perambulator); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 7426ff9b..63c925ce 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -127,10 +127,17 @@ void TPerambLight::setup(void) // auto &noise = envGet(std::vector>>, par().noise); const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; + //const char * IndexNames[6] = {"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + //std::complex z{0.6,-3.1}; - //envCreate(Perambulator, getName() + "_perambulator_light", 1, - env().template createObject >(getName() + "_perambulator_light", Environment::Storage::object, 1, - Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); + //envCreate(std::string, getName() + "_debug_delete_me", 1, "Bingonuts"); + //envCreate(std::complex, getName() + "_debug_delete_me_2", 1, 0.6); + //envCreate(std::complex, getName() + "_debug_delete_me_3", 1, z); + //envCreate(std::complex, getName() + "_debug_delete_me_4", 1, {0.6 COMMA -3.1}); + //envCreate(std::array, getName() + "_debug_delete_me_5", 1, {"One" COMMA "Two" COMMA "Three"}); + envCreate(Perambulator, getName() + "_perambulator_light", 1, + sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); @@ -183,9 +190,7 @@ void TPerambLight::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, getName() + "_noise"); - auto &perambulator = //envGet(Perambulator, - *env().template getObject >( - getName() + "_perambulator_light"); + auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); envGetTmp(GaugeField, Umu); @@ -252,9 +257,10 @@ void TPerambLight::execute(void) noise[inoise + nnoise*(t + Nt*(ivec+nvec*is))] = 1.; //noises[inoise][t][ivec]()(is)() = 1.; else{ - random(sRNG,rn); - noise[inoise + nnoise*(t + Nt*(ivec+nvec*is))] = (rn-0.5 > 0) - (rn-0.5 < 0); //TODO: This could be 0 if rn==0.5!! - //noises[inoise][t][ivec]()(is)() = (rn-0.5 > 0) - (rn-0.5 < 0); //TODO: This could be 0 if rn==0.5!! + random(sRNG,rn); + // We could use a greater number of complex roots of unity + // ... but this seems to work well + noise[inoise + nnoise*(t + Nt*(ivec+nvec*is))] = (rn > 0.5) ? -1 : 1; } } } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 5fedd812..ae20b8f5 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -237,9 +237,8 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) typedef Eigen::Tensor MyTensor; -bool DebugEigenTest() +void DebugShowTensor(MyTensor &x) { - MyTensor x(2,3,4); const MyTensor::Index s{x.size()}; std::cout << "x.size() = " << s << std::endl; std::cout << "x.NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; @@ -261,11 +260,11 @@ bool DebugEigenTest() // Initialise assert( d.size() == 3 ); for( int i = 0 ; i < d[0] ; i++ ) - for( int j = 0 ; j < d[1] ; j++ ) - for( int k = 0 ; k < d[2] ; k++ ) { - x(i,j,k) = std::complex(SizeCalculated, SizeCalculated); - SizeCalculated--; - } + for( int j = 0 ; j < d[1] ; j++ ) + for( int k = 0 ; k < d[2] ; k++ ) { + x(i,j,k) = std::complex(SizeCalculated, SizeCalculated); + SizeCalculated--; + } // Show raw data std::cout << "Data follow : " << std::endl; Complex * p = x.data(); @@ -274,6 +273,21 @@ bool DebugEigenTest() std::cout << "x.data()[" << i << "]=" << * p++; } std::cout << std::endl; +} + +bool DebugEigenTest() +{ + MyTensor x(2,3,4); + DebugShowTensor(x); + // Test initialisation of an array of strings + std::array as={"Alpha", "Beta", "Gamma"}; + for( auto a : as ) + std::cout << a << std::endl; + Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; + DebugShowTensor(p); + std::cout << "p.IndexNames follow" << std::endl; + for( auto a : p.IndexNames ) + std::cout << a << std::endl; return true; } #endif @@ -282,7 +296,7 @@ int main(int argc, char *argv[]) { #ifdef DEBUG // Debug only - test of Eigen::Tensor - //if( DebugEigenTest() ) return 0; + if( DebugEigenTest() ) return 0; #endif // Decode command-line parameters. 1st one is which test to run From caabbcd95106a0190712a9bd3084eb49e64faf41 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 1 Feb 2019 17:50:18 +0000 Subject: [PATCH 054/347] minor change --- Hadrons/Modules/MDistil/PerambLight.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 5f9882b5..a8f42714 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -325,7 +325,6 @@ void TPerambLight::execute(void) Dop.ImportPhysicalFermionSource(dist_source,src5); SchurSolver(Dop,src5,sol5); Dop.ExportPhysicalFermionSolution(sol5,result); //These are the meson sinks - // TODO: Can we inherit something from MContraction to compute the fourier-transformed sinks??? if ((1)) // comment out if unsmeared sink is too large??? unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; From 8865bf5d7c18bbefc562a7ad50c9ee03f130565e Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sun, 3 Feb 2019 17:05:19 +0000 Subject: [PATCH 055/347] Implemented perambulator read/write ... but in binary format. Will switch to Hdf5 when I have Antonins feedback --- Hadrons/Modules/MDistil/Distil.hpp | 121 +++++++++++++++++++++++++-- tests/hadrons/Test_hadrons_distil.cc | 28 ++++--- 2 files changed, 133 insertions(+), 16 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index c46c89c3..f4cd06c0 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -201,28 +201,139 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) ******************************************************************************/ template -class Perambulator : public Eigen::Tensor +class NamedTensor : public Eigen::Tensor { -protected: public: + typedef Eigen::Tensor ET; std::array IndexNames; public: template - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Perambulator(std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) : IndexNames{IndexNames_}, Eigen::Tensor(firstDimension, otherDimensions...) { // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. EIGEN_STATIC_ASSERT(sizeof...(otherDimensions) + 1 == NumIndices_, YOU_MADE_A_PROGRAMMING_MISTAKE) } - inline void WriteTemporary(const std::string &FileName){} - // Share data for timeslices we calculated with other nodes inline void SliceShare( GridCartesian * gridLowDim, GridCartesian * gridHighDim ) { Grid::SliceShare( gridLowDim, gridHighDim, this->data(), (int) (this->size() * sizeof(Scalar_))); } + + // load and save - not virtual - probably all changes + inline void load(const std::string filename); + inline void save(const std::string filename) const; + inline void ReadTemporary(const std::string filename); + inline void WriteTemporary(const std::string filename) const; }; +/****************************************************************************** + Save NamedTensor binary format + ******************************************************************************/ + +template +void NamedTensor::WriteTemporary(const std::string filename) const { + std::cout << GridLogMessage << "Writing NamedTensor to \"" << filename << "\"" << std::endl; + std::ofstream w(filename, std::ios::binary); + // total number of elements + uint32_t ul = htonl( static_cast( this->size() ) ); + w.write(reinterpret_cast(&ul), sizeof(ul)); + // number of dimensions + uint16_t us = htons( static_cast( this->NumIndices ) ); + w.write(reinterpret_cast(&us), sizeof(us)); + // dimensions together with names + int d = 0; + for( auto dim : this->dimensions() ) { + // size of this dimension + us = htons( static_cast( dim ) ); + w.write(reinterpret_cast(&us), sizeof(us)); + // length of this dimension name + us = htons( static_cast( IndexNames[d].size() ) ); + w.write(reinterpret_cast(&us), sizeof(us)); + // dimension name + w.write(IndexNames[d].c_str(), IndexNames[d].size()); + d++; + } + // Actual data + w.write(reinterpret_cast(this->data()),(int) (this->size() * sizeof(Scalar_))); +} + +/****************************************************************************** + Load NamedTensor binary format + ******************************************************************************/ + +template +void NamedTensor::ReadTemporary(const std::string filename) { + std::cout << GridLogMessage << "Reading NamedTensor from \"" << filename << "\"" << std::endl; + std::ifstream r(filename, std::ios::binary); + // total number of elements + uint32_t ul; + r.read(reinterpret_cast(&ul), sizeof(ul)); + assert( this->size() == ntohl( ul ) && "Error: total number of elements" ); + // number of dimensions + uint16_t us; + r.read(reinterpret_cast(&us), sizeof(us)); + assert( this->NumIndices == ntohs( us ) && "Error: number of dimensions" ); + // dimensions together with names + int d = 0; + for( auto dim : this->dimensions() ) { + // size of dimension + r.read(reinterpret_cast(&us), sizeof(us)); + assert( dim == ntohs( us ) && "size of dimension" ); + // length of dimension name + r.read(reinterpret_cast(&us), sizeof(us)); + size_t l = ntohs( us ); + assert( l == IndexNames[d].size() && "length of dimension name" ); + // dimension name + std::string s( l, '?' ); + r.read(&s[0], l); + assert( s == IndexNames[d] && "dimension name" ); + d++; + } + // Actual data + r.read(reinterpret_cast(this->data()),(int) (this->size() * sizeof(Scalar_))); +} + +/****************************************************************************** + Save NamedTensor Hdf5 format + ******************************************************************************/ + +template +void NamedTensor::save(const std::string filename) const { + std::cout << GridLogMessage << "Writing NamedTensor to \"" << filename << "\"" << std::endl; +#ifndef HAVE_HDF5 + std::cout << GridErrorMessage << "Error: I/O for NamedTensor requires HDF5" << std::endl; +#else + Hdf5Writer w(filename); + //w << this->NumIndices << this->dimensions() << this->IndexNames; +#endif +} + +/****************************************************************************** + Load NamedTensor Hdf5 format + ******************************************************************************/ + +template +void NamedTensor::load(const std::string filename) { + std::cout << GridLogMessage << "Reading NamedTensor from \"" << filename << "\"" << std::endl; +#ifndef HAVE_HDF5 + std::cout << GridErrorMessage << "Error: I/O for NamedTensor requires HDF5" << std::endl; +#else + Hdf5Reader r(filename); + typename ET::Dimensions d; + std::array n; + //r >> this->NumIndices >> d >> n; + //this->IndexNames = n; +#endif +} + +/****************************************************************************** + Perambulator object + ******************************************************************************/ + +template +using Perambulator = NamedTensor; + /************************************************************************************* Rotate eigenvectors into our phase convention diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index b3fbc2b8..35b85d74 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -252,16 +252,16 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) #ifdef DEBUG -typedef Eigen::Tensor MyTensor; +typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; -void DebugShowTensor(MyTensor &x) +void DebugShowTensor(MyTensor &x, const char * n) { const MyTensor::Index s{x.size()}; - std::cout << "x.size() = " << s << std::endl; - std::cout << "x.NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; - std::cout << "x.NumIndices = " << x.NumIndices << std::endl; + std::cout << n << ".size() = " << s << std::endl; + std::cout << n << ".NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; + std::cout << n << ".NumIndices = " << x.NumIndices << std::endl; const MyTensor::Dimensions & d{x.dimensions()}; - std::cout << "d.size() = " << d.size() << std::endl; + std::cout << n << ".dimensions().size() = " << d.size() << std::endl; std::cout << "Dimensions are "; for(auto i : d ) std::cout << "[" << i << "]"; std::cout << std::endl; @@ -287,24 +287,30 @@ void DebugShowTensor(MyTensor &x) Complex * p = x.data(); for( auto i = 0 ; i < s ; i++ ) { if( i ) std::cout << ", "; - std::cout << "x.data()[" << i << "]=" << * p++; + std::cout << n << ".data()[" << i << "]=" << * p++; } std::cout << std::endl; } bool DebugEigenTest() { - MyTensor x(2,3,4); - DebugShowTensor(x); - // Test initialisation of an array of strings + const char pszTestFileName[] = "test_tensor.bin"; std::array as={"Alpha", "Beta", "Gamma"}; + MyTensor x(as, 2,3,4); + DebugShowTensor(x, "x"); + x.WriteTemporary(pszTestFileName); + // Test initialisation of an array of strings for( auto a : as ) std::cout << a << std::endl; Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; - DebugShowTensor(p); + DebugShowTensor(p, "p"); std::cout << "p.IndexNames follow" << std::endl; for( auto a : p.IndexNames ) std::cout << a << std::endl; + // Now see whether we can read a tensor back + MyTensor y(as, 2,3,4); + y.ReadTemporary(pszTestFileName); + DebugShowTensor(y, "y"); return true; } #endif From bd75b843fab0e5b67e90db7313a3704157471327 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sun, 3 Feb 2019 20:31:42 +0000 Subject: [PATCH 056/347] Added checksum to data --- Hadrons/Modules/MDistil/Distil.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index f4cd06c0..051b0c19 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -256,6 +256,13 @@ void NamedTensor::WriteTemporary(const std::string filenam } // Actual data w.write(reinterpret_cast(this->data()),(int) (this->size() * sizeof(Scalar_))); + // checksum +#ifdef USE_IPP + ul = htonl( GridChecksum::crc32c(this->data(), (int) (this->size() * sizeof(Scalar_))) ); +#else + ul = htonl( GridChecksum::crc32(this->data(), (int) (this->size() * sizeof(Scalar_))) ); +#endif + w.write(reinterpret_cast(&ul), sizeof(ul)); } /****************************************************************************** @@ -292,6 +299,15 @@ void NamedTensor::ReadTemporary(const std::string filename } // Actual data r.read(reinterpret_cast(this->data()),(int) (this->size() * sizeof(Scalar_))); + // checksum + r.read(reinterpret_cast(&ul), sizeof(ul)); + ul = htonl( ul ); +#ifdef USE_IPP + ul -= GridChecksum::crc32c(this->data(), (int) (this->size() * sizeof(Scalar_))); +#else + ul -= GridChecksum::crc32(this->data(), (int) (this->size() * sizeof(Scalar_))); +#endif + assert( ul == 0 && "checksum"); } /****************************************************************************** From 7eda54bb87c35b2b1e772686eca336cd7bc33352 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sun, 3 Feb 2019 20:58:58 +0000 Subject: [PATCH 057/347] Only write indices with dimesion!=1 --- Hadrons/Modules/MDistil/Distil.hpp | 58 +++++++++++++++++----------- tests/hadrons/Test_hadrons_distil.cc | 5 ++- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 051b0c19..725078ef 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -238,20 +238,26 @@ void NamedTensor::WriteTemporary(const std::string filenam // total number of elements uint32_t ul = htonl( static_cast( this->size() ) ); w.write(reinterpret_cast(&ul), sizeof(ul)); - // number of dimensions - uint16_t us = htons( static_cast( this->NumIndices ) ); + // number of dimensions which aren't 1 + uint16_t us = static_cast( this->NumIndices ); + for( auto dim : this->dimensions() ) + if( dim == 1 ) + us--; + us = htons( us ); w.write(reinterpret_cast(&us), sizeof(us)); // dimensions together with names int d = 0; for( auto dim : this->dimensions() ) { - // size of this dimension - us = htons( static_cast( dim ) ); - w.write(reinterpret_cast(&us), sizeof(us)); - // length of this dimension name - us = htons( static_cast( IndexNames[d].size() ) ); - w.write(reinterpret_cast(&us), sizeof(us)); - // dimension name - w.write(IndexNames[d].c_str(), IndexNames[d].size()); + if( dim != 1 ) { + // size of this dimension + us = htons( static_cast( dim ) ); + w.write(reinterpret_cast(&us), sizeof(us)); + // length of this dimension name + us = htons( static_cast( IndexNames[d].size() ) ); + w.write(reinterpret_cast(&us), sizeof(us)); + // dimension name + w.write(IndexNames[d].c_str(), IndexNames[d].size()); + } d++; } // Actual data @@ -277,24 +283,30 @@ void NamedTensor::ReadTemporary(const std::string filename uint32_t ul; r.read(reinterpret_cast(&ul), sizeof(ul)); assert( this->size() == ntohl( ul ) && "Error: total number of elements" ); - // number of dimensions + // number of dimensions which aren't 1 uint16_t us; r.read(reinterpret_cast(&us), sizeof(us)); - assert( this->NumIndices == ntohs( us ) && "Error: number of dimensions" ); + us = ntohs( us ); + for( auto dim : this->dimensions() ) + if( dim == 1 ) + us++; + assert( this->NumIndices == us && "Error: number of dimensions which aren't 1" ); // dimensions together with names int d = 0; for( auto dim : this->dimensions() ) { - // size of dimension - r.read(reinterpret_cast(&us), sizeof(us)); - assert( dim == ntohs( us ) && "size of dimension" ); - // length of dimension name - r.read(reinterpret_cast(&us), sizeof(us)); - size_t l = ntohs( us ); - assert( l == IndexNames[d].size() && "length of dimension name" ); - // dimension name - std::string s( l, '?' ); - r.read(&s[0], l); - assert( s == IndexNames[d] && "dimension name" ); + if( dim != 1 ) { + // size of dimension + r.read(reinterpret_cast(&us), sizeof(us)); + assert( dim == ntohs( us ) && "size of dimension" ); + // length of dimension name + r.read(reinterpret_cast(&us), sizeof(us)); + size_t l = ntohs( us ); + assert( l == IndexNames[d].size() && "length of dimension name" ); + // dimension name + std::string s( l, '?' ); + r.read(&s[0], l); + assert( s == IndexNames[d] && "dimension name" ); + } d++; } // Actual data diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 35b85d74..bf390174 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -296,7 +296,7 @@ bool DebugEigenTest() { const char pszTestFileName[] = "test_tensor.bin"; std::array as={"Alpha", "Beta", "Gamma"}; - MyTensor x(as, 2,3,4); + MyTensor x(as, 2,1,4); DebugShowTensor(x, "x"); x.WriteTemporary(pszTestFileName); // Test initialisation of an array of strings @@ -308,7 +308,8 @@ bool DebugEigenTest() for( auto a : p.IndexNames ) std::cout << a << std::endl; // Now see whether we can read a tensor back - MyTensor y(as, 2,3,4); + std::array a2={"Alpha", "Delta", "Gamma"}; + MyTensor y(a2, 2,1,4); y.ReadTemporary(pszTestFileName); DebugShowTensor(y, "y"); return true; From 43bd918a47860fee66e84d8cac2f972189fc08c5 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sun, 3 Feb 2019 21:48:50 +0000 Subject: [PATCH 058/347] Logging tweak --- Hadrons/Modules/MDistil/Distil.hpp | 12 ++++++------ tests/hadrons/Test_hadrons_distil.cc | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 725078ef..f831a3a6 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -233,7 +233,7 @@ public: template void NamedTensor::WriteTemporary(const std::string filename) const { - std::cout << GridLogMessage << "Writing NamedTensor to \"" << filename << "\"" << std::endl; + LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; std::ofstream w(filename, std::ios::binary); // total number of elements uint32_t ul = htonl( static_cast( this->size() ) ); @@ -277,7 +277,7 @@ void NamedTensor::WriteTemporary(const std::string filenam template void NamedTensor::ReadTemporary(const std::string filename) { - std::cout << GridLogMessage << "Reading NamedTensor from \"" << filename << "\"" << std::endl; + LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; std::ifstream r(filename, std::ios::binary); // total number of elements uint32_t ul; @@ -328,9 +328,9 @@ void NamedTensor::ReadTemporary(const std::string filename template void NamedTensor::save(const std::string filename) const { - std::cout << GridLogMessage << "Writing NamedTensor to \"" << filename << "\"" << std::endl; + LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; #ifndef HAVE_HDF5 - std::cout << GridErrorMessage << "Error: I/O for NamedTensor requires HDF5" << std::endl; + LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; #else Hdf5Writer w(filename); //w << this->NumIndices << this->dimensions() << this->IndexNames; @@ -343,9 +343,9 @@ void NamedTensor::save(const std::string filename) const { template void NamedTensor::load(const std::string filename) { - std::cout << GridLogMessage << "Reading NamedTensor from \"" << filename << "\"" << std::endl; + LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; #ifndef HAVE_HDF5 - std::cout << GridErrorMessage << "Error: I/O for NamedTensor requires HDF5" << std::endl; + LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; #else Hdf5Reader r(filename); typename ET::Dimensions d; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index bf390174..6629c35f 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -320,7 +320,7 @@ int main(int argc, char *argv[]) { #ifdef DEBUG // Debug only - test of Eigen::Tensor - if( DebugEigenTest() ) return 0; + //if( DebugEigenTest() ) return 0; #endif // Decode command-line parameters. 1st one is which test to run From c7aa4e0c1fe74e783ee4a7bdc5cbcd79a1c7468a Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 4 Feb 2019 11:30:30 +0000 Subject: [PATCH 059/347] Perambulator filename can be specified in xml. NB: Perambulator binary format now includes data size in bytes to avoid type mismatches. --- Hadrons/Modules/MDistil/Distil.hpp | 109 ++++++++++++++++-------- Hadrons/Modules/MDistil/PerambLight.hpp | 6 +- tests/hadrons/Test_hadrons_distil.cc | 4 + 3 files changed, 83 insertions(+), 36 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index f831a3a6..ae68ebcd 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -34,10 +34,48 @@ #include #include +/****************************************************************************** + Needed to make sure envCreate() (see Hadrons) works with specialisations + with more than one parameter, eg obj + I imagine this exists already? + ******************************************************************************/ + #ifndef COMMA #define COMMA , #endif +/****************************************************************************** + A consistent set of cross-platform methods for big endian <-> host byte ordering + I imagine this exists already? + ******************************************************************************/ + +#if defined(__linux__) +# include +#elif defined(__FreeBSD__) || defined(__NetBSD__) +# include +#elif defined(__OpenBSD__) +# include +# define be16toh(x) betoh16(x) +# define be32toh(x) betoh32(x) +# define be64toh(x) betoh64(x) +#elif defined(__APPLE__) +#include +#define htobe16(x) OSSwapHostToBigInt16(x) +#define htole16(x) OSSwapHostToLittleInt16(x) +#define be16toh(x) OSSwapBigToHostInt16(x) +#define le16toh(x) OSSwapLittleToHostInt16(x) + +#define htobe32(x) OSSwapHostToBigInt32(x) +#define htole32(x) OSSwapHostToLittleInt32(x) +#define be32toh(x) OSSwapBigToHostInt32(x) +#define le32toh(x) OSSwapLittleToHostInt32(x) + +#define htobe64(x) OSSwapHostToBigInt64(x) +#define htole64(x) OSSwapHostToLittleInt64(x) +#define be64toh(x) OSSwapBigToHostInt64(x) +#define le64toh(x) OSSwapLittleToHostInt64(x) +#endif + /****************************************************************************** This potentially belongs in CartesianCommunicator Turns out I don't actually need this when running inside hadrons @@ -235,40 +273,41 @@ template void NamedTensor::WriteTemporary(const std::string filename) const { LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; std::ofstream w(filename, std::ios::binary); - // total number of elements - uint32_t ul = htonl( static_cast( this->size() ) ); - w.write(reinterpret_cast(&ul), sizeof(ul)); + // Size of the data (in bytes) + const std::streamsize TotalDataSize{static_cast(this->size() * sizeof(Scalar_))}; + uint64_t u64 = htobe64(static_cast(TotalDataSize)); + w.write(reinterpret_cast(&u64), sizeof(u64)); // number of dimensions which aren't 1 - uint16_t us = static_cast( this->NumIndices ); + uint16_t u16 = static_cast(this->NumIndices); for( auto dim : this->dimensions() ) if( dim == 1 ) - us--; - us = htons( us ); - w.write(reinterpret_cast(&us), sizeof(us)); + u16--; + u16 = htobe16( u16 ); + w.write(reinterpret_cast(&u16), sizeof(u16)); // dimensions together with names int d = 0; for( auto dim : this->dimensions() ) { if( dim != 1 ) { // size of this dimension - us = htons( static_cast( dim ) ); - w.write(reinterpret_cast(&us), sizeof(us)); + u16 = htobe16( static_cast( dim ) ); + w.write(reinterpret_cast(&u16), sizeof(u16)); // length of this dimension name - us = htons( static_cast( IndexNames[d].size() ) ); - w.write(reinterpret_cast(&us), sizeof(us)); + u16 = htobe16( static_cast( IndexNames[d].size() ) ); + w.write(reinterpret_cast(&u16), sizeof(u16)); // dimension name w.write(IndexNames[d].c_str(), IndexNames[d].size()); } d++; } // Actual data - w.write(reinterpret_cast(this->data()),(int) (this->size() * sizeof(Scalar_))); + w.write(reinterpret_cast(this->data()), TotalDataSize); // checksum #ifdef USE_IPP - ul = htonl( GridChecksum::crc32c(this->data(), (int) (this->size() * sizeof(Scalar_))) ); + uint32_t u32 = htobe32(GridChecksum::crc32c(this->data(), TotalDataSize)); #else - ul = htonl( GridChecksum::crc32(this->data(), (int) (this->size() * sizeof(Scalar_))) ); + uint32_t u32 = htobe32(GridChecksum::crc32(this->data(), TotalDataSize)); #endif - w.write(reinterpret_cast(&ul), sizeof(ul)); + w.write(reinterpret_cast(&u32), sizeof(u32)); } /****************************************************************************** @@ -279,28 +318,29 @@ template void NamedTensor::ReadTemporary(const std::string filename) { LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; std::ifstream r(filename, std::ios::binary); - // total number of elements - uint32_t ul; - r.read(reinterpret_cast(&ul), sizeof(ul)); - assert( this->size() == ntohl( ul ) && "Error: total number of elements" ); + // Size of the data in bytes + const std::streamsize TotalDataSize{static_cast(this->size() * sizeof(Scalar_))}; + uint64_t u64; + r.read(reinterpret_cast(&u64), sizeof(u64)); + assert( TotalDataSize == be64toh( u64 ) && "Error: Size of the data in bytes" ); // number of dimensions which aren't 1 - uint16_t us; - r.read(reinterpret_cast(&us), sizeof(us)); - us = ntohs( us ); + uint16_t u16; + r.read(reinterpret_cast(&u16), sizeof(u16)); + u16 = be16toh( u16 ); for( auto dim : this->dimensions() ) if( dim == 1 ) - us++; - assert( this->NumIndices == us && "Error: number of dimensions which aren't 1" ); + u16++; + assert( this->NumIndices == u16 && "Error: number of dimensions which aren't 1" ); // dimensions together with names int d = 0; for( auto dim : this->dimensions() ) { if( dim != 1 ) { // size of dimension - r.read(reinterpret_cast(&us), sizeof(us)); - assert( dim == ntohs( us ) && "size of dimension" ); + r.read(reinterpret_cast(&u16), sizeof(u16)); + assert( dim == be16toh( u16 ) && "size of dimension" ); // length of dimension name - r.read(reinterpret_cast(&us), sizeof(us)); - size_t l = ntohs( us ); + r.read(reinterpret_cast(&u16), sizeof(u16)); + size_t l = be16toh( u16 ); assert( l == IndexNames[d].size() && "length of dimension name" ); // dimension name std::string s( l, '?' ); @@ -310,16 +350,17 @@ void NamedTensor::ReadTemporary(const std::string filename d++; } // Actual data - r.read(reinterpret_cast(this->data()),(int) (this->size() * sizeof(Scalar_))); + r.read(reinterpret_cast(this->data()),TotalDataSize); // checksum - r.read(reinterpret_cast(&ul), sizeof(ul)); - ul = htonl( ul ); + uint32_t u32; + r.read(reinterpret_cast(&u32), sizeof(u32)); + u32 = be32toh( u32 ); #ifdef USE_IPP - ul -= GridChecksum::crc32c(this->data(), (int) (this->size() * sizeof(Scalar_))); + u32 -= GridChecksum::crc32c(this->data(), TotalDataSize); #else - ul -= GridChecksum::crc32(this->data(), (int) (this->size() * sizeof(Scalar_))); + u32 -= GridChecksum::crc32(this->data(), TotalDataSize); #endif - assert( ul == 0 && "checksum"); + assert( u32 == 0 && "checksum"); } /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index a8f42714..85b3ac35 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -49,6 +49,7 @@ class PerambLightPar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, std::string, eigenPack, + std::string, PerambFileName, bool, multiFile, int, nvec, int, Ls, // For makeFiveDimGrid @@ -351,8 +352,9 @@ void TPerambLight::execute(void) perambulator.SliceShare( grid3d, grid4d ); // THIS IS WHERE WE WANT TO SAVE THE PERAMBULATORS TO DISK - perambulator.WriteTemporary(std::string("perambulators/file")); // TODO: Specify the file name in the xml - + const std::string &FileName{par().PerambFileName}; + if(FileName.length()) + perambulator.WriteTemporary(FileName); } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 6629c35f..1fc97921 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -87,6 +87,7 @@ void test_Perambulators(Application &application) // PerambLight parameters MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; + PerambPar.PerambFileName="peramb.bin"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; PerambPar.Distil.LI=5; @@ -130,6 +131,7 @@ void test_PerambulatorsS(Application &application) // PerambLight parameters MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; + PerambPar.PerambFileName="peramb.bin"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; PerambPar.Distil.LI=3; @@ -320,6 +322,8 @@ int main(int argc, char *argv[]) { #ifdef DEBUG // Debug only - test of Eigen::Tensor + std::cout << "sizeof(std::streamsize) = " << sizeof(std::streamsize) << std::endl; + std::cout << "sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; //if( DebugEigenTest() ) return 0; #endif From 008ac6b5aedce06a39fdb89a288e48cd4f21bf03 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 4 Feb 2019 12:06:32 +0000 Subject: [PATCH 060/347] Permabulator is read back from disk if it exists instead of being created --- Hadrons/Modules/MDistil/PerambLight.hpp | 68 +++++++++++++++---------- tests/hadrons/Test_hadrons_distil.cc | 2 +- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 85b3ac35..3471ce38 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -234,6 +234,45 @@ void TPerambLight::execute(void) std::cout << GridLogMessage << "reading done." << std::endl; } + //Create Noises + //std::cout << pszGaugeConfigFile << std::endl; + //GridSerialRNG sRNG; sRNG.SeedUniqueString(std::string(pszGaugeConfigFile)); + GridSerialRNG sRNG; sRNG.SeedUniqueString("unique_string"); // TODO: Proper unique string. Include quark mass, gauge field? Maybe also nvec, but in a way that more nvec would only add noises, not change all of them??? + Real rn; + + for (int inoise=0;inoise 0.5) ? -1 : 1; + } + } + } + } + } + + // Load perambulator if it exists on disk instead of creating it + const std::string &PerambFileName{par().PerambFileName}; + if( PerambFileName.length() ){ + bool bExists = false; + { + std::ifstream f(PerambFileName, std::ios::binary); + if( f.is_open() ) + bExists = true; + } + if( bExists ) { + perambulator.ReadTemporary(PerambFileName); + return; + } + } + envGetTmp(LatticeSpinColourVector, dist_source); envGetTmp(LatticeSpinColourVector, tmp2); envGetTmp(LatticeSpinColourVector, result); @@ -249,30 +288,6 @@ void TPerambLight::execute(void) const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - //Create Noises - //std::cout << pszGaugeConfigFile << std::endl; - //GridSerialRNG sRNG; sRNG.SeedUniqueString(std::string(pszGaugeConfigFile)); - GridSerialRNG sRNG; sRNG.SeedUniqueString("unique_string"); // TODO: Proper unique string. Include quark mass, gauge field? Maybe also nvec, but in a way that more nvec would only add noises, not change all of them??? - Real rn; - - for (int inoise=0;inoise 0.5) ? -1 : 1; - } - } - } - } - } - const Real mass{Solver.mass}; const Real M5 {Solver.M5}; std::cout << "init RBG " << std::endl; @@ -352,9 +367,8 @@ void TPerambLight::execute(void) perambulator.SliceShare( grid3d, grid4d ); // THIS IS WHERE WE WANT TO SAVE THE PERAMBULATORS TO DISK - const std::string &FileName{par().PerambFileName}; - if(FileName.length()) - perambulator.WriteTemporary(FileName); + if(PerambFileName.length()) + perambulator.WriteTemporary(PerambFileName); } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 1fc97921..eaa2ef19 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -131,7 +131,7 @@ void test_PerambulatorsS(Application &application) // PerambLight parameters MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; - PerambPar.PerambFileName="peramb.bin"; + PerambPar.PerambFileName="perambS.bin"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; PerambPar.Distil.LI=3; From 7f5354630a58ead85e7b2af8d869968cc97be4dc Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 4 Feb 2019 23:07:59 +0000 Subject: [PATCH 061/347] Updated perambulator binary format to save payload in big endian format on disk --- Hadrons/Modules/MDistil/Distil.hpp | 86 +++++++++++++++++------ Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 8 +-- tests/hadrons/Test_hadrons_distil.cc | 17 ++--- 4 files changed, 80 insertions(+), 33 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index ae68ebcd..4071d5bd 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -238,7 +238,7 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) Perambulator object ******************************************************************************/ -template +template class NamedTensor : public Eigen::Tensor { public: @@ -261,20 +261,26 @@ public: // load and save - not virtual - probably all changes inline void load(const std::string filename); inline void save(const std::string filename) const; - inline void ReadTemporary(const std::string filename); - inline void WriteTemporary(const std::string filename) const; + inline void ReadBinary(const std::string filename); + inline void WriteBinary(const std::string filename); }; /****************************************************************************** - Save NamedTensor binary format + Save NamedTensor binary format (NB: On-disk format is Big Endian) ******************************************************************************/ -template -void NamedTensor::WriteTemporary(const std::string filename) const { +template +void NamedTensor::WriteBinary(const std::string filename) { LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; std::ofstream w(filename, std::ios::binary); + // Number of Scalar_Unit objects per Scalar_ + constexpr unsigned int Scalar_Unit_Size{sizeof(Scalar_Unit)}; + assert((Scalar_Unit_Size == 2 || Scalar_Unit_Size == 4 || Scalar_Unit_Size == 8 ) + && "Scalar_Unit_Size should be 2, 4 or 8"); + assert((sizeof(Scalar_) % Scalar_Unit_Size) == 0 && "Scalar_ is not composed of Scalar_Unit_Size" ); // Size of the data (in bytes) - const std::streamsize TotalDataSize{static_cast(this->size() * sizeof(Scalar_))}; + const auto NumElements{this->size()}; + const std::streamsize TotalDataSize{static_cast(NumElements * sizeof(Scalar_))}; uint64_t u64 = htobe64(static_cast(TotalDataSize)); w.write(reinterpret_cast(&u64), sizeof(u64)); // number of dimensions which aren't 1 @@ -300,7 +306,29 @@ void NamedTensor::WriteTemporary(const std::string filenam d++; } // Actual data - w.write(reinterpret_cast(this->data()), TotalDataSize); + char * const pStart{reinterpret_cast(this->data())}; + // Swap to network byte order in place (alternative is to copy memory - still slow) + void * const pEnd{pStart + TotalDataSize}; + if(Scalar_Unit_Size == 8) + for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = htobe64( * p ); + else if(Scalar_Unit_Size == 4) + for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = htobe32( * p ); + else if(Scalar_Unit_Size == 2) + for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = htobe16( * p ); + w.write(pStart, TotalDataSize); + // Swap back from network byte order + if(Scalar_Unit_Size == 8) + for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = be64toh( * p ); + else if(Scalar_Unit_Size == 4) + for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = be32toh( * p ); + else if(Scalar_Unit_Size == 2) + for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = be16toh( * p ); // checksum #ifdef USE_IPP uint32_t u32 = htobe32(GridChecksum::crc32c(this->data(), TotalDataSize)); @@ -311,15 +339,21 @@ void NamedTensor::WriteTemporary(const std::string filenam } /****************************************************************************** - Load NamedTensor binary format + Load NamedTensor binary format (NB: On-disk format is Big Endian) ******************************************************************************/ -template -void NamedTensor::ReadTemporary(const std::string filename) { +template +void NamedTensor::ReadBinary(const std::string filename) { LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; std::ifstream r(filename, std::ios::binary); + // Number of Scalar_Unit objects per Scalar_ + constexpr unsigned int Scalar_Unit_Size{sizeof(Scalar_Unit)}; + assert((Scalar_Unit_Size == 2 || Scalar_Unit_Size == 4 || Scalar_Unit_Size == 8 ) + && "Scalar_Unit_Size should be 2, 4 or 8"); + assert((sizeof(Scalar_) % Scalar_Unit_Size) == 0 && "Scalar_ is not composed of Scalar_Unit_Size" ); // Size of the data in bytes - const std::streamsize TotalDataSize{static_cast(this->size() * sizeof(Scalar_))}; + const auto NumElements{this->size()}; + const std::streamsize TotalDataSize{static_cast(NumElements * sizeof(Scalar_))}; uint64_t u64; r.read(reinterpret_cast(&u64), sizeof(u64)); assert( TotalDataSize == be64toh( u64 ) && "Error: Size of the data in bytes" ); @@ -350,7 +384,19 @@ void NamedTensor::ReadTemporary(const std::string filename d++; } // Actual data - r.read(reinterpret_cast(this->data()),TotalDataSize); + char * const pStart{reinterpret_cast(this->data())}; + void * const pEnd{pStart + TotalDataSize}; + r.read(pStart,TotalDataSize); + // Swap back from network byte order + if(Scalar_Unit_Size == 8) + for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = be64toh( * p ); + else if(Scalar_Unit_Size == 4) + for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = be32toh( * p ); + else if(Scalar_Unit_Size == 2) + for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) + * p = be16toh( * p ); // checksum uint32_t u32; r.read(reinterpret_cast(&u32), sizeof(u32)); @@ -360,15 +406,15 @@ void NamedTensor::ReadTemporary(const std::string filename #else u32 -= GridChecksum::crc32(this->data(), TotalDataSize); #endif - assert( u32 == 0 && "checksum"); + assert( u32 == 0 && "Perambulator checksum invalid"); } /****************************************************************************** Save NamedTensor Hdf5 format ******************************************************************************/ -template -void NamedTensor::save(const std::string filename) const { +template +void NamedTensor::save(const std::string filename) const { LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; #ifndef HAVE_HDF5 LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; @@ -382,8 +428,8 @@ void NamedTensor::save(const std::string filename) const { Load NamedTensor Hdf5 format ******************************************************************************/ -template -void NamedTensor::load(const std::string filename) { +template +void NamedTensor::load(const std::string filename) { LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; #ifndef HAVE_HDF5 LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; @@ -400,8 +446,8 @@ void NamedTensor::load(const std::string filename) { Perambulator object ******************************************************************************/ -template -using Perambulator = NamedTensor; +template +using Perambulator = NamedTensor; /************************************************************************************* diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index c5526bc5..39ba4d09 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -134,7 +134,7 @@ void TDistilVectors::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); - auto &perambulator = envGet(Perambulator, par().perambulator); + auto &perambulator = envGet(Perambulator, par().perambulator); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 3471ce38..7e710d51 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -139,7 +139,7 @@ void TPerambLight::setup(void) //envCreate(std::complex, getName() + "_debug_delete_me_3", 1, z); //envCreate(std::complex, getName() + "_debug_delete_me_4", 1, {0.6 COMMA -3.1}); //envCreate(std::array, getName() + "_debug_delete_me_5", 1, {"One" COMMA "Two" COMMA "Three"}); - envCreate(Perambulator, getName() + "_perambulator_light", 1, + envCreate(Perambulator, getName() + "_perambulator_light", 1, sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); @@ -195,7 +195,7 @@ void TPerambLight::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, getName() + "_noise"); - auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); + auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); @@ -268,7 +268,7 @@ void TPerambLight::execute(void) bExists = true; } if( bExists ) { - perambulator.ReadTemporary(PerambFileName); + perambulator.ReadBinary(PerambFileName); return; } } @@ -368,7 +368,7 @@ void TPerambLight::execute(void) // THIS IS WHERE WE WANT TO SAVE THE PERAMBULATORS TO DISK if(PerambFileName.length()) - perambulator.WriteTemporary(PerambFileName); + perambulator.WriteBinary(PerambFileName); } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index eaa2ef19..34c27df3 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -254,7 +254,7 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) #ifdef DEBUG -typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; +typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; void DebugShowTensor(MyTensor &x, const char * n) { @@ -270,7 +270,7 @@ void DebugShowTensor(MyTensor &x, const char * n) MyTensor::Index SizeCalculated{1}; std::cout << "Dimensions again"; for(int i=0 ; i < d.size() ; i++ ) { - std::cout << " : [" << i << "]=" << d[i]; + std::cout << " : [" << i << ", " << x.IndexNames[i] << "]=" << d[i]; SizeCalculated *= d[i]; } std::cout << std::endl; @@ -281,7 +281,7 @@ void DebugShowTensor(MyTensor &x, const char * n) for( int i = 0 ; i < d[0] ; i++ ) for( int j = 0 ; j < d[1] ; j++ ) for( int k = 0 ; k < d[2] ; k++ ) { - x(i,j,k) = std::complex(SizeCalculated, SizeCalculated); + x(i,j,k) = std::complex(SizeCalculated, -SizeCalculated); SizeCalculated--; } // Show raw data @@ -300,19 +300,20 @@ bool DebugEigenTest() std::array as={"Alpha", "Beta", "Gamma"}; MyTensor x(as, 2,1,4); DebugShowTensor(x, "x"); - x.WriteTemporary(pszTestFileName); + x.WriteBinary(pszTestFileName); + DebugShowTensor(x, "x"); // Test initialisation of an array of strings for( auto a : as ) std::cout << a << std::endl; - Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; + Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; DebugShowTensor(p, "p"); std::cout << "p.IndexNames follow" << std::endl; for( auto a : p.IndexNames ) std::cout << a << std::endl; // Now see whether we can read a tensor back - std::array a2={"Alpha", "Delta", "Gamma"}; - MyTensor y(a2, 2,1,4); - y.ReadTemporary(pszTestFileName); + std::array a2={"Alpha", "Gamma", "Delta"}; + MyTensor y(a2, 2,4,1); + y.ReadBinary(pszTestFileName); DebugShowTensor(y, "y"); return true; } From 5b0870bb1992582e7f5c28e3d0510c63b42acfe3 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 5 Feb 2019 09:07:05 +0000 Subject: [PATCH 062/347] Added Scalar_ length and Scalar_Unit_Size to Perambulator file for validation --- Hadrons/Modules/MDistil/Distil.hpp | 76 +++++++++++++---------- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 5 +- tests/hadrons/Test_hadrons_distil.cc | 4 +- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 4071d5bd..6d4603b5 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -238,7 +238,7 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) Perambulator object ******************************************************************************/ -template +template class NamedTensor : public Eigen::Tensor { public: @@ -269,22 +269,28 @@ public: Save NamedTensor binary format (NB: On-disk format is Big Endian) ******************************************************************************/ -template -void NamedTensor::WriteBinary(const std::string filename) { +template +void NamedTensor::WriteBinary(const std::string filename) { LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; std::ofstream w(filename, std::ios::binary); - // Number of Scalar_Unit objects per Scalar_ - constexpr unsigned int Scalar_Unit_Size{sizeof(Scalar_Unit)}; - assert((Scalar_Unit_Size == 2 || Scalar_Unit_Size == 4 || Scalar_Unit_Size == 8 ) - && "Scalar_Unit_Size should be 2, 4 or 8"); + // Enforce assumption that the scalar is composed of fundamental elements of size Scalar_Unit_Size + assert((Scalar_Unit_Size == 1 || Scalar_Unit_Size == 2 || Scalar_Unit_Size == 4 || Scalar_Unit_Size == 8 ) + && "Scalar_Unit_Size should be 1, 2, 4 or 8"); assert((sizeof(Scalar_) % Scalar_Unit_Size) == 0 && "Scalar_ is not composed of Scalar_Unit_Size" ); // Size of the data (in bytes) + const uint32_t Scalar_Size{sizeof(Scalar_)}; const auto NumElements{this->size()}; - const std::streamsize TotalDataSize{static_cast(NumElements * sizeof(Scalar_))}; + const std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; uint64_t u64 = htobe64(static_cast(TotalDataSize)); w.write(reinterpret_cast(&u64), sizeof(u64)); + // Size of a Scalar_ + uint32_t u32{htobe32(Scalar_Size)}; + w.write(reinterpret_cast(&u32), sizeof(u32)); + // Scalar_Unit_Size + uint16_t u16{htobe16(Scalar_Unit_Size)}; + w.write(reinterpret_cast(&u16), sizeof(u16)); // number of dimensions which aren't 1 - uint16_t u16 = static_cast(this->NumIndices); + u16 = static_cast(this->NumIndices); for( auto dim : this->dimensions() ) if( dim == 1 ) u16--; @@ -331,9 +337,9 @@ void NamedTensor::WriteBinary(const std::stri * p = be16toh( * p ); // checksum #ifdef USE_IPP - uint32_t u32 = htobe32(GridChecksum::crc32c(this->data(), TotalDataSize)); + u32 = htobe32(GridChecksum::crc32c(this->data(), TotalDataSize)); #else - uint32_t u32 = htobe32(GridChecksum::crc32(this->data(), TotalDataSize)); + u32 = htobe32(GridChecksum::crc32(this->data(), TotalDataSize)); #endif w.write(reinterpret_cast(&u32), sizeof(u32)); } @@ -342,29 +348,36 @@ void NamedTensor::WriteBinary(const std::stri Load NamedTensor binary format (NB: On-disk format is Big Endian) ******************************************************************************/ -template -void NamedTensor::ReadBinary(const std::string filename) { +template +void NamedTensor::ReadBinary(const std::string filename) { LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; std::ifstream r(filename, std::ios::binary); - // Number of Scalar_Unit objects per Scalar_ - constexpr unsigned int Scalar_Unit_Size{sizeof(Scalar_Unit)}; - assert((Scalar_Unit_Size == 2 || Scalar_Unit_Size == 4 || Scalar_Unit_Size == 8 ) - && "Scalar_Unit_Size should be 2, 4 or 8"); - assert((sizeof(Scalar_) % Scalar_Unit_Size) == 0 && "Scalar_ is not composed of Scalar_Unit_Size" ); + // Enforce assumption that the scalar is composed of fundamental elements of size Scalar_Unit_Size + assert((Scalar_Unit_Size == 1 || Scalar_Unit_Size == 2 || Scalar_Unit_Size == 4 || Scalar_Unit_Size == 8 ) + && "NamedTensor error: Scalar_Unit_Size should be 1, 2, 4 or 8"); + assert((sizeof(Scalar_) % Scalar_Unit_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Scalar_Unit_Size" ); // Size of the data in bytes + const uint32_t Scalar_Size{sizeof(Scalar_)}; const auto NumElements{this->size()}; - const std::streamsize TotalDataSize{static_cast(NumElements * sizeof(Scalar_))}; + const std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; uint64_t u64; r.read(reinterpret_cast(&u64), sizeof(u64)); - assert( TotalDataSize == be64toh( u64 ) && "Error: Size of the data in bytes" ); - // number of dimensions which aren't 1 + assert( TotalDataSize == be64toh( u64 ) && "NamedTensor error: Size of the data in bytes" ); + // Size of a Scalar_ + uint32_t u32; + r.read(reinterpret_cast(&u32), sizeof(u32)); + assert( Scalar_Size == be32toh( u32 ) && "NamedTensor error: sizeof(Scalar_)"); + // Scalar_Unit_Size uint16_t u16; r.read(reinterpret_cast(&u16), sizeof(u16)); + assert( Scalar_Unit_Size == be16toh( u16 ) && "NamedTensor error: Scalar_Unit_size"); + // number of dimensions which aren't 1 + r.read(reinterpret_cast(&u16), sizeof(u16)); u16 = be16toh( u16 ); for( auto dim : this->dimensions() ) if( dim == 1 ) u16++; - assert( this->NumIndices == u16 && "Error: number of dimensions which aren't 1" ); + assert( this->NumIndices == u16 && "NamedTensor error: number of dimensions which aren't 1" ); // dimensions together with names int d = 0; for( auto dim : this->dimensions() ) { @@ -375,11 +388,11 @@ void NamedTensor::ReadBinary(const std::strin // length of dimension name r.read(reinterpret_cast(&u16), sizeof(u16)); size_t l = be16toh( u16 ); - assert( l == IndexNames[d].size() && "length of dimension name" ); + assert( l == IndexNames[d].size() && "NamedTensor error: length of dimension name" ); // dimension name std::string s( l, '?' ); r.read(&s[0], l); - assert( s == IndexNames[d] && "dimension name" ); + assert( s == IndexNames[d] && "NamedTensor error: dimension name" ); } d++; } @@ -398,7 +411,6 @@ void NamedTensor::ReadBinary(const std::strin for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = be16toh( * p ); // checksum - uint32_t u32; r.read(reinterpret_cast(&u32), sizeof(u32)); u32 = be32toh( u32 ); #ifdef USE_IPP @@ -406,15 +418,15 @@ void NamedTensor::ReadBinary(const std::strin #else u32 -= GridChecksum::crc32(this->data(), TotalDataSize); #endif - assert( u32 == 0 && "Perambulator checksum invalid"); + assert( u32 == 0 && "NamedTensor error: Perambulator checksum invalid"); } /****************************************************************************** Save NamedTensor Hdf5 format ******************************************************************************/ -template -void NamedTensor::save(const std::string filename) const { +template +void NamedTensor::save(const std::string filename) const { LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; #ifndef HAVE_HDF5 LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; @@ -428,8 +440,8 @@ void NamedTensor::save(const std::string file Load NamedTensor Hdf5 format ******************************************************************************/ -template -void NamedTensor::load(const std::string filename) { +template +void NamedTensor::load(const std::string filename) { LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; #ifndef HAVE_HDF5 LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; @@ -446,8 +458,8 @@ void NamedTensor::load(const std::string file Perambulator object ******************************************************************************/ -template -using Perambulator = NamedTensor; +template +using Perambulator = NamedTensor; /************************************************************************************* diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 39ba4d09..775719c7 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -134,7 +134,7 @@ void TDistilVectors::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); - auto &perambulator = envGet(Perambulator, par().perambulator); + auto &perambulator = envGet(Perambulator, par().perambulator); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 7e710d51..ed6bc370 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -139,7 +139,7 @@ void TPerambLight::setup(void) //envCreate(std::complex, getName() + "_debug_delete_me_3", 1, z); //envCreate(std::complex, getName() + "_debug_delete_me_4", 1, {0.6 COMMA -3.1}); //envCreate(std::array, getName() + "_debug_delete_me_5", 1, {"One" COMMA "Two" COMMA "Three"}); - envCreate(Perambulator, getName() + "_perambulator_light", 1, + envCreate(Perambulator, getName() + "_perambulator_light", 1, sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); @@ -195,7 +195,8 @@ void TPerambLight::execute(void) //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, getName() + "_noise"); - auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); + auto &perambulator = envGet(Perambulator, + getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 34c27df3..8246a182 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -254,7 +254,7 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) #ifdef DEBUG -typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; +typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; void DebugShowTensor(MyTensor &x, const char * n) { @@ -305,7 +305,7 @@ bool DebugEigenTest() // Test initialisation of an array of strings for( auto a : as ) std::cout << a << std::endl; - Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; + Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; DebugShowTensor(p, "p"); std::cout << "p.IndexNames follow" << std::endl; for( auto a : p.IndexNames ) From 57e57d162f800e75fb5fed52b071f78b4feee28e Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 5 Feb 2019 12:50:28 +0000 Subject: [PATCH 063/347] Removed Eigen::DontAlign attribute --- Hadrons/Modules/MDistil/Distil.hpp | 81 +++++++++++++++++------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 6d4603b5..6c2dc93d 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -236,21 +236,28 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) /****************************************************************************** Perambulator object + This is an Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) + They can be persisted to disk, with the on-disk format being big endian. + Scalar_ objects are assumed to be composite objects of size Endian_Scalar_Size. + (Disable big-endian by setting Endian_Scalar_Size=1) + IndexNames contains one name for each index, and IndexNames are validated on load. + (NB: Indices of dimension 1 are not saved, and not validated on load) ******************************************************************************/ -template -class NamedTensor : public Eigen::Tensor +template +class NamedTensor : public Eigen::Tensor { public: - typedef Eigen::Tensor ET; + typedef Eigen::Tensor ET; std::array IndexNames; public: template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) - : IndexNames{IndexNames_}, Eigen::Tensor(firstDimension, otherDimensions...) + : IndexNames{IndexNames_}, ET(firstDimension, otherDimensions...) { // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. - EIGEN_STATIC_ASSERT(sizeof...(otherDimensions) + 1 == NumIndices_, YOU_MADE_A_PROGRAMMING_MISTAKE) + assert(sizeof...(otherDimensions) + 1 == NumIndices_ + && "NamedTensor error: dimensions in constructor != tensor rank"); } // Share data for timeslices we calculated with other nodes @@ -267,16 +274,17 @@ public: /****************************************************************************** Save NamedTensor binary format (NB: On-disk format is Big Endian) + Assumes the Scalar_ objects are contiguous (no padding) ******************************************************************************/ -template -void NamedTensor::WriteBinary(const std::string filename) { +template +void NamedTensor::WriteBinary(const std::string filename) { LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; std::ofstream w(filename, std::ios::binary); - // Enforce assumption that the scalar is composed of fundamental elements of size Scalar_Unit_Size - assert((Scalar_Unit_Size == 1 || Scalar_Unit_Size == 2 || Scalar_Unit_Size == 4 || Scalar_Unit_Size == 8 ) - && "Scalar_Unit_Size should be 1, 2, 4 or 8"); - assert((sizeof(Scalar_) % Scalar_Unit_Size) == 0 && "Scalar_ is not composed of Scalar_Unit_Size" ); + // Enforce assumption that the scalar is composed of fundamental elements of size Endian_Scalar_Size + assert((Endian_Scalar_Size == 1 || Endian_Scalar_Size == 2 || Endian_Scalar_Size == 4 || Endian_Scalar_Size == 8 ) + && "NamedTensor error: Endian_Scalar_Size should be 1, 2, 4 or 8"); + assert((sizeof(Scalar_) % Endian_Scalar_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Endian_Scalar_Size" ); // Size of the data (in bytes) const uint32_t Scalar_Size{sizeof(Scalar_)}; const auto NumElements{this->size()}; @@ -286,8 +294,8 @@ void NamedTensor::WriteBinary(const std: // Size of a Scalar_ uint32_t u32{htobe32(Scalar_Size)}; w.write(reinterpret_cast(&u32), sizeof(u32)); - // Scalar_Unit_Size - uint16_t u16{htobe16(Scalar_Unit_Size)}; + // Endian_Scalar_Size + uint16_t u16{htobe16(Endian_Scalar_Size)}; w.write(reinterpret_cast(&u16), sizeof(u16)); // number of dimensions which aren't 1 u16 = static_cast(this->NumIndices); @@ -315,24 +323,24 @@ void NamedTensor::WriteBinary(const std: char * const pStart{reinterpret_cast(this->data())}; // Swap to network byte order in place (alternative is to copy memory - still slow) void * const pEnd{pStart + TotalDataSize}; - if(Scalar_Unit_Size == 8) + if(Endian_Scalar_Size == 8) for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = htobe64( * p ); - else if(Scalar_Unit_Size == 4) + else if(Endian_Scalar_Size == 4) for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = htobe32( * p ); - else if(Scalar_Unit_Size == 2) + else if(Endian_Scalar_Size == 2) for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = htobe16( * p ); w.write(pStart, TotalDataSize); // Swap back from network byte order - if(Scalar_Unit_Size == 8) + if(Endian_Scalar_Size == 8) for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = be64toh( * p ); - else if(Scalar_Unit_Size == 4) + else if(Endian_Scalar_Size == 4) for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = be32toh( * p ); - else if(Scalar_Unit_Size == 2) + else if(Endian_Scalar_Size == 2) for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = be16toh( * p ); // checksum @@ -346,16 +354,17 @@ void NamedTensor::WriteBinary(const std: /****************************************************************************** Load NamedTensor binary format (NB: On-disk format is Big Endian) + Assumes the Scalar_ objects are contiguous (no padding) ******************************************************************************/ -template -void NamedTensor::ReadBinary(const std::string filename) { +template +void NamedTensor::ReadBinary(const std::string filename) { LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; std::ifstream r(filename, std::ios::binary); - // Enforce assumption that the scalar is composed of fundamental elements of size Scalar_Unit_Size - assert((Scalar_Unit_Size == 1 || Scalar_Unit_Size == 2 || Scalar_Unit_Size == 4 || Scalar_Unit_Size == 8 ) - && "NamedTensor error: Scalar_Unit_Size should be 1, 2, 4 or 8"); - assert((sizeof(Scalar_) % Scalar_Unit_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Scalar_Unit_Size" ); + // Enforce assumption that the scalar is composed of fundamental elements of size Endian_Scalar_Size + assert((Endian_Scalar_Size == 1 || Endian_Scalar_Size == 2 || Endian_Scalar_Size == 4 || Endian_Scalar_Size == 8 ) + && "NamedTensor error: Endian_Scalar_Size should be 1, 2, 4 or 8"); + assert((sizeof(Scalar_) % Endian_Scalar_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Endian_Scalar_Size" ); // Size of the data in bytes const uint32_t Scalar_Size{sizeof(Scalar_)}; const auto NumElements{this->size()}; @@ -367,10 +376,10 @@ void NamedTensor::ReadBinary(const std:: uint32_t u32; r.read(reinterpret_cast(&u32), sizeof(u32)); assert( Scalar_Size == be32toh( u32 ) && "NamedTensor error: sizeof(Scalar_)"); - // Scalar_Unit_Size + // Endian_Scalar_Size uint16_t u16; r.read(reinterpret_cast(&u16), sizeof(u16)); - assert( Scalar_Unit_Size == be16toh( u16 ) && "NamedTensor error: Scalar_Unit_size"); + assert( Endian_Scalar_Size == be16toh( u16 ) && "NamedTensor error: Scalar_Unit_size"); // number of dimensions which aren't 1 r.read(reinterpret_cast(&u16), sizeof(u16)); u16 = be16toh( u16 ); @@ -401,13 +410,13 @@ void NamedTensor::ReadBinary(const std:: void * const pEnd{pStart + TotalDataSize}; r.read(pStart,TotalDataSize); // Swap back from network byte order - if(Scalar_Unit_Size == 8) + if(Endian_Scalar_Size == 8) for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = be64toh( * p ); - else if(Scalar_Unit_Size == 4) + else if(Endian_Scalar_Size == 4) for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = be32toh( * p ); - else if(Scalar_Unit_Size == 2) + else if(Endian_Scalar_Size == 2) for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) * p = be16toh( * p ); // checksum @@ -425,8 +434,8 @@ void NamedTensor::ReadBinary(const std:: Save NamedTensor Hdf5 format ******************************************************************************/ -template -void NamedTensor::save(const std::string filename) const { +template +void NamedTensor::save(const std::string filename) const { LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; #ifndef HAVE_HDF5 LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; @@ -440,8 +449,8 @@ void NamedTensor::save(const std::string Load NamedTensor Hdf5 format ******************************************************************************/ -template -void NamedTensor::load(const std::string filename) { +template +void NamedTensor::load(const std::string filename) { LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; #ifndef HAVE_HDF5 LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; @@ -458,8 +467,8 @@ void NamedTensor::load(const std::string Perambulator object ******************************************************************************/ -template -using Perambulator = NamedTensor; +template +using Perambulator = NamedTensor; /************************************************************************************* From 1ee84509b55f46fc16f6c3633ea14f02557ab315 Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 5 Feb 2019 17:32:26 +0000 Subject: [PATCH 064/347] added baryons project - not working yet --- Hadrons/Modules.hpp | 132 ++++++------ Hadrons/Modules/MDistil/BContraction.cc | 7 + Hadrons/Modules/MDistil/BContraction.hpp | 189 +++++++++++++++++ Hadrons/Modules/MDistil/PerambLight.hpp | 4 + Hadrons/modules.inc | 259 ++++++++++++----------- tests/hadrons/Test_hadrons_distil.cc | 22 ++ 6 files changed, 420 insertions(+), 193 deletions(-) create mode 100644 Hadrons/Modules/MDistil/BContraction.cc create mode 100644 Hadrons/Modules/MDistil/BContraction.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 3e611907..7105e743 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,77 +1,79 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/Modules/MDistil/BContraction.cc b/Hadrons/Modules/MDistil/BContraction.cc new file mode 100644 index 00000000..1ac09aac --- /dev/null +++ b/Hadrons/Modules/MDistil/BContraction.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TBContraction; diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp new file mode 100644 index 00000000..c1d27ddc --- /dev/null +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -0,0 +1,189 @@ +#ifndef Hadrons_MDistil_BContraction_hpp_ +#define Hadrons_MDistil_BContraction_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation +#include +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * BContraction * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class BContractionPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(BContractionPar, + std::string, one, + std::string, two, + std::string, three, + std::string, output, + std::vector, mom); +}; + +template +class TBContraction: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl,); +public: + // constructor + TBContraction(const std::string name); + // destructor + virtual ~TBContraction(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +private: + bool hasPhase_{false}; + std::string momphName_; + std::vector gamma12_; + std::vector gamma23_; + std::vector> mom_; +protected: + GridCartesian * grid4d; + GridCartesian * grid3d; +}; + +MODULE_REGISTER_TMP(BContraction, TBContraction, MDistil); + +/****************************************************************************** + * TBContraction implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TBContraction::TBContraction(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TBContraction::getInput(void) +{ + std::vector in = {par().one, par().two, par().three}; + + return in; +} + +template +std::vector TBContraction::getOutput(void) +{ + std::vector out = {}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TBContraction::setup(void) +{ + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TBContraction::execute(void) +{ + auto &one = envGet(std::vector, par().one); + auto &two = envGet(std::vector, par().two); + auto &three = envGet(std::vector, par().three); + + int N_1 = one.size(); + int N_2 = two.size(); + int N_3 = three.size(); + + LOG(Message) << "Computing distillation baryon fields" << std::endl; + LOG(Message) << "One: '" << par().one << "' Two: '" << par().two << "' Three: '" << par().three << "'" << std::endl; + LOG(Message) << "Momenta:" << std::endl; + for (auto &p: mom_) + { + LOG(Message) << " " << p << std::endl; + } + + grid4d = env().getGrid(); + grid3d = MakeLowerDimGrid(grid4d); + int Nmom=1; + int Nt=64; + std::vector BField(Nmom*Nt*N_1*N_2*N_3); + int Bindex; + int Nc=3; //Num colours + + FermionField tmp1(grid3d); + FermionField tmp2(grid3d); + FermionField tmp3(grid3d); + //std::complex * tmp33 = reinterpret_cast *>(&(tmp3[0]()(0)(0))); + + SpinColourVector * tmp11 = reinterpret_cast(&(tmp1[0]()(0)(0))); + SpinColourVector * tmp22 = reinterpret_cast(&(tmp2[0]()(0)(0))); + SpinColourVector * tmp33 = reinterpret_cast(&(tmp3[0]()(0)(0))); + + SpinVector tmp33s; + SpinColourVector * tmp333; + SpinColourVector * diquark; + SpinColourVector * tmp222; + + std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; + std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + + gamma12_ = { + Gamma::Algebra::Identity, // I + Gamma::Algebra::Gamma5, // gamma_5 + Gamma::Algebra::Identity, // I + }; + gamma23_ = { // C = i gamma_2 gamma_4 + Gamma::Algebra::SigmaXZ, // C gamma_5 = -i gamma_1 gamma_3 + Gamma::Algebra::SigmaYT, // C = i gamma_2 gamma_4 + Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 + }; + std::vector factor23 = {(0.,-1.),(0.,1.),(0.,1.)}; + + // SpinColourVector a; + // SpinVector b; + // b = peekColour(a,0); + //b= a()(0)(); + //tmp33s = peekColour(a,0); + + for (int i1=0 ; i1 < N_1 ; i1++){ + for (int i2=0 ; i2 < N_2 ; i2++){ + for (int i3=0 ; i3 < N_3 ; i3++){ + for (int imom=0 ; imom < Nmom ; imom++){ + Bindex = i1 + N_1*(i2 + N_2*(i3 + N_3*imom)); + for (int t=0 ; t < Nt ; t++){ + ExtractSliceLocal(tmp1,one[i1],0,t,3); + parallel_for (unsigned int sU = 0; sU < grid3d->oSites(); ++sU) + { + for (int ie=0 ; ie < 6 ; ie++){ + //tmp33s = peekColour(tmp33[sU],epsilon[ie][2]); + //tmp333 = Gamma(gamma23_[0])*tmp33s; + //tmp333 = Gamma(gamma23_[0])*tmp33[sU]()()(epsilon[ie][2]); + /*diquark = tmp22[sU]()()(epsilon[ie][1])*factor23[0]*tmp333; + tmp222 = Gamma(gamma12_[0])*diquark; + BField[Bindex]+=(double)epsilon_sgn[ie]*(tmp11[sU]()()(epsilon[ie][0])*tmp222); + */ + } + } + } + } + } + } + } + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_BContraction_hpp_ diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index ed6bc370..742c25a3 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -310,6 +310,10 @@ void TPerambLight::execute(void) ConjugateGradient CG(CGPrecision,MaxIterations); SchurRedBlackDiagMooeeSolve SchurSolver(CG); + LatticeSpinColourVector a(grid4d); + LatticeColourVector b(grid4d); + b= peekSpin(a,0); + int t_inv; for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index efede8ec..6d5ce763 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,154 +1,157 @@ modules_cc =\ - Modules/MContraction/WeakHamiltonianEye.cc \ - Modules/MContraction/Baryon.cc \ - Modules/MContraction/Meson.cc \ - Modules/MContraction/WeakNeutral4ptDisc.cc \ - Modules/MContraction/WeakHamiltonianNonEye.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MContraction/WardIdentity.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MFermion/FreeProp.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MSource/Momentum.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/SeqConserved.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/Z2.cc \ - Modules/MSink/Point.cc \ - Modules/MSink/Smear.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MUtilities/TestSeqConserved.cc \ Modules/MUtilities/RandomVectors.cc \ Modules/MUtilities/TestSeqGamma.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MUtilities/TestSeqConserved.cc \ - Modules/MLoop/NoiseLoop.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/VPCounterTerms.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MScalar/ScalarVP.cc \ - Modules/MDistil/DistilVectors.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/A2AVectors.cc \ + Modules/MSolver/A2AAslashVectors.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSolver/RBPrecCG.cc \ + Modules/MDistil/BContraction.cc \ Modules/MDistil/LapEvec.cc \ Modules/MDistil/PerambLight.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MContraction/WeakHamiltonianEye.cc \ + Modules/MContraction/Gamma3pt.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/Meson.cc \ + Modules/MContraction/WeakNeutral4ptDisc.cc \ + Modules/MContraction/WardIdentity.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MContraction/WeakHamiltonianNonEye.cc \ + Modules/MContraction/Baryon.cc \ + Modules/MContraction/A2AAslashField.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MAction/WilsonClover.cc \ + Modules/MAction/Wilson.cc \ + Modules/MAction/DWF.cc \ + Modules/MAction/MobiusDWF.cc \ + Modules/MAction/ScaledDWF.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/GaugeFix.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadA2AVectors.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadCosmHol.cc \ Modules/MNPR/Amputate.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MAction/Wilson.cc \ - Modules/MAction/MobiusDWF.cc \ - Modules/MAction/ZMobiusDWF.cc \ - Modules/MAction/WilsonClover.cc \ - Modules/MAction/DWF.cc \ - Modules/MAction/ScaledDWF.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/ShiftProbe.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/StochFreeField.cc \ + Modules/MLoop/NoiseLoop.cc \ Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ Modules/MScalarSUN/Div.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MIO/LoadA2AVectors.cc + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/ShiftProbe.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/Grad.cc \ + Modules/MSink/Smear.cc \ + Modules/MSink/Point.cc \ + Modules/MFermion/GaugeProp.cc \ + Modules/MFermion/FreeProp.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/ScalarVP.cc \ + Modules/MScalar/VPCounterTerms.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MSource/SeqConserved.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Wall.cc \ + Modules/MSource/Z2.cc \ + Modules/MSource/Point.cc \ + Modules/MSource/Momentum.cc modules_hpp =\ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MContraction/WeakHamiltonian.hpp \ - Modules/MContraction/WeakHamiltonianNonEye.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/WeakNeutral4ptDisc.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/WardIdentity.hpp \ - Modules/MContraction/WeakHamiltonianEye.hpp \ - Modules/MFermion/FreeProp.hpp \ - Modules/MFermion/GaugeProp.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Point.hpp \ - Modules/MSource/Wall.hpp \ - Modules/MSource/Z2.hpp \ - Modules/MSource/SeqConserved.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ - Modules/MSolver/Guesser.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MUtilities/PrecisionCast.hpp \ Modules/MUtilities/RandomVectors.hpp \ Modules/MUtilities/TestSeqGamma.hpp \ + Modules/MUtilities/PrecisionCast.hpp \ Modules/MUtilities/TestSeqConserved.hpp \ - Modules/MLoop/NoiseLoop.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/VPCounterTerms.hpp \ - Modules/MScalar/ScalarVP.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/ChargedProp.hpp \ + Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MSolver/Guesser.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSolver/A2AVectors.hpp \ Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/Distil.hpp \ Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/BContraction.hpp \ Modules/MDistil/PerambLight.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ + Modules/MContraction/WeakHamiltonian.hpp \ + Modules/MContraction/WeakNeutral4ptDisc.hpp \ + Modules/MContraction/WeakHamiltonianEye.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/A2AMesonField.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/WeakHamiltonianNonEye.hpp \ + Modules/MContraction/Meson.hpp \ + Modules/MContraction/WardIdentity.hpp \ Modules/MAction/Wilson.hpp \ Modules/MAction/WilsonClover.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ + Modules/MAction/DWF.hpp \ Modules/MAction/ScaledDWF.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/ShiftProbe.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MIO/LoadEigenPack.hpp \ + Modules/MAction/ZMobiusDWF.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/Unit.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MGauge/StoutSmearing.hpp \ + Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ + Modules/MGauge/FundtoHirep.hpp \ + Modules/MGauge/GaugeFix.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ + Modules/MIO/LoadBinary.hpp \ + Modules/MIO/LoadCosmHol.hpp \ Modules/MIO/LoadNersc.hpp \ Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCosmHol.hpp \ Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadBinary.hpp + Modules/MIO/LoadEigenPack.hpp \ + Modules/MNPR/Amputate.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MLoop/NoiseLoop.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MScalarSUN/ShiftProbe.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MSink/Point.hpp \ + Modules/MFermion/GaugeProp.hpp \ + Modules/MFermion/FreeProp.hpp \ + Modules/MScalar/Scalar.hpp \ + Modules/MScalar/ScalarVP.hpp \ + Modules/MScalar/FreeProp.hpp \ + Modules/MScalar/ChargedProp.hpp \ + Modules/MScalar/VPCounterTerms.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Z2.hpp \ + Modules/MSource/Wall.hpp \ + Modules/MSource/SeqConserved.hpp diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 8246a182..808308dc 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -223,6 +223,21 @@ void test_MesonField(Application &application) A2AMesonFieldPar.block=4; application.createModule("DistilMesonField",A2AMesonFieldPar); } +///////////////////////////////////////////////////////////// +// BaryonFields +///////////////////////////////////////////////////////////// + +void test_BaryonField(Application &application) +{ + // DistilVectors parameters + MDistil::BContraction::Par BContractionPar; + BContractionPar.one="DistilVecs_phi"; + BContractionPar.two="DistilVecs_phi"; + BContractionPar.three="DistilVecs_phi"; + BContractionPar.output="BaryonField"; + BContractionPar.mom={"0 0 0"}; + application.createModule("BaryonField",BContractionPar); +} bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { @@ -409,6 +424,13 @@ int main(int argc, char *argv[]) test_Perambulators( application ); test_MesonSink( application ); break; + case 7: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_BaryonField( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From 7423f5af1a967fbb42001fcf3d97b57a4c14aea1 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 6 Feb 2019 09:25:24 +0000 Subject: [PATCH 065/347] Examples of how to access Grid Tensors --- Hadrons/Modules/MDistil/Distil.hpp | 6 +- tests/hadrons/Test_hadrons_distil.cc | 96 +++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 6c2dc93d..f2ada50f 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -35,7 +35,7 @@ #include /****************************************************************************** - Needed to make sure envCreate() (see Hadrons) works with specialisations + Needed to make sure envCreate() (see Hadrons) work with specialisations with more than one parameter, eg obj I imagine this exists already? ******************************************************************************/ @@ -191,8 +191,8 @@ public: OperatorFunction & _poly; LinearOperatorBase &_Linop; - LinOpPeardonNablaHerm(OperatorFunction & poly,LinearOperatorBase& linop) : _poly(poly), _Linop(linop) { - } + LinOpPeardonNablaHerm(OperatorFunction & poly,LinearOperatorBase& linop) + : _poly{poly}, _Linop{linop} {} void operator()(const Field& in, Field& out) { _poly(_Linop,in,out); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 8246a182..79d70387 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -27,6 +27,7 @@ *************************************************************************************/ /* END LEGAL */ +#include #include #include @@ -294,6 +295,66 @@ void DebugShowTensor(MyTensor &x, const char * n) std::cout << std::endl; } +// Test whether typedef and underlying types are the same + +void DebugTestTypeEqualities(void) +{ + Real r1; + RealD r2; + double r3; + const std::type_info &tr1{typeid(r1)}; + const std::type_info &tr2{typeid(r2)}; + const std::type_info &tr3{typeid(r3)}; + if( tr1 == tr2 && tr2 == tr3 ) + std::cout << "r1, r2 and r3 are the same type" << std::endl; + else + std::cout << "r1, r2 and r3 are different types" << std::endl; + std::cout << "r1 is a " << tr1.name() << std::endl; + std::cout << "r2 is a " << tr2.name() << std::endl; + std::cout << "r3 is a " << tr3.name() << std::endl; + + // These are the same + Complex c1; + std::complex c2; + const std::type_info &tc1{typeid(c1)}; + const std::type_info &tc2{typeid(c2)}; + const std::type_info &tc3{typeid(SpinVector::scalar_type)}; + if( tc1 == tc2 && tc2 == tc3) + std::cout << "c1, c2 and SpinVector::scalar_type are the same type" << std::endl; + else + std::cout << "c1, c2 and SpinVector::scalar_type are different types" << std::endl; + std::cout << "c1 is a " << tc1.name() << std::endl; + std::cout << "c2 is a " << tc2.name() << std::endl; + std::cout << "SpinVector::scalar_type is a " << tc3.name() << std::endl; + + // These are the same + SpinVector s1; + iSpinVector s2; + iScalar, Ns> > s3; + const std::type_info &ts1{typeid(s1)}; + const std::type_info &ts2{typeid(s2)}; + const std::type_info &ts3{typeid(s3)}; + if( ts1 == ts2 && ts2 == ts3 ) + std::cout << "s1, s2 and s3 are the same type" << std::endl; + else + std::cout << "s1, s2 and s3 are different types" << std::endl; + std::cout << "s1 is a " << ts1.name() << std::endl; + std::cout << "s2 is a " << ts2.name() << std::endl; + std::cout << "s3 is a " << ts3.name() << std::endl; + + // These are the same + SpinColourVector sc1; + iSpinColourVector sc2; + const std::type_info &tsc1{typeid(sc1)}; + const std::type_info &tsc2{typeid(sc2)}; + if( tsc1 == tsc2 ) + std::cout << "sc1 and sc2 are the same type" << std::endl; + else + std::cout << "sc1 and sc2 are different types" << std::endl; + std::cout << "sc1 is a " << tsc1.name() << std::endl; + std::cout << "sc2 is a " << tsc2.name() << std::endl; +} + bool DebugEigenTest() { const char pszTestFileName[] = "test_tensor.bin"; @@ -311,10 +372,39 @@ bool DebugEigenTest() for( auto a : p.IndexNames ) std::cout << a << std::endl; // Now see whether we can read a tensor back - std::array a2={"Alpha", "Gamma", "Delta"}; - MyTensor y(a2, 2,4,1); + std::array Names2={"Alpha", "Gamma", "Delta"}; + MyTensor y(Names2, 2,4,1); y.ReadBinary(pszTestFileName); DebugShowTensor(y, "y"); + + // Testing whether typedef produces the same type - yes it does + + DebugTestTypeEqualities(); + std::cout << std::endl; + + // How to access members of SpinColourVector + SpinColourVector sc; + for( int s = 0 ; s < Ns ; s++ ) { + auto cv{sc()(s)}; + iVector c2{sc()(s)}; + std::cout << " cv is a " << typeid(cv).name() << std::endl; + std::cout << " c2 is a " << typeid(c2).name() << std::endl; + for( int c = 0 ; c < Nc ; c++ ) { + Complex & z{cv(c)}; + std::cout << " sc[spin=" << s << ", colour=" << c << "] = " << z << std::endl; + } + } + // We could have removed the Lorentz index independently, but much easier to do as we do above + iVector,Ns> sc2{sc()}; + std::cout << "sc() is a " << typeid(sc()).name() << std::endl; + std::cout << "sc2 is a " << typeid(sc2 ).name() << std::endl; + + // Or you can access elements directly + std::complex z = sc()(0)(0); + std::cout << "z = " << z << std::endl; + sc()(3)(2) = std::complex{3.141,-3.141}; + std::cout << "sc()(3)(2) = " << sc()(3)(2) << std::endl; + return true; } #endif @@ -325,7 +415,7 @@ int main(int argc, char *argv[]) // Debug only - test of Eigen::Tensor std::cout << "sizeof(std::streamsize) = " << sizeof(std::streamsize) << std::endl; std::cout << "sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; - //if( DebugEigenTest() ) return 0; + if( DebugEigenTest() ) return 0; #endif // Decode command-line parameters. 1st one is which test to run From ed7175076b9ede5dcf51599fff1769dd190187d4 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 6 Feb 2019 09:32:13 +0000 Subject: [PATCH 066/347] Turned off warning of unused variable line 150 --- Hadrons/Modules/MDistil/BContraction.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index c1d27ddc..2e5e1727 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -147,7 +147,7 @@ void TBContraction::execute(void) Gamma::Algebra::SigmaYT, // C = i gamma_2 gamma_4 Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 }; - std::vector factor23 = {(0.,-1.),(0.,1.),(0.,1.)}; + //std::vector factor23 = {(0.,-1.),(0.,1.),(0.,1.)}; // SpinColourVector a; // SpinVector b; From 6cdb1eb62ce628e58b2004128addd7b21df20146 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 6 Feb 2019 12:23:52 +0000 Subject: [PATCH 067/347] BContraction now computes what might be a baryon function, but probably isn't --- Hadrons/Modules/MDistil/BContraction.hpp | 56 +++++++++++++++++------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index c1d27ddc..92bc0bf6 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -129,10 +129,14 @@ void TBContraction::execute(void) SpinColourVector * tmp22 = reinterpret_cast(&(tmp2[0]()(0)(0))); SpinColourVector * tmp33 = reinterpret_cast(&(tmp3[0]()(0)(0))); + SpinVector tmp11s; + SpinVector tmp22s; SpinVector tmp33s; - SpinColourVector * tmp333; - SpinColourVector * diquark; - SpinColourVector * tmp222; + SpinVector tmp333; + SpinMatrix diquark; + SpinMatrix g_diquark; + SpinVector tmp222; + SpinVector tmp111; std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; @@ -149,30 +153,44 @@ void TBContraction::execute(void) }; std::vector factor23 = {(0.,-1.),(0.,1.),(0.,1.)}; - // SpinColourVector a; - // SpinVector b; - // b = peekColour(a,0); - //b= a()(0)(); - //tmp33s = peekColour(a,0); - for (int i1=0 ; i1 < N_1 ; i1++){ for (int i2=0 ; i2 < N_2 ; i2++){ for (int i3=0 ; i3 < N_3 ; i3++){ for (int imom=0 ; imom < Nmom ; imom++){ - Bindex = i1 + N_1*(i2 + N_2*(i3 + N_3*imom)); for (int t=0 ; t < Nt ; t++){ + Bindex = i1 + N_1*(i2 + N_2*(i3 + N_3*(imom+Nmom*t))); ExtractSliceLocal(tmp1,one[i1],0,t,3); parallel_for (unsigned int sU = 0; sU < grid3d->oSites(); ++sU) { for (int ie=0 ; ie < 6 ; ie++){ - //tmp33s = peekColour(tmp33[sU],epsilon[ie][2]); - //tmp333 = Gamma(gamma23_[0])*tmp33s; - //tmp333 = Gamma(gamma23_[0])*tmp33[sU]()()(epsilon[ie][2]); - /*diquark = tmp22[sU]()()(epsilon[ie][1])*factor23[0]*tmp333; - tmp222 = Gamma(gamma12_[0])*diquark; - BField[Bindex]+=(double)epsilon_sgn[ie]*(tmp11[sU]()()(epsilon[ie][0])*tmp222); - */ + // Why does peekColour not work???? + for (int is=0 ; is < 4 ; is++){ + tmp11s()(is)() = tmp11[sU]()(is)(epsilon[ie][0]); + tmp22s()(is)() = tmp22[sU]()(is)(epsilon[ie][1]); + tmp33s()(is)() = tmp33[sU]()(is)(epsilon[ie][2]); + } + tmp333 = Gamma(gamma23_[0])*tmp33s; + // this should be outerProduct??? Does not work. + for (int isl=0 ; isl < 4 ; isl++){ + for (int isr=0 ; isr < 4 ; isr++){ + diquark()(isl,isr)() = factor23[0]*tmp22s()(isl)(),tmp333()(isr)(); + } + } + // Is there a way to compute gamma * SpinMatrix (left component)??? + for (int isr=0 ; isr < 4 ; isr++){ + for (int isl=0 ; isl < 4 ; isl++){ + tmp222()(isl)() = diquark()(isl,isr)(); + } + tmp111 = Gamma(gamma12_[0]) * tmp222; + for (int isl=0 ; isl < 4 ; isl++){ + g_diquark()(isl,isr)() = tmp111()(isl)(); + } + } + // Really only the trace? Should check baryons again! laph paper lists c_{alpha,beta,gamma}, gattringer-lang two gamma matrices. + for (int is=0 ; is < 4 ; is++){ + BField[Bindex]+=(double)epsilon_sgn[ie]*tmp11s()(is)()*g_diquark()(is,is)(); } + } } } } @@ -180,6 +198,10 @@ void TBContraction::execute(void) } } + for (int t=0 ; t < Nt ; t++){ + Bindex = 0 + N_1*(0 + N_2*(0 + N_3*(0+Nmom*t))); + std::cout << "BaryonField(t=" << t << ") = " << BField[Bindex] << std::endl; + } } END_MODULE_NAMESPACE From 4b3c566c89c4ea2ef5d5ea6d605830f04b7d8eef Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 6 Feb 2019 21:36:46 +0000 Subject: [PATCH 068/347] ../tests/hadrons/Test_hadrons_distil.cc --- Grid/serialisation/BaseIO.h | 12 +++++++ tests/hadrons/Test_hadrons_distil.cc | 48 +++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index dd15e7da..e96a2f9a 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -33,6 +33,7 @@ Author: Guido Cossu #include #include #include +#include namespace Grid { // Abstract writer/reader classes //////////////////////////////////////////// @@ -60,6 +61,9 @@ namespace Grid { void write(const std::string &s, const iVector &output); template void write(const std::string &s, const iMatrix &output); + template + void write(const std::string &s, const Eigen::Tensor &output); + void scientificFormat(const bool set); bool isScientific(void); void setPrecision(const unsigned int prec); @@ -162,6 +166,14 @@ namespace Grid { upcast->writeDefault(s, tensorToVec(output)); } + template + template + void Writer::write(const std::string &s, const Eigen::Tensor &output) + { + //upcast->writeDefault(s, tensorToVec(output)); + std::cout << "I really should add code to write Eigen::Tensor ..." << std::endl; + } + template void Writer::scientificFormat(const bool set) { diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 33f883ee..1529eb15 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -422,6 +422,51 @@ bool DebugEigenTest() return true; } + +typedef iMatrix OddBall; + +// From Test_serialisation.cc +class myclass: Serializable { +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(myclass + , OddBall, critter + , SpinColourVector, scv + , SpinColourMatrix, scm + ); +}; + +template +bool ioTest(const std::string &filename, const O &object, const std::string &name) +{ + // writer needs to be destroyed so that writing physically happens + { + W writer(filename); + write(writer, "testobject", object); + } + + /*R reader(filename); + O buf; + bool good; + + read(reader, "testobject", buf); + good = (object == buf); + std::cout << name << " IO test: " << (good ? "success" : "failure"); + std::cout << std::endl; + return good;*/ + return true; +} + +bool DebugIOTest(void) { + OddBall critter; + ioTest("iotest_oddball.h5", critter, "OddBall"); + SpinColourMatrix scm; + ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); + SpinColourVector scv; + ioTest("iotest_vector.h5", scv, "SpinColourVector"); + myclass o; + ioTest("iotest_object.h5", o, "myclass_object_instance_name"); + return true; +} #endif int main(int argc, char *argv[]) @@ -430,7 +475,8 @@ int main(int argc, char *argv[]) // Debug only - test of Eigen::Tensor std::cout << "sizeof(std::streamsize) = " << sizeof(std::streamsize) << std::endl; std::cout << "sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; - if( DebugEigenTest() ) return 0; + //if( DebugEigenTest() ) return 0; + if(DebugIOTest()) return 0; #endif // Decode command-line parameters. 1st one is which test to run From a0a39e4b003c84ae7e37c6f38a2c86b38b428d98 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 6 Feb 2019 21:56:44 +0000 Subject: [PATCH 069/347] Fixed initialisation of vector of Complex --- Hadrons/Modules/MDistil/BContraction.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 9a017ebd..a1778ab4 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -151,7 +151,7 @@ void TBContraction::execute(void) Gamma::Algebra::SigmaYT, // C = i gamma_2 gamma_4 Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 }; - //std::vector factor23 = {(0.,-1.),(0.,1.),(0.,1.)}; + std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; for (int i1=0 ; i1 < N_1 ; i1++){ for (int i2=0 ; i2 < N_2 ; i2++){ From 6a4515d0cd3ca5adc823000ed7574e6dc9152084 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 7 Feb 2019 12:27:57 +0000 Subject: [PATCH 070/347] baryons have now the correct (?) structure - also easier! --- Hadrons/Modules/MDistil/BContraction.hpp | 41 ++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index a1778ab4..699deb3f 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -152,7 +152,7 @@ void TBContraction::execute(void) Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 }; std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; - + if((0)){ for (int i1=0 ; i1 < N_1 ; i1++){ for (int i2=0 ; i2 < N_2 ; i2++){ for (int i3=0 ; i3 < N_3 ; i3++){ @@ -173,7 +173,7 @@ void TBContraction::execute(void) // this should be outerProduct??? Does not work. for (int isl=0 ; isl < 4 ; isl++){ for (int isr=0 ; isr < 4 ; isr++){ - diquark()(isl,isr)() = factor23[0]*tmp22s()(isl)(),tmp333()(isr)(); + diquark()(isl,isr)() = factor23[0]*tmp22s()(isl)()*tmp333()(isr)(); } } // Is there a way to compute gamma * SpinMatrix (left component)??? @@ -202,6 +202,43 @@ void TBContraction::execute(void) Bindex = 0 + N_1*(0 + N_2*(0 + N_3*(0+Nmom*t))); std::cout << "BaryonField(t=" << t << ") = " << BField[Bindex] << std::endl; } + } // end if 0 + + // ONLY THIS IS CORRECT? + std::vector BField2(Nmom*Nt*N_1*N_2*N_3); + Complex diquark2; + for (int i1=0 ; i1 < N_1 ; i1++){ + for (int i2=0 ; i2 < N_2 ; i2++){ + for (int i3=0 ; i3 < N_3 ; i3++){ + for (int imom=0 ; imom < Nmom ; imom++){ + for (int t=0 ; t < Nt ; t++){ + Bindex = i1 + N_1*(i2 + N_2*(i3 + N_3*(imom+Nmom*t))); + ExtractSliceLocal(tmp1,one[i1],0,t,3); + parallel_for (unsigned int sU = 0; sU < grid3d->oSites(); ++sU) + { + for (int ie=0 ; ie < 6 ; ie++){ + // Why does peekColour not work???? + for (int is=0 ; is < 4 ; is++){ + tmp11s()(is)() = tmp11[sU]()(is)(epsilon[ie][0]); + tmp22s()(is)() = tmp22[sU]()(is)(epsilon[ie][1]); + tmp33s()(is)() = tmp33[sU]()(is)(epsilon[ie][2]); + } + tmp333 = Gamma(gamma23_[0])*tmp33s; + diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); + BField2[Bindex]+=(double)epsilon_sgn[ie]*tmp11s*diquark2; + } + } + } + } + } + } + } + for (int is=0 ; is < 4 ; is++){ + for (int t=0 ; t < Nt ; t++){ + Bindex = 0 + N_1*(0 + N_2*(0 + N_3*(0+Nmom*t))); + std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField2[Bindex]()(is)() << std::endl; + } + } } END_MODULE_NAMESPACE From c1341b8ed234eeb651757c6112f57aee3e22e92a Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 8 Feb 2019 14:33:06 +0000 Subject: [PATCH 071/347] bugfix --- Hadrons/Modules/MDistil/BContraction.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 9a017ebd..c0843273 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -151,7 +151,7 @@ void TBContraction::execute(void) Gamma::Algebra::SigmaYT, // C = i gamma_2 gamma_4 Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 }; - //std::vector factor23 = {(0.,-1.),(0.,1.),(0.,1.)}; + std::vector factor23 = {(0.,-1.),(0.,1.),(0.,1.)}; for (int i1=0 ; i1 < N_1 ; i1++){ for (int i2=0 ; i2 < N_2 ; i2++){ @@ -160,6 +160,8 @@ void TBContraction::execute(void) for (int t=0 ; t < Nt ; t++){ Bindex = i1 + N_1*(i2 + N_2*(i3 + N_3*(imom+Nmom*t))); ExtractSliceLocal(tmp1,one[i1],0,t,3); + ExtractSliceLocal(tmp2,two[i2],0,t,3); + ExtractSliceLocal(tmp3,three[i3],0,t,3); parallel_for (unsigned int sU = 0; sU < grid3d->oSites(); ++sU) { for (int ie=0 ; ie < 6 ; ie++){ @@ -186,7 +188,6 @@ void TBContraction::execute(void) g_diquark()(isl,isr)() = tmp111()(isl)(); } } - // Really only the trace? Should check baryons again! laph paper lists c_{alpha,beta,gamma}, gattringer-lang two gamma matrices. for (int is=0 ; is < 4 ; is++){ BField[Bindex]+=(double)epsilon_sgn[ie]*tmp11s()(is)()*g_diquark()(is,is)(); } From d26a5dce12de0cfcdccda02e938aad2bfbbb2562 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 8 Feb 2019 14:37:09 +0000 Subject: [PATCH 072/347] bugfix --- Hadrons/Modules/MDistil/BContraction.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 7fc5fc7c..b1d1f3dd 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -215,6 +215,8 @@ void TBContraction::execute(void) for (int t=0 ; t < Nt ; t++){ Bindex = i1 + N_1*(i2 + N_2*(i3 + N_3*(imom+Nmom*t))); ExtractSliceLocal(tmp1,one[i1],0,t,3); + ExtractSliceLocal(tmp2,two[i2],0,t,3); + ExtractSliceLocal(tmp3,three[i3],0,t,3); parallel_for (unsigned int sU = 0; sU < grid3d->oSites(); ++sU) { for (int ie=0 ; ie < 6 ; ie++){ From c4d27ee30f860549ea102f8c59d1a593a61a6fcf Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 8 Feb 2019 15:49:52 +0000 Subject: [PATCH 073/347] added parity operator to baryon fields --- Hadrons/Modules/MDistil/BContraction.hpp | 12 +++++++++- tests/hadrons/Test_hadrons_distil.cc | 28 +++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index b1d1f3dd..4401e8cd 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -26,6 +26,7 @@ public: std::string, two, std::string, three, std::string, output, + int, parity, std::vector, mom); }; @@ -104,6 +105,8 @@ void TBContraction::execute(void) int N_2 = two.size(); int N_3 = three.size(); + int parity = par().parity; + LOG(Message) << "Computing distillation baryon fields" << std::endl; LOG(Message) << "One: '" << par().one << "' Two: '" << par().two << "' Three: '" << par().three << "'" << std::endl; LOG(Message) << "Momenta:" << std::endl; @@ -138,9 +141,13 @@ void TBContraction::execute(void) SpinVector tmp222; SpinVector tmp111; + assert(parity == 1 || parity == -1); + std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + Gamma g4(Gamma::Algebra::GammaT); + gamma12_ = { Gamma::Algebra::Identity, // I Gamma::Algebra::Gamma5, // gamma_5 @@ -227,8 +234,11 @@ void TBContraction::execute(void) tmp33s()(is)() = tmp33[sU]()(is)(epsilon[ie][2]); } tmp333 = Gamma(gamma23_[0])*tmp33s; + tmp111 = Gamma(gamma12_[0])*tmp11s; + tmp222 = g4*tmp111; + tmp111 = 0.5*(double)parity*(tmp111 + tmp222); // P_\pm * ... diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); - BField2[Bindex]+=(double)epsilon_sgn[ie]*tmp11s*diquark2; + BField2[Bindex]+=(double)epsilon_sgn[ie]*tmp111*diquark2; } } } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 1529eb15..b5b5503c 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -225,19 +225,36 @@ void test_MesonField(Application &application) application.createModule("DistilMesonField",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// -// BaryonFields +// BaryonFields - phiphiphi ///////////////////////////////////////////////////////////// -void test_BaryonField(Application &application) +void test_BaryonFieldPhi(Application &application) { // DistilVectors parameters MDistil::BContraction::Par BContractionPar; BContractionPar.one="DistilVecs_phi"; BContractionPar.two="DistilVecs_phi"; BContractionPar.three="DistilVecs_phi"; - BContractionPar.output="BaryonField"; + BContractionPar.output="BaryonFieldPhi"; + BContractionPar.parity=1; BContractionPar.mom={"0 0 0"}; - application.createModule("BaryonField",BContractionPar); + application.createModule("BaryonFieldPhi",BContractionPar); +} +///////////////////////////////////////////////////////////// +// BaryonFields - rhorhorho +///////////////////////////////////////////////////////////// + +void test_BaryonFieldRho(Application &application) +{ + // DistilVectors parameters + MDistil::BContraction::Par BContractionPar; + BContractionPar.one="DistilVecs_rho"; + BContractionPar.two="DistilVecs_rho"; + BContractionPar.three="DistilVecs_rho"; + BContractionPar.output="BaryonFieldRho"; + BContractionPar.parity=1; + BContractionPar.mom={"0 0 0"}; + application.createModule("BaryonFieldRho",BContractionPar); } bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) @@ -565,7 +582,8 @@ int main(int argc, char *argv[]) test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); - test_BaryonField( application ); + test_BaryonFieldPhi( application ); + test_BaryonFieldRho( application ); break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From 3720103f41dd2e2f6a92d36007602c1bfe35ad84 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sat, 9 Feb 2019 17:12:36 +0000 Subject: [PATCH 074/347] Adding Eigen::Tensor still WIP --- Grid/serialisation/BaseIO.h | 57 +++++++++++++++++++++++----- Grid/serialisation/VectorUtils.h | 2 +- tests/hadrons/Test_hadrons_distil.cc | 8 +++- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index e96a2f9a..26b6ab5a 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -55,14 +55,21 @@ namespace Grid { template typename std::enable_if::value, void>::type write(const std::string& s, const U &output); + template + typename std::enable_if::value || Grid::is_complex::value, void>::type + write(const std::string &s, const Eigen::Tensor &output); + template + void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); + template + void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); + template + void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); template void write(const std::string &s, const iScalar &output); template void write(const std::string &s, const iVector &output); template void write(const std::string &s, const iMatrix &output); - template - void write(const std::string &s, const Eigen::Tensor &output); void scientificFormat(const bool set); bool isScientific(void); @@ -145,6 +152,44 @@ namespace Grid { upcast->writeDefault(s, output); } + // Eigen::Tensors of arithmetic/complex base type + template + template + typename std::enable_if::value || Grid::is_complex::value, void>::type + Writer::write(const std::string &s, const Eigen::Tensor &output) + { + //upcast->writeDefault(s, tensorToVec(output)); + std::cout << "I really should add code to write Eigen::Tensor (arithmetic/complex) ..." << std::endl; + } + + // Eigen::Tensors of iScalar + template + template + void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + { + //upcast->writeDefault(s, tensorToVec(output)); + std::cout << "I really should add code to write Eigen::Tensor (iScalar) ..." << std::endl; + } + + // Eigen::Tensors of iVector + template + template + void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + { + //upcast->writeDefault(s, tensorToVec(output)); + std::cout << "I really should add code to write Eigen::Tensor (iVector) ..." << std::endl; + } + + // Eigen::Tensors of iMatrix + template + template + void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + { + //upcast->writeDefault(s, tensorToVec(output)); + std::cout << "I really should add code to write Eigen::Tensor (iMatrix) ..." << std::endl; + } + + template template void Writer::write(const std::string &s, const iScalar &output) @@ -166,14 +211,6 @@ namespace Grid { upcast->writeDefault(s, tensorToVec(output)); } - template - template - void Writer::write(const std::string &s, const Eigen::Tensor &output) - { - //upcast->writeDefault(s, tensorToVec(output)); - std::cout << "I really should add code to write Eigen::Tensor ..." << std::endl; - } - template void Writer::scientificFormat(const bool set) { diff --git a/Grid/serialisation/VectorUtils.h b/Grid/serialisation/VectorUtils.h index b6b95c10..3372c8ad 100644 --- a/Grid/serialisation/VectorUtils.h +++ b/Grid/serialisation/VectorUtils.h @@ -436,4 +436,4 @@ std::string vecToStr(const std::vector &v) return sstr.str(); } -#endif \ No newline at end of file +#endif diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 1529eb15..6be63841 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -424,12 +424,15 @@ bool DebugEigenTest() } typedef iMatrix OddBall; +typedef Eigen::Tensor TensorInt; +typedef Eigen::Tensor, 3, Eigen::RowMajor> TensorComplex; +typedef Eigen::Tensor TensorOddBall; // From Test_serialisation.cc class myclass: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(myclass - , OddBall, critter + //, OddBall, critter , SpinColourVector, scv , SpinColourMatrix, scm ); @@ -465,6 +468,9 @@ bool DebugIOTest(void) { ioTest("iotest_vector.h5", scv, "SpinColourVector"); myclass o; ioTest("iotest_object.h5", o, "myclass_object_instance_name"); + TensorInt t(3,6,2); + ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); + return true; } #endif From d5024bd07e01ec5ca44207a26c7408fdd2731c5f Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sun, 10 Feb 2019 15:33:16 +0000 Subject: [PATCH 075/347] Hdf5 writing of scalar (i.e. no Grid subtypes) Eigen::Tensor works. But issues when adding Eigen::Tensor to serialisable object. --- Grid/serialisation/BaseIO.h | 128 ++++++++++++++++++--------- Grid/serialisation/Hdf5IO.h | 58 ++++++------ Grid/serialisation/VectorUtils.h | 11 +++ tests/hadrons/Test_hadrons_distil.cc | 33 +++++-- 4 files changed, 156 insertions(+), 74 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 26b6ab5a..84e45861 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -55,6 +55,12 @@ namespace Grid { template typename std::enable_if::value, void>::type write(const std::string& s, const U &output); + template + void write(const std::string &s, const iScalar &output); + template + void write(const std::string &s, const iVector &output); + template + void write(const std::string &s, const iMatrix &output); template typename std::enable_if::value || Grid::is_complex::value, void>::type write(const std::string &s, const Eigen::Tensor &output); @@ -64,13 +70,7 @@ namespace Grid { void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); template void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); - template - void write(const std::string &s, const iScalar &output); - template - void write(const std::string &s, const iVector &output); - template - void write(const std::string &s, const iMatrix &output); - + void scientificFormat(const bool set); bool isScientific(void); void setPrecision(const unsigned int prec); @@ -152,43 +152,6 @@ namespace Grid { upcast->writeDefault(s, output); } - // Eigen::Tensors of arithmetic/complex base type - template - template - typename std::enable_if::value || Grid::is_complex::value, void>::type - Writer::write(const std::string &s, const Eigen::Tensor &output) - { - //upcast->writeDefault(s, tensorToVec(output)); - std::cout << "I really should add code to write Eigen::Tensor (arithmetic/complex) ..." << std::endl; - } - - // Eigen::Tensors of iScalar - template - template - void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) - { - //upcast->writeDefault(s, tensorToVec(output)); - std::cout << "I really should add code to write Eigen::Tensor (iScalar) ..." << std::endl; - } - - // Eigen::Tensors of iVector - template - template - void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) - { - //upcast->writeDefault(s, tensorToVec(output)); - std::cout << "I really should add code to write Eigen::Tensor (iVector) ..." << std::endl; - } - - // Eigen::Tensors of iMatrix - template - template - void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) - { - //upcast->writeDefault(s, tensorToVec(output)); - std::cout << "I really should add code to write Eigen::Tensor (iMatrix) ..." << std::endl; - } - template template @@ -211,6 +174,83 @@ namespace Grid { upcast->writeDefault(s, tensorToVec(output)); } + // Eigen::Tensors of arithmetic/complex base type + template + template + typename std::enable_if::value || Grid::is_complex::value, void>::type + Writer::write(const std::string &s, const Eigen::Tensor &output) + { + typedef Eigen::Tensor Tensor; + const typename Tensor::Index NumElements{output.size()}; + assert( NumElements > 0 ); + if( NumElements == 1 ) + upcast->writeDefault(s, * output.data()); + else { + // Create a single, flat vector to hold all the data + std::vector flat(NumElements); + // We're not interested in trivial dimensions, i.e. dimensions = 1 + const typename Tensor::Dimensions & DimsOriginal{output.dimensions()}; + assert(DimsOriginal.size() == NumIndices_); + unsigned int TrivialDimCount{0}; + for(auto i : DimsOriginal ) { + if( i <= 1 ) { + TrivialDimCount++; + assert( i == 1 ); // Not expecting dimension to be <= 0 + } + } + const unsigned int ReducedDimCount{NumIndices_ - TrivialDimCount}; + assert( ReducedDimCount > 0 ); // NB: We've already checked this is not a scalar + // Save a flat vector of the non-trivial dimensions + std::vector ReducedDims(ReducedDimCount); + unsigned int ui = 0; + for(auto i : DimsOriginal ) { + if( i > 1 ) { + ReducedDims[ui] = static_cast(i); + assert( ReducedDims[ui] == i ); // check we didn't lose anything in the conversion + ui++; + } + } + // Now copy all the data to my flat vector + // Regardless of the Eigen::Tensor storage order, the copy will be Row Major + std::array MyIndex; + for( int i = 0 ; i < NumIndices_ ; i++ ) MyIndex[i] = 0; + for( typename Tensor::Index n = 0; n < NumElements; n++ ) { + flat[n] = output( MyIndex ); + // Now increment the index + for( int i = NumIndices_ - 1; i >= 0 && ++MyIndex[i] == DimsOriginal[i]; i-- ) + MyIndex[i] = 0; + } + upcast->template writeMultiDim(s, ReducedDims, flat); + } + } + + // Eigen::Tensors of iScalar + template + template + void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + { + //upcast->writeDefault(s, tensorToVec(output)); + std::cout << "I really should add code to write Eigen::Tensor (iScalar) ..." << std::endl; + } + + // Eigen::Tensors of iVector + template + template + void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + { + //upcast->writeDefault(s, tensorToVec(output)); + std::cout << "I really should add code to write Eigen::Tensor (iVector) ..." << std::endl; + } + + // Eigen::Tensors of iMatrix + template + template + void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + { + //upcast->writeDefault(s, tensorToVec(output)); + std::cout << "I really should add code to write Eigen::Tensor (iMatrix) ..." << std::endl; + } + template void Writer::scientificFormat(const bool set) { diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 59804240..c3f111be 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -38,6 +38,8 @@ namespace Grid template typename std::enable_if>::is_number, void>::type writeDefault(const std::string &s, const std::vector &x); + template + void writeMultiDim(const std::string &s, const std::vector & Dimensions, const std::vector & DataRowMajor); H5NS::Group & getGroup(void); private: template @@ -101,6 +103,35 @@ namespace Grid template <> void Hdf5Writer::writeDefault(const std::string &s, const std::string &x); + template + void Hdf5Writer::writeMultiDim(const std::string &s, const std::vector & Dimensions, const std::vector & DataRowMajor) + { + // Hdf5 needs the dimensions as hsize_t + std::vector dim; + for (auto &d: Dimensions) + dim.push_back(d); + // write to file + H5NS::DataSpace dataSpace(dim.size(), dim.data()); + + if (DataRowMajor.size() > dataSetThres_) + { + H5NS::DataSet dataSet; + H5NS::DSetCreatPropList plist; + + plist.setChunk(dim.size(), dim.data()); + plist.setFletcher32(); + dataSet = group_.createDataSet(s, Hdf5Type::type(), dataSpace, plist); + dataSet.write(&DataRowMajor[0], Hdf5Type::type()); + } + else + { + H5NS::Attribute attribute; + + attribute = group_.createAttribute(s, Hdf5Type::type(), dataSpace); + attribute.write(Hdf5Type::type(), &DataRowMajor[0]); + } + } + template typename std::enable_if>::is_number, void>::type Hdf5Writer::writeDefault(const std::string &s, const std::vector &x) @@ -110,34 +141,11 @@ namespace Grid // flatten the vector and getting dimensions Flatten> flat(x); - std::vector dim; + std::vector dim; const auto &flatx = flat.getFlatVector(); - for (auto &d: flat.getDim()) - { dim.push_back(d); - } - - // write to file - H5NS::DataSpace dataSpace(dim.size(), dim.data()); - - if (flatx.size() > dataSetThres_) - { - H5NS::DataSet dataSet; - H5NS::DSetCreatPropList plist; - - plist.setChunk(dim.size(), dim.data()); - plist.setFletcher32(); - dataSet = group_.createDataSet(s, Hdf5Type::type(), dataSpace, plist); - dataSet.write(flatx.data(), Hdf5Type::type()); - } - else - { - H5NS::Attribute attribute; - - attribute = group_.createAttribute(s, Hdf5Type::type(), dataSpace); - attribute.write(Hdf5Type::type(), flatx.data()); - } + writeMultiDim(s, dim, flatx); } template diff --git a/Grid/serialisation/VectorUtils.h b/Grid/serialisation/VectorUtils.h index 3372c8ad..658bc187 100644 --- a/Grid/serialisation/VectorUtils.h +++ b/Grid/serialisation/VectorUtils.h @@ -53,6 +53,17 @@ namespace Grid { return os; } + // std::vector> nested to specified Rank ////////////////////////////////// + template + struct NestedStdVector { + typedef typename std::vector::type> type; + }; + + template + struct NestedStdVector { + typedef T type; + }; + // Grid scalar tensors to nested std::vectors ////////////////////////////////// template struct TensorToVec diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index c94e56c7..521a65f0 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -441,18 +441,21 @@ bool DebugEigenTest() } typedef iMatrix OddBall; -typedef Eigen::Tensor TensorInt; -typedef Eigen::Tensor, 3, Eigen::RowMajor> TensorComplex; typedef Eigen::Tensor TensorOddBall; +typedef int TestScalar; +//typedef std::complex TestScalar; +typedef Eigen::Tensor TestTensor; + // From Test_serialisation.cc class myclass: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(myclass - //, OddBall, critter + //, TestTensor, critter , SpinColourVector, scv , SpinColourMatrix, scm ); + //myclass() : critter(7,3,2) {} }; template @@ -483,11 +486,31 @@ bool DebugIOTest(void) { ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); SpinColourVector scv; ioTest("iotest_vector.h5", scv, "SpinColourVector"); + + TestTensor t(3,6,2); + TestScalar Val{1}; + const TestScalar Inc{1}; + for( int i = 0 ; i < 3 ; i++) + for( int j = 0 ; j < 6 ; j++) + for( int k = 0 ; k < 2 ; k++) { + t(i,j,k) = Val; + Val += Inc; + } + ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); + + TestTensor t2(t); + auto bEqual = (t == t2).all(); + std::cout << "(t2 == t) = " << bEqual << std::endl; + //if( * bEqual.data() != 0 ) + //std::cout << "t2 == t" << std::endl; + //else + //std::cout << "t2 != t" << std::endl; + myclass o; ioTest("iotest_object.h5", o, "myclass_object_instance_name"); - TensorInt t(3,6,2); - ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); + std::cout << "Wow!" << std::endl; + return true; } #endif From 6f2663edf66f39ce9faf45a093b074c7d14e7331 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sun, 10 Feb 2019 23:19:20 +0000 Subject: [PATCH 076/347] Serialisation of an object containing an Eigen::Tensor works for Hdf5. Still quite a lot of tidying up to do. --- Grid/serialisation/BaseIO.h | 14 +++++++- Grid/serialisation/MacroMagic.h | 2 +- tests/hadrons/Test_hadrons_distil.cc | 49 +++++++++++++++++++++------- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 84e45861..0c527925 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -378,8 +378,20 @@ namespace Grid { { return os; } + + template + static inline bool CompareMember(const T &lhs, const T &rhs) { + return lhs == rhs; + } + + template + static inline bool CompareMember(const Eigen::Tensor &lhs, + const Eigen::Tensor &rhs) { + Eigen::Tensor bResult = (lhs == rhs).all(); + return bResult(0); + } }; - + // Generic writer interface ////////////////////////////////////////////////// template inline void push(Writer &w, const std::string &s) { diff --git a/Grid/serialisation/MacroMagic.h b/Grid/serialisation/MacroMagic.h index 95cd1c3c..f867ed7b 100644 --- a/Grid/serialisation/MacroMagic.h +++ b/Grid/serialisation/MacroMagic.h @@ -109,7 +109,7 @@ THE SOFTWARE. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define GRID_MACRO_MEMBER(A,B) A B; -#define GRID_MACRO_COMP_MEMBER(A,B) result = (result and (lhs. B == rhs. B)); +#define GRID_MACRO_COMP_MEMBER(A,B) result = (result and CompareMember(lhs. B, rhs. B)); #define GRID_MACRO_OS_WRITE_MEMBER(A,B) os<< #A <<" " #B << " = " << obj. B << " ; " < OddBall; -typedef Eigen::Tensor TensorOddBall; +typedef Eigen::Tensor TensorOddBall; -typedef int TestScalar; -//typedef std::complex TestScalar; -typedef Eigen::Tensor TestTensor; +//typedef int TestScalar; +typedef std::complex TestScalar; +typedef Eigen::Tensor TestTensor; // From Test_serialisation.cc class myclass: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(myclass - //, TestTensor, critter + , TestTensor, critter , SpinColourVector, scv , SpinColourMatrix, scm ); - //myclass() : critter(7,3,2) {} + myclass() : critter(7,3,2) {} }; template @@ -479,6 +479,30 @@ bool ioTest(const std::string &filename, const O &object, const std::string &nam return true; } +/* + template +eval Eigen::TensorForcedEvalOp, const Eigen::Tensor, const Eigen::Tensor >, Eigen::MakePointer> + + template + std::ostream& operator << (std::ostream& os, const TensorBase& expr) { + typedef TensorEvaluator, DefaultDevice> Evaluator; + typedef typename Evaluator::Dimensions Dimensions; + + // Evaluate the expression if needed + TensorForcedEvalOp eval = expr.eval(); + Evaluator tensor(eval, DefaultDevice()); + tensor.evalSubExprsIfNeeded(NULL); + + // Print the result + static const int rank = internal::array_size::value; + internal::TensorPrinter::run(os, tensor); + + // Cleanup. + tensor.cleanup(); + return os; + } +*/ + bool DebugIOTest(void) { OddBall critter; ioTest("iotest_oddball.h5", critter, "OddBall"); @@ -499,12 +523,13 @@ bool DebugIOTest(void) { ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); TestTensor t2(t); - auto bEqual = (t == t2).all(); - std::cout << "(t2 == t) = " << bEqual << std::endl; - //if( * bEqual.data() != 0 ) - //std::cout << "t2 == t" << std::endl; - //else - //std::cout << "t2 != t" << std::endl; + t2(1,1,1) += Inc; + Eigen::Tensor rResult = (t == t2).all(); + if( rResult(0) ) + std::cout << "t2 == t" << std::endl; + else + std::cout << "t2 != t" << std::endl; + //std::cout << "(t == t2) : " << (t == t2).all()(0) << std::endl; myclass o; ioTest("iotest_object.h5", o, "myclass_object_instance_name"); From dff7d9261d3fcb8eb7e41de15e8355d918e04473 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 11 Feb 2019 15:47:40 +0000 Subject: [PATCH 077/347] Can write both fixed and dynamic sized tensors --- Grid/serialisation/BaseIO.h | 76 +++++++++++++++------------- tests/hadrons/Test_hadrons_distil.cc | 28 +++++++--- 2 files changed, 62 insertions(+), 42 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 0c527925..3dd01a63 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -53,7 +53,8 @@ namespace Grid { typename std::enable_if::value, void>::type write(const std::string& s, const U &output); template - typename std::enable_if::value, void>::type + typename std::enable_if::value + && !std::is_base_of, U>::value, void>::type write(const std::string& s, const U &output); template void write(const std::string &s, const iScalar &output); @@ -61,9 +62,12 @@ namespace Grid { void write(const std::string &s, const iVector &output); template void write(const std::string &s, const iMatrix &output); - template + /*template typename std::enable_if::value || Grid::is_complex::value, void>::type - write(const std::string &s, const Eigen::Tensor &output); + write(const std::string &s, const Eigen::Tensor &output);*/ + template + typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type + write(const std::string &s, const ETensor &output); template void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); template @@ -146,7 +150,8 @@ namespace Grid { template template - typename std::enable_if::value, void>::type + typename std::enable_if::value + && !std::is_base_of, U>::value, void>::type Writer::write(const std::string &s, const U &output) { upcast->writeDefault(s, output); @@ -176,51 +181,49 @@ namespace Grid { // Eigen::Tensors of arithmetic/complex base type template - template + /*template typename std::enable_if::value || Grid::is_complex::value, void>::type - Writer::write(const std::string &s, const Eigen::Tensor &output) + Writer::write(const std::string &s, const Eigen::Tensor &output)*/ + template + typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type + Writer::write(const std::string &s, const ETensor &output) { - typedef Eigen::Tensor Tensor; - const typename Tensor::Index NumElements{output.size()}; + //typedef Eigen::Tensor Tensor; + //typedef Eigen::TensorBase Tensor; + const typename ETensor::Index NumElements{output.size()}; assert( NumElements > 0 ); if( NumElements == 1 ) upcast->writeDefault(s, * output.data()); else { // Create a single, flat vector to hold all the data - std::vector flat(NumElements); + std::vector flat(NumElements); // We're not interested in trivial dimensions, i.e. dimensions = 1 - const typename Tensor::Dimensions & DimsOriginal{output.dimensions()}; - assert(DimsOriginal.size() == NumIndices_); unsigned int TrivialDimCount{0}; - for(auto i : DimsOriginal ) { - if( i <= 1 ) { + std::vector ReducedDims; + for(auto i = 0; i < output.NumDimensions; i++ ) { + auto dim = output.dimension(i); + if( dim <= 1 ) { TrivialDimCount++; - assert( i == 1 ); // Not expecting dimension to be <= 0 - } - } - const unsigned int ReducedDimCount{NumIndices_ - TrivialDimCount}; - assert( ReducedDimCount > 0 ); // NB: We've already checked this is not a scalar - // Save a flat vector of the non-trivial dimensions - std::vector ReducedDims(ReducedDimCount); - unsigned int ui = 0; - for(auto i : DimsOriginal ) { - if( i > 1 ) { - ReducedDims[ui] = static_cast(i); - assert( ReducedDims[ui] == i ); // check we didn't lose anything in the conversion - ui++; + assert( dim == 1 ); // Not expecting dimension to be <= 0 + } else { + size_t s = static_cast(dim); + assert( s == dim ); // check we didn't lose anything in the conversion + ReducedDims.push_back(s); } } + const unsigned int ReducedDimCount{output.NumDimensions - TrivialDimCount}; + assert( ReducedDimCount > 0 ); // NB: NumElements > 1 implies this is not a scalar // Now copy all the data to my flat vector // Regardless of the Eigen::Tensor storage order, the copy will be Row Major - std::array MyIndex; - for( int i = 0 ; i < NumIndices_ ; i++ ) MyIndex[i] = 0; - for( typename Tensor::Index n = 0; n < NumElements; n++ ) { + std::array MyIndex; + for( int i = 0 ; i < output.NumDimensions ; i++ ) MyIndex[i] = 0; + for( typename ETensor::Index n = 0; n < NumElements; n++ ) { flat[n] = output( MyIndex ); // Now increment the index - for( int i = NumIndices_ - 1; i >= 0 && ++MyIndex[i] == DimsOriginal[i]; i-- ) + for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) MyIndex[i] = 0; } - upcast->template writeMultiDim(s, ReducedDims, flat); + upcast->template writeMultiDim(s, ReducedDims, flat); } } @@ -380,14 +383,15 @@ namespace Grid { } template - static inline bool CompareMember(const T &lhs, const T &rhs) { + static inline typename std::enable_if, T>::value, bool>::type + CompareMember(const T &lhs, const T &rhs) { return lhs == rhs; } - template - static inline bool CompareMember(const Eigen::Tensor &lhs, - const Eigen::Tensor &rhs) { - Eigen::Tensor bResult = (lhs == rhs).all(); + template + static inline typename std::enable_if, T>::value, bool>::type + CompareMember(const T &lhs, const T &rhs) { + Eigen::Tensor bResult = (lhs == rhs).all(); return bResult(0); } }; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 4902072c..db4ae273 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -446,16 +446,18 @@ typedef Eigen::Tensor TensorOddBall; //typedef int TestScalar; typedef std::complex TestScalar; typedef Eigen::Tensor TestTensor; +typedef Eigen::TensorFixedSize> TestTensorFixed; // From Test_serialisation.cc class myclass: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(myclass - , TestTensor, critter + , TestTensor, Critter + , TestTensorFixed, FixedCritter , SpinColourVector, scv , SpinColourMatrix, scm ); - myclass() : critter(7,3,2) {} + myclass() : Critter(7,3,2) {} }; template @@ -503,6 +505,9 @@ eval Eigen::TensorForcedEvalOp constexpr T Inc{1,-1}; +//template <> constexpr std::complex Inc< < std::complex > >{1,1}; + bool DebugIOTest(void) { OddBall critter; ioTest("iotest_oddball.h5", critter, "OddBall"); @@ -512,18 +517,17 @@ bool DebugIOTest(void) { ioTest("iotest_vector.h5", scv, "SpinColourVector"); TestTensor t(3,6,2); - TestScalar Val{1}; - const TestScalar Inc{1}; + TestScalar Val{Inc}; for( int i = 0 ; i < 3 ; i++) for( int j = 0 ; j < 6 ; j++) for( int k = 0 ; k < 2 ; k++) { t(i,j,k) = Val; - Val += Inc; + Val += Inc; } ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); TestTensor t2(t); - t2(1,1,1) += Inc; + t2(1,1,1) += Inc; Eigen::Tensor rResult = (t == t2).all(); if( rResult(0) ) std::cout << "t2 == t" << std::endl; @@ -531,6 +535,18 @@ bool DebugIOTest(void) { std::cout << "t2 != t" << std::endl; //std::cout << "(t == t2) : " << (t == t2).all()(0) << std::endl; + // Now serialise a fixed size tensor + using FixedTensor = Eigen::TensorFixedSize>; + FixedTensor tf; + Val = Inc; + for( int i = 0 ; i < 8 ; i++) + for( int j = 0 ; j < 4 ; j++) + for( int k = 0 ; k < 3 ; k++) { + tf(i,j,k) = Val; + Val += Inc; + } + ioTest("iotest_tensor_fixed.h5", tf, "eigen_tensor_fixed_name"); + myclass o; ioTest("iotest_object.h5", o, "myclass_object_instance_name"); From 9a225235b628e30b36678b2ae471825255d0a7e9 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 11 Feb 2019 17:15:38 +0000 Subject: [PATCH 078/347] Can write both fixed and dynamic sized tensors (small tidy) --- Grid/serialisation/BaseIO.h | 20 +++-- tests/hadrons/Test_hadrons_distil.cc | 116 +++++++++------------------ 2 files changed, 51 insertions(+), 85 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 3dd01a63..d5692490 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -62,9 +62,6 @@ namespace Grid { void write(const std::string &s, const iVector &output); template void write(const std::string &s, const iMatrix &output); - /*template - typename std::enable_if::value || Grid::is_complex::value, void>::type - write(const std::string &s, const Eigen::Tensor &output);*/ template typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type write(const std::string &s, const ETensor &output); @@ -181,15 +178,10 @@ namespace Grid { // Eigen::Tensors of arithmetic/complex base type template - /*template - typename std::enable_if::value || Grid::is_complex::value, void>::type - Writer::write(const std::string &s, const Eigen::Tensor &output)*/ template typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type Writer::write(const std::string &s, const ETensor &output) { - //typedef Eigen::Tensor Tensor; - //typedef Eigen::TensorBase Tensor; const typename ETensor::Index NumElements{output.size()}; assert( NumElements > 0 ); if( NumElements == 1 ) @@ -394,6 +386,18 @@ namespace Grid { Eigen::Tensor bResult = (lhs == rhs).all(); return bResult(0); } + + template + static inline typename std::enable_if, T>::value, bool>::type + CompareMember(const std::vector &lhs, const std::vector &rhs) { + const auto NumElements{lhs.size()}; + bool bResult = ( NumElements == rhs.size() ); + for( auto i = 0 ; i < NumElements && bResult ; i++ ) { + Eigen::Tensor b = (lhs[i] == rhs[i]).all(); + bResult = b(0); + } + return bResult; + } }; // Generic writer interface ////////////////////////////////////////////////// diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index db4ae273..ef98d8b0 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -440,26 +440,6 @@ bool DebugEigenTest() return true; } -typedef iMatrix OddBall; -typedef Eigen::Tensor TensorOddBall; - -//typedef int TestScalar; -typedef std::complex TestScalar; -typedef Eigen::Tensor TestTensor; -typedef Eigen::TensorFixedSize> TestTensorFixed; - -// From Test_serialisation.cc -class myclass: Serializable { -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(myclass - , TestTensor, Critter - , TestTensorFixed, FixedCritter - , SpinColourVector, scv - , SpinColourMatrix, scm - ); - myclass() : Critter(7,3,2) {} -}; - template bool ioTest(const std::string &filename, const O &object, const std::string &name) { @@ -470,80 +450,62 @@ bool ioTest(const std::string &filename, const O &object, const std::string &nam } /*R reader(filename); - O buf; - bool good; - - read(reader, "testobject", buf); - good = (object == buf); - std::cout << name << " IO test: " << (good ? "success" : "failure"); - std::cout << std::endl; - return good;*/ + O buf; + bool good; + + read(reader, "testobject", buf); + good = (object == buf); + std::cout << name << " IO test: " << (good ? "success" : "failure"); + std::cout << std::endl; + return good;*/ return true; } -/* - template -eval Eigen::TensorForcedEvalOp, const Eigen::Tensor, const Eigen::Tensor >, Eigen::MakePointer> - - template - std::ostream& operator << (std::ostream& os, const TensorBase& expr) { - typedef TensorEvaluator, DefaultDevice> Evaluator; - typedef typename Evaluator::Dimensions Dimensions; - - // Evaluate the expression if needed - TensorForcedEvalOp eval = expr.eval(); - Evaluator tensor(eval, DefaultDevice()); - tensor.evalSubExprsIfNeeded(NULL); - - // Print the result - static const int rank = internal::array_size::value; - internal::TensorPrinter::run(os, tensor); - - // Cleanup. - tensor.cleanup(); - return os; - } -*/ - -template constexpr T Inc{1,-1}; -//template <> constexpr std::complex Inc< < std::complex > >{1,1}; +//typedef int TestScalar; +typedef std::complex TestScalar; +typedef Eigen::Tensor TestTensor; +typedef Eigen::TensorFixedSize> TestTensorFixed; +typedef std::vector aTestTensorFixed; +// From Test_serialisation.cc +class myclass: Serializable { +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(myclass + , SpinColourVector, scv + , SpinColourMatrix, scm + , TestTensor, Critter + , TestTensorFixed, FixedCritter + , aTestTensorFixed, aFixedCritter + ); + myclass() : Critter(7,3,2), aFixedCritter(3) {} +}; bool DebugIOTest(void) { - OddBall critter; - ioTest("iotest_oddball.h5", critter, "OddBall"); - SpinColourMatrix scm; - ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); SpinColourVector scv; ioTest("iotest_vector.h5", scv, "SpinColourVector"); + SpinColourMatrix scm; + ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); + + constexpr TestScalar Inc{1,-1}; TestTensor t(3,6,2); - TestScalar Val{Inc}; - for( int i = 0 ; i < 3 ; i++) - for( int j = 0 ; j < 6 ; j++) - for( int k = 0 ; k < 2 ; k++) { + TestScalar Val{Inc}; + for( int i = 0 ; i < t.dimension(0) ; i++) + for( int j = 0 ; j < t.dimension(1) ; j++) + for( int k = 0 ; k < t.dimension(2) ; k++) { t(i,j,k) = Val; - Val += Inc; + Val += Inc; } ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); - TestTensor t2(t); - t2(1,1,1) += Inc; - Eigen::Tensor rResult = (t == t2).all(); - if( rResult(0) ) - std::cout << "t2 == t" << std::endl; - else - std::cout << "t2 != t" << std::endl; - //std::cout << "(t == t2) : " << (t == t2).all()(0) << std::endl; - // Now serialise a fixed size tensor using FixedTensor = Eigen::TensorFixedSize>; FixedTensor tf; - Val = Inc; - for( int i = 0 ; i < 8 ; i++) - for( int j = 0 ; j < 4 ; j++) - for( int k = 0 ; k < 3 ; k++) { + Val = Inc; + for( int i = 0 ; i < tf.dimension(0) ; i++) + for( int j = 0 ; j < tf.dimension(1) ; j++) + for( int k = 0 ; k < tf.dimension(2) ; k++) { tf(i,j,k) = Val; - Val += Inc; + Val += Inc; } ioTest("iotest_tensor_fixed.h5", tf, "eigen_tensor_fixed_name"); From d889cebc608129f2cf8ab1199990269491b814b8 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 11 Feb 2019 17:39:42 +0000 Subject: [PATCH 079/347] unique string is now used --- Hadrons/Modules/MDistil/PerambLight.hpp | 20 +++++++++++++++----- tests/hadrons/Test_hadrons_distil.cc | 6 ++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 742c25a3..00ac74cc 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -50,6 +50,9 @@ public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, std::string, eigenPack, std::string, PerambFileName, + std::string, ConfigFileDir, + std::string, ConfigFileName, + std::string, UniqueIdentifier, bool, multiFile, int, nvec, int, Ls, // For makeFiveDimGrid @@ -189,10 +192,17 @@ void TPerambLight::execute(void) const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; const int tsrc{Distil.tsrc}; const int Ns{Distil.Ns}; - + + const Real mass{Solver.mass}; + const Real M5 {Solver.M5}; + const bool full_tdil{TI==Nt}; const bool exact_distillation{full_tdil && LI==nvec}; + const std::string &ConfigFileDir{par().ConfigFileDir}; + const std::string &ConfigFileName{par().ConfigFileName}; + const std::string &UniqueIdentifier{par().UniqueIdentifier}; + //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, getName() + "_noise"); auto &perambulator = envGet(Perambulator, @@ -229,7 +239,8 @@ void TPerambLight::execute(void) Umu = where(coor==t,Usft,Umu); } } else { - std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); + //std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); + std::string fileName(ConfigFileDir + ConfigFileName); std::cout << GridLogMessage << "Loading NERSC configuration from '" << fileName << "'" << std::endl; NerscIO::readConfiguration(Umu, header, fileName); std::cout << GridLogMessage << "reading done." << std::endl; @@ -238,7 +249,8 @@ void TPerambLight::execute(void) //Create Noises //std::cout << pszGaugeConfigFile << std::endl; //GridSerialRNG sRNG; sRNG.SeedUniqueString(std::string(pszGaugeConfigFile)); - GridSerialRNG sRNG; sRNG.SeedUniqueString("unique_string"); // TODO: Proper unique string. Include quark mass, gauge field? Maybe also nvec, but in a way that more nvec would only add noises, not change all of them??? + GridSerialRNG sRNG; + sRNG.SeedUniqueString(ConfigFileName + "_" + std::to_string(mass) + "_" + UniqueIdentifier); Real rn; for (int inoise=0;inoise::execute(void) const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - const Real mass{Solver.mass}; - const Real M5 {Solver.M5}; std::cout << "init RBG " << std::endl; GridRedBlackCartesian RBGrid(grid4d); std::cout << "init RBG done" << std::endl; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index db4ae273..1e2378fa 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -89,6 +89,9 @@ void test_Perambulators(Application &application) MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="peramb.bin"; + PerambPar.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; + PerambPar.ConfigFileName="ckpoint_lat.3000"; + PerambPar.UniqueIdentifier="full_dilution"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; PerambPar.Distil.LI=5; @@ -133,6 +136,9 @@ void test_PerambulatorsS(Application &application) MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="perambS.bin"; + PerambPar.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; + PerambPar.ConfigFileName="ckpoint_lat.3000"; + PerambPar.UniqueIdentifier="full_dilution"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; PerambPar.Distil.LI=3; From fb2cb3015eb2a660b616b23b2b201a21423ee2a6 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 11 Feb 2019 23:26:18 +0000 Subject: [PATCH 080/347] Writing of Eigen::Tensor of grid objects now works (for Hdf5) --- Grid/serialisation/BaseIO.h | 118 ++++++++++++++++++++++----- Grid/serialisation/MacroMagic.h | 2 +- tests/hadrons/Test_hadrons_distil.cc | 36 +++++++- 3 files changed, 133 insertions(+), 23 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index d5692490..618da6a6 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -65,12 +65,20 @@ namespace Grid { template typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type write(const std::string &s, const ETensor &output); - template - void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); - template - void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); - template - void write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output); + template + typename std::enable_if, ETensor>::value && !(std::is_arithmetic::value || Grid::is_complex::value) + /*&& ( std::is_base_of >::value + || std::is_base_of>::value + || std::is_base_of>::value )*/, void>::type + write(const std::string &s, const ETensor &output); + /*template + typename std::enable_if, ETensor>::value + && std::is_base_of>::value, void>::type + write(const std::string &s, const ETensor &output); + template + typename std::enable_if, ETensor>::value + && std::is_base_of>::value, void>::type + write(const std::string &s, const ETensor &output);*/ void scientificFormat(const bool set); bool isScientific(void); @@ -182,13 +190,12 @@ namespace Grid { typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type Writer::write(const std::string &s, const ETensor &output) { + std::cout << "Eigen::Tensors of arithmetic/complex base type" << std::endl; const typename ETensor::Index NumElements{output.size()}; assert( NumElements > 0 ); if( NumElements == 1 ) upcast->writeDefault(s, * output.data()); else { - // Create a single, flat vector to hold all the data - std::vector flat(NumElements); // We're not interested in trivial dimensions, i.e. dimensions = 1 unsigned int TrivialDimCount{0}; std::vector ReducedDims; @@ -203,8 +210,9 @@ namespace Grid { ReducedDims.push_back(s); } } - const unsigned int ReducedDimCount{output.NumDimensions - TrivialDimCount}; - assert( ReducedDimCount > 0 ); // NB: NumElements > 1 implies this is not a scalar + assert( output.NumDimensions > TrivialDimCount > 0 ); // NB: NumElements > 1 implies this is not a scalar, so some dims should be left + // Create a single, flat vector to hold all the data + std::vector flat(NumElements); // Now copy all the data to my flat vector // Regardless of the Eigen::Tensor storage order, the copy will be Row Major std::array MyIndex; @@ -221,17 +229,71 @@ namespace Grid { // Eigen::Tensors of iScalar template - template - void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + template + typename std::enable_if, ETensor>::value && !(std::is_arithmetic::value || Grid::is_complex::value) + /*&& ( std::is_base_of >::value + || std::is_base_of>::value + || std::is_base_of>::value )*/, void>::type + Writer::write(const std::string &s, const ETensor &output) { - //upcast->writeDefault(s, tensorToVec(output)); - std::cout << "I really should add code to write Eigen::Tensor (iScalar) ..." << std::endl; + std::cout << "Eigen::Tensors of iScalar" << std::endl; + const typename ETensor::Index NumElements{output.size()}; + assert( NumElements > 0 ); + if( NumElements == 1 ) + upcast->writeDefault(s, tensorToVec(* output.data())); + else { + // We're not interested in trivial dimensions, i.e. dimensions = 1 + unsigned int TrivialDimCount{0}; + std::vector ReducedDims; + for(auto i = 0; i < output.NumDimensions; i++ ) { + auto dim = output.dimension(i); + if( dim <= 1 ) { + TrivialDimCount++; + assert( dim == 1 ); // Not expecting dimension to be <= 0 + } else { + size_t s = static_cast(dim); + assert( s == dim ); // check we didn't lose anything in the conversion + ReducedDims.push_back(s); + } + } + assert( output.NumDimensions > TrivialDimCount > 0 ); // NB: NumElements > 1 implies this is not a scalar, so some dims should be left + // Now add the extra dimensions, based on object zero + typename TensorToVec::type ttv = tensorToVec(* output.data()); + Flatten::type> f(ttv); + const std::vector & ExtraDims{f.getDim()}; + size_t ExtraCount{1}; + for( auto i : ExtraDims ) { + assert( i > 0 ); + ExtraCount *= i; + ReducedDims.push_back(i); + } + typedef typename ETensor::Scalar::scalar_type Scalar; + assert( sizeof( typename ETensor::Scalar ) == ExtraCount * sizeof( Scalar ) ); + // Create a single, flat vector to hold all the data + const typename ETensor::Index TotalNumElements = NumElements * ExtraCount; + std::vector flat(TotalNumElements); + // Now copy all the data to my flat vector + // Regardless of the Eigen::Tensor storage order, the copy will be Row Major + std::array MyIndex; + for( int i = 0 ; i < output.NumDimensions ; i++ ) MyIndex[i] = 0; + for( typename ETensor::Index n = 0; n < TotalNumElements; ) { + const Scalar * p = reinterpret_cast( &output( MyIndex )); + for( auto j = 0; j < ExtraCount ; j++ ) + flat[n++] = * p++; + // Now increment the index + for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) + MyIndex[i] = 0; + } + upcast->template writeMultiDim(s, ReducedDims, flat); + } } - + // Eigen::Tensors of iVector - template - template - void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + /*template + template + typename std::enable_if, ETensor>::value + && std::is_base_of>::value, void>::type + Writer::write(const std::string &s, const ETensor &output) { //upcast->writeDefault(s, tensorToVec(output)); std::cout << "I really should add code to write Eigen::Tensor (iVector) ..." << std::endl; @@ -239,12 +301,14 @@ namespace Grid { // Eigen::Tensors of iMatrix template - template - void Writer::write(const std::string &s, const Eigen::Tensor, NumIndices_, Options_, IndexType_> &output) + template + typename std::enable_if, ETensor>::value + && std::is_base_of>::value, void>::type + Writer::write(const std::string &s, const ETensor &output) { //upcast->writeDefault(s, tensorToVec(output)); std::cout << "I really should add code to write Eigen::Tensor (iMatrix) ..." << std::endl; - } + }*/ template void Writer::scientificFormat(const bool set) @@ -398,6 +462,18 @@ namespace Grid { } return bResult; } + + template + static inline typename std::enable_if, T>::value, void>::type + WriteMember(std::ostream &os, const T &object) { + os << object; + } + + template + static inline typename std::enable_if, T>::value, void>::type + WriteMember(std::ostream &os, const T &object) { + os << "Eigen::Tensor"; + } }; // Generic writer interface ////////////////////////////////////////////////// diff --git a/Grid/serialisation/MacroMagic.h b/Grid/serialisation/MacroMagic.h index f867ed7b..ce305673 100644 --- a/Grid/serialisation/MacroMagic.h +++ b/Grid/serialisation/MacroMagic.h @@ -110,7 +110,7 @@ THE SOFTWARE. #define GRID_MACRO_MEMBER(A,B) A B; #define GRID_MACRO_COMP_MEMBER(A,B) result = (result and CompareMember(lhs. B, rhs. B)); -#define GRID_MACRO_OS_WRITE_MEMBER(A,B) os<< #A <<" " #B << " = " << obj. B << " ; " < TestScalar; typedef Eigen::Tensor TestTensor; typedef Eigen::TensorFixedSize> TestTensorFixed; typedef std::vector aTestTensorFixed; +typedef Eigen::TensorFixedSize> LSCTensor; +typedef Eigen::TensorFixedSize> LCMTensor; // From Test_serialisation.cc class myclass: Serializable { public: @@ -475,12 +477,15 @@ public: , TestTensor, Critter , TestTensorFixed, FixedCritter , aTestTensorFixed, aFixedCritter + , LSCTensor, MyLSCTensor + , LCMTensor, MyLCMTensor ); myclass() : Critter(7,3,2), aFixedCritter(3) {} }; bool DebugIOTest(void) { - SpinColourVector scv; + SpinColourVector scv, scv2; + scv2 = scv; ioTest("iotest_vector.h5", scv, "SpinColourVector"); SpinColourMatrix scm; ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); @@ -512,6 +517,35 @@ bool DebugIOTest(void) { myclass o; ioTest("iotest_object.h5", o, "myclass_object_instance_name"); + // Tensor of spin colour + LSCTensor l; + Val = 0; + for( int i = 0 ; i < l.dimension(0) ; i++) + for( int j = 0 ; j < l.dimension(1) ; j++) + for( int k = 0 ; k < l.dimension(2) ; k++) + for( int s = 0 ; s < Ns ; s++ ) + for( int c = 0 ; c < Nc ; c++ ) + { + l(i,j,k)()(s)(c) = Val; + Val += Inc; + } + ioTest("iotest_LSCTensor.h5", l, "LSCTensor_object_instance_name"); + + // Tensor of spin colour + LCMTensor l2; + Val = 0; + for( int i = 0 ; i < l2.dimension(0) ; i++) + for( int j = 0 ; j < l2.dimension(1) ; j++) + for( int k = 0 ; k < l2.dimension(2) ; k++) + for( int l = 0 ; l < Ns ; l++ ) + for( int c = 0 ; c < Nc ; c++ ) + for( int c2 = 0 ; c2 < Nc ; c2++ ) + { + l2(i,j,k)(l)()(c,c2) = Val; + Val += Inc; + } + ioTest("iotest_LCMTensor.h5", l2, "LCMTensor_object_instance_name"); + std::cout << "Wow!" << std::endl; return true; From e7048231bc5a90c067132ec1d3df48c4e1aa22af Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 12 Feb 2019 13:59:48 +0000 Subject: [PATCH 081/347] Working version with additional Grid traits pre: review by Antonin --- Grid/serialisation/BaseIO.h | 64 ++++++++++++++++++++++++++-- tests/hadrons/Test_hadrons_distil.cc | 41 ++++++++++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 618da6a6..945fa632 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -39,7 +39,61 @@ namespace Grid { // Abstract writer/reader classes //////////////////////////////////////////// // static polymorphism implemented using CRTP idiom class Serializable; - + + // which types are supported scalar types for Eigen::Tensor + template struct is_eigen_tensor_scalar : std::integral_constant::value || is_complex::value> {}; + // which types are grid tensors + template struct is_grid_tensor : public std::false_type { + static constexpr unsigned int rank = 0; + static constexpr unsigned int dim = 1; + }; + template struct is_grid_tensor> : public std::true_type {}; + template struct is_grid_tensor> : public std::true_type {}; + template struct is_grid_tensor> : public std::true_type {}; + + // Rank and dimension of grid tensors, i.e. compositions of iScalar, iVector and iMatrix + template struct grid_tensor_att { + static constexpr unsigned int depth = 0; // How many levels of Grid Tensor there are + static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) + static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) + typedef T scalar_type; // Type of the underlying scalar + static constexpr size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes + static constexpr size_t size = scalar_size * count; // total size of elements in bytes + // e.g. iVector,4> + // depth = 2 + // rank = 3 + // size = 36 + // e.g. iScalar,3>> + // depth = 3 + // rank = 3 + // size = 48 + }; + template struct grid_tensor_att> { + static constexpr unsigned int depth = 1 + grid_tensor_att::depth; + static constexpr unsigned int rank = 0 + grid_tensor_att::rank; + static constexpr unsigned int count = 1 * grid_tensor_att::count; + typedef typename grid_tensor_att::scalar_type scalar_type; + static constexpr size_t scalar_size = grid_tensor_att::scalar_size; + static constexpr size_t size = scalar_size * count; + }; + template struct grid_tensor_att> { + static constexpr unsigned int depth = 1 + grid_tensor_att::depth; + static constexpr unsigned int rank = 1 + grid_tensor_att::rank; + static constexpr unsigned int count = N * grid_tensor_att::count; + typedef typename grid_tensor_att::scalar_type scalar_type; + static constexpr size_t scalar_size = grid_tensor_att::scalar_size; + static constexpr size_t size = scalar_size * count; + }; + template struct grid_tensor_att> { + static constexpr unsigned int depth = 1 + grid_tensor_att::depth; + static constexpr unsigned int rank = 2 + grid_tensor_att::rank; + static constexpr unsigned int count = N * N * grid_tensor_att::count; + typedef typename grid_tensor_att::scalar_type scalar_type; + static constexpr size_t scalar_size = grid_tensor_att::scalar_size; + static constexpr size_t size = scalar_size * count; + }; + // Static abstract writer template class Writer @@ -66,7 +120,9 @@ namespace Grid { typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type write(const std::string &s, const ETensor &output); template - typename std::enable_if, ETensor>::value && !(std::is_arithmetic::value || Grid::is_complex::value) + typename std::enable_if, ETensor>::value + && is_grid_tensor::value + //&& !(std::is_arithmetic::value || Grid::is_complex::value) /*&& ( std::is_base_of >::value || std::is_base_of>::value || std::is_base_of>::value )*/, void>::type @@ -230,7 +286,9 @@ namespace Grid { // Eigen::Tensors of iScalar template template - typename std::enable_if, ETensor>::value && !(std::is_arithmetic::value || Grid::is_complex::value) + typename std::enable_if, ETensor>::value + && is_grid_tensor::value + //&& !(std::is_arithmetic::value || Grid::is_complex::value) /*&& ( std::is_base_of >::value || std::is_base_of>::value || std::is_base_of>::value )*/, void>::type diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index ef9465c4..c60f7124 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -556,6 +556,46 @@ bool DebugIOTest(void) { return true; } + +//template ReallyIsGridTensor struct { + //false_type; +//} + +/*template +struct GridSize : public std::integral_constant {}; + +template +struct GridRank> : public std::integral_constant::value + 1> {}; + +template +struct rank : public std::integral_constant::value + 1> {}; +*/ + +template +void DebugGridTensorTest_print( int i ) +{ + std::cout << i << " : " << is_grid_tensor::value << ", depth" << grid_tensor_att::depth << ", rank" << grid_tensor_att::rank << ", count" << grid_tensor_att::count << ", scalar_size" << grid_tensor_att::scalar_size << ", size" << grid_tensor_att::size << std::endl; +} + +bool DebugGridTensorTest( void ) +{ + typedef Complex t1; + typedef iScalar t2; + typedef iVector t3; + typedef iMatrix t4; + typedef iVector,4> t5; + typedef iScalar t6; + typedef iMatrix>,2>,7> t7; + int i = 1; + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + return true; +} #endif int main(int argc, char *argv[]) @@ -565,6 +605,7 @@ int main(int argc, char *argv[]) std::cout << "sizeof(std::streamsize) = " << sizeof(std::streamsize) << std::endl; std::cout << "sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; //if( DebugEigenTest() ) return 0; + //if(DebugGridTensorTest()) return 0; if(DebugIOTest()) return 0; #endif From 76c6a6772ab6660299ae3ae6313026c98d285936 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 12 Feb 2019 22:15:55 +0000 Subject: [PATCH 082/347] Added rank_non_trivial --- Grid/serialisation/BaseIO.h | 15 +++++++++++++-- tests/hadrons/Test_hadrons_distil.cc | 17 ++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 945fa632..d2f4bbfe 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -56,22 +56,31 @@ namespace Grid { template struct grid_tensor_att { static constexpr unsigned int depth = 0; // How many levels of Grid Tensor there are static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) + static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) typedef T scalar_type; // Type of the underlying scalar static constexpr size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes static constexpr size_t size = scalar_size * count; // total size of elements in bytes + // e.g. iScalar> + // depth = 2 + // rank = 1 + // rank_non_trivial = 0 + // count = 1 // e.g. iVector,4> // depth = 2 // rank = 3 - // size = 36 + // rank_non_trivial = 3 + // count = 36 // e.g. iScalar,3>> // depth = 3 // rank = 3 - // size = 48 + // rank_non_trivial = 3 + // count = 48 }; template struct grid_tensor_att> { static constexpr unsigned int depth = 1 + grid_tensor_att::depth; static constexpr unsigned int rank = 0 + grid_tensor_att::rank; + static constexpr unsigned int rank_non_trivial = 0 + grid_tensor_att::rank_non_trivial; static constexpr unsigned int count = 1 * grid_tensor_att::count; typedef typename grid_tensor_att::scalar_type scalar_type; static constexpr size_t scalar_size = grid_tensor_att::scalar_size; @@ -80,6 +89,7 @@ namespace Grid { template struct grid_tensor_att> { static constexpr unsigned int depth = 1 + grid_tensor_att::depth; static constexpr unsigned int rank = 1 + grid_tensor_att::rank; + static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + grid_tensor_att::rank_non_trivial; static constexpr unsigned int count = N * grid_tensor_att::count; typedef typename grid_tensor_att::scalar_type scalar_type; static constexpr size_t scalar_size = grid_tensor_att::scalar_size; @@ -88,6 +98,7 @@ namespace Grid { template struct grid_tensor_att> { static constexpr unsigned int depth = 1 + grid_tensor_att::depth; static constexpr unsigned int rank = 2 + grid_tensor_att::rank; + static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + grid_tensor_att::rank_non_trivial; static constexpr unsigned int count = N * N * grid_tensor_att::count; typedef typename grid_tensor_att::scalar_type scalar_type; static constexpr size_t scalar_size = grid_tensor_att::scalar_size; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index c60f7124..e170bc35 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -574,7 +574,14 @@ struct rank : public std::integral_constant::value + template void DebugGridTensorTest_print( int i ) { - std::cout << i << " : " << is_grid_tensor::value << ", depth" << grid_tensor_att::depth << ", rank" << grid_tensor_att::rank << ", count" << grid_tensor_att::count << ", scalar_size" << grid_tensor_att::scalar_size << ", size" << grid_tensor_att::size << std::endl; + std::cout << i << " : " << is_grid_tensor::value + << ", depth " << grid_tensor_att::depth + << ", rank " << grid_tensor_att::rank + << ", rank_non_trivial " << grid_tensor_att::rank_non_trivial + << ", count " << grid_tensor_att::count + << ", scalar_size " << grid_tensor_att::scalar_size + << ", size " << grid_tensor_att::size + << std::endl; } bool DebugGridTensorTest( void ) @@ -583,9 +590,9 @@ bool DebugGridTensorTest( void ) typedef iScalar t2; typedef iVector t3; typedef iMatrix t4; - typedef iVector,4> t5; + typedef iVector,4> t5; typedef iScalar t6; - typedef iMatrix>,2>,7> t7; + typedef iMatrix>,2>,7> t7; int i = 1; DebugGridTensorTest_print( i++ ); DebugGridTensorTest_print( i++ ); @@ -605,8 +612,8 @@ int main(int argc, char *argv[]) std::cout << "sizeof(std::streamsize) = " << sizeof(std::streamsize) << std::endl; std::cout << "sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; //if( DebugEigenTest() ) return 0; - //if(DebugGridTensorTest()) return 0; - if(DebugIOTest()) return 0; + if(DebugGridTensorTest()) return 0; + //if(DebugIOTest()) return 0; #endif // Decode command-line parameters. 1st one is which test to run From 65731546b7eda85e06f1eabc832748dc38e27713 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Feb 2019 11:48:34 +0000 Subject: [PATCH 083/347] merge... --- tests/hadrons/Test_hadrons_distil.cc | 30 ++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index ef9465c4..ba8f61b4 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -213,7 +213,7 @@ void test_MesonFieldSL(Application &application) application.createModule("DistilMesonFieldS",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// -// MesonFields +// MesonFields - phiphi ///////////////////////////////////////////////////////////// void test_MesonField(Application &application) @@ -223,7 +223,7 @@ void test_MesonField(Application &application) A2AMesonFieldPar.left="DistilVecs_phi"; //A2AMesonFieldPar.right="DistilVecs_rho"; A2AMesonFieldPar.right="DistilVecs_phi"; - A2AMesonFieldPar.output="MesonSinks"; + A2AMesonFieldPar.output="MesonSinksPhi"; A2AMesonFieldPar.gammas="all"; A2AMesonFieldPar.mom={"0 0 0"}; A2AMesonFieldPar.cacheBlock=2; @@ -231,6 +231,24 @@ void test_MesonField(Application &application) application.createModule("DistilMesonField",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// +// MesonFields - rhorho +///////////////////////////////////////////////////////////// + +void test_MesonFieldRho(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_rho"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.output="MesonSinksRho"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldRho",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// // BaryonFields - phiphiphi ///////////////////////////////////////////////////////////// @@ -657,6 +675,14 @@ int main(int argc, char *argv[]) test_BaryonFieldPhi( application ); test_BaryonFieldRho( application ); break; + case 8: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_MesonField( application ); + test_MesonFieldRho( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From 41ff592515c0c2599f7a2f34f8f07e30bddf5aba Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 13 Feb 2019 12:14:01 +0000 Subject: [PATCH 084/347] Moved serialisation tests into Test_serialisation --- tests/IO/Test_serialisation.cc | 115 ++++++++++++++++++++++-- tests/hadrons/Test_hadrons_distil.cc | 126 --------------------------- 2 files changed, 106 insertions(+), 135 deletions(-) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 15602e93..445abe6d 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -91,15 +91,107 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam R reader(filename); O buf; - bool good; +#ifndef DEBUG read(reader, "testobject", buf); - good = (object == buf); - std::cout << name << " IO test: " << (good ? "success" : "failure"); - std::cout << std::endl; + bool good = (object == buf); + std::cout << name << " IO test: " << std::endl; if (!good) exit(EXIT_FAILURE); +#endif } +#ifdef DEBUG +//typedef int TestScalar; +typedef std::complex TestScalar; +typedef Eigen::Tensor TestTensor; +typedef Eigen::TensorFixedSize> TestTensorFixed; +typedef std::vector aTestTensorFixed; +typedef Eigen::TensorFixedSize> LSCTensor; +typedef Eigen::TensorFixedSize> LCMTensor; +// From Test_serialisation.cc +class ETSerClass: Serializable { +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(ETSerClass + , SpinColourVector, scv + , SpinColourMatrix, scm + , TestTensor, Critter + , TestTensorFixed, FixedCritter + , aTestTensorFixed, aFixedCritter + , LSCTensor, MyLSCTensor + , LCMTensor, MyLCMTensor + ); + ETSerClass() : Critter(7,3,2), aFixedCritter(3) {} +}; + +bool EigenIOTest(void) { + SpinColourVector scv, scv2; + scv2 = scv; + ioTest("iotest_vector.h5", scv, "SpinColourVector"); + SpinColourMatrix scm; + ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); + + constexpr TestScalar Inc{1,-1}; + + TestTensor t(3,6,2); + TestScalar Val{Inc}; + for( int i = 0 ; i < t.dimension(0) ; i++) + for( int j = 0 ; j < t.dimension(1) ; j++) + for( int k = 0 ; k < t.dimension(2) ; k++) { + t(i,j,k) = Val; + Val += Inc; + } + ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); + + // Now serialise a fixed size tensor + using FixedTensor = Eigen::TensorFixedSize>; + FixedTensor tf; + Val = Inc; + for( int i = 0 ; i < tf.dimension(0) ; i++) + for( int j = 0 ; j < tf.dimension(1) ; j++) + for( int k = 0 ; k < tf.dimension(2) ; k++) { + tf(i,j,k) = Val; + Val += Inc; + } + ioTest("iotest_tensor_fixed.h5", tf, "eigen_tensor_fixed_name"); + + ETSerClass o; + ioTest("iotest_object.h5", o, "ETSerClass_object_instance_name"); + + // Tensor of spin colour + LSCTensor l; + Val = 0; + for( int i = 0 ; i < l.dimension(0) ; i++) + for( int j = 0 ; j < l.dimension(1) ; j++) + for( int k = 0 ; k < l.dimension(2) ; k++) + for( int s = 0 ; s < Ns ; s++ ) + for( int c = 0 ; c < Nc ; c++ ) + { + l(i,j,k)()(s)(c) = Val; + Val += Inc; + } + ioTest("iotest_LSCTensor.h5", l, "LSCTensor_object_instance_name"); + + // Tensor of spin colour + LCMTensor l2; + Val = 0; + for( int i = 0 ; i < l2.dimension(0) ; i++) + for( int j = 0 ; j < l2.dimension(1) ; j++) + for( int k = 0 ; k < l2.dimension(2) ; k++) + for( int l = 0 ; l < Ns ; l++ ) + for( int c = 0 ; c < Nc ; c++ ) + for( int c2 = 0 ; c2 < Nc ; c2++ ) + { + l2(i,j,k)(l)()(c,c2) = Val; + Val += Inc; + } + ioTest("iotest_LCMTensor.h5", l2, "LCMTensor_object_instance_name"); + + std::cout << "Wow!" << std::endl; + + return true; +} +#endif + template void tensorConvTestFn(GridSerialRNG &rng, const std::string label) { @@ -121,12 +213,13 @@ void tensorConvTestFn(GridSerialRNG &rng, const std::string label) int main(int argc,char **argv) { Grid_init(&argc,&argv); - + std::cout << std::boolalpha << "==== basic IO" << std::endl; // display true / false for boolean + +#ifndef DEBUG GridSerialRNG rng; rng.SeedFixedIntegers(std::vector({42,10,81,9})); - - std::cout << "==== basic IO" << std::endl; + XmlWriter WR("bother.xml"); // test basic type writing @@ -160,8 +253,8 @@ int main(int argc,char **argv) std::cout << "-- serialisable class writing to std::cout:" << std::endl; std::cout << obj << std::endl; std::cout << "-- serialisable class comparison:" << std::endl; - std::cout << "vec[0] == obj: " << ((vec[0] == obj) ? "true" : "false") << std::endl; - std::cout << "vec[1] == obj: " << ((vec[1] == obj) ? "true" : "false") << std::endl; + std::cout << "vec[0] == obj: " << (vec[0] == obj) << std::endl; + std::cout << "vec[1] == obj: " << (vec[1] == obj) << std::endl; std::cout << "-- pair writing to std::cout:" << std::endl; std::cout << pair << std::endl; @@ -227,4 +320,8 @@ int main(int argc,char **argv) tensorConvTest(rng, ColourVector); tensorConvTest(rng, SpinMatrix); tensorConvTest(rng, SpinVector); +#elif HAVE_HDF5 + if(! EigenIOTest() ) exit(EXIT_FAILURE); +#endif + Grid_finalize(); } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index e170bc35..5a0338a9 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -446,131 +446,6 @@ bool DebugEigenTest() return true; } -template -bool ioTest(const std::string &filename, const O &object, const std::string &name) -{ - // writer needs to be destroyed so that writing physically happens - { - W writer(filename); - write(writer, "testobject", object); - } - - /*R reader(filename); - O buf; - bool good; - - read(reader, "testobject", buf); - good = (object == buf); - std::cout << name << " IO test: " << (good ? "success" : "failure"); - std::cout << std::endl; - return good;*/ - return true; -} - -//typedef int TestScalar; -typedef std::complex TestScalar; -typedef Eigen::Tensor TestTensor; -typedef Eigen::TensorFixedSize> TestTensorFixed; -typedef std::vector aTestTensorFixed; -typedef Eigen::TensorFixedSize> LSCTensor; -typedef Eigen::TensorFixedSize> LCMTensor; -// From Test_serialisation.cc -class myclass: Serializable { -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(myclass - , SpinColourVector, scv - , SpinColourMatrix, scm - , TestTensor, Critter - , TestTensorFixed, FixedCritter - , aTestTensorFixed, aFixedCritter - , LSCTensor, MyLSCTensor - , LCMTensor, MyLCMTensor - ); - myclass() : Critter(7,3,2), aFixedCritter(3) {} -}; - -bool DebugIOTest(void) { - SpinColourVector scv, scv2; - scv2 = scv; - ioTest("iotest_vector.h5", scv, "SpinColourVector"); - SpinColourMatrix scm; - ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); - - constexpr TestScalar Inc{1,-1}; - - TestTensor t(3,6,2); - TestScalar Val{Inc}; - for( int i = 0 ; i < t.dimension(0) ; i++) - for( int j = 0 ; j < t.dimension(1) ; j++) - for( int k = 0 ; k < t.dimension(2) ; k++) { - t(i,j,k) = Val; - Val += Inc; - } - ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); - - // Now serialise a fixed size tensor - using FixedTensor = Eigen::TensorFixedSize>; - FixedTensor tf; - Val = Inc; - for( int i = 0 ; i < tf.dimension(0) ; i++) - for( int j = 0 ; j < tf.dimension(1) ; j++) - for( int k = 0 ; k < tf.dimension(2) ; k++) { - tf(i,j,k) = Val; - Val += Inc; - } - ioTest("iotest_tensor_fixed.h5", tf, "eigen_tensor_fixed_name"); - - myclass o; - ioTest("iotest_object.h5", o, "myclass_object_instance_name"); - - // Tensor of spin colour - LSCTensor l; - Val = 0; - for( int i = 0 ; i < l.dimension(0) ; i++) - for( int j = 0 ; j < l.dimension(1) ; j++) - for( int k = 0 ; k < l.dimension(2) ; k++) - for( int s = 0 ; s < Ns ; s++ ) - for( int c = 0 ; c < Nc ; c++ ) - { - l(i,j,k)()(s)(c) = Val; - Val += Inc; - } - ioTest("iotest_LSCTensor.h5", l, "LSCTensor_object_instance_name"); - - // Tensor of spin colour - LCMTensor l2; - Val = 0; - for( int i = 0 ; i < l2.dimension(0) ; i++) - for( int j = 0 ; j < l2.dimension(1) ; j++) - for( int k = 0 ; k < l2.dimension(2) ; k++) - for( int l = 0 ; l < Ns ; l++ ) - for( int c = 0 ; c < Nc ; c++ ) - for( int c2 = 0 ; c2 < Nc ; c2++ ) - { - l2(i,j,k)(l)()(c,c2) = Val; - Val += Inc; - } - ioTest("iotest_LCMTensor.h5", l2, "LCMTensor_object_instance_name"); - - std::cout << "Wow!" << std::endl; - - return true; -} - -//template ReallyIsGridTensor struct { - //false_type; -//} - -/*template -struct GridSize : public std::integral_constant {}; - -template -struct GridRank> : public std::integral_constant::value + 1> {}; - -template -struct rank : public std::integral_constant::value + 1> {}; -*/ - template void DebugGridTensorTest_print( int i ) { @@ -613,7 +488,6 @@ int main(int argc, char *argv[]) std::cout << "sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; //if( DebugEigenTest() ) return 0; if(DebugGridTensorTest()) return 0; - //if(DebugIOTest()) return 0; #endif // Decode command-line parameters. 1st one is which test to run From 9f2ca98dfcf97c72e0417f07e3a19a1283897d1d Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Feb 2019 13:54:31 +0000 Subject: [PATCH 085/347] enseble can now be specified in LapEvec --- Hadrons/Modules/MDistil/LapEvec.hpp | 17 ++++++++++++++--- tests/hadrons/Test_hadrons_distil.cc | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index e5b1f63c..5dfc830b 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -84,10 +84,12 @@ struct LanczosParameters: Serializable { class LapEvecPar: Serializable { public: - GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar - ,std::string, gauge + GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, + std::string, gauge, + std::string, ConfigFileDir, + std::string, ConfigFileName, //,std::string, EigenPackName - ,StoutParameters, Stout + StoutParameters, Stout ,ChebyshevParameters, Cheby ,LanczosParameters, Lanczos //,DistilParameters, Distil @@ -229,10 +231,14 @@ void TLapEvec::execute(void) //else //assert(nnoise>1); + const std::string &ConfigFileDir{par().ConfigFileDir}; + const std::string &ConfigFileName{par().ConfigFileName}; + // Debugging only //envGetTmp(GaugeField, Umu); auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); + FieldMetaData header; if((1)) { const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(gridHD); @@ -258,6 +264,11 @@ void TLapEvec::execute(void) Umu = where(coor==t,Umu_smear,Umu); } // std::cout << "Umu is "<(szGaugeName); // Now make an instance of the LapEvec object MDistil::LapEvecPar p; + p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; + p.ConfigFileName="ckpoint_lat.3000"; p.gauge = szGaugeName; //p.EigenPackName = "ePack"; //p.Distil.TI = 8; From 11467a994d903d5878205635323886e280f62587 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 13 Feb 2019 21:48:35 +0000 Subject: [PATCH 086/347] Enough for tonight --- Grid/serialisation/BaseIO.h | 279 +++++++++++++-------------- Grid/serialisation/Hdf5IO.h | 27 +-- Grid/tensors/Tensor_class.h | 94 +++++++++ Grid/tensors/Tensor_traits.h | 73 +++++++ tests/IO/Test_serialisation.cc | 26 ++- tests/hadrons/Test_hadrons_distil.cc | 49 ++++- 6 files changed, 382 insertions(+), 166 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index d2f4bbfe..90897188 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -42,68 +42,79 @@ namespace Grid { // which types are supported scalar types for Eigen::Tensor template struct is_eigen_tensor_scalar : std::integral_constant::value || is_complex::value> {}; - // which types are grid tensors - template struct is_grid_tensor : public std::false_type { - static constexpr unsigned int rank = 0; - static constexpr unsigned int dim = 1; - }; - template struct is_grid_tensor> : public std::true_type {}; - template struct is_grid_tensor> : public std::true_type {}; - template struct is_grid_tensor> : public std::true_type {}; + std::is_arithmetic::value || Grid::is_complex::value> {}; - // Rank and dimension of grid tensors, i.e. compositions of iScalar, iVector and iMatrix - template struct grid_tensor_att { - static constexpr unsigned int depth = 0; // How many levels of Grid Tensor there are - static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) - static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 - static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) - typedef T scalar_type; // Type of the underlying scalar - static constexpr size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes - static constexpr size_t size = scalar_size * count; // total size of elements in bytes - // e.g. iScalar> - // depth = 2 - // rank = 1 - // rank_non_trivial = 0 - // count = 1 - // e.g. iVector,4> - // depth = 2 - // rank = 3 - // rank_non_trivial = 3 - // count = 36 - // e.g. iScalar,3>> - // depth = 3 - // rank = 3 - // rank_non_trivial = 3 - // count = 48 - }; - template struct grid_tensor_att> { - static constexpr unsigned int depth = 1 + grid_tensor_att::depth; - static constexpr unsigned int rank = 0 + grid_tensor_att::rank; - static constexpr unsigned int rank_non_trivial = 0 + grid_tensor_att::rank_non_trivial; - static constexpr unsigned int count = 1 * grid_tensor_att::count; - typedef typename grid_tensor_att::scalar_type scalar_type; - static constexpr size_t scalar_size = grid_tensor_att::scalar_size; - static constexpr size_t size = scalar_size * count; - }; - template struct grid_tensor_att> { - static constexpr unsigned int depth = 1 + grid_tensor_att::depth; - static constexpr unsigned int rank = 1 + grid_tensor_att::rank; - static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + grid_tensor_att::rank_non_trivial; - static constexpr unsigned int count = N * grid_tensor_att::count; - typedef typename grid_tensor_att::scalar_type scalar_type; - static constexpr size_t scalar_size = grid_tensor_att::scalar_size; - static constexpr size_t size = scalar_size * count; - }; - template struct grid_tensor_att> { - static constexpr unsigned int depth = 1 + grid_tensor_att::depth; - static constexpr unsigned int rank = 2 + grid_tensor_att::rank; - static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + grid_tensor_att::rank_non_trivial; - static constexpr unsigned int count = N * N * grid_tensor_att::count; - typedef typename grid_tensor_att::scalar_type scalar_type; - static constexpr size_t scalar_size = grid_tensor_att::scalar_size; - static constexpr size_t size = scalar_size * count; - }; + // Helper to allow iteration through an Eigen::Tensor (using a lambda) + template + typename std::enable_if, ETensor>::value && is_grid_tensor::value, void>::type + for_all( ETensor &ET, Lambda lambda ) + { + using Scalar = typename ETensor::Scalar::scalar_type; + const std::size_t NumElements = ET.size(); + assert( NumElements > 0 ); + if( NumElements == 1 ) { + const auto MyRank{grid_tensor_att::rank_non_trivial}; + std::vector SubIndex(MyRank); + for( auto &idx : SubIndex ) idx = 0; + typename ETensor::Index n = 0; + for( Scalar &Source : * ET.data() ) { + lambda(Source, n++, &SubIndex[0] ); + // Now increment SubIndex + for( auto i = MyRank - 1; i >= 0 && ++SubIndex[i] == 11/*ReducedDims[i]*/; i-- ) + SubIndex[i] = 0; + } + } + else { + // We're only interested in non-trivial dimensions (i.e. dimensions > 1) + unsigned int TrivialDimCount{0}; + std::vector ReducedDims; + ReducedDims.reserve(ET.NumDimensions + grid_tensor_att::rank_non_trivial); // Make sure we only do one malloc + for(auto i = 0; i < ET.NumDimensions; i++ ) { + auto dim = ET.dimension(i); + if( dim <= 1 ) { + TrivialDimCount++; + assert( dim == 1 ); // Not expecting dimension to be <= 0 + } else { + size_t s = static_cast(dim); + assert( s == dim ); // check we didn't lose anything in the conversion + ReducedDims.push_back(s); + } + } + // NB: NumElements > 1 implies this is not a scalar, so some dims should be left + assert( ET.NumDimensions > TrivialDimCount ); + // Now add the extra dimensions, based on object zero + typename TensorToVec::type ttv = tensorToVec(* ET.data()); + Flatten::type> f(ttv); + const std::vector & ExtraDims{f.getDim()}; + assert(ExtraDims.size() == grid_tensor_att::rank_non_trivial); + size_t ExtraCount{1}; + for( auto i : ExtraDims ) { + assert( i > 0 ); + ExtraCount *= i; + ReducedDims.push_back(i); + } + assert(grid_tensor_att::count == ExtraCount); + assert(grid_tensor_att::size == sizeof( typename ETensor::Scalar )); + const unsigned int ReducedDimsSize = static_cast(ReducedDims.size()); + assert( ReducedDimsSize == ReducedDims.size() ); + const typename ETensor::Index TotalNumElements = NumElements * ExtraCount; + std::array MyIndex; + for( auto &idx : MyIndex ) idx = 0; + std::vector SubIndex(ReducedDimsSize); + for( auto &idx : SubIndex ) idx = 0; + for( typename ETensor::Index n = 0; n < TotalNumElements; ) { + for( Scalar &Source : ET( MyIndex ) ) { + lambda(Source, n++, &SubIndex[0] ); + // Now increment MyIndex + for( auto i = ET.NumDimensions - 1; i >= 0 && ++MyIndex[i] == ET.dimension(i); i-- ) + MyIndex[i] = 0; + // Now increment SubIndex + for( auto i = ReducedDimsSize - 1; i >= 0 && ++SubIndex[i] == ReducedDims[i]; i-- ) + SubIndex[i] = 0; + } + } + } + } // Static abstract writer template @@ -128,24 +139,11 @@ namespace Grid { template void write(const std::string &s, const iMatrix &output); template - typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type + typename std::enable_if, ETensor>::value && is_eigen_tensor_scalar::value, void>::type write(const std::string &s, const ETensor &output); - template - typename std::enable_if, ETensor>::value - && is_grid_tensor::value - //&& !(std::is_arithmetic::value || Grid::is_complex::value) - /*&& ( std::is_base_of >::value - || std::is_base_of>::value - || std::is_base_of>::value )*/, void>::type + template + typename std::enable_if, ETensor>::value && is_grid_tensor::value, void>::type write(const std::string &s, const ETensor &output); - /*template - typename std::enable_if, ETensor>::value - && std::is_base_of>::value, void>::type - write(const std::string &s, const ETensor &output); - template - typename std::enable_if, ETensor>::value - && std::is_base_of>::value, void>::type - write(const std::string &s, const ETensor &output);*/ void scientificFormat(const bool set); bool isScientific(void); @@ -254,18 +252,18 @@ namespace Grid { // Eigen::Tensors of arithmetic/complex base type template template - typename std::enable_if, ETensor>::value && (std::is_arithmetic::value || Grid::is_complex::value), void>::type + typename std::enable_if, ETensor>::value && is_eigen_tensor_scalar::value, void>::type Writer::write(const std::string &s, const ETensor &output) { - std::cout << "Eigen::Tensors of arithmetic/complex base type" << std::endl; const typename ETensor::Index NumElements{output.size()}; assert( NumElements > 0 ); if( NumElements == 1 ) upcast->writeDefault(s, * output.data()); else { - // We're not interested in trivial dimensions, i.e. dimensions = 1 + // We're only interested in non-trivial dimensions (i.e. dimensions > 1) unsigned int TrivialDimCount{0}; std::vector ReducedDims; + ReducedDims.reserve(output.NumDimensions); // Make sure we only do one malloc for(auto i = 0; i < output.NumDimensions; i++ ) { auto dim = output.dimension(i); if( dim <= 1 ) { @@ -277,43 +275,48 @@ namespace Grid { ReducedDims.push_back(s); } } - assert( output.NumDimensions > TrivialDimCount > 0 ); // NB: NumElements > 1 implies this is not a scalar, so some dims should be left - // Create a single, flat vector to hold all the data - std::vector flat(NumElements); - // Now copy all the data to my flat vector - // Regardless of the Eigen::Tensor storage order, the copy will be Row Major - std::array MyIndex; - for( int i = 0 ; i < output.NumDimensions ; i++ ) MyIndex[i] = 0; - for( typename ETensor::Index n = 0; n < NumElements; n++ ) { - flat[n] = output( MyIndex ); - // Now increment the index - for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) - MyIndex[i] = 0; + // NB: NumElements > 1 implies this is not a scalar, so some dims should be left + assert( output.NumDimensions > TrivialDimCount ); + // If the Tensor isn't in Row-Major order, then we'll need to copy it's data + const bool CopyData{ETensor::Layout != Eigen::StorageOptions::RowMajor}; + using Scalar = typename ETensor::Scalar; + const Scalar * pWriteBuffer; + Scalar * pCopyBuffer = nullptr; + if( !CopyData ) + pWriteBuffer = output.data(); + else { + // Regardless of the Eigen::Tensor storage order, the copy will be Row Major + pCopyBuffer = static_cast(malloc(sizeof(Scalar) * NumElements)); + pWriteBuffer = pCopyBuffer; + std::array MyIndex; + for( auto &idx : MyIndex ) idx = 0; + for( typename ETensor::Index n = 0; n < NumElements; n++ ) { + pCopyBuffer[n] = output( MyIndex ); + // Now increment the index + for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) + MyIndex[i] = 0; + } } - upcast->template writeMultiDim(s, ReducedDims, flat); + upcast->template writeMultiDim(s, ReducedDims, pWriteBuffer, NumElements); + if( pCopyBuffer ) free( pCopyBuffer ); } } - // Eigen::Tensors of iScalar + // Eigen::Tensors of Grid tensors (iScalar, iVector, iMatrix) template template - typename std::enable_if, ETensor>::value - && is_grid_tensor::value - //&& !(std::is_arithmetic::value || Grid::is_complex::value) - /*&& ( std::is_base_of >::value - || std::is_base_of>::value - || std::is_base_of>::value )*/, void>::type + typename std::enable_if, ETensor>::value && is_grid_tensor::value, void>::type Writer::write(const std::string &s, const ETensor &output) { - std::cout << "Eigen::Tensors of iScalar" << std::endl; const typename ETensor::Index NumElements{output.size()}; assert( NumElements > 0 ); if( NumElements == 1 ) upcast->writeDefault(s, tensorToVec(* output.data())); else { - // We're not interested in trivial dimensions, i.e. dimensions = 1 + // We're only interested in non-trivial dimensions (i.e. dimensions > 1) unsigned int TrivialDimCount{0}; std::vector ReducedDims; + ReducedDims.reserve(output.NumDimensions + grid_tensor_att::rank_non_trivial); // Make sure we only do one malloc for(auto i = 0; i < output.NumDimensions; i++ ) { auto dim = output.dimension(i); if( dim <= 1 ) { @@ -325,60 +328,50 @@ namespace Grid { ReducedDims.push_back(s); } } - assert( output.NumDimensions > TrivialDimCount > 0 ); // NB: NumElements > 1 implies this is not a scalar, so some dims should be left + // NB: NumElements > 1 implies this is not a scalar, so some dims should be left + assert( output.NumDimensions > TrivialDimCount ); // Now add the extra dimensions, based on object zero typename TensorToVec::type ttv = tensorToVec(* output.data()); Flatten::type> f(ttv); const std::vector & ExtraDims{f.getDim()}; + assert(ExtraDims.size() == grid_tensor_att::rank_non_trivial); size_t ExtraCount{1}; for( auto i : ExtraDims ) { assert( i > 0 ); ExtraCount *= i; ReducedDims.push_back(i); } - typedef typename ETensor::Scalar::scalar_type Scalar; - assert( sizeof( typename ETensor::Scalar ) == ExtraCount * sizeof( Scalar ) ); - // Create a single, flat vector to hold all the data + assert(grid_tensor_att::count == ExtraCount); + assert(grid_tensor_att::size == sizeof( typename ETensor::Scalar )); + // If the Tensor isn't in Row-Major order, then we'll need to copy it's data + const bool CopyData{ETensor::Layout != Eigen::StorageOptions::RowMajor}; + using Scalar = typename ETensor::Scalar::scalar_type; + const Scalar * pWriteBuffer; + Scalar * pCopyBuffer = nullptr; const typename ETensor::Index TotalNumElements = NumElements * ExtraCount; - std::vector flat(TotalNumElements); - // Now copy all the data to my flat vector - // Regardless of the Eigen::Tensor storage order, the copy will be Row Major - std::array MyIndex; - for( int i = 0 ; i < output.NumDimensions ; i++ ) MyIndex[i] = 0; - for( typename ETensor::Index n = 0; n < TotalNumElements; ) { - const Scalar * p = reinterpret_cast( &output( MyIndex )); - for( auto j = 0; j < ExtraCount ; j++ ) - flat[n++] = * p++; - // Now increment the index - for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) - MyIndex[i] = 0; + if( !CopyData ) + pWriteBuffer = output.data()->begin(); + else { + // Regardless of the Eigen::Tensor storage order, the copy will be Row Major + pCopyBuffer = static_cast(malloc(TotalNumElements * sizeof(Scalar))); + pWriteBuffer = pCopyBuffer; + Scalar * pCopy = pCopyBuffer; + std::array MyIndex; + for( auto &idx : MyIndex ) idx = 0; + for( typename ETensor::Index n = 0; n < NumElements; n++ ) { + // Copy the grid tensor + for( const Scalar &Source : output( MyIndex ) ) + * pCopy ++ = Source; + // Now increment the index + for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) + MyIndex[i] = 0; + } } - upcast->template writeMultiDim(s, ReducedDims, flat); + upcast->template writeMultiDim(s, ReducedDims, pWriteBuffer, TotalNumElements); + if( pCopyBuffer ) free( pCopyBuffer ); } } - // Eigen::Tensors of iVector - /*template - template - typename std::enable_if, ETensor>::value - && std::is_base_of>::value, void>::type - Writer::write(const std::string &s, const ETensor &output) - { - //upcast->writeDefault(s, tensorToVec(output)); - std::cout << "I really should add code to write Eigen::Tensor (iVector) ..." << std::endl; - } - - // Eigen::Tensors of iMatrix - template - template - typename std::enable_if, ETensor>::value - && std::is_base_of>::value, void>::type - Writer::write(const std::string &s, const ETensor &output) - { - //upcast->writeDefault(s, tensorToVec(output)); - std::cout << "I really should add code to write Eigen::Tensor (iMatrix) ..." << std::endl; - }*/ - template void Writer::scientificFormat(const bool set) { @@ -516,7 +509,7 @@ namespace Grid { template static inline typename std::enable_if, T>::value, bool>::type CompareMember(const T &lhs, const T &rhs) { - Eigen::Tensor bResult = (lhs == rhs).all(); + Eigen::Tensor bResult = (lhs == rhs).all(); return bResult(0); } @@ -526,7 +519,7 @@ namespace Grid { const auto NumElements{lhs.size()}; bool bResult = ( NumElements == rhs.size() ); for( auto i = 0 ; i < NumElements && bResult ; i++ ) { - Eigen::Tensor b = (lhs[i] == rhs[i]).all(); + Eigen::Tensor b = (lhs[i] == rhs[i]).all(); bResult = b(0); } return bResult; diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index c3f111be..c486801a 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -39,7 +39,7 @@ namespace Grid typename std::enable_if>::is_number, void>::type writeDefault(const std::string &s, const std::vector &x); template - void writeMultiDim(const std::string &s, const std::vector & Dimensions, const std::vector & DataRowMajor); + void writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements); H5NS::Group & getGroup(void); private: template @@ -104,31 +104,32 @@ namespace Grid void Hdf5Writer::writeDefault(const std::string &s, const std::string &x); template - void Hdf5Writer::writeMultiDim(const std::string &s, const std::vector & Dimensions, const std::vector & DataRowMajor) + void Hdf5Writer::writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements) { // Hdf5 needs the dimensions as hsize_t - std::vector dim; - for (auto &d: Dimensions) - dim.push_back(d); + int rank = static_cast(Dimensions.size()); + std::vector dim(rank); + for(int i = 0; i < rank; i++) + dim[i] = Dimensions[i]; // write to file - H5NS::DataSpace dataSpace(dim.size(), dim.data()); - - if (DataRowMajor.size() > dataSetThres_) + H5NS::DataSpace dataSpace(rank, dim.data()); + + size_t DataSize = NumElements * sizeof(U); + if (DataSize > dataSetThres_) { H5NS::DataSet dataSet; H5NS::DSetCreatPropList plist; - plist.setChunk(dim.size(), dim.data()); + plist.setChunk(rank, dim.data()); plist.setFletcher32(); dataSet = group_.createDataSet(s, Hdf5Type::type(), dataSpace, plist); - dataSet.write(&DataRowMajor[0], Hdf5Type::type()); + dataSet.write(pDataRowMajor, Hdf5Type::type()); } else { H5NS::Attribute attribute; - attribute = group_.createAttribute(s, Hdf5Type::type(), dataSpace); - attribute.write(Hdf5Type::type(), &DataRowMajor[0]); + attribute.write(Hdf5Type::type(), pDataRowMajor); } } @@ -145,7 +146,7 @@ namespace Grid const auto &flatx = flat.getFlatVector(); for (auto &d: flat.getDim()) dim.push_back(d); - writeMultiDim(s, dim, flatx); + writeMultiDim(s, dim, &flatx[0], flatx.size()); } template diff --git a/Grid/tensors/Tensor_class.h b/Grid/tensors/Tensor_class.h index c7f868db..d9af736b 100644 --- a/Grid/tensors/Tensor_class.h +++ b/Grid/tensors/Tensor_class.h @@ -173,7 +173,37 @@ class iScalar { return stream; }; + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline begin() const { return &_internal; } + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline begin() const { return _internal.begin(); } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return (&_internal) + grid_tensor_att>::count; } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal.begin() + grid_tensor_att>::count; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline begin() { return &_internal; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline begin() { return _internal.begin(); } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return (&_internal) + grid_tensor_att>::count; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal.begin() + grid_tensor_att>::count; } }; /////////////////////////////////////////////////////////// // Allows to turn scalar>>> back to double. @@ -303,6 +333,38 @@ class iVector { // strong_inline vtype && operator ()(int i) { // return _internal[i]; // } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline begin() const { return _internal; } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline begin() const { return _internal[0].begin(); } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal + grid_tensor_att>::count; } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal[0].begin() + grid_tensor_att>::count; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline begin() { return _internal; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline begin() { return _internal[0].begin(); } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal + grid_tensor_att>::count; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal[0].begin() + grid_tensor_att>::count; } }; template @@ -458,6 +520,38 @@ class iMatrix { // strong_inline vtype && operator ()(int i,int j) { // return _internal[i][j]; // } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline begin() const { return _internal[0]; } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline begin() const { return _internal[0][0].begin(); } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal[0] + grid_tensor_att>::count; } + + template + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal[0][0].begin() + grid_tensor_att>::count; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline begin() { return _internal[0]; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline begin() { return _internal[0][0].begin(); } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal[0] + grid_tensor_att>::count; } + + template + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal[0][0].begin() + grid_tensor_att>::count; } }; template diff --git a/Grid/tensors/Tensor_traits.h b/Grid/tensors/Tensor_traits.h index c1ef397a..3abf16d0 100644 --- a/Grid/tensors/Tensor_traits.h +++ b/Grid/tensors/Tensor_traits.h @@ -288,6 +288,79 @@ namespace Grid { enum { value = sizeof(real_scalar_type)/sizeof(float) }; }; + + //////////////////////////////////////////////////////////////////////////// + // Review with Peter - obvious (but partial) overlap with the above + // What of this is good and should be kept ... vs what functions should I really be using? + //////////////////////////////////////////////////////////////////////////// + + // Need some forward references or the below won't work + template + class iScalar; + template + class iVector; + template + class iMatrix; + + // which types are grid tensors + template struct is_grid_tensor : public std::false_type {}; + template struct is_grid_tensor> : public std::true_type {}; + template struct is_grid_tensor> : public std::true_type {}; + template struct is_grid_tensor> : public std::true_type {}; + + // Rank and dimension of grid tensors, i.e. compositions of iScalar, iVector and iMatrix + // This defines the bottom level - i.e. it's a description of the underlying scalar + template struct grid_tensor_att { + static constexpr unsigned int depth = 0; // How many levels of Grid Tensor there are (TensorLevel) + static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) + static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 + static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) + using scalar_type = T; // Type of the underlying scalar + static constexpr std::size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes + static constexpr std::size_t size = scalar_size * count; // total size of elements in bytes + // e.g. iScalar> + // depth = 2 + // rank = 1 + // rank_non_trivial = 0 + // count = 1 + // e.g. iVector,4> + // depth = 2 + // rank = 3 + // rank_non_trivial = 3 + // count = 36 + // e.g. iScalar,3>> + // depth = 3 + // rank = 3 + // rank_non_trivial = 3 + // count = 48 + }; + template struct grid_tensor_att> { + static constexpr unsigned int depth = 1 + grid_tensor_att::depth; + static constexpr unsigned int rank = 0 + grid_tensor_att::rank; + static constexpr unsigned int rank_non_trivial = 0 + grid_tensor_att::rank_non_trivial; + static constexpr unsigned int count = 1 * grid_tensor_att::count; + using scalar_type = typename grid_tensor_att::scalar_type; + static constexpr std::size_t scalar_size = grid_tensor_att::scalar_size; + static constexpr std::size_t size = scalar_size * count; + }; + template struct grid_tensor_att> { + static constexpr unsigned int depth = 1 + grid_tensor_att::depth; + static constexpr unsigned int rank = 1 + grid_tensor_att::rank; + static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + grid_tensor_att::rank_non_trivial; + static constexpr unsigned int count = N * grid_tensor_att::count; + using scalar_type = typename grid_tensor_att::scalar_type; + static constexpr std::size_t scalar_size = grid_tensor_att::scalar_size; + static constexpr std::size_t size = scalar_size * count; + }; + template struct grid_tensor_att> { + static constexpr unsigned int depth = 1 + grid_tensor_att::depth; + static constexpr unsigned int rank = 2 + grid_tensor_att::rank; + static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + grid_tensor_att::rank_non_trivial; + static constexpr unsigned int count = N * N * grid_tensor_att::count; + using scalar_type = typename grid_tensor_att::scalar_type; + static constexpr std::size_t scalar_size = grid_tensor_att::scalar_size; + static constexpr std::size_t size = scalar_size * count; + }; } #endif diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 445abe6d..dc7f83c7 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -103,8 +103,9 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam #ifdef DEBUG //typedef int TestScalar; typedef std::complex TestScalar; -typedef Eigen::Tensor TestTensor; -typedef Eigen::TensorFixedSize> TestTensorFixed; +typedef Eigen::Tensor, 6> TestTensorSingle; +typedef Eigen::Tensor TestTensor; +typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TestTensorFixed; typedef std::vector aTestTensorFixed; typedef Eigen::TensorFixedSize> LSCTensor; typedef Eigen::TensorFixedSize> LCMTensor; @@ -124,15 +125,18 @@ public: }; bool EigenIOTest(void) { + constexpr TestScalar Inc{1,-1}; + TestTensorSingle ts(1,1,1,1,1,1); + ts(0,0,0,0,0,0) = Inc * 3.1415927; + ioTest("iotest_single.h5", ts, "Singlet"); + SpinColourVector scv, scv2; scv2 = scv; ioTest("iotest_vector.h5", scv, "SpinColourVector"); SpinColourMatrix scm; ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); - constexpr TestScalar Inc{1,-1}; - - TestTensor t(3,6,2); + TestTensor t(6,3,2); TestScalar Val{Inc}; for( int i = 0 ; i < t.dimension(0) ; i++) for( int j = 0 ; j < t.dimension(1) ; j++) @@ -141,7 +145,7 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); - + // Now serialise a fixed size tensor using FixedTensor = Eigen::TensorFixedSize>; FixedTensor tf; @@ -170,7 +174,15 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_LSCTensor.h5", l, "LSCTensor_object_instance_name"); - + std::cout << "t:"; + for_all( l, [](std::complex &c, LSCTensor::Index index, const std::size_t * pDims ){ + std::cout << " "; + for( int i = 0 ; i < 5; i++ ) + std::cout << "[" << pDims[i] << "]"; + std::cout << " = " << c << std::endl; + } ); + std::cout << std::endl; + // Tensor of spin colour LCMTensor l2; Val = 0; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index c2cfc271..81299d0d 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -477,6 +477,23 @@ void DebugGridTensorTest_print( int i ) << std::endl; } +// begin() and end() are the minimum necessary to support range-for loops +// should really turn this into an iterator ... +template +class TestObject { +public: + using value_type = T; +private: + value_type * m_p; +public: + TestObject() { + m_p = reinterpret_cast(std::malloc(N * sizeof(value_type))); + } + ~TestObject() { std::free(m_p); } + inline value_type * begin(void) { return m_p; } + inline value_type * end(void) { return m_p + N; } +}; + bool DebugGridTensorTest( void ) { typedef Complex t1; @@ -485,7 +502,8 @@ bool DebugGridTensorTest( void ) typedef iMatrix t4; typedef iVector,4> t5; typedef iScalar t6; - typedef iMatrix>,2>,7> t7; + typedef iMatrix t7; + typedef iMatrix,4>,2> t8; int i = 1; DebugGridTensorTest_print( i++ ); DebugGridTensorTest_print( i++ ); @@ -494,6 +512,27 @@ bool DebugGridTensorTest( void ) DebugGridTensorTest_print( i++ ); DebugGridTensorTest_print( i++ ); DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + + //using TOC7 = TestObject, 7>; + using TOC7 = t7; + TOC7 toc7; + constexpr std::complex Inc{1,-1}; + std::complex Start{Inc}; + for( auto &x : toc7 ) { + x = Start; + Start += Inc; + } + i = 0; + for( auto x : toc7 ) std::cout << "toc7[" << i++ << "] = " << x << std::endl; + + t2 o2; + auto a2 = TensorRemove(o2); + //t3 o3; + //t4 o4; + //auto a3 = TensorRemove(o3); + //auto a4 = TensorRemove(o4); + return true; } #endif @@ -502,8 +541,12 @@ int main(int argc, char *argv[]) { #ifdef DEBUG // Debug only - test of Eigen::Tensor - std::cout << "sizeof(std::streamsize) = " << sizeof(std::streamsize) << std::endl; - std::cout << "sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; + std::cout << "sizeof(int) = " << sizeof(int) + << ", sizeof(long) = " << sizeof(long) + << ", sizeof(size_t) = " << sizeof(size_t) + << ", sizeof(std::size_t) = " << sizeof(std::size_t) + << ", sizeof(std::streamsize) = " << sizeof(std::streamsize) + << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; //if( DebugEigenTest() ) return 0; if(DebugGridTensorTest()) return 0; #endif From 59c8cc1588d51f4f61a74f3629ba737039166a6b Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 13 Feb 2019 22:11:24 +0000 Subject: [PATCH 087/347] Minor bugfix --- Grid/serialisation/BaseIO.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 90897188..60d13f01 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -102,16 +102,17 @@ namespace Grid { for( auto &idx : MyIndex ) idx = 0; std::vector SubIndex(ReducedDimsSize); for( auto &idx : SubIndex ) idx = 0; - for( typename ETensor::Index n = 0; n < TotalNumElements; ) { + typename ETensor::Index n = 0; + for( std::size_t j = 0; j < NumElements; j++ ) { for( Scalar &Source : ET( MyIndex ) ) { lambda(Source, n++, &SubIndex[0] ); - // Now increment MyIndex - for( auto i = ET.NumDimensions - 1; i >= 0 && ++MyIndex[i] == ET.dimension(i); i-- ) - MyIndex[i] = 0; // Now increment SubIndex - for( auto i = ReducedDimsSize - 1; i >= 0 && ++SubIndex[i] == ReducedDims[i]; i-- ) + for( long i = ReducedDimsSize - 1; i >= 0 && ++SubIndex[i] == ReducedDims[i]; i-- ) SubIndex[i] = 0; } + // Now increment MyIndex + for( long i = ET.NumDimensions - 1; i >= 0 && ++MyIndex[i] == ET.dimension(i); i-- ) + MyIndex[i] = 0; } } } From 886c895f81f4ebacf7317289a4cb0a43ff2d3f39 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 14 Feb 2019 16:44:54 +0000 Subject: [PATCH 088/347] baryon field structure is now eigentensor - started on contractions for 2pt functions --- Hadrons/Modules/MDistil/BContraction.hpp | 89 +++++++++--------------- 1 file changed, 32 insertions(+), 57 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 4401e8cd..71f6474c 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -18,6 +18,20 @@ BEGIN_HADRONS_NAMESPACE ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) + + + // general baryon tensor set based on Eigen tensors and Grid-allocated memory + // Dimensions: + // 0 - ext - external field (momentum, EM field, ...) + // 1 - str - spin-color structure + // 2 - t - timeslice + // 3 - i - left distillation mode index + // 4 - j - middle distillation mode index + // 5 - k - left distillation mode index + // template + // using BaryonTensorSet = Eigen::TensorMap>; + + class BContractionPar: Serializable { public: @@ -141,6 +155,7 @@ void TBContraction::execute(void) SpinVector tmp222; SpinVector tmp111; + assert(parity == 1 || parity == -1); std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; @@ -159,61 +174,11 @@ void TBContraction::execute(void) Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 }; std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; - if((0)){ - for (int i1=0 ; i1 < N_1 ; i1++){ - for (int i2=0 ; i2 < N_2 ; i2++){ - for (int i3=0 ; i3 < N_3 ; i3++){ - for (int imom=0 ; imom < Nmom ; imom++){ - for (int t=0 ; t < Nt ; t++){ - Bindex = i1 + N_1*(i2 + N_2*(i3 + N_3*(imom+Nmom*t))); - ExtractSliceLocal(tmp1,one[i1],0,t,3); - ExtractSliceLocal(tmp2,two[i2],0,t,3); - ExtractSliceLocal(tmp3,three[i3],0,t,3); - parallel_for (unsigned int sU = 0; sU < grid3d->oSites(); ++sU) - { - for (int ie=0 ; ie < 6 ; ie++){ - // Why does peekColour not work???? - for (int is=0 ; is < 4 ; is++){ - tmp11s()(is)() = tmp11[sU]()(is)(epsilon[ie][0]); - tmp22s()(is)() = tmp22[sU]()(is)(epsilon[ie][1]); - tmp33s()(is)() = tmp33[sU]()(is)(epsilon[ie][2]); - } - tmp333 = Gamma(gamma23_[0])*tmp33s; - // this should be outerProduct??? Does not work. - for (int isl=0 ; isl < 4 ; isl++){ - for (int isr=0 ; isr < 4 ; isr++){ - diquark()(isl,isr)() = factor23[0]*tmp22s()(isl)()*tmp333()(isr)(); - } - } - // Is there a way to compute gamma * SpinMatrix (left component)??? - for (int isr=0 ; isr < 4 ; isr++){ - for (int isl=0 ; isl < 4 ; isl++){ - tmp222()(isl)() = diquark()(isl,isr)(); - } - tmp111 = Gamma(gamma12_[0]) * tmp222; - for (int isl=0 ; isl < 4 ; isl++){ - g_diquark()(isl,isr)() = tmp111()(isl)(); - } - } - for (int is=0 ; is < 4 ; is++){ - BField[Bindex]+=(double)epsilon_sgn[ie]*tmp11s()(is)()*g_diquark()(is,is)(); - } - } - } - } - } - } - } - } + //BaryonTensorSet BField3(storage,Nmom,4,Nt,N_1,N_2,N_3); + using BaryonTensorSet = Eigen::Tensor; + BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); - for (int t=0 ; t < Nt ; t++){ - Bindex = 0 + N_1*(0 + N_2*(0 + N_3*(0+Nmom*t))); - std::cout << "BaryonField(t=" << t << ") = " << BField[Bindex] << std::endl; - } - } // end if 0 - - // ONLY THIS IS CORRECT? - std::vector BField2(Nmom*Nt*N_1*N_2*N_3); + std::vector BField2(Nmom*Nt*N_1*N_2*N_3); Complex diquark2; for (int i1=0 ; i1 < N_1 ; i1++){ for (int i2=0 ; i2 < N_2 ; i2++){ @@ -238,7 +203,10 @@ void TBContraction::execute(void) tmp222 = g4*tmp111; tmp111 = 0.5*(double)parity*(tmp111 + tmp222); // P_\pm * ... diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); - BField2[Bindex]+=(double)epsilon_sgn[ie]*tmp111*diquark2; + //BField2[Bindex]+=(double)epsilon_sgn[ie]*tmp111*diquark2; + for (int is=0 ; is < 4 ; is++){ + BField3(imom,is,t,i1,i2,i3)+=(double)epsilon_sgn[ie]*tmp111()(is)()*diquark2; + } } } } @@ -248,10 +216,17 @@ void TBContraction::execute(void) } for (int is=0 ; is < 4 ; is++){ for (int t=0 ; t < Nt ; t++){ - Bindex = 0 + N_1*(0 + N_2*(0 + N_3*(0+Nmom*t))); - std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField2[Bindex]()(is)() << std::endl; + // Bindex = 0 + N_1*(0 + N_2*(0 + N_3*(0+Nmom*t))); + // std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField2[Bindex]()(is)() << std::endl; + std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField3(0,is,t,0,0,0) << std::endl; } } + + //Product ijk * ijk + // for ijk * jik: (4,5),(5,4),(6,6) z.b. + Eigen::array, 3> product_dims = { Eigen::IndexPair(4, 4),Eigen::IndexPair(5, 5) ,Eigen::IndexPair(6, 6) }; + // Whycan't I choose the dimension to be 3??? Want to sum over them, not save each element! + Eigen::Tensor C2 = BField3.contract(BField3,product_dims); } END_MODULE_NAMESPACE From bee24655cd458f3810a44eaef76f5c81644bbf10 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 14 Feb 2019 19:05:35 +0000 Subject: [PATCH 089/347] Finalising traits --- Grid/serialisation/BaseIO.h | 267 +++++++++++++++++++-------- Grid/tensors/Tensor_class.h | 72 ++++---- Grid/tensors/Tensor_traits.h | 73 -------- tests/IO/Test_serialisation.cc | 30 ++- tests/hadrons/Test_hadrons_distil.cc | 30 ++- 5 files changed, 267 insertions(+), 205 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 60d13f01..535ab035 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -36,87 +36,194 @@ Author: Guido Cossu #include namespace Grid { - // Abstract writer/reader classes //////////////////////////////////////////// - // static polymorphism implemented using CRTP idiom - class Serializable; + namespace EigenIO { + template struct is_complex : public std::false_type {}; + template struct is_complex> + : std::integral_constant::value> {}; - // which types are supported scalar types for Eigen::Tensor - template struct is_eigen_tensor_scalar : std::integral_constant::value || Grid::is_complex::value> {}; + // Eigen tensors can be composed of arithmetic scalar and complex types + template struct is_scalar : std::integral_constant::value || is_complex::value> {}; + + // Eigen tensors can also be composed of a limited number of containers + // NB: grid tensors (iScalar, iVector, iMatrix) have stricter limits on complex types + template struct is_container : public std::false_type {}; + template struct is_container> : public std::true_type {}; + template struct is_container> : public std::true_type {}; + template struct is_container> : public std::true_type {}; + template struct is_container> : public std::true_type {}; + + // Is this an Eigen tensor + template struct is_tensor : std::integral_constant, T>::value> {}; + + // Is this an Eigen tensor of a supported scalar + template struct is_tensor_of_scalar : std::integral_constant::value && is_scalar::value> {}; + + // Is this an Eigen tensor of a supported container + template struct is_tensor_of_container : std::integral_constant::value && is_container::value> {}; + + // These traits describe the Eigen tensor scalar objects supported for IO + // Mostly intended for grid tensors, i.e. compositions of iScalar, iVector and iMatrix + // but also supports fixed size arrays + template struct Traits {}; // C needed for specialisation + // This defines the bottom level - i.e. it's a description of the underlying scalar + template struct Traits::value, void>::type> { + static constexpr unsigned int depth = 0; // How many levels of Grid Tensor there are (TensorLevel) + static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) + static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 + static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) + using scalar_type = T; // Type of the underlying scalar + static constexpr std::size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes + static constexpr std::size_t size = scalar_size * count; // total size of elements in bytes + static constexpr std::size_t Dimension(unsigned int dim) { return 0; } // Dimension size + static constexpr std::size_t DimensionNT(unsigned int dim) { return 0; } // non-trivial dim size + // e.g. iScalar> + // depth = 2 + // rank = 1 + // rank_non_trivial = 0 + // count = 1 + // e.g. iVector,4> + // depth = 2 + // rank = 3 + // rank_non_trivial = 3 + // count = 36 + // e.g. iScalar,3>> + // depth = 3 + // rank = 3 + // rank_non_trivial = 3 + // count = 48 + }; + template struct Traits> { + static constexpr unsigned int depth = 1 + Traits::depth; + static constexpr unsigned int rank = 0 + Traits::rank; + static constexpr unsigned int rank_non_trivial = 0 + Traits::rank_non_trivial; + static constexpr unsigned int count = 1 * Traits::count; + using scalar_type = typename Traits::scalar_type; + static constexpr std::size_t scalar_size = Traits::scalar_size; + static constexpr std::size_t size = scalar_size * count; + static constexpr std::size_t Dimension(unsigned int dim) { + return ( dim == 0 ) ? 1 : Traits::Dimension(dim - 1); } + static constexpr std::size_t DimensionNT(unsigned int dim) { + return Traits::DimensionNT(dim); } + }; + template struct Traits> { + static constexpr unsigned int depth = 1 + Traits::depth; + static constexpr unsigned int rank = 1 + Traits::rank; + static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + Traits::rank_non_trivial; + static constexpr unsigned int count = N * Traits::count; + using scalar_type = typename Traits::scalar_type; + static constexpr std::size_t scalar_size = Traits::scalar_size; + static constexpr std::size_t size = scalar_size * count; + static constexpr std::size_t Dimension(unsigned int dim) { + return ( dim == 0 ) ? N : Traits::Dimension(dim - 1); } + static constexpr std::size_t DimensionNT(unsigned int dim) { + if( N != 1 ) { + if( dim == 0 ) return N; + dim--; + } + return Traits::DimensionNT(dim); + } + }; + template struct Traits> { + static constexpr unsigned int depth = 1 + Traits::depth; + static constexpr unsigned int rank = 2 + Traits::rank; + static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + Traits::rank_non_trivial; + static constexpr unsigned int count = N * N * Traits::count; + using scalar_type = typename Traits::scalar_type; + static constexpr std::size_t scalar_size = Traits::scalar_size; + static constexpr std::size_t size = scalar_size * count; + static constexpr std::size_t Dimension(unsigned int dim) { + return ( dim == 0 || dim == 1 ) ? N : Traits::Dimension(dim - 2); } + static constexpr std::size_t DimensionNT(unsigned int dim) { + if( N != 1 ) { + if( dim == 0 || dim == 1 ) return N; + dim -= 2; + } + return Traits::DimensionNT(dim); + } + }; + template struct Traits> : Traits> {}; + // Tensors have the same traits as their top-level scalar + // Shouldn't be necessary ... but I make the mistake of getting traits of the tensor so often + // that I am tempted to define this. + // HOWEVER, Eigen tensors have a dynamic size flavour, but the scalars are (currently) all fixed size + //template struct Traits::value, void>::type> : Traits {}; + } // Helper to allow iteration through an Eigen::Tensor (using a lambda) template - typename std::enable_if, ETensor>::value && is_grid_tensor::value, void>::type + typename std::enable_if::value, void>::type for_all( ETensor &ET, Lambda lambda ) { - using Scalar = typename ETensor::Scalar::scalar_type; - const std::size_t NumElements = ET.size(); - assert( NumElements > 0 ); - if( NumElements == 1 ) { - const auto MyRank{grid_tensor_att::rank_non_trivial}; - std::vector SubIndex(MyRank); - for( auto &idx : SubIndex ) idx = 0; - typename ETensor::Index n = 0; - for( Scalar &Source : * ET.data() ) { - lambda(Source, n++, &SubIndex[0] ); + using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later + const std::size_t NumScalars = ET.size(); + assert( NumScalars > 0 ); + // Assemble a vector containing all the non-trivial dimensions (i.e. dimensions > 1) + unsigned int ScalarElementCount{1}; + std::vector NonTrivialDims; + NonTrivialDims.reserve(ET.NumDimensions + EigenIO::Traits::rank_non_trivial); + for(auto i = 0; i < ET.NumDimensions; i++ ) { + auto dim = ET.dimension(i); + if( dim <= 1 ) { + assert( dim == 1 ); // Not expecting dimension to be <= 0 + } else { + size_t s = static_cast(dim); + assert( s == dim ); // check we didn't lose anything in the conversion + NonTrivialDims.push_back(s); + ScalarElementCount *= s; + } + } + // Check that the number of containers is correct + assert( NumScalars == ScalarElementCount ); + // If the Scalar is actually a container, add the inner Scalar's non-trivial dimensions + size_t InnerScalarCount{1}; + for(auto i = 0; i < EigenIO::Traits::rank_non_trivial; i++ ) { + auto dim = EigenIO::Traits::DimensionNT(i); + assert( dim > 1 ); + NonTrivialDims.push_back(dim); + InnerScalarCount *= dim; + } + assert(EigenIO::Traits::count == InnerScalarCount); + assert(EigenIO::Traits::size == sizeof( Scalar )); + const unsigned int NonTrivialDimsSize = static_cast(NonTrivialDims.size()); + assert( NonTrivialDimsSize == NonTrivialDims.size() ); + //const typename ETensor::Index TotalNumElements = NumScalars * InnerScalarCount; + using Index = typename ETensor::Index; + std::array MyIndex; + for( auto &idx : MyIndex ) idx = 0; + std::vector SubIndex(NonTrivialDimsSize); + for( auto &idx : SubIndex ) idx = 0; + Index n = 0; + for( std::size_t j = 0; j < NumScalars; j++ ) { + // if constexpr is C++ 17 ... but otherwise need two specialisations (Container vs Scalar) + if constexpr ( EigenIO::Traits::rank_non_trivial == 0 ) { + lambda(ET( MyIndex ), n++, &SubIndex[0] ); // Now increment SubIndex - for( auto i = MyRank - 1; i >= 0 && ++SubIndex[i] == 11/*ReducedDims[i]*/; i-- ) + for( auto i = NonTrivialDimsSize - 1; i != -1 && ++SubIndex[i] == NonTrivialDims[i]; i-- ) SubIndex[i] = 0; } - } - else { - // We're only interested in non-trivial dimensions (i.e. dimensions > 1) - unsigned int TrivialDimCount{0}; - std::vector ReducedDims; - ReducedDims.reserve(ET.NumDimensions + grid_tensor_att::rank_non_trivial); // Make sure we only do one malloc - for(auto i = 0; i < ET.NumDimensions; i++ ) { - auto dim = ET.dimension(i); - if( dim <= 1 ) { - TrivialDimCount++; - assert( dim == 1 ); // Not expecting dimension to be <= 0 - } else { - size_t s = static_cast(dim); - assert( s == dim ); // check we didn't lose anything in the conversion - ReducedDims.push_back(s); - } - } - // NB: NumElements > 1 implies this is not a scalar, so some dims should be left - assert( ET.NumDimensions > TrivialDimCount ); - // Now add the extra dimensions, based on object zero - typename TensorToVec::type ttv = tensorToVec(* ET.data()); - Flatten::type> f(ttv); - const std::vector & ExtraDims{f.getDim()}; - assert(ExtraDims.size() == grid_tensor_att::rank_non_trivial); - size_t ExtraCount{1}; - for( auto i : ExtraDims ) { - assert( i > 0 ); - ExtraCount *= i; - ReducedDims.push_back(i); - } - assert(grid_tensor_att::count == ExtraCount); - assert(grid_tensor_att::size == sizeof( typename ETensor::Scalar )); - const unsigned int ReducedDimsSize = static_cast(ReducedDims.size()); - assert( ReducedDimsSize == ReducedDims.size() ); - const typename ETensor::Index TotalNumElements = NumElements * ExtraCount; - std::array MyIndex; - for( auto &idx : MyIndex ) idx = 0; - std::vector SubIndex(ReducedDimsSize); - for( auto &idx : SubIndex ) idx = 0; - typename ETensor::Index n = 0; - for( std::size_t j = 0; j < NumElements; j++ ) { - for( Scalar &Source : ET( MyIndex ) ) { + else + { + for( typename Scalar::scalar_type &Source : ET( MyIndex ) ) { lambda(Source, n++, &SubIndex[0] ); // Now increment SubIndex - for( long i = ReducedDimsSize - 1; i >= 0 && ++SubIndex[i] == ReducedDims[i]; i-- ) + for( auto i = NonTrivialDimsSize - 1; i != -1 && ++SubIndex[i] == NonTrivialDims[i]; i-- ) SubIndex[i] = 0; } - // Now increment MyIndex - for( long i = ET.NumDimensions - 1; i >= 0 && ++MyIndex[i] == ET.dimension(i); i-- ) - MyIndex[i] = 0; } + // Now increment MyIndex + for( auto i = ET.NumDimensions - 1; i != -1 && ++MyIndex[i] == ET.dimension(i); i-- ) + MyIndex[i] = 0; } } + // Abstract writer/reader classes //////////////////////////////////////////// + // static polymorphism implemented using CRTP idiom + class Serializable; + // Static abstract writer template class Writer @@ -140,10 +247,10 @@ namespace Grid { template void write(const std::string &s, const iMatrix &output); template - typename std::enable_if, ETensor>::value && is_eigen_tensor_scalar::value, void>::type + typename std::enable_if, ETensor>::value && EigenIO::is_scalar::value, void>::type write(const std::string &s, const ETensor &output); template - typename std::enable_if, ETensor>::value && is_grid_tensor::value, void>::type + typename std::enable_if, ETensor>::value && EigenIO::is_container::value, void>::type write(const std::string &s, const ETensor &output); void scientificFormat(const bool set); @@ -253,7 +360,7 @@ namespace Grid { // Eigen::Tensors of arithmetic/complex base type template template - typename std::enable_if, ETensor>::value && is_eigen_tensor_scalar::value, void>::type + typename std::enable_if, ETensor>::value && EigenIO::is_scalar::value, void>::type Writer::write(const std::string &s, const ETensor &output) { const typename ETensor::Index NumElements{output.size()}; @@ -263,8 +370,8 @@ namespace Grid { else { // We're only interested in non-trivial dimensions (i.e. dimensions > 1) unsigned int TrivialDimCount{0}; - std::vector ReducedDims; - ReducedDims.reserve(output.NumDimensions); // Make sure we only do one malloc + std::vector NonTrivialDims; + NonTrivialDims.reserve(output.NumDimensions); // Make sure we only do one malloc for(auto i = 0; i < output.NumDimensions; i++ ) { auto dim = output.dimension(i); if( dim <= 1 ) { @@ -273,7 +380,7 @@ namespace Grid { } else { size_t s = static_cast(dim); assert( s == dim ); // check we didn't lose anything in the conversion - ReducedDims.push_back(s); + NonTrivialDims.push_back(s); } } // NB: NumElements > 1 implies this is not a scalar, so some dims should be left @@ -298,7 +405,7 @@ namespace Grid { MyIndex[i] = 0; } } - upcast->template writeMultiDim(s, ReducedDims, pWriteBuffer, NumElements); + upcast->template writeMultiDim(s, NonTrivialDims, pWriteBuffer, NumElements); if( pCopyBuffer ) free( pCopyBuffer ); } } @@ -306,7 +413,7 @@ namespace Grid { // Eigen::Tensors of Grid tensors (iScalar, iVector, iMatrix) template template - typename std::enable_if, ETensor>::value && is_grid_tensor::value, void>::type + typename std::enable_if, ETensor>::value && EigenIO::is_container::value, void>::type Writer::write(const std::string &s, const ETensor &output) { const typename ETensor::Index NumElements{output.size()}; @@ -316,8 +423,8 @@ namespace Grid { else { // We're only interested in non-trivial dimensions (i.e. dimensions > 1) unsigned int TrivialDimCount{0}; - std::vector ReducedDims; - ReducedDims.reserve(output.NumDimensions + grid_tensor_att::rank_non_trivial); // Make sure we only do one malloc + std::vector NonTrivialDims; + NonTrivialDims.reserve(output.NumDimensions + EigenIO::Traits::rank_non_trivial); // Make sure we only do one malloc for(auto i = 0; i < output.NumDimensions; i++ ) { auto dim = output.dimension(i); if( dim <= 1 ) { @@ -326,7 +433,7 @@ namespace Grid { } else { size_t s = static_cast(dim); assert( s == dim ); // check we didn't lose anything in the conversion - ReducedDims.push_back(s); + NonTrivialDims.push_back(s); } } // NB: NumElements > 1 implies this is not a scalar, so some dims should be left @@ -335,15 +442,15 @@ namespace Grid { typename TensorToVec::type ttv = tensorToVec(* output.data()); Flatten::type> f(ttv); const std::vector & ExtraDims{f.getDim()}; - assert(ExtraDims.size() == grid_tensor_att::rank_non_trivial); + assert(ExtraDims.size() == EigenIO::Traits::rank_non_trivial); size_t ExtraCount{1}; for( auto i : ExtraDims ) { assert( i > 0 ); ExtraCount *= i; - ReducedDims.push_back(i); + NonTrivialDims.push_back(i); } - assert(grid_tensor_att::count == ExtraCount); - assert(grid_tensor_att::size == sizeof( typename ETensor::Scalar )); + assert(EigenIO::Traits::count == ExtraCount); + assert(EigenIO::Traits::size == sizeof( typename ETensor::Scalar )); // If the Tensor isn't in Row-Major order, then we'll need to copy it's data const bool CopyData{ETensor::Layout != Eigen::StorageOptions::RowMajor}; using Scalar = typename ETensor::Scalar::scalar_type; @@ -368,7 +475,7 @@ namespace Grid { MyIndex[i] = 0; } } - upcast->template writeMultiDim(s, ReducedDims, pWriteBuffer, TotalNumElements); + upcast->template writeMultiDim(s, NonTrivialDims, pWriteBuffer, TotalNumElements); if( pCopyBuffer ) free( pCopyBuffer ); } } diff --git a/Grid/tensors/Tensor_class.h b/Grid/tensors/Tensor_class.h index d9af736b..6dd9cbcc 100644 --- a/Grid/tensors/Tensor_class.h +++ b/Grid/tensors/Tensor_class.h @@ -174,36 +174,36 @@ class iScalar { }; template - typename std::enable_if::value, const scalar_type *>::type + typename std::enable_if::value, const scalar_type *>::type strong_inline begin() const { return &_internal; } template - typename std::enable_if::value, const scalar_type *>::type + typename std::enable_if::value, const scalar_type *>::type strong_inline begin() const { return _internal.begin(); } template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return (&_internal) + grid_tensor_att>::count; } + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return (&_internal) + 1; } template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal.begin() + grid_tensor_att>::count; } + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal.begin() + sizeof(_internal)/sizeof(scalar_type); } template - typename std::enable_if::value, scalar_type *>::type + typename std::enable_if::value, scalar_type *>::type strong_inline begin() { return &_internal; } template - typename std::enable_if::value, scalar_type *>::type + typename std::enable_if::value, scalar_type *>::type strong_inline begin() { return _internal.begin(); } template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return (&_internal) + grid_tensor_att>::count; } + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return (&_internal) + 1; } template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal.begin() + grid_tensor_att>::count; } + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal.begin() + sizeof(_internal)/sizeof(scalar_type); } }; /////////////////////////////////////////////////////////// // Allows to turn scalar>>> back to double. @@ -335,36 +335,36 @@ class iVector { // } template - typename std::enable_if::value, const scalar_type *>::type + typename std::enable_if::value, const scalar_type *>::type strong_inline begin() const { return _internal; } template - typename std::enable_if::value, const scalar_type *>::type + typename std::enable_if::value, const scalar_type *>::type strong_inline begin() const { return _internal[0].begin(); } template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal + grid_tensor_att>::count; } + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal + N; } template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal[0].begin() + grid_tensor_att>::count; } + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal[0].begin() + sizeof(_internal)/sizeof(scalar_type); } template - typename std::enable_if::value, scalar_type *>::type + typename std::enable_if::value, scalar_type *>::type strong_inline begin() { return _internal; } template - typename std::enable_if::value, scalar_type *>::type + typename std::enable_if::value, scalar_type *>::type strong_inline begin() { return _internal[0].begin(); } template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal + grid_tensor_att>::count; } + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal + N; } template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal[0].begin() + grid_tensor_att>::count; } + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal[0].begin() + sizeof(_internal)/sizeof(scalar_type); } }; template @@ -522,36 +522,36 @@ class iMatrix { // } template - typename std::enable_if::value, const scalar_type *>::type + typename std::enable_if::value, const scalar_type *>::type strong_inline begin() const { return _internal[0]; } template - typename std::enable_if::value, const scalar_type *>::type + typename std::enable_if::value, const scalar_type *>::type strong_inline begin() const { return _internal[0][0].begin(); } template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal[0] + grid_tensor_att>::count; } + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal[0] + N * N; } template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal[0][0].begin() + grid_tensor_att>::count; } + typename std::enable_if::value, const scalar_type *>::type + strong_inline end() const { return _internal[0][0].begin() + sizeof(_internal)/sizeof(scalar_type); } template - typename std::enable_if::value, scalar_type *>::type + typename std::enable_if::value, scalar_type *>::type strong_inline begin() { return _internal[0]; } template - typename std::enable_if::value, scalar_type *>::type + typename std::enable_if::value, scalar_type *>::type strong_inline begin() { return _internal[0][0].begin(); } template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal[0] + grid_tensor_att>::count; } + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal[0] + N * N; } template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal[0][0].begin() + grid_tensor_att>::count; } + typename std::enable_if::value, scalar_type *>::type + strong_inline end() { return _internal[0][0].begin() + sizeof(_internal)/sizeof(scalar_type); } }; template diff --git a/Grid/tensors/Tensor_traits.h b/Grid/tensors/Tensor_traits.h index 3abf16d0..c1ef397a 100644 --- a/Grid/tensors/Tensor_traits.h +++ b/Grid/tensors/Tensor_traits.h @@ -288,79 +288,6 @@ namespace Grid { enum { value = sizeof(real_scalar_type)/sizeof(float) }; }; - - //////////////////////////////////////////////////////////////////////////// - // Review with Peter - obvious (but partial) overlap with the above - // What of this is good and should be kept ... vs what functions should I really be using? - //////////////////////////////////////////////////////////////////////////// - - // Need some forward references or the below won't work - template - class iScalar; - template - class iVector; - template - class iMatrix; - - // which types are grid tensors - template struct is_grid_tensor : public std::false_type {}; - template struct is_grid_tensor> : public std::true_type {}; - template struct is_grid_tensor> : public std::true_type {}; - template struct is_grid_tensor> : public std::true_type {}; - - // Rank and dimension of grid tensors, i.e. compositions of iScalar, iVector and iMatrix - // This defines the bottom level - i.e. it's a description of the underlying scalar - template struct grid_tensor_att { - static constexpr unsigned int depth = 0; // How many levels of Grid Tensor there are (TensorLevel) - static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) - static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 - static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) - using scalar_type = T; // Type of the underlying scalar - static constexpr std::size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes - static constexpr std::size_t size = scalar_size * count; // total size of elements in bytes - // e.g. iScalar> - // depth = 2 - // rank = 1 - // rank_non_trivial = 0 - // count = 1 - // e.g. iVector,4> - // depth = 2 - // rank = 3 - // rank_non_trivial = 3 - // count = 36 - // e.g. iScalar,3>> - // depth = 3 - // rank = 3 - // rank_non_trivial = 3 - // count = 48 - }; - template struct grid_tensor_att> { - static constexpr unsigned int depth = 1 + grid_tensor_att::depth; - static constexpr unsigned int rank = 0 + grid_tensor_att::rank; - static constexpr unsigned int rank_non_trivial = 0 + grid_tensor_att::rank_non_trivial; - static constexpr unsigned int count = 1 * grid_tensor_att::count; - using scalar_type = typename grid_tensor_att::scalar_type; - static constexpr std::size_t scalar_size = grid_tensor_att::scalar_size; - static constexpr std::size_t size = scalar_size * count; - }; - template struct grid_tensor_att> { - static constexpr unsigned int depth = 1 + grid_tensor_att::depth; - static constexpr unsigned int rank = 1 + grid_tensor_att::rank; - static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + grid_tensor_att::rank_non_trivial; - static constexpr unsigned int count = N * grid_tensor_att::count; - using scalar_type = typename grid_tensor_att::scalar_type; - static constexpr std::size_t scalar_size = grid_tensor_att::scalar_size; - static constexpr std::size_t size = scalar_size * count; - }; - template struct grid_tensor_att> { - static constexpr unsigned int depth = 1 + grid_tensor_att::depth; - static constexpr unsigned int rank = 2 + grid_tensor_att::rank; - static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + grid_tensor_att::rank_non_trivial; - static constexpr unsigned int count = N * N * grid_tensor_att::count; - using scalar_type = typename grid_tensor_att::scalar_type; - static constexpr std::size_t scalar_size = grid_tensor_att::scalar_size; - static constexpr std::size_t size = scalar_size * count; - }; } #endif diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index dc7f83c7..d8e02ea0 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -101,6 +101,20 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam } #ifdef DEBUG +template +//typename std::enable_if::value, void>::type +void dump_tensor(T & t) +{ + using Traits = typename EigenIO::Traits; + for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::size_t * pDims ){ + std::cout << " "; + for( int i = 0 ; i < t.NumDimensions + Traits::rank_non_trivial; i++ ) + std::cout << "[" << pDims[i] << "]"; + std::cout << " = " << c << std::endl; + } ); + std::cout << "========================================" << std::endl; +} + //typedef int TestScalar; typedef std::complex TestScalar; typedef Eigen::Tensor, 6> TestTensorSingle; @@ -145,6 +159,8 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); + std::cout << "t:"; + dump_tensor(t); // Now serialise a fixed size tensor using FixedTensor = Eigen::TensorFixedSize>; @@ -157,7 +173,9 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_tensor_fixed.h5", tf, "eigen_tensor_fixed_name"); - + std::cout << "tf:"; + dump_tensor(tf); + ETSerClass o; ioTest("iotest_object.h5", o, "ETSerClass_object_instance_name"); @@ -174,14 +192,8 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_LSCTensor.h5", l, "LSCTensor_object_instance_name"); - std::cout << "t:"; - for_all( l, [](std::complex &c, LSCTensor::Index index, const std::size_t * pDims ){ - std::cout << " "; - for( int i = 0 ; i < 5; i++ ) - std::cout << "[" << pDims[i] << "]"; - std::cout << " = " << c << std::endl; - } ); - std::cout << std::endl; + std::cout << "l:"; + dump_tensor(l); // Tensor of spin colour LCMTensor l2; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 81299d0d..685f5f13 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -467,13 +467,13 @@ bool DebugEigenTest() template void DebugGridTensorTest_print( int i ) { - std::cout << i << " : " << is_grid_tensor::value - << ", depth " << grid_tensor_att::depth - << ", rank " << grid_tensor_att::rank - << ", rank_non_trivial " << grid_tensor_att::rank_non_trivial - << ", count " << grid_tensor_att::count - << ", scalar_size " << grid_tensor_att::scalar_size - << ", size " << grid_tensor_att::size + std::cout << i << " : " << EigenIO::is_tensor::value + << ", depth " << EigenIO::Traits::depth + << ", rank " << EigenIO::Traits::rank + << ", rank_non_trivial " << EigenIO::Traits::rank_non_trivial + << ", count " << EigenIO::Traits::count + << ", scalar_size " << EigenIO::Traits::scalar_size + << ", size " << EigenIO::Traits::size << std::endl; } @@ -494,8 +494,24 @@ public: inline value_type * end(void) { return m_p + N; } }; +bool DebugFelixTensorTest( void ) +{ + unsigned int Nmom = 2; + unsigned int Nt = 2; + unsigned int N_1 = 2; + unsigned int N_2 = 2; + unsigned int N_3 = 2; + using BaryonTensorSet = Eigen::Tensor; + BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); + std::vector Memory(Nmom * Nt * N_1 * N_2 * N_3 * 2); + using BaryonTensorMap = Eigen::TensorMap; + BaryonTensorMap BField4 (&Memory[0], Nmom,4,Nt,N_1,N_2,N_3); + return true; +} + bool DebugGridTensorTest( void ) { + DebugFelixTensorTest(); typedef Complex t1; typedef iScalar t2; typedef iVector t3; From 8cb96cb69377b2cb97d391544d7b126f202d5081 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 14 Feb 2019 19:17:12 +0000 Subject: [PATCH 090/347] Hmmm lots of warnings depending on compiler ... --- Grid/serialisation/BaseIO.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 535ab035..531d0e41 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -120,11 +120,16 @@ namespace Grid { static constexpr std::size_t Dimension(unsigned int dim) { return ( dim == 0 ) ? N : Traits::Dimension(dim - 1); } static constexpr std::size_t DimensionNT(unsigned int dim) { + std::size_t DimSize = N; + bool bGotDimSize = false; if( N != 1 ) { - if( dim == 0 ) return N; + if( dim == 0 ) + bGotDimSize = true; dim--; } - return Traits::DimensionNT(dim); + if( !bGotDimSize ) + DimSize = Traits::DimensionNT(dim); + return DimSize; } }; template struct Traits> { @@ -138,11 +143,16 @@ namespace Grid { static constexpr std::size_t Dimension(unsigned int dim) { return ( dim == 0 || dim == 1 ) ? N : Traits::Dimension(dim - 2); } static constexpr std::size_t DimensionNT(unsigned int dim) { + std::size_t DimSize = N; + bool bGotDimSize = false; if( N != 1 ) { - if( dim == 0 || dim == 1 ) return N; + if( dim == 0 || dim == 1 ) + bGotDimSize = true; dim -= 2; } - return Traits::DimensionNT(dim); + if( !bGotDimSize ) + DimSize = Traits::DimensionNT(dim); + return DimSize; } }; template struct Traits> : Traits> {}; From e8bd8767c07304591cb657af8fda2b95ae55447a Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 15 Feb 2019 10:06:15 +0000 Subject: [PATCH 091/347] Get rid of declarations inside constexpr functions. if constexpr warning remains --- Grid/serialisation/BaseIO.h | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 531d0e41..0321c747 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -120,16 +120,7 @@ namespace Grid { static constexpr std::size_t Dimension(unsigned int dim) { return ( dim == 0 ) ? N : Traits::Dimension(dim - 1); } static constexpr std::size_t DimensionNT(unsigned int dim) { - std::size_t DimSize = N; - bool bGotDimSize = false; - if( N != 1 ) { - if( dim == 0 ) - bGotDimSize = true; - dim--; - } - if( !bGotDimSize ) - DimSize = Traits::DimensionNT(dim); - return DimSize; + return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 ) ? N : Traits::DimensionNT(dim - 1); } }; template struct Traits> { @@ -143,16 +134,7 @@ namespace Grid { static constexpr std::size_t Dimension(unsigned int dim) { return ( dim == 0 || dim == 1 ) ? N : Traits::Dimension(dim - 2); } static constexpr std::size_t DimensionNT(unsigned int dim) { - std::size_t DimSize = N; - bool bGotDimSize = false; - if( N != 1 ) { - if( dim == 0 || dim == 1 ) - bGotDimSize = true; - dim -= 2; - } - if( !bGotDimSize ) - DimSize = Traits::DimensionNT(dim); - return DimSize; + return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 || dim == 1 ) ? N : Traits::DimensionNT(dim - 2); } }; template struct Traits> : Traits> {}; From df0c8b5d846d757d600020321c563b43b176aa87 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 15 Feb 2019 13:52:49 +0000 Subject: [PATCH 092/347] Test of Eigen slices --- tests/hadrons/Test_hadrons_distil.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 685f5f13..6c871df3 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -506,6 +506,25 @@ bool DebugFelixTensorTest( void ) std::vector Memory(Nmom * Nt * N_1 * N_2 * N_3 * 2); using BaryonTensorMap = Eigen::TensorMap; BaryonTensorMap BField4 (&Memory[0], Nmom,4,Nt,N_1,N_2,N_3); + + using TestScalar = std::complex; + //typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TestTensorFixed; + using T3 = Eigen::Tensor; + using T2 = Eigen::Tensor; + T3 a(4,3,2); + for_all( a, [&](TestScalar &c, float n, const std::size_t * pDims ){ + c = std::complex{n,-n}; + } ); + std::cout << "a initialised to:\n" << a << std::endl; + Eigen::array offsets = {0,0,0}; + Eigen::array extents = {1,3,2}; + T2 b(3,2); + auto c = a.slice( offsets, extents).reshape(extents); + std::cout << "c is:\n" << c << std::endl; + b = a.chip(0,0); + std::cout << "b is:\n" << b << std::endl; + //b = c; + return true; } From e0987d7d81f410e03c1c51ffde3047ac03b12346 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 15 Feb 2019 14:32:17 +0000 Subject: [PATCH 093/347] first contraction version done --- Hadrons/Modules/MDistil/BContraction.hpp | 27 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 71f6474c..2eaefd7f 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -175,8 +175,10 @@ void TBContraction::execute(void) }; std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; //BaryonTensorSet BField3(storage,Nmom,4,Nt,N_1,N_2,N_3); - using BaryonTensorSet = Eigen::Tensor; + using BaryonTensorSet = Eigen::Tensor; BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); + using C2Set = Eigen::Tensor; + C2Set corr(Nmom,4,Nt); std::vector BField2(Nmom*Nt*N_1*N_2*N_3); Complex diquark2; @@ -223,10 +225,25 @@ void TBContraction::execute(void) } //Product ijk * ijk - // for ijk * jik: (4,5),(5,4),(6,6) z.b. - Eigen::array, 3> product_dims = { Eigen::IndexPair(4, 4),Eigen::IndexPair(5, 5) ,Eigen::IndexPair(6, 6) }; - // Whycan't I choose the dimension to be 3??? Want to sum over them, not save each element! - Eigen::Tensor C2 = BField3.contract(BField3,product_dims); + // for ijk * jik: (0,1),(1,0),(2,2) z.b. + Eigen::array, 3> product_dims = { Eigen::IndexPair(0,0),Eigen::IndexPair(1,1) ,Eigen::IndexPair(2,2) }; + for (int imom=0 ; imom < Nmom ; imom++){ + Eigen::Tensor B5 = BField3.chip(imom,0); + for (int is=0 ; is < 4 ; is++){ + Eigen::Tensor B4 = B5.chip(is,0); + for (int t=0 ; t < Nt ; t++){ + Eigen::Tensor B3 = B4.chip(t,0); + Eigen::Tensor C2 = B3.contract(B3,product_dims); + corr(imom,is,t) = C2(0); + } + } + } + //Eigen::Tensor C2 = BField3.contract(BField3,product_dims); + for (int is=0 ; is < 4 ; is++){ + for (int t=0 ; t < Nt ; t++){ + std::cout << "C2(is=" << is << ",t=" << t << ") = " << corr(0,is,t) << std::endl; + } + } } END_MODULE_NAMESPACE From 668b1e77c7458906b2838ea6826c0deadc040451 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 15 Feb 2019 15:31:53 +0000 Subject: [PATCH 094/347] small changes --- Hadrons/Modules/MDistil/BContraction.hpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 2eaefd7f..8273d30d 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -104,7 +104,7 @@ std::vector TBContraction::getOutput(void) template void TBContraction::setup(void) { - + } // execution /////////////////////////////////////////////////////////////////// @@ -173,14 +173,12 @@ void TBContraction::execute(void) Gamma::Algebra::SigmaYT, // C = i gamma_2 gamma_4 Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 }; - std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; - //BaryonTensorSet BField3(storage,Nmom,4,Nt,N_1,N_2,N_3); + std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; using BaryonTensorSet = Eigen::Tensor; BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); - using C2Set = Eigen::Tensor; - C2Set corr(Nmom,4,Nt); - std::vector BField2(Nmom*Nt*N_1*N_2*N_3); + Eigen::Tensor corr(Nmom,4,Nt); + Complex diquark2; for (int i1=0 ; i1 < N_1 ; i1++){ for (int i2=0 ; i2 < N_2 ; i2++){ @@ -205,7 +203,6 @@ void TBContraction::execute(void) tmp222 = g4*tmp111; tmp111 = 0.5*(double)parity*(tmp111 + tmp222); // P_\pm * ... diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); - //BField2[Bindex]+=(double)epsilon_sgn[ie]*tmp111*diquark2; for (int is=0 ; is < 4 ; is++){ BField3(imom,is,t,i1,i2,i3)+=(double)epsilon_sgn[ie]*tmp111()(is)()*diquark2; } @@ -218,8 +215,6 @@ void TBContraction::execute(void) } for (int is=0 ; is < 4 ; is++){ for (int t=0 ; t < Nt ; t++){ - // Bindex = 0 + N_1*(0 + N_2*(0 + N_3*(0+Nmom*t))); - // std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField2[Bindex]()(is)() << std::endl; std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField3(0,is,t,0,0,0) << std::endl; } } @@ -238,7 +233,6 @@ void TBContraction::execute(void) } } } - //Eigen::Tensor C2 = BField3.contract(BField3,product_dims); for (int is=0 ; is < 4 ; is++){ for (int t=0 ; t < Nt ; t++){ std::cout << "C2(is=" << is << ",t=" << t << ") = " << corr(0,is,t) << std::endl; From bfd2770657ad0a4d928ee3b8d7ef171abe7e18e4 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 15 Feb 2019 15:51:46 +0000 Subject: [PATCH 095/347] started on baryon flavour sums --- Hadrons/Modules/MDistil/BContraction.hpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 8273d30d..37578e09 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -178,7 +178,24 @@ void TBContraction::execute(void) BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); Eigen::Tensor corr(Nmom,4,Nt); - + + //Needs more work - but this is important for contraction + /* int Npairs = 0; + char left[] = "uud"; + char right[] = "uud"; + std::vector> pairs; + for (int il=0, i=0 ; il < 3 ; il++){ + for (int ir=0 ; ir < 3 ; ir++){ + if (il>ir) continue; + if (left[il] != right[il]) continue; + pairs[i][0]=il; + pairs[i][1]=ir; + i++; + Npairs = i; + } + std::cout << "pairs: " << pairs << std::endl; + } + */ Complex diquark2; for (int i1=0 ; i1 < N_1 ; i1++){ for (int i2=0 ; i2 < N_2 ; i2++){ From b6803a070a4616dbc758a3797a4690a9cc3938ee Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sat, 16 Feb 2019 16:18:28 +0000 Subject: [PATCH 096/347] Making sure I understand row-major vs column-major ordering --- Grid/serialisation/BaseIO.h | 127 ++++++++++++++++++--------- tests/hadrons/Test_hadrons_distil.cc | 98 +++++++++++++++------ 2 files changed, 159 insertions(+), 66 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 0321c747..0b907e32 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -145,7 +145,9 @@ namespace Grid { //template struct Traits::value, void>::type> : Traits {}; } - // Helper to allow iteration through an Eigen::Tensor (using a lambda) + // Calls a lamda (passing index and sequence number) for every member of an Eigen::Tensor + // For efficiency, iteration proceeds in memory order, + // ... but parameters guaranteed to be the same regardless of memory order template typename std::enable_if::value, void>::type for_all( ETensor &ET, Lambda lambda ) @@ -153,62 +155,107 @@ namespace Grid { using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later const std::size_t NumScalars = ET.size(); assert( NumScalars > 0 ); - // Assemble a vector containing all the non-trivial dimensions (i.e. dimensions > 1) - unsigned int ScalarElementCount{1}; - std::vector NonTrivialDims; - NonTrivialDims.reserve(ET.NumDimensions + EigenIO::Traits::rank_non_trivial); - for(auto i = 0; i < ET.NumDimensions; i++ ) { + using Index = typename ETensor::Index; + Index ScalarElementCount{1}; + const auto InnerRank = EigenIO::Traits::rank_non_trivial; + const auto rank{ETensor::NumIndices}; + std::array Dims; + for(auto i = 0; i < rank; i++ ) { auto dim = ET.dimension(i); - if( dim <= 1 ) { - assert( dim == 1 ); // Not expecting dimension to be <= 0 - } else { - size_t s = static_cast(dim); - assert( s == dim ); // check we didn't lose anything in the conversion - NonTrivialDims.push_back(s); - ScalarElementCount *= s; - } + assert( dim > 0 ); + Dims[i] = static_cast(dim); + assert( Dims[i] == dim ); // check we didn't lose anything in the conversion + ScalarElementCount *= Dims[i]; } - // Check that the number of containers is correct + // Check that the number of containers is correct ... and we didn't lose anything in conversions assert( NumScalars == ScalarElementCount ); // If the Scalar is actually a container, add the inner Scalar's non-trivial dimensions size_t InnerScalarCount{1}; - for(auto i = 0; i < EigenIO::Traits::rank_non_trivial; i++ ) { + for(auto i = 0; i < InnerRank; i++ ) { auto dim = EigenIO::Traits::DimensionNT(i); assert( dim > 1 ); - NonTrivialDims.push_back(dim); + Dims[rank + i] = static_cast(dim); + assert( Dims[rank + i] == dim ); // check we didn't lose anything in the conversion InnerScalarCount *= dim; } assert(EigenIO::Traits::count == InnerScalarCount); assert(EigenIO::Traits::size == sizeof( Scalar )); - const unsigned int NonTrivialDimsSize = static_cast(NonTrivialDims.size()); - assert( NonTrivialDimsSize == NonTrivialDims.size() ); - //const typename ETensor::Index TotalNumElements = NumScalars * InnerScalarCount; - using Index = typename ETensor::Index; - std::array MyIndex; + std::array MyIndex; for( auto &idx : MyIndex ) idx = 0; - std::vector SubIndex(NonTrivialDimsSize); - for( auto &idx : SubIndex ) idx = 0; - Index n = 0; + Index Seq = 0; + Scalar * pScalar = ET.data(); for( std::size_t j = 0; j < NumScalars; j++ ) { // if constexpr is C++ 17 ... but otherwise need two specialisations (Container vs Scalar) - if constexpr ( EigenIO::Traits::rank_non_trivial == 0 ) { - lambda(ET( MyIndex ), n++, &SubIndex[0] ); - // Now increment SubIndex - for( auto i = NonTrivialDimsSize - 1; i != -1 && ++SubIndex[i] == NonTrivialDims[i]; i-- ) - SubIndex[i] = 0; - } - else - { - for( typename Scalar::scalar_type &Source : ET( MyIndex ) ) { - lambda(Source, n++, &SubIndex[0] ); + if constexpr ( InnerRank == 0 ) { + lambda( * pScalar, Seq++, &MyIndex[0] ); + } else { + for( typename Scalar::scalar_type &Source : * pScalar ) { + lambda(Source, Seq++, &MyIndex[0] ); // Now increment SubIndex - for( auto i = NonTrivialDimsSize - 1; i != -1 && ++SubIndex[i] == NonTrivialDims[i]; i-- ) - SubIndex[i] = 0; + for( auto i = rank + InnerRank - 1; i != rank - 1 && ++MyIndex[i] == Dims[i]; i-- ) + MyIndex[i] = 0; } } - // Now increment MyIndex - for( auto i = ET.NumDimensions - 1; i != -1 && ++MyIndex[i] == ET.dimension(i); i-- ) - MyIndex[i] = 0; + // Now increment the index to pass to the lambda (bearing in mind we're walking in memory order) + if( ETensor::Options & Eigen::RowMajor ) { + for( auto i = rank - 1; i != -1 && ++MyIndex[i] == Dims[i]; i-- ) + MyIndex[i] = 0; + } else { + for( auto i = 0; i < rank && ++MyIndex[i] == Dims[i]; i++ ) + MyIndex[i] = 0; + Seq = 0; + for( auto i = 0; i < rank + InnerRank ; i++ ) { + Seq *= Dims[i]; + Seq += MyIndex[i]; + } + } + pScalar++; + } + } + + // Helper to dump a tensor in memory order + template + typename std::enable_if::value, void>::type + DumpMemoryOrder(T t, const char * pName = nullptr) + { + const auto dims = t.dimensions(); + const auto rank = t.rank(); + std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; + if( pName ) + std::cout << pName; + for( auto d : dims ) std::cout << "[" << d << "]"; + std::cout << " in memory order:" << std::endl; + const typename T::Scalar * p = t.data(); + const auto size = t.size(); + const typename T::Scalar * pEnd = p + size; + if( rank <= 2 ) { + for( unsigned int i = 0 ; i < t.size() ; i++ ) + std::cout << "[" << i << "]=" << *p++ << " "; + std::cout << std::endl; + } else { + const auto innersize = dims[rank-2] * dims[rank-1]; + using Index = typename T::Index; + std::vector idx(rank - 2); + for( auto &i : idx ) i = 0; + Index idxCounter = 0; + while( p < pEnd ) { + if( T::Options & Eigen::RowMajor ) { + if( pName ) + std::cout << pName; + idxCounter = 0; + for(auto i = 0 ; i < rank - 2 ; i++) + std::cout << "[" << idx[i] << "]:"; + } + for( unsigned int i = 0 ; i < innersize ; i++ ) + std::cout << " [" << idxCounter++ << "]=" << *p++; + if( T::Options & Eigen::RowMajor ) + std::cout << std::endl; + // Now increment MyIndex + for( auto i = rank - 3; i != -1 && ++idx[i] == dims[i]; i-- ) + idx[i] = 0; + } + if( ! ( T::Options & Eigen::RowMajor ) ) + std::cout << std::endl; } } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 349156bf..1dba073e 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -338,11 +338,11 @@ void DebugShowTensor(MyTensor &x, const char * n) // Initialise assert( d.size() == 3 ); for( int i = 0 ; i < d[0] ; i++ ) - for( int j = 0 ; j < d[1] ; j++ ) - for( int k = 0 ; k < d[2] ; k++ ) { - x(i,j,k) = std::complex(SizeCalculated, -SizeCalculated); - SizeCalculated--; - } + for( int j = 0 ; j < d[1] ; j++ ) + for( int k = 0 ; k < d[2] ; k++ ) { + x(i,j,k) = std::complex(SizeCalculated, -SizeCalculated); + SizeCalculated--; + } // Show raw data std::cout << "Data follow : " << std::endl; Complex * p = x.data(); @@ -496,7 +496,66 @@ public: inline value_type * end(void) { return m_p + N; } }; -bool DebugFelixTensorTest( void ) +template +void EigenSliceExample() +{ + std::cout << "Eigen example, Options = " << Options << std::endl; + using T2 = Eigen::Tensor; + T2 a(4, 3); + a.setValues({{0, 100, 200}, {300, 400, 500}, + {600, 700, 800}, {900, 1000, 1100}}); + std::cout << "a\n" << a << std::endl; + DumpMemoryOrder( a, "a" ); + Eigen::array offsets = {1, 0}; + Eigen::array extents = {2, 2}; + T2 slice = a.slice(offsets, extents); + std::cout << "slice\n" << slice << std::endl; + DumpMemoryOrder( slice, "slice" ); + std::cout << "\n========================================" << std::endl; +} + +template +void EigenSliceExample2() +{ + using TestScalar = std::complex; + using T3 = Eigen::Tensor; + using T2 = Eigen::Tensor; + T3 a(2,3,4); + + std::cout << "Initialising:a"; + float z = 0; + for( int i = 0 ; i < a.dimension(0) ; i++ ) + for( int j = 0 ; j < a.dimension(1) ; j++ ) + for( int k = 0 ; k < a.dimension(2) ; k++ ) { + TestScalar w{z, -z}; + a(i,j,k) = w; + std::cout << " a(" << i << "," << j << "," << k << ")=" << w; + z++; + } + std::cout << std::endl; + //std::cout << "a initialised to:\n" << a << std::endl; + DumpMemoryOrder( a, "a" ); + std::cout << "for_all(a):"; + for_all( a, [&](TestScalar c, typename T3::Index n, const std::size_t * pDims ){ + std::cout << " (" << pDims[0] << "," << pDims[1] << "," << pDims[2] << ")<" << n << ">=" << c; + } ); + std::cout << std::endl; + Eigen::array offsets = {0,1,1}; + Eigen::array extents = {1,2,2}; + T3 b; + b = a.slice( offsets, extents );//.reshape(NewExtents); + std::cout << "b = a.slice( offsets, extents ):\n" << b << std::endl; + DumpMemoryOrder( b, "b" ); + T2 c(3,4); + c = a.chip(0,1); + std::cout << "c = a.chip(0,0):\n" << c << std::endl; + DumpMemoryOrder( c, "c" ); + //T2 d = b.reshape(extents); + //std::cout << "b.reshape(extents) is:\n" << d << std::endl; + std::cout << "\n========================================" << std::endl; +} + +void DebugFelixTensorTest( void ) { unsigned int Nmom = 2; unsigned int Nt = 2; @@ -509,25 +568,10 @@ bool DebugFelixTensorTest( void ) using BaryonTensorMap = Eigen::TensorMap; BaryonTensorMap BField4 (&Memory[0], Nmom,4,Nt,N_1,N_2,N_3); - using TestScalar = std::complex; - //typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TestTensorFixed; - using T3 = Eigen::Tensor; - using T2 = Eigen::Tensor; - T3 a(4,3,2); - for_all( a, [&](TestScalar &c, float n, const std::size_t * pDims ){ - c = std::complex{n,-n}; - } ); - std::cout << "a initialised to:\n" << a << std::endl; - Eigen::array offsets = {0,0,0}; - Eigen::array extents = {1,3,2}; - T2 b(3,2); - auto c = a.slice( offsets, extents).reshape(extents); - std::cout << "c is:\n" << c << std::endl; - b = a.chip(0,0); - std::cout << "b is:\n" << b << std::endl; - //b = c; - - return true; + EigenSliceExample(); + EigenSliceExample<0>(); + EigenSliceExample2(); + EigenSliceExample2<0>(); } bool DebugGridTensorTest( void ) @@ -561,7 +605,9 @@ bool DebugGridTensorTest( void ) Start += Inc; } i = 0; - for( auto x : toc7 ) std::cout << "toc7[" << i++ << "] = " << x << std::endl; + std::cout << "toc7:"; + for( auto x : toc7 ) std::cout << " [" << i++ << "]=" << x; + std::cout << std::endl; t2 o2; auto a2 = TensorRemove(o2); From 00e9416e0a84d633bf64995207d07782e68fdd8b Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sat, 16 Feb 2019 17:08:22 +0000 Subject: [PATCH 097/347] Tweak to initialisation example --- Grid/serialisation/BaseIO.h | 4 ++-- tests/hadrons/Test_hadrons_distil.cc | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 0b907e32..64e5a12c 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -187,10 +187,10 @@ namespace Grid { for( std::size_t j = 0; j < NumScalars; j++ ) { // if constexpr is C++ 17 ... but otherwise need two specialisations (Container vs Scalar) if constexpr ( InnerRank == 0 ) { - lambda( * pScalar, Seq++, &MyIndex[0] ); + lambda( * pScalar, Seq++, MyIndex ); } else { for( typename Scalar::scalar_type &Source : * pScalar ) { - lambda(Source, Seq++, &MyIndex[0] ); + lambda(Source, Seq++, MyIndex ); // Now increment SubIndex for( auto i = rank + InnerRank - 1; i != rank - 1 && ++MyIndex[i] == Dims[i]; i-- ) MyIndex[i] = 0; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 1dba073e..3c38e915 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -522,22 +522,28 @@ void EigenSliceExample2() using T2 = Eigen::Tensor; T3 a(2,3,4); - std::cout << "Initialising:a"; + std::cout << "Initialising a:"; + for_all( a, [&](TestScalar &c, float f, const std::array &Dims ){ + c = TestScalar{f,-f}; + std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; + } ); + std::cout << std::endl; + //std::cout << "Validating a:"; float z = 0; for( int i = 0 ; i < a.dimension(0) ; i++ ) for( int j = 0 ; j < a.dimension(1) ; j++ ) for( int k = 0 ; k < a.dimension(2) ; k++ ) { TestScalar w{z, -z}; - a(i,j,k) = w; - std::cout << " a(" << i << "," << j << "," << k << ")=" << w; + //std::cout << " a(" << i << "," << j << "," << k << ")=" << w; + assert( a(i,j,k) == w ); z++; } - std::cout << std::endl; + //std::cout << std::endl; //std::cout << "a initialised to:\n" << a << std::endl; DumpMemoryOrder( a, "a" ); std::cout << "for_all(a):"; - for_all( a, [&](TestScalar c, typename T3::Index n, const std::size_t * pDims ){ - std::cout << " (" << pDims[0] << "," << pDims[1] << "," << pDims[2] << ")<" << n << ">=" << c; + for_all( a, [&](TestScalar c, typename T3::Index n, const std::array &Dims ){ + std::cout << " (" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")<" << n << ">=" << c; } ); std::cout << std::endl; Eigen::array offsets = {0,1,1}; From 74a3a5b825bec459b6912c7ae0c7c7673b084a01 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sat, 16 Feb 2019 18:45:46 +0000 Subject: [PATCH 098/347] Fixed existing bug in Hdf5Reader::readDefault for std::vector --- Grid/serialisation/BaseIO.h | 34 ++++++++++++++++++++++++++++++++-- Grid/serialisation/Hdf5IO.h | 2 +- tests/IO/Test_serialisation.cc | 23 +++-------------------- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 64e5a12c..bf4eb42a 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -213,13 +213,42 @@ namespace Grid { } } + // Helper to dump a tensor +#ifdef DEBUG + template + typename std::enable_if::value, void>::type + dump_tensor_func(T &t, const char * pName = nullptr) + { + using Traits = typename EigenIO::Traits; + const auto rank{T::NumIndices}; + const auto &dims = t.dimensions(); + std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; + if( pName ) + std::cout << pName; + for( auto i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; + std::cout << " in memory order:" << std::endl; + for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array Dims ){ + std::cout << " "; + for( auto dim : Dims ) + std::cout << "[" << dim << "]"; + std::cout << " = " << c << std::endl; + } ); + std::cout << "========================================" << std::endl; + } +#define dump_tensor(args...) dump_tensor_func(args) +#else +#define dump_tensor(args...) +#endif + // Helper to dump a tensor in memory order + // Kind of superfluous given the above +#ifdef DEBUG template typename std::enable_if::value, void>::type - DumpMemoryOrder(T t, const char * pName = nullptr) + DumpMemoryOrder(T &t, const char * pName = nullptr) { - const auto dims = t.dimensions(); const auto rank = t.rank(); + const auto &dims = t.dimensions(); std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; if( pName ) std::cout << pName; @@ -258,6 +287,7 @@ namespace Grid { std::cout << std::endl; } } +#endif // Abstract writer/reader classes //////////////////////////////////////////// // static polymorphism implemented using CRTP idiom diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index c486801a..7de6dc88 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -215,7 +215,7 @@ namespace Grid // read the flat vector std::vector buf(size); - if (size > dataSetThres_) + if (size * sizeof(Element) > dataSetThres_) { H5NS::DataSet dataSet; diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index d8e02ea0..509241ab 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -101,20 +101,6 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam } #ifdef DEBUG -template -//typename std::enable_if::value, void>::type -void dump_tensor(T & t) -{ - using Traits = typename EigenIO::Traits; - for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::size_t * pDims ){ - std::cout << " "; - for( int i = 0 ; i < t.NumDimensions + Traits::rank_non_trivial; i++ ) - std::cout << "[" << pDims[i] << "]"; - std::cout << " = " << c << std::endl; - } ); - std::cout << "========================================" << std::endl; -} - //typedef int TestScalar; typedef std::complex TestScalar; typedef Eigen::Tensor, 6> TestTensorSingle; @@ -159,8 +145,7 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); - std::cout << "t:"; - dump_tensor(t); + dump_tensor(t, "t"); // Now serialise a fixed size tensor using FixedTensor = Eigen::TensorFixedSize>; @@ -173,8 +158,7 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_tensor_fixed.h5", tf, "eigen_tensor_fixed_name"); - std::cout << "tf:"; - dump_tensor(tf); + dump_tensor(tf, "tf"); ETSerClass o; ioTest("iotest_object.h5", o, "ETSerClass_object_instance_name"); @@ -192,8 +176,7 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_LSCTensor.h5", l, "LSCTensor_object_instance_name"); - std::cout << "l:"; - dump_tensor(l); + dump_tensor(l, "l"); // Tensor of spin colour LCMTensor l2; From 9815ddb853afeec53a5ba5138d01c42ef5a07a87 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sat, 16 Feb 2019 19:30:33 +0000 Subject: [PATCH 099/347] Started read routines. Introduced readMultiDim and tested I didnt break anything --- Grid/serialisation/BaseIO.h | 40 ++++++++++++++++++++++++++++--------- Grid/serialisation/Hdf5IO.h | 26 +++++++++++++++++------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index bf4eb42a..96bb3bb7 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -215,6 +215,7 @@ namespace Grid { // Helper to dump a tensor #ifdef DEBUG +#define dump_tensor(args...) dump_tensor_func(args) template typename std::enable_if::value, void>::type dump_tensor_func(T &t, const char * pName = nullptr) @@ -235,17 +236,13 @@ namespace Grid { } ); std::cout << "========================================" << std::endl; } -#define dump_tensor(args...) dump_tensor_func(args) -#else -#define dump_tensor(args...) -#endif // Helper to dump a tensor in memory order // Kind of superfluous given the above -#ifdef DEBUG +#define DumpMemoryOrder(args...) DumpMemoryOrder_func(args) template typename std::enable_if::value, void>::type - DumpMemoryOrder(T &t, const char * pName = nullptr) + DumpMemoryOrder_func(T &t, const char * pName = nullptr) { const auto rank = t.rank(); const auto &dims = t.dimensions(); @@ -287,6 +284,9 @@ namespace Grid { std::cout << std::endl; } } +#else +#define dump_tensor(args...) +#define DumpMemoryOrder(args...) #endif // Abstract writer/reader classes //////////////////////////////////////////// @@ -345,7 +345,8 @@ namespace Grid { typename std::enable_if::value, void>::type read(const std::string& s, U &output); template - typename std::enable_if::value, void>::type + typename std::enable_if::value + && !std::is_base_of, U>::value, void>::type read(const std::string& s, U &output); template void read(const std::string &s, iScalar &output); @@ -353,6 +354,12 @@ namespace Grid { void read(const std::string &s, iVector &output); template void read(const std::string &s, iMatrix &output); + template + typename std::enable_if, ETensor>::value && EigenIO::is_scalar::value, void>::type + read(const std::string &s, ETensor &output); + template + typename std::enable_if, ETensor>::value && EigenIO::is_container::value, void>::type + read(const std::string &s, ETensor &output); protected: template void fromString(U &output, const std::string &s); @@ -398,7 +405,7 @@ namespace Grid { template template typename std::enable_if::value - && !std::is_base_of, U>::value, void>::type + && !std::is_base_of, U>::value, void>::type Writer::write(const std::string &s, const U &output) { upcast->writeDefault(s, output); @@ -602,7 +609,8 @@ namespace Grid { template template - typename std::enable_if::value, void>::type + typename std::enable_if::value + && !std::is_base_of, U>::value, void>::type Reader::read(const std::string &s, U &output) { upcast->readDefault(s, output); @@ -638,6 +646,20 @@ namespace Grid { vecToTensor(output, v); } + template + template + typename std::enable_if, ETensor>::value && EigenIO::is_scalar::value, void>::type + Reader::read(const std::string &s, ETensor &output) + { + } + + template + template + typename std::enable_if, ETensor>::value && EigenIO::is_container::value, void>::type + Reader::read(const std::string &s, ETensor &output) + { + } + template template void Reader::fromString(U &output, const std::string &s) diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 7de6dc88..c1bb891c 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -68,6 +68,8 @@ namespace Grid template typename std::enable_if>::is_number, void>::type readDefault(const std::string &s, std::vector &x); + template + void readMultiDim(const std::string &s, std::vector &buf, std::vector &dim); H5NS::Group & getGroup(void); private: template @@ -182,10 +184,9 @@ namespace Grid template <> void Hdf5Reader::readDefault(const std::string &s, std::string &x); - + template - typename std::enable_if>::is_number, void>::type - Hdf5Reader::readDefault(const std::string &s, std::vector &x) + void Hdf5Reader::readMultiDim(const std::string &s, std::vector &buf, std::vector &dim) { // alias to element type typedef typename element>::type Element; @@ -193,7 +194,6 @@ namespace Grid // read the dimensions H5NS::DataSpace dataSpace; std::vector hdim; - std::vector dim; hsize_t size = 1; if (group_.attrExists(s)) @@ -213,8 +213,8 @@ namespace Grid } // read the flat vector - std::vector buf(size); - + buf.resize(size); + if (size * sizeof(Element) > dataSetThres_) { H5NS::DataSet dataSet; @@ -229,7 +229,19 @@ namespace Grid attribute = group_.openAttribute(s); attribute.read(Hdf5Type::type(), buf.data()); } - + } + + template + typename std::enable_if>::is_number, void>::type + Hdf5Reader::readDefault(const std::string &s, std::vector &x) + { + // alias to element type + typedef typename element>::type Element; + + std::vector dim; + std::vector buf; + readMultiDim( s, buf, dim ); + // reconstruct the multidimensional vector Reconstruct> r(buf, dim); From c77069244dafa391fc6b87fe0a79719ff99b766b Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 18 Feb 2019 08:55:50 +0000 Subject: [PATCH 100/347] Nearly ready. Just finishing off readback and compare --- Grid/serialisation/BaseIO.h | 177 ++++++++++++++++++++++++++++----- tests/IO/Test_serialisation.cc | 36 ++++--- 2 files changed, 177 insertions(+), 36 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 96bb3bb7..7793b061 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -58,16 +58,32 @@ namespace Grid { std::is_base_of, T>::value> {}; // Is this an Eigen tensor of a supported scalar - template struct is_tensor_of_scalar : std::integral_constant::value && is_scalar::value> {}; + template struct is_tensor_of_scalar : public std::false_type {}; + template struct is_tensor_of_scalar::value && is_scalar::value, void>::type> : public std::true_type {}; // Is this an Eigen tensor of a supported container - template struct is_tensor_of_container : std::integral_constant::value && is_container::value> {}; + template struct is_tensor_of_container : public std::false_type {}; + template struct is_tensor_of_container::value && is_container::value, void>::type> : public std::true_type {}; - // These traits describe the Eigen tensor scalar objects supported for IO - // Mostly intended for grid tensors, i.e. compositions of iScalar, iVector and iMatrix - // but also supports fixed size arrays + // Is this a fixed-size Eigen tensor + template struct is_tensor_fixed : public std::false_type {}; + template + struct is_tensor_fixed> + : public std::true_type {}; + template class MapPointer_> + struct is_tensor_fixed, MapOptions_, MapPointer_>> + : public std::true_type {}; + + // Is this a variable-size Eigen tensor + template struct is_tensor_variable : public std::false_type {}; + template struct is_tensor_variable::value + && !is_tensor_fixed::value, void>::type> : public std::true_type {}; + + // These traits describe the Eigen tensor scalar and container objects supported for IO + // Containers arbitrarily deeply nested compositions of fixed size objects: + // ... grid tensors (iScalar, iVector, and iMatrix) and fixed size arrays template struct Traits {}; // C needed for specialisation // This defines the bottom level - i.e. it's a description of the underlying scalar template struct Traits::value, void>::type> { @@ -238,7 +254,7 @@ namespace Grid { } // Helper to dump a tensor in memory order - // Kind of superfluous given the above + // Kind of superfluous given the above ... just keeping in case I need to fall back to this #define DumpMemoryOrder(args...) DumpMemoryOrder_func(args) template typename std::enable_if::value, void>::type @@ -307,7 +323,7 @@ namespace Grid { write(const std::string& s, const U &output); template typename std::enable_if::value - && !std::is_base_of, U>::value, void>::type + && !EigenIO::is_tensor::value, void>::type write(const std::string& s, const U &output); template void write(const std::string &s, const iScalar &output); @@ -316,10 +332,10 @@ namespace Grid { template void write(const std::string &s, const iMatrix &output); template - typename std::enable_if, ETensor>::value && EigenIO::is_scalar::value, void>::type + typename std::enable_if::value, void>::type write(const std::string &s, const ETensor &output); template - typename std::enable_if, ETensor>::value && EigenIO::is_container::value, void>::type + typename std::enable_if::value, void>::type write(const std::string &s, const ETensor &output); void scientificFormat(const bool set); @@ -346,7 +362,7 @@ namespace Grid { read(const std::string& s, U &output); template typename std::enable_if::value - && !std::is_base_of, U>::value, void>::type + && !EigenIO::is_tensor::value, void>::type read(const std::string& s, U &output); template void read(const std::string &s, iScalar &output); @@ -355,11 +371,20 @@ namespace Grid { template void read(const std::string &s, iMatrix &output); template - typename std::enable_if, ETensor>::value && EigenIO::is_scalar::value, void>::type + typename std::enable_if::value, void>::type read(const std::string &s, ETensor &output); template - typename std::enable_if, ETensor>::value && EigenIO::is_container::value, void>::type - read(const std::string &s, ETensor &output); + typename std::enable_if::value, void>::type + Reshape(ETensor &t, const std::array &dims ); + template + typename std::enable_if::value, void>::type + Reshape(ETensor &t, const std::array &dims ); + template + typename std::enable_if::value, std::size_t>::type + DimSize(ETensor &t, std::size_t dim ); + template + typename std::enable_if::value, std::size_t>::type + DimSize(ETensor &t, std::size_t dim ); protected: template void fromString(U &output, const std::string &s); @@ -405,7 +430,7 @@ namespace Grid { template template typename std::enable_if::value - && !std::is_base_of, U>::value, void>::type + && !EigenIO::is_tensor::value, void>::type Writer::write(const std::string &s, const U &output) { upcast->writeDefault(s, output); @@ -436,7 +461,7 @@ namespace Grid { // Eigen::Tensors of arithmetic/complex base type template template - typename std::enable_if, ETensor>::value && EigenIO::is_scalar::value, void>::type + typename std::enable_if::value, void>::type Writer::write(const std::string &s, const ETensor &output) { const typename ETensor::Index NumElements{output.size()}; @@ -489,7 +514,7 @@ namespace Grid { // Eigen::Tensors of Grid tensors (iScalar, iVector, iMatrix) template template - typename std::enable_if, ETensor>::value && EigenIO::is_container::value, void>::type + typename std::enable_if::value, void>::type Writer::write(const std::string &s, const ETensor &output) { const typename ETensor::Index NumElements{output.size()}; @@ -610,7 +635,7 @@ namespace Grid { template template typename std::enable_if::value - && !std::is_base_of, U>::value, void>::type + && !EigenIO::is_tensor::value, void>::type Reader::read(const std::string &s, U &output) { upcast->readDefault(s, output); @@ -648,16 +673,113 @@ namespace Grid { template template - typename std::enable_if, ETensor>::value && EigenIO::is_scalar::value, void>::type + typename std::enable_if::value, void>::type Reader::read(const std::string &s, ETensor &output) { + // alias to element type + using Scalar = typename EigenIO::Traits::scalar_type; + + // read the (flat) data and dimensionality + std::vector dimData; + std::vector buf; + upcast->readMultiDim( s, buf, dimData ); + // Make sure that the number of elements read matches dimensions read + const std::size_t NumElements{buf.size()}; + std::size_t NumElements_check = 1; + std::size_t RankRequired = 0; + std::vector dimNonTrivial; + dimNonTrivial.reserve(dimData.size()); + for( auto d : dimData ) { + NumElements_check *= d; + if( d > 1 ) { + RankRequired++; + dimNonTrivial.push_back(d); + } + } + //if( RankRequired == 0 ) RankRequired++; + assert( NumElements_check == NumElements ); + // Make sure our object has the right rank + using Container = typename ETensor::Scalar; + const auto InnerRank = EigenIO::Traits::rank_non_trivial; + assert( ETensor::NumDimensions + InnerRank >= RankRequired ); + bool bShapeOK = true; + std::size_t RankNonTrivial = 0; + // Make sure fixed dimension objects have allocated memory + using ETDims = std::array; + ETDims dimsNew; + /*if constexpr( EigenIO::is_tensor_fixed::value ) { + for( auto &d : dimsNew ) d = 0; + output( dimsNew ) = 0; + }*/ + //const auto & dims{output.dimensions()}; + for( auto i = 0, j = 0 ; bShapeOK && i < ETensor::NumDimensions ; i++ ) { + auto d = DimSize( output, i ); + if( d < 1 ) + bShapeOK = false; + else if( d > 1 ) { + RankNonTrivial++; + if( d != dimNonTrivial[j] ) + bShapeOK = false; + j++; + } + dimsNew[i] = d; + } + //if( RankNonTrivial == 0 ) RankNonTrivial++; + // Make the tensor the same size as the data read + if ( !bShapeOK || RankNonTrivial != RankRequired ) { + for( auto i = 0 ; i < ETensor::NumDimensions ; i++ ) + dimsNew[i] = ( i < RankRequired ) ? dimNonTrivial[i] : 1; + Reshape(output, dimsNew); + } + // Copy the data into the tensor + ETDims MyIndex; + for( auto &d : MyIndex ) d = 0; + std::size_t idx = 0; + for( auto n = 0 ; n < NumElements ; n++ ) { + Container & c = output( MyIndex ); + if constexpr( InnerRank == 0 ) { + c = buf[idx++]; + } else { + for( Scalar & s : c ) + s = buf[idx++]; + } + // Now increment the index + for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) + MyIndex[i] = 0; + } } template template - typename std::enable_if, ETensor>::value && EigenIO::is_container::value, void>::type - Reader::read(const std::string &s, ETensor &output) + typename std::enable_if::value, void>::type + Reader::Reshape(ETensor &t, const std::array &dims ) { + //assert( 0 && "EigenIO: Fixed tensor dimensions can't be changed" ); + } + + template + template + typename std::enable_if::value, void>::type + Reader::Reshape(ETensor &t, const std::array &dims ) + { + //t.reshape( dims ); + t.resize( dims ); + } + + template + template + typename std::enable_if::value, std::size_t>::type + Reader::DimSize(ETensor &t, std::size_t dim ) + { + return 0;//ETensor::Dimension[dim]; + } + + template + template + typename std::enable_if::value, std::size_t>::type + Reader::DimSize(ETensor &t, std::size_t dim ) + { + return t.dimension(dim); } template @@ -708,8 +830,15 @@ namespace Grid { template static inline typename std::enable_if, T>::value, bool>::type CompareMember(const T &lhs, const T &rhs) { - Eigen::Tensor bResult = (lhs == rhs).all(); - return bResult(0); + // First check whether dimensions match (Eigen tensor library will assert if they don't match) + bool bReturnValue = true; + for( auto i = 0 ; bReturnValue && i < T::NumIndices ; i++ ) + bReturnValue = ( lhs.dimension(i)) == rhs.dimension(i); + if( bReturnValue ) { + Eigen::Tensor bResult = (lhs == rhs).all(); + bReturnValue = bResult(0); + } + return bReturnValue; } template diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 509241ab..b330d133 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -82,6 +82,7 @@ bool b = false; template void ioTest(const std::string &filename, const O &object, const std::string &name) { + std::cout << name << " IO test: writing ..."; // writer needs to be destroyed so that writing physically happens { W writer(filename); @@ -89,21 +90,22 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam write(writer, "testobject", object); } + std::cout << " done. reading..."; R reader(filename); O buf; -#ifndef DEBUG read(reader, "testobject", buf); - bool good = (object == buf); - std::cout << name << " IO test: " << std::endl; - if (!good) exit(EXIT_FAILURE); -#endif + bool good = Serializable::CompareMember(object, buf); + if (!good) { + std::cout << " failure!" << std::endl; + exit(EXIT_FAILURE); + } + std::cout << " done." << std::endl; } #ifdef DEBUG //typedef int TestScalar; typedef std::complex TestScalar; -typedef Eigen::Tensor, 6> TestTensorSingle; typedef Eigen::Tensor TestTensor; typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TestTensorFixed; typedef std::vector aTestTensorFixed; @@ -125,17 +127,27 @@ public: }; bool EigenIOTest(void) { - constexpr TestScalar Inc{1,-1}; - TestTensorSingle ts(1,1,1,1,1,1); - ts(0,0,0,0,0,0) = Inc * 3.1415927; - ioTest("iotest_single.h5", ts, "Singlet"); - SpinColourVector scv, scv2; scv2 = scv; ioTest("iotest_vector.h5", scv, "SpinColourVector"); SpinColourMatrix scm; ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); - + + constexpr TestScalar Inc{1,-1}; + { + using TestTensorSingle = Eigen::TensorFixedSize>; + TestTensorSingle ts; + ts(0) = 7; // lucky + ioTest("iotest_single.h5", ts, "Tensor_single"); + } + + { + using TestTensorSimple = Eigen::Tensor, 6>; + TestTensorSimple ts(1,1,1,1,1,1); + ts(0,0,0,0,0,0) = Inc * 3.1415927; + ioTest("iotest_simple.h5", ts, "Tensor_simple"); + } + TestTensor t(6,3,2); TestScalar Val{Inc}; for( int i = 0 ; i < t.dimension(0) ; i++) From 625ccfcd72b46477d3c97108f7f7af04af27530a Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 18 Feb 2019 13:10:34 +0000 Subject: [PATCH 101/347] continued baryon contraction code --- Hadrons/Modules/MDistil/BContraction.hpp | 81 +++++++++++++----------- tests/hadrons/Test_hadrons_distil.cc | 4 +- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 37578e09..bddbe8a4 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -175,27 +175,12 @@ void TBContraction::execute(void) }; std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; using BaryonTensorSet = Eigen::Tensor; - BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); + int Ngamma=3; + BaryonTensorSet BField3(Nmom,4*Ngamma,Nt,N_1,N_2,N_3); Eigen::Tensor corr(Nmom,4,Nt); - //Needs more work - but this is important for contraction - /* int Npairs = 0; - char left[] = "uud"; - char right[] = "uud"; - std::vector> pairs; - for (int il=0, i=0 ; il < 3 ; il++){ - for (int ir=0 ; ir < 3 ; ir++){ - if (il>ir) continue; - if (left[il] != right[il]) continue; - pairs[i][0]=il; - pairs[i][1]=ir; - i++; - Npairs = i; - } - std::cout << "pairs: " << pairs << std::endl; - } - */ + Complex diquark2; for (int i1=0 ; i1 < N_1 ; i1++){ for (int i2=0 ; i2 < N_2 ; i2++){ @@ -215,14 +200,16 @@ void TBContraction::execute(void) tmp22s()(is)() = tmp22[sU]()(is)(epsilon[ie][1]); tmp33s()(is)() = tmp33[sU]()(is)(epsilon[ie][2]); } - tmp333 = Gamma(gamma23_[0])*tmp33s; - tmp111 = Gamma(gamma12_[0])*tmp11s; - tmp222 = g4*tmp111; - tmp111 = 0.5*(double)parity*(tmp111 + tmp222); // P_\pm * ... - diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); - for (int is=0 ; is < 4 ; is++){ - BField3(imom,is,t,i1,i2,i3)+=(double)epsilon_sgn[ie]*tmp111()(is)()*diquark2; - } + for (int ig=0 ; ig < Ngamma ; ig++){ + tmp333 = Gamma(gamma23_[ig])*tmp33s; + tmp111 = Gamma(gamma12_[ig])*tmp11s; + tmp222 = g4*tmp111; + tmp111 = 0.5*(double)parity*(tmp111 + tmp222); // P_\pm * ... + diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); + for (int is=0 ; is < 4 ; is++){ + BField3(imom,is+4*ig,t,i1,i2,i3)+=(double)epsilon_sgn[ie]*tmp111()(is)()*diquark2; + } + } } } } @@ -236,17 +223,37 @@ void TBContraction::execute(void) } } - //Product ijk * ijk - // for ijk * jik: (0,1),(1,0),(2,2) z.b. - Eigen::array, 3> product_dims = { Eigen::IndexPair(0,0),Eigen::IndexPair(1,1) ,Eigen::IndexPair(2,2) }; - for (int imom=0 ; imom < Nmom ; imom++){ - Eigen::Tensor B5 = BField3.chip(imom,0); - for (int is=0 ; is < 4 ; is++){ - Eigen::Tensor B4 = B5.chip(is,0); - for (int t=0 ; t < Nt ; t++){ - Eigen::Tensor B3 = B4.chip(t,0); - Eigen::Tensor C2 = B3.contract(B3,product_dims); - corr(imom,is,t) = C2(0); + int Npairs = 0; + char left[] = "uud"; + char right[] = "uud"; + std::vector pairs(6); + for (int ie=0, i=0 ; ie < 6 ; ie++){ + if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]){ + pairs[i] = ie; + i++; + Npairs++; + } + } + pairs.resize(Npairs); + std::cout << Npairs << " pairs: " << pairs << std::endl; + for (int imom=0 ; imom < Nmom ; imom++){ + for (int is=0 ; is < 4 ; is++){ + for (int t=0 ; t < Nt ; t++){ + corr(imom,is,t) = 0.; + } + } + } + for (int ipair=0 ; ipair < Npairs ; ipair++){ + Eigen::array, 3> product_dims = { Eigen::IndexPair(0,epsilon[pairs[ipair]][0]),Eigen::IndexPair(1,epsilon[pairs[ipair]][1]) ,Eigen::IndexPair(2,epsilon[pairs[ipair]][2]) }; + for (int imom=0 ; imom < Nmom ; imom++){ + Eigen::Tensor B5 = BField3.chip(imom,0); + for (int is=0 ; is < 4 ; is++){ + Eigen::Tensor B4 = B5.chip(is,0); + for (int t=0 ; t < Nt ; t++){ + Eigen::Tensor B3 = B4.chip(t,0); + Eigen::Tensor C2 = B3.contract(B3,product_dims); + corr(imom,is,t) += C2(0); + } } } } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 3c38e915..c10cd42f 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -506,8 +506,8 @@ void EigenSliceExample() {600, 700, 800}, {900, 1000, 1100}}); std::cout << "a\n" << a << std::endl; DumpMemoryOrder( a, "a" ); - Eigen::array offsets = {1, 0}; - Eigen::array extents = {2, 2}; + Eigen::array offsets = {0, 1}; + Eigen::array extents = {4, 2}; T2 slice = a.slice(offsets, extents); std::cout << "slice\n" << slice << std::endl; DumpMemoryOrder( slice, "slice" ); From 6e822b720110f99b89b7d922d52d69bf247d2d13 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 18 Feb 2019 15:21:13 +0000 Subject: [PATCH 102/347] added sign for contraction sum --- Hadrons/Modules/MDistil/BContraction.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index bddbe8a4..75c505cc 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -252,7 +252,7 @@ void TBContraction::execute(void) for (int t=0 ; t < Nt ; t++){ Eigen::Tensor B3 = B4.chip(t,0); Eigen::Tensor C2 = B3.contract(B3,product_dims); - corr(imom,is,t) += C2(0); + corr(imom,is,t) += (double)epsilon_sgn[pairs[ipair]]*C2(0); } } } From 04b58de5de9b4b468104a9ca68b8b2a2ccecc1bf Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 18 Feb 2019 17:12:27 +0000 Subject: [PATCH 103/347] Read-back working. --- Grid/serialisation/BaseIO.h | 62 +++++++++++++++++---------- tests/IO/Test_serialisation.cc | 63 +++++++++++++++++++--------- tests/hadrons/Test_hadrons_distil.cc | 24 +++++++---- 3 files changed, 100 insertions(+), 49 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 7793b061..11dfee0e 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -42,6 +42,7 @@ namespace Grid { : std::integral_constant::value> {}; // Eigen tensors can be composed of arithmetic scalar and complex types + // TODO Support Grid::comples from GPU port template struct is_scalar : std::integral_constant::value || is_complex::value> {}; @@ -202,10 +203,10 @@ namespace Grid { Scalar * pScalar = ET.data(); for( std::size_t j = 0; j < NumScalars; j++ ) { // if constexpr is C++ 17 ... but otherwise need two specialisations (Container vs Scalar) - if constexpr ( InnerRank == 0 ) { + if constexpr ( EigenIO::is_scalar::value ) { lambda( * pScalar, Seq++, MyIndex ); } else { - for( typename Scalar::scalar_type &Source : * pScalar ) { + for( typename EigenIO::Traits::scalar_type &Source : * pScalar ) { lambda(Source, Seq++, MyIndex ); // Now increment SubIndex for( auto i = rank + InnerRank - 1; i != rank - 1 && ++MyIndex[i] == Dims[i]; i-- ) @@ -244,7 +245,7 @@ namespace Grid { std::cout << pName; for( auto i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; std::cout << " in memory order:" << std::endl; - for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array Dims ){ + for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array &Dims ){ std::cout << " "; for( auto dim : Dims ) std::cout << "[" << dim << "]"; @@ -253,6 +254,16 @@ namespace Grid { std::cout << "========================================" << std::endl; } + template + typename std::enable_if::value, void>::type + dump_tensor_func(T &t, const char * pName = nullptr) + { + std::cout << "Dumping non-tensor object "; + if( pName ) + std::cout << pName; + std::cout << "=" << t; + } + // Helper to dump a tensor in memory order // Kind of superfluous given the above ... just keeping in case I need to fall back to this #define DumpMemoryOrder(args...) DumpMemoryOrder_func(args) @@ -379,12 +390,12 @@ namespace Grid { template typename std::enable_if::value, void>::type Reshape(ETensor &t, const std::array &dims ); - template + /*template typename std::enable_if::value, std::size_t>::type DimSize(ETensor &t, std::size_t dim ); template typename std::enable_if::value, std::size_t>::type - DimSize(ETensor &t, std::size_t dim ); + DimSize(ETensor &t, std::size_t dim );*/ protected: template void fromString(U &output, const std::string &s); @@ -677,43 +688,52 @@ namespace Grid { Reader::read(const std::string &s, ETensor &output) { // alias to element type - using Scalar = typename EigenIO::Traits::scalar_type; + using Container = typename ETensor::Scalar; + using Traits = EigenIO::Traits; + using Scalar = typename Traits::scalar_type; // read the (flat) data and dimensionality std::vector dimData; std::vector buf; upcast->readMultiDim( s, buf, dimData ); // Make sure that the number of elements read matches dimensions read - const std::size_t NumElements{buf.size()}; - std::size_t NumElements_check = 1; + std::size_t NumElements = 1; std::size_t RankRequired = 0; std::vector dimNonTrivial; dimNonTrivial.reserve(dimData.size()); for( auto d : dimData ) { - NumElements_check *= d; + NumElements *= d; if( d > 1 ) { RankRequired++; dimNonTrivial.push_back(d); } } - //if( RankRequired == 0 ) RankRequired++; - assert( NumElements_check == NumElements ); + assert( NumElements == buf.size() && "Number of elements read back <> product of dimensions" ); + // If our scalar object is a Container, make sure it's dimensions match what we read back + const auto InnerRank{Traits::rank_non_trivial}; + if ( InnerRank > 0 ) { + assert( RankRequired >= InnerRank && "Tensor Container too complex for data" ); + for( auto i = InnerRank - 1 ; i != -1 ; i-- ) { + auto d = dimNonTrivial[--RankRequired]; + assert( d == Traits::DimensionNT(i) && "Tensor Container dimensions don't match data" ); + NumElements /= d; + dimNonTrivial.pop_back(); + } + } // Make sure our object has the right rank - using Container = typename ETensor::Scalar; - const auto InnerRank = EigenIO::Traits::rank_non_trivial; - assert( ETensor::NumDimensions + InnerRank >= RankRequired ); + assert( ETensor::NumDimensions >= RankRequired ); bool bShapeOK = true; std::size_t RankNonTrivial = 0; - // Make sure fixed dimension objects have allocated memory + const auto & dims{output.dimensions()}; using ETDims = std::array; ETDims dimsNew; + // Make sure fixed dimension objects have allocated memory /*if constexpr( EigenIO::is_tensor_fixed::value ) { for( auto &d : dimsNew ) d = 0; output( dimsNew ) = 0; }*/ - //const auto & dims{output.dimensions()}; for( auto i = 0, j = 0 ; bShapeOK && i < ETensor::NumDimensions ; i++ ) { - auto d = DimSize( output, i ); + auto d = dims[i]; if( d < 1 ) bShapeOK = false; else if( d > 1 ) { @@ -737,14 +757,14 @@ namespace Grid { std::size_t idx = 0; for( auto n = 0 ; n < NumElements ; n++ ) { Container & c = output( MyIndex ); - if constexpr( InnerRank == 0 ) { + if constexpr ( EigenIO::is_scalar::value ) { c = buf[idx++]; } else { for( Scalar & s : c ) s = buf[idx++]; } // Now increment the index - for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) + for( int i = ETensor::NumDimensions - 1; i >= 0 && ++MyIndex[i] == dims[i]; i-- ) MyIndex[i] = 0; } } @@ -766,7 +786,7 @@ namespace Grid { t.resize( dims ); } - template + /*template template typename std::enable_if::value, std::size_t>::type Reader::DimSize(ETensor &t, std::size_t dim ) @@ -780,7 +800,7 @@ namespace Grid { Reader::DimSize(ETensor &t, std::size_t dim ) { return t.dimension(dim); - } + }*/ template template diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index b330d133..73045155 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -98,6 +98,8 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam bool good = Serializable::CompareMember(object, buf); if (!good) { std::cout << " failure!" << std::endl; + if constexpr (EigenIO::is_tensor::value) + dump_tensor(buf,"???"); exit(EXIT_FAILURE); } std::cout << " done." << std::endl; @@ -109,21 +111,28 @@ typedef std::complex TestScalar; typedef Eigen::Tensor TestTensor; typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TestTensorFixed; typedef std::vector aTestTensorFixed; -typedef Eigen::TensorFixedSize> LSCTensor; -typedef Eigen::TensorFixedSize> LCMTensor; +typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> LSCTensor; +typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> LCMTensor; // From Test_serialisation.cc -class ETSerClass: Serializable { +class PerambIOTestClass: Serializable { public: - GRID_SERIALIZABLE_CLASS_MEMBERS(ETSerClass - , SpinColourVector, scv - , SpinColourMatrix, scm - , TestTensor, Critter - , TestTensorFixed, FixedCritter - , aTestTensorFixed, aFixedCritter - , LSCTensor, MyLSCTensor - , LCMTensor, MyLCMTensor + using PerambTensor = Eigen::Tensor; + GRID_SERIALIZABLE_CLASS_MEMBERS(PerambIOTestClass + //, SpinColourVector, scv + //, SpinColourMatrix, scm + , PerambTensor, Perambulator + , std::vector, DistilParameterNames + , std::vector, DistilParameterValues + //, TestTensor, Critter + //, TestTensorFixed, FixedCritter + //, aTestTensorFixed, aFixedCritter + //, LSCTensor, MyLSCTensor + //, LCMTensor, MyLCMTensor ); - ETSerClass() : Critter(7,3,2), aFixedCritter(3) {} + PerambIOTestClass() : Perambulator(2,3,1,4,5,1), + DistilParameterNames {"alpha", "beta", "gamma", "delta", "epsilon", "what's f?"}, + DistilParameterValues{2,3,1,4,5,1}//, Critter(7,3,2), aFixedCritter(3) + {} }; bool EigenIOTest(void) { @@ -157,7 +166,7 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); - dump_tensor(t, "t"); + //dump_tensor(t, "t"); // Now serialise a fixed size tensor using FixedTensor = Eigen::TensorFixedSize>; @@ -170,11 +179,26 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_tensor_fixed.h5", tf, "eigen_tensor_fixed_name"); - dump_tensor(tf, "tf"); + //dump_tensor(tf, "tf"); + + PerambIOTestClass o; + for_all( o.Perambulator, [&](TestScalar &c, float f, const std::array::rank_non_trivial> &Dims ){ + c = TestScalar{f,-f}; + //std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; + } ); + dump_tensor(o.Perambulator, "PerambIOTestClass" ); + /*for_all( o.FixedCritter, [&](TestScalar &c, float f, const std::array &Dims ){ + c = TestScalar{f,-f}; + //std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; + } ); + for( auto &z : o.aFixedCritter ) + for_all( z, [&](TestScalar &c, float f, const std::array &Dims ){ + c = TestScalar{f,-f}; + //std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; + } );*/ + ioTest("iotest_object.h5", o, "PerambIOTestClass_object_instance_name"); + //DumpMemoryOrder(o.Perambulator); - ETSerClass o; - ioTest("iotest_object.h5", o, "ETSerClass_object_instance_name"); - // Tensor of spin colour LSCTensor l; Val = 0; @@ -188,7 +212,7 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_LSCTensor.h5", l, "LSCTensor_object_instance_name"); - dump_tensor(l, "l"); + //dump_tensor(l, "l"); // Tensor of spin colour LCMTensor l2; @@ -204,7 +228,8 @@ bool EigenIOTest(void) { Val += Inc; } ioTest("iotest_LCMTensor.h5", l2, "LCMTensor_object_instance_name"); - + //dump_tensor(l2, "l2"); + std::cout << "Wow!" << std::endl; return true; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 3c38e915..00a681f0 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -315,28 +315,30 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; -void DebugShowTensor(MyTensor &x, const char * n) +template +void DebugShowTensor(T &x, const char * n) { const MyTensor::Index s{x.size()}; std::cout << n << ".size() = " << s << std::endl; std::cout << n << ".NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; std::cout << n << ".NumIndices = " << x.NumIndices << std::endl; - const MyTensor::Dimensions & d{x.dimensions()}; - std::cout << n << ".dimensions().size() = " << d.size() << std::endl; + const auto d{x.dimensions()}; + //std::cout << n << ".dimensions().size() = " << d.size() << std::endl; std::cout << "Dimensions are "; - for(auto i : d ) std::cout << "[" << i << "]"; + for(auto i = 0; i < x.NumDimensions ; i++) + std::cout << "[" << d[i] << "]"; std::cout << std::endl; MyTensor::Index SizeCalculated{1}; std::cout << "Dimensions again"; - for(int i=0 ; i < d.size() ; i++ ) { - std::cout << " : [" << i << ", " << x.IndexNames[i] << "]=" << d[i]; + for(int i=0 ; i < x.NumDimensions ; i++ ) { + std::cout << " : [" << i << /*", " << x.IndexNames[i] << */"]=" << x.dimension(i); SizeCalculated *= d[i]; } std::cout << std::endl; std::cout << "SizeCalculated = " << SizeCalculated << std::endl;\ assert( SizeCalculated == s ); // Initialise - assert( d.size() == 3 ); + assert( x.NumDimensions == 3 ); for( int i = 0 ; i < d[0] ; i++ ) for( int j = 0 ; j < d[1] ; j++ ) for( int k = 0 ; k < d[2] ; k++ ) { @@ -345,7 +347,7 @@ void DebugShowTensor(MyTensor &x, const char * n) } // Show raw data std::cout << "Data follow : " << std::endl; - Complex * p = x.data(); + typename T::Scalar * p = x.data(); for( auto i = 0 ; i < s ; i++ ) { if( i ) std::cout << ", "; std::cout << n << ".data()[" << i << "]=" << * p++; @@ -415,6 +417,10 @@ void DebugTestTypeEqualities(void) bool DebugEigenTest() { + { + Eigen::TensorFixedSize,Eigen::Sizes<3,4,5>> x; + DebugShowTensor(x, "fixed"); + } const char pszTestFileName[] = "test_tensor.bin"; std::array as={"Alpha", "Beta", "Gamma"}; MyTensor x(as, 2,1,4); @@ -636,7 +642,7 @@ int main(int argc, char *argv[]) << ", sizeof(std::size_t) = " << sizeof(std::size_t) << ", sizeof(std::streamsize) = " << sizeof(std::streamsize) << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; - //if( DebugEigenTest() ) return 0; + if( DebugEigenTest() ) return 0; if(DebugGridTensorTest()) return 0; #endif From 6ebb32ffbf22f6ae845bd28d4ae4c3c2fca1e464 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 18 Feb 2019 21:40:53 +0000 Subject: [PATCH 104/347] Rationalised Test_serialisation --- Grid/serialisation/BaseIO.h | 20 ++++++++++++++- tests/IO/Test_serialisation.cc | 46 +++++++++++++++++----------------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 11dfee0e..6adb87d3 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -230,6 +230,24 @@ namespace Grid { } } + // Used for sequential initialisations + template constexpr T Flag = 1; + template constexpr std::complex Flag> {1, -1}; + // Returns the type of the real part of an arithmetic type + template struct RealType { using type = T; }; + template struct RealType> { using type = T; }; + + template + typename std::enable_if::value, void>::type + Sequential_Init( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = Flag::scalar_type> ) + { + using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + c = Inc * static_cast::type>(n); + } ); + } + // Helper to dump a tensor #ifdef DEBUG #define dump_tensor(args...) dump_tensor_func(args) @@ -774,7 +792,7 @@ namespace Grid { typename std::enable_if::value, void>::type Reader::Reshape(ETensor &t, const std::array &dims ) { - //assert( 0 && "EigenIO: Fixed tensor dimensions can't be changed" ); + assert( 0 && "EigenIO: Fixed tensor dimensions can't be changed" ); } template diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 73045155..ae61eebe 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -105,7 +105,8 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam std::cout << " done." << std::endl; } -#ifdef DEBUG +#ifdef HAVE_HDF5 +typedef Eigen::Tensor ShortRank5Tensor; //typedef int TestScalar; typedef std::complex TestScalar; typedef Eigen::Tensor TestTensor; @@ -118,24 +119,32 @@ class PerambIOTestClass: Serializable { public: using PerambTensor = Eigen::Tensor; GRID_SERIALIZABLE_CLASS_MEMBERS(PerambIOTestClass - //, SpinColourVector, scv - //, SpinColourMatrix, scm - , PerambTensor, Perambulator + , ShortRank5Tensor, shortRank5Tensor + , PerambTensor, Perambulator , std::vector, DistilParameterNames , std::vector, DistilParameterValues + , PerambTensor, Perambulator2 + , SpinColourVector, scv + , SpinColourMatrix, scm //, TestTensor, Critter //, TestTensorFixed, FixedCritter //, aTestTensorFixed, aFixedCritter //, LSCTensor, MyLSCTensor //, LCMTensor, MyLCMTensor ); - PerambIOTestClass() : Perambulator(2,3,1,4,5,1), + PerambIOTestClass() : Perambulator(2,3,1,4,5,1), Perambulator2(7,1,6,1,5,1), DistilParameterNames {"alpha", "beta", "gamma", "delta", "epsilon", "what's f?"}, - DistilParameterValues{2,3,1,4,5,1}//, Critter(7,3,2), aFixedCritter(3) - {} + DistilParameterValues{2,3,1,4,5,1} + , shortRank5Tensor{5,4,3,2,1} + //, Critter(7,3,2)//, aFixedCritter(3) + { + Sequential_Init(Perambulator); + Sequential_Init(Perambulator2, {-3.1415927,7}); + Sequential_Init(shortRank5Tensor); + } }; -bool EigenIOTest(void) { +void EigenHdf5IOTest(void) { SpinColourVector scv, scv2; scv2 = scv; ioTest("iotest_vector.h5", scv, "SpinColourVector"); @@ -182,11 +191,8 @@ bool EigenIOTest(void) { //dump_tensor(tf, "tf"); PerambIOTestClass o; - for_all( o.Perambulator, [&](TestScalar &c, float f, const std::array::rank_non_trivial> &Dims ){ - c = TestScalar{f,-f}; - //std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; - } ); - dump_tensor(o.Perambulator, "PerambIOTestClass" ); + //dump_tensor(o.Perambulator, "Perambulator" ); + dump_tensor(o.shortRank5Tensor, "shortRank5Tensor"); /*for_all( o.FixedCritter, [&](TestScalar &c, float f, const std::array &Dims ){ c = TestScalar{f,-f}; //std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; @@ -229,10 +235,6 @@ bool EigenIOTest(void) { } ioTest("iotest_LCMTensor.h5", l2, "LCMTensor_object_instance_name"); //dump_tensor(l2, "l2"); - - std::cout << "Wow!" << std::endl; - - return true; } #endif @@ -259,7 +261,6 @@ int main(int argc,char **argv) Grid_init(&argc,&argv); std::cout << std::boolalpha << "==== basic IO" << std::endl; // display true / false for boolean -#ifndef DEBUG GridSerialRNG rng; rng.SeedFixedIntegers(std::vector({42,10,81,9})); @@ -283,7 +284,6 @@ int main(int argc,char **argv) // test serializable class writing myclass obj(1234); // non-trivial constructor std::vector vec; - std::pair pair; std::cout << "-- serialisable class writing to 'bother.xml'..." << std::endl; write(WR,"obj",obj); @@ -291,7 +291,6 @@ int main(int argc,char **argv) vec.push_back(obj); vec.push_back(myclass(5678)); vec.push_back(myclass(3838)); - pair = std::make_pair(myenum::red, myenum::blue); write(WR, "objvec", vec); std::cout << "-- serialisable class writing to std::cout:" << std::endl; @@ -300,6 +299,7 @@ int main(int argc,char **argv) std::cout << "vec[0] == obj: " << (vec[0] == obj) << std::endl; std::cout << "vec[1] == obj: " << (vec[1] == obj) << std::endl; std::cout << "-- pair writing to std::cout:" << std::endl; + std::pair pair = std::make_pair(myenum::red, myenum::blue); std::cout << pair << std::endl; // read tests @@ -321,6 +321,8 @@ int main(int argc,char **argv) #ifdef HAVE_HDF5 ioTest("iotest.h5", obj, "HDF5 (object) "); ioTest("iotest.h5", vec, "HDF5 (vector of objects)"); + std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl; + EigenHdf5IOTest(); #endif std::cout << "\n==== vector flattening/reconstruction" << std::endl; @@ -364,8 +366,6 @@ int main(int argc,char **argv) tensorConvTest(rng, ColourVector); tensorConvTest(rng, SpinMatrix); tensorConvTest(rng, SpinVector); -#elif HAVE_HDF5 - if(! EigenIOTest() ) exit(EXIT_FAILURE); -#endif + Grid_finalize(); } From 63c97db41468b2b599a6650b9d9d809eabf4e98c Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 19 Feb 2019 13:29:08 +0000 Subject: [PATCH 105/347] Prior to rationalising 2 versions of BaseIO::write (scalar and vector) --- Grid/serialisation/BaseIO.h | 46 +++++--- tests/IO/Test_serialisation.cc | 192 +++++++++++++-------------------- 2 files changed, 105 insertions(+), 133 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 6adb87d3..771cc628 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -36,13 +36,19 @@ Author: Guido Cossu #include namespace Grid { + // TODO Support Grid::complex from GPU port + template using Grid_complex = std::complex; + + // Returns original type, except for Grid_complex, where it returns the underlying type + template struct RealType { using type = T; }; + template struct RealType> { using type = T; }; + namespace EigenIO { template struct is_complex : public std::false_type {}; - template struct is_complex> + template struct is_complex> : std::integral_constant::value> {}; // Eigen tensors can be composed of arithmetic scalar and complex types - // TODO Support Grid::comples from GPU port template struct is_scalar : std::integral_constant::value || is_complex::value> {}; @@ -88,11 +94,12 @@ namespace Grid { template struct Traits {}; // C needed for specialisation // This defines the bottom level - i.e. it's a description of the underlying scalar template struct Traits::value, void>::type> { + using scalar_type = T; // Type of the underlying scalar + using scalar_real = typename RealType::type; // real type underlying scalar_type static constexpr unsigned int depth = 0; // How many levels of Grid Tensor there are (TensorLevel) static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) - using scalar_type = T; // Type of the underlying scalar static constexpr std::size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes static constexpr std::size_t size = scalar_size * count; // total size of elements in bytes static constexpr std::size_t Dimension(unsigned int dim) { return 0; } // Dimension size @@ -114,11 +121,12 @@ namespace Grid { // count = 48 }; template struct Traits> { + using scalar_type = typename Traits::scalar_type; + using scalar_real = typename RealType::type; static constexpr unsigned int depth = 1 + Traits::depth; static constexpr unsigned int rank = 0 + Traits::rank; static constexpr unsigned int rank_non_trivial = 0 + Traits::rank_non_trivial; static constexpr unsigned int count = 1 * Traits::count; - using scalar_type = typename Traits::scalar_type; static constexpr std::size_t scalar_size = Traits::scalar_size; static constexpr std::size_t size = scalar_size * count; static constexpr std::size_t Dimension(unsigned int dim) { @@ -127,11 +135,12 @@ namespace Grid { return Traits::DimensionNT(dim); } }; template struct Traits> { + using scalar_type = typename Traits::scalar_type; + using scalar_real = typename RealType::type; static constexpr unsigned int depth = 1 + Traits::depth; static constexpr unsigned int rank = 1 + Traits::rank; static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + Traits::rank_non_trivial; static constexpr unsigned int count = N * Traits::count; - using scalar_type = typename Traits::scalar_type; static constexpr std::size_t scalar_size = Traits::scalar_size; static constexpr std::size_t size = scalar_size * count; static constexpr std::size_t Dimension(unsigned int dim) { @@ -141,11 +150,12 @@ namespace Grid { } }; template struct Traits> { + using scalar_type = typename Traits::scalar_type; + using scalar_real = typename RealType::type; static constexpr unsigned int depth = 1 + Traits::depth; static constexpr unsigned int rank = 2 + Traits::rank; static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + Traits::rank_non_trivial; static constexpr unsigned int count = N * N * Traits::count; - using scalar_type = typename Traits::scalar_type; static constexpr std::size_t scalar_size = Traits::scalar_size; static constexpr std::size_t size = scalar_size * count; static constexpr std::size_t Dimension(unsigned int dim) { @@ -230,16 +240,22 @@ namespace Grid { } } - // Used for sequential initialisations - template constexpr T Flag = 1; - template constexpr std::complex Flag> {1, -1}; - // Returns the type of the real part of an arithmetic type - template struct RealType { using type = T; }; - template struct RealType> { using type = T; }; + // Sequential initialisation of tensors + // Would have preferred to define template variables for this, but that's c++ 17 + template + typename std::enable_if::value && !EigenIO::is_complex::scalar_type>::value, void>::type + SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = 1 ) + { + using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + c = Inc * static_cast(n); + } ); + } template - typename std::enable_if::value, void>::type - Sequential_Init( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = Flag::scalar_type> ) + typename std::enable_if::value && EigenIO::is_complex::scalar_type>::value, void>::type + SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc={1,-1}) { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; @@ -247,7 +263,7 @@ namespace Grid { c = Inc * static_cast::type>(n); } ); } - + // Helper to dump a tensor #ifdef DEBUG #define dump_tensor(args...) dump_tensor_func(args) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index ae61eebe..342fc214 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -82,7 +82,7 @@ bool b = false; template void ioTest(const std::string &filename, const O &object, const std::string &name) { - std::cout << name << " IO test: writing ..."; + std::cout << "IO test: " << name << " -> " << filename << " ..."; // writer needs to be destroyed so that writing physically happens { W writer(filename); @@ -92,149 +92,105 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam std::cout << " done. reading..."; R reader(filename); - O buf; + std::unique_ptr buf( new O ); // In case object too big for stack - read(reader, "testobject", buf); - bool good = Serializable::CompareMember(object, buf); + read(reader, "testobject", *buf); + bool good = Serializable::CompareMember(object, *buf); if (!good) { std::cout << " failure!" << std::endl; if constexpr (EigenIO::is_tensor::value) - dump_tensor(buf,"???"); + dump_tensor(*buf,"???"); exit(EXIT_FAILURE); } std::cout << " done." << std::endl; } #ifdef HAVE_HDF5 -typedef Eigen::Tensor ShortRank5Tensor; -//typedef int TestScalar; typedef std::complex TestScalar; -typedef Eigen::Tensor TestTensor; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TestTensorFixed; -typedef std::vector aTestTensorFixed; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> LSCTensor; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> LCMTensor; -// From Test_serialisation.cc +using TensorSingle = Eigen::TensorFixedSize>; +using TensorSimple = Eigen::Tensor, 6>; +typedef Eigen::Tensor TensorRank5UShort; +typedef Eigen::Tensor TensorRank5IntAlt; +typedef Eigen::Tensor TensorRank3; +typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; +typedef std::vector aTensor_9_4_2; +typedef Eigen::TensorFixedSize> LSCTensor; +#ifdef DEBUG +typedef Eigen::TensorFixedSize,2>,7>,3>, Eigen::Sizes<2,2,11,10,9>, Eigen::StorageOptions::RowMajor> LCMTensor; +#endif + class PerambIOTestClass: Serializable { public: using PerambTensor = Eigen::Tensor; GRID_SERIALIZABLE_CLASS_MEMBERS(PerambIOTestClass - , ShortRank5Tensor, shortRank5Tensor - , PerambTensor, Perambulator + , SpinColourVector, spinColourVector + , SpinColourMatrix, spinColourMatrix , std::vector, DistilParameterNames , std::vector, DistilParameterValues + , PerambTensor, Perambulator , PerambTensor, Perambulator2 - , SpinColourVector, scv - , SpinColourMatrix, scm - //, TestTensor, Critter - //, TestTensorFixed, FixedCritter - //, aTestTensorFixed, aFixedCritter - //, LSCTensor, MyLSCTensor - //, LCMTensor, MyLCMTensor + , TensorRank5UShort, tensorRank5UShort + , TensorRank3, tensorRank3 + , Tensor_9_4_2, tensor_9_4_2 + , aTensor_9_4_2, atensor_9_4_2 + , LSCTensor, MyLSCTensor +#ifdef DEBUG + , LCMTensor, MyLCMTensor +#endif ); - PerambIOTestClass() : Perambulator(2,3,1,4,5,1), Perambulator2(7,1,6,1,5,1), - DistilParameterNames {"alpha", "beta", "gamma", "delta", "epsilon", "what's f?"}, - DistilParameterValues{2,3,1,4,5,1} - , shortRank5Tensor{5,4,3,2,1} - //, Critter(7,3,2)//, aFixedCritter(3) + PerambIOTestClass() + : DistilParameterNames {"alpha", "beta", "gamma", "delta", "epsilon", "zeta"} + , DistilParameterValues{2,3,1,4,5,1} + , Perambulator(2,3,1,4,5,1) + , Perambulator2(7,1,6,1,5,1) + , tensorRank5UShort{5,4,3,2,1} + , tensorRank3(7,3,2) { - Sequential_Init(Perambulator); - Sequential_Init(Perambulator2, {-3.1415927,7}); - Sequential_Init(shortRank5Tensor); + Grid_complex Flag{1,-3.1415927}; + SequentialInit(Perambulator, Flag); + SequentialInit(Perambulator2, Flag); + SequentialInit(tensorRank5UShort); + SequentialInit(tensorRank3, Flag); + SequentialInit(tensor_9_4_2, Flag); + for( auto &t : atensor_9_4_2 ) + SequentialInit(t, Flag); + SequentialInit( MyLSCTensor ); +#ifdef DEBUG + SequentialInit( MyLCMTensor ); +#endif } }; -void EigenHdf5IOTest(void) { - SpinColourVector scv, scv2; - scv2 = scv; - ioTest("iotest_vector.h5", scv, "SpinColourVector"); - SpinColourMatrix scm; - ioTest("iotest_matrix.h5", scm, "SpinColourMatrix"); +#define TensorWriteReadInnerNoInit( T ) \ + ioTest("iotest_"s + std::to_string(++TestNum) + "_" #T ".h5", t, #T); +#define TensorWriteReadInner( T ) SequentialInit( t ); TensorWriteReadInnerNoInit( T ) +#define TensorWriteRead( T ) { T t ; TensorWriteReadInner( T ) } +#define TensorWriteReadV(T, ... ) { T t( __VA_ARGS__ ); TensorWriteReadInner( T ) } +#define TensorWriteReadLarge( T ) { std::unique_ptr p{new T}; T &t{*p}; TensorWriteReadInnerNoInit(T) } - constexpr TestScalar Inc{1,-1}; +void EigenHdf5IOTest(void) +{ + unsigned int TestNum = 0; + using namespace std::string_literals; + TensorWriteRead( TensorSingle ) + TensorWriteReadV( TensorSimple, 1, 1, 1, 1, 1, 1 ) + TensorWriteReadV( TensorRank3, 6, 3, 2 ) + TensorWriteRead ( Tensor_9_4_2 ) + TensorWriteRead ( LSCTensor ) + TensorWriteReadLarge( PerambIOTestClass ) +#ifdef DEBUG + std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; + TensorWriteReadLarge ( LCMTensor ) + // Also write > 4GB of complex numbers (I suspect this will fail inside Hdf5) { - using TestTensorSingle = Eigen::TensorFixedSize>; - TestTensorSingle ts; - ts(0) = 7; // lucky - ioTest("iotest_single.h5", ts, "Tensor_single"); + static constexpr size_t Num = 0x11000000; + std::cout << "Stress test: " << Num * sizeof( Grid_complex ) / 1024 / 1024 + << " MB array of complex" << std::endl; + using Stress = std::vector>; + Stress t (Num); + TensorWriteReadInnerNoInit( Stress ); } - - { - using TestTensorSimple = Eigen::Tensor, 6>; - TestTensorSimple ts(1,1,1,1,1,1); - ts(0,0,0,0,0,0) = Inc * 3.1415927; - ioTest("iotest_simple.h5", ts, "Tensor_simple"); - } - - TestTensor t(6,3,2); - TestScalar Val{Inc}; - for( int i = 0 ; i < t.dimension(0) ; i++) - for( int j = 0 ; j < t.dimension(1) ; j++) - for( int k = 0 ; k < t.dimension(2) ; k++) { - t(i,j,k) = Val; - Val += Inc; - } - ioTest("iotest_tensor.h5", t, "eigen_tensor_instance_name"); - //dump_tensor(t, "t"); - - // Now serialise a fixed size tensor - using FixedTensor = Eigen::TensorFixedSize>; - FixedTensor tf; - Val = Inc; - for( int i = 0 ; i < tf.dimension(0) ; i++) - for( int j = 0 ; j < tf.dimension(1) ; j++) - for( int k = 0 ; k < tf.dimension(2) ; k++) { - tf(i,j,k) = Val; - Val += Inc; - } - ioTest("iotest_tensor_fixed.h5", tf, "eigen_tensor_fixed_name"); - //dump_tensor(tf, "tf"); - - PerambIOTestClass o; - //dump_tensor(o.Perambulator, "Perambulator" ); - dump_tensor(o.shortRank5Tensor, "shortRank5Tensor"); - /*for_all( o.FixedCritter, [&](TestScalar &c, float f, const std::array &Dims ){ - c = TestScalar{f,-f}; - //std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; - } ); - for( auto &z : o.aFixedCritter ) - for_all( z, [&](TestScalar &c, float f, const std::array &Dims ){ - c = TestScalar{f,-f}; - //std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; - } );*/ - ioTest("iotest_object.h5", o, "PerambIOTestClass_object_instance_name"); - //DumpMemoryOrder(o.Perambulator); - - // Tensor of spin colour - LSCTensor l; - Val = 0; - for( int i = 0 ; i < l.dimension(0) ; i++) - for( int j = 0 ; j < l.dimension(1) ; j++) - for( int k = 0 ; k < l.dimension(2) ; k++) - for( int s = 0 ; s < Ns ; s++ ) - for( int c = 0 ; c < Nc ; c++ ) - { - l(i,j,k)()(s)(c) = Val; - Val += Inc; - } - ioTest("iotest_LSCTensor.h5", l, "LSCTensor_object_instance_name"); - //dump_tensor(l, "l"); - - // Tensor of spin colour - LCMTensor l2; - Val = 0; - for( int i = 0 ; i < l2.dimension(0) ; i++) - for( int j = 0 ; j < l2.dimension(1) ; j++) - for( int k = 0 ; k < l2.dimension(2) ; k++) - for( int l = 0 ; l < Ns ; l++ ) - for( int c = 0 ; c < Nc ; c++ ) - for( int c2 = 0 ; c2 < Nc ; c2++ ) - { - l2(i,j,k)(l)()(c,c2) = Val; - Val += Inc; - } - ioTest("iotest_LCMTensor.h5", l2, "LCMTensor_object_instance_name"); - //dump_tensor(l2, "l2"); +#endif } #endif From c14547ddbecb1bfacf17841e719de167ffcdc2ce Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 19 Feb 2019 16:12:55 +0000 Subject: [PATCH 106/347] EigenIO writing rationalised. All indices (trivial or not) written --- Grid/serialisation/BaseIO.h | 193 +++++++++++---------------------- tests/IO/Test_serialisation.cc | 41 +++++-- 2 files changed, 92 insertions(+), 142 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 771cc628..92c02476 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -96,7 +96,6 @@ namespace Grid { template struct Traits::value, void>::type> { using scalar_type = T; // Type of the underlying scalar using scalar_real = typename RealType::type; // real type underlying scalar_type - static constexpr unsigned int depth = 0; // How many levels of Grid Tensor there are (TensorLevel) static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) @@ -123,8 +122,7 @@ namespace Grid { template struct Traits> { using scalar_type = typename Traits::scalar_type; using scalar_real = typename RealType::type; - static constexpr unsigned int depth = 1 + Traits::depth; - static constexpr unsigned int rank = 0 + Traits::rank; + static constexpr unsigned int rank = 1 + Traits::rank; static constexpr unsigned int rank_non_trivial = 0 + Traits::rank_non_trivial; static constexpr unsigned int count = 1 * Traits::count; static constexpr std::size_t scalar_size = Traits::scalar_size; @@ -137,7 +135,6 @@ namespace Grid { template struct Traits> { using scalar_type = typename Traits::scalar_type; using scalar_real = typename RealType::type; - static constexpr unsigned int depth = 1 + Traits::depth; static constexpr unsigned int rank = 1 + Traits::rank; static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + Traits::rank_non_trivial; static constexpr unsigned int count = N * Traits::count; @@ -152,7 +149,6 @@ namespace Grid { template struct Traits> { using scalar_type = typename Traits::scalar_type; using scalar_real = typename RealType::type; - static constexpr unsigned int depth = 1 + Traits::depth; static constexpr unsigned int rank = 2 + Traits::rank; static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + Traits::rank_non_trivial; static constexpr unsigned int count = N * N * Traits::count; @@ -377,10 +373,7 @@ namespace Grid { template void write(const std::string &s, const iMatrix &output); template - typename std::enable_if::value, void>::type - write(const std::string &s, const ETensor &output); - template - typename std::enable_if::value, void>::type + typename std::enable_if::value, void>::type write(const std::string &s, const ETensor &output); void scientificFormat(const bool set); @@ -502,128 +495,64 @@ namespace Grid { { upcast->writeDefault(s, tensorToVec(output)); } - - // Eigen::Tensors of arithmetic/complex base type - template - template - typename std::enable_if::value, void>::type - Writer::write(const std::string &s, const ETensor &output) - { - const typename ETensor::Index NumElements{output.size()}; - assert( NumElements > 0 ); - if( NumElements == 1 ) - upcast->writeDefault(s, * output.data()); - else { - // We're only interested in non-trivial dimensions (i.e. dimensions > 1) - unsigned int TrivialDimCount{0}; - std::vector NonTrivialDims; - NonTrivialDims.reserve(output.NumDimensions); // Make sure we only do one malloc - for(auto i = 0; i < output.NumDimensions; i++ ) { - auto dim = output.dimension(i); - if( dim <= 1 ) { - TrivialDimCount++; - assert( dim == 1 ); // Not expecting dimension to be <= 0 - } else { - size_t s = static_cast(dim); - assert( s == dim ); // check we didn't lose anything in the conversion - NonTrivialDims.push_back(s); - } - } - // NB: NumElements > 1 implies this is not a scalar, so some dims should be left - assert( output.NumDimensions > TrivialDimCount ); - // If the Tensor isn't in Row-Major order, then we'll need to copy it's data - const bool CopyData{ETensor::Layout != Eigen::StorageOptions::RowMajor}; - using Scalar = typename ETensor::Scalar; - const Scalar * pWriteBuffer; - Scalar * pCopyBuffer = nullptr; - if( !CopyData ) - pWriteBuffer = output.data(); - else { - // Regardless of the Eigen::Tensor storage order, the copy will be Row Major - pCopyBuffer = static_cast(malloc(sizeof(Scalar) * NumElements)); - pWriteBuffer = pCopyBuffer; - std::array MyIndex; - for( auto &idx : MyIndex ) idx = 0; - for( typename ETensor::Index n = 0; n < NumElements; n++ ) { - pCopyBuffer[n] = output( MyIndex ); - // Now increment the index - for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) - MyIndex[i] = 0; - } - } - upcast->template writeMultiDim(s, NonTrivialDims, pWriteBuffer, NumElements); - if( pCopyBuffer ) free( pCopyBuffer ); - } - } // Eigen::Tensors of Grid tensors (iScalar, iVector, iMatrix) template template - typename std::enable_if::value, void>::type + typename std::enable_if::value, void>::type Writer::write(const std::string &s, const ETensor &output) { - const typename ETensor::Index NumElements{output.size()}; + using Index = typename ETensor::Index; + using Container = typename ETensor::Scalar; // NB: could be same as Scalar + using Traits = EigenIO::Traits; + using Scalar = typename Traits::scalar_type; // type of the underlying scalar + constexpr unsigned int TensorRank{ETensor::NumIndices}; + constexpr unsigned int ContainerRank{Traits::rank}; // Only non-zero for containers + constexpr unsigned int TotalRank{TensorRank + ContainerRank}; + const Index NumElements{output.size()}; assert( NumElements > 0 ); - if( NumElements == 1 ) - upcast->writeDefault(s, tensorToVec(* output.data())); - else { - // We're only interested in non-trivial dimensions (i.e. dimensions > 1) - unsigned int TrivialDimCount{0}; - std::vector NonTrivialDims; - NonTrivialDims.reserve(output.NumDimensions + EigenIO::Traits::rank_non_trivial); // Make sure we only do one malloc - for(auto i = 0; i < output.NumDimensions; i++ ) { - auto dim = output.dimension(i); - if( dim <= 1 ) { - TrivialDimCount++; - assert( dim == 1 ); // Not expecting dimension to be <= 0 - } else { - size_t s = static_cast(dim); - assert( s == dim ); // check we didn't lose anything in the conversion - NonTrivialDims.push_back(s); - } - } - // NB: NumElements > 1 implies this is not a scalar, so some dims should be left - assert( output.NumDimensions > TrivialDimCount ); - // Now add the extra dimensions, based on object zero - typename TensorToVec::type ttv = tensorToVec(* output.data()); - Flatten::type> f(ttv); - const std::vector & ExtraDims{f.getDim()}; - assert(ExtraDims.size() == EigenIO::Traits::rank_non_trivial); - size_t ExtraCount{1}; - for( auto i : ExtraDims ) { - assert( i > 0 ); - ExtraCount *= i; - NonTrivialDims.push_back(i); - } - assert(EigenIO::Traits::count == ExtraCount); - assert(EigenIO::Traits::size == sizeof( typename ETensor::Scalar )); - // If the Tensor isn't in Row-Major order, then we'll need to copy it's data - const bool CopyData{ETensor::Layout != Eigen::StorageOptions::RowMajor}; - using Scalar = typename ETensor::Scalar::scalar_type; - const Scalar * pWriteBuffer; - Scalar * pCopyBuffer = nullptr; - const typename ETensor::Index TotalNumElements = NumElements * ExtraCount; - if( !CopyData ) - pWriteBuffer = output.data()->begin(); - else { - // Regardless of the Eigen::Tensor storage order, the copy will be Row Major - pCopyBuffer = static_cast(malloc(TotalNumElements * sizeof(Scalar))); - pWriteBuffer = pCopyBuffer; - Scalar * pCopy = pCopyBuffer; - std::array MyIndex; - for( auto &idx : MyIndex ) idx = 0; - for( typename ETensor::Index n = 0; n < NumElements; n++ ) { - // Copy the grid tensor - for( const Scalar &Source : output( MyIndex ) ) - * pCopy ++ = Source; - // Now increment the index - for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) - MyIndex[i] = 0; - } - } - upcast->template writeMultiDim(s, NonTrivialDims, pWriteBuffer, TotalNumElements); - if( pCopyBuffer ) free( pCopyBuffer ); + + // Get the dimensionality of the tensor + std::vector TotalDims(TotalRank); + for(auto i = 0; i < TensorRank; i++ ) { + auto dim = output.dimension(i); + TotalDims[i] = static_cast(dim); + assert( TotalDims[i] == dim ); // check we didn't lose anything in the conversion } + for(auto i = 0; i < ContainerRank; i++ ) + TotalDims[TensorRank + i] = Traits::Dimension(i); + + // If the Tensor isn't in Row-Major order, then we'll need to copy it's data + const bool CopyData{NumElements > 1 && ETensor::Layout != Eigen::StorageOptions::RowMajor}; + const Scalar * pWriteBuffer; + Scalar * pCopyBuffer = nullptr; + const Index TotalNumElements = NumElements * Traits::count; + if( !CopyData ) { + if constexpr ( ContainerRank == 0 ) + pWriteBuffer = output.data(); + else + pWriteBuffer = output.data()->begin(); + } else { + // Regardless of the Eigen::Tensor storage order, the copy will be Row Major + pCopyBuffer = static_cast(malloc(TotalNumElements * sizeof(Scalar))); + pWriteBuffer = pCopyBuffer; + Scalar * pCopy = pCopyBuffer; + std::array MyIndex; + for( auto &idx : MyIndex ) idx = 0; + for( auto n = 0; n < NumElements; n++ ) { + if constexpr ( ContainerRank == 0 ) + * pCopy ++ = output( MyIndex ); + else { + for( const Scalar &Source : output( MyIndex ) ) + * pCopy ++ = Source; + } + // Now increment the index + for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) + MyIndex[i] = 0; + } + } + upcast->template writeMultiDim(s, TotalDims, pWriteBuffer, TotalNumElements); + if( pCopyBuffer ) free( pCopyBuffer ); } template @@ -875,21 +804,21 @@ namespace Grid { return os; } - template - static inline typename std::enable_if, T>::value, bool>::type - CompareMember(const T &lhs, const T &rhs) { + template + static inline typename std::enable_if::value || !EigenIO::is_tensor::value, bool>::type + CompareMember(const T1 &lhs, const T2 &rhs) { return lhs == rhs; } - template - static inline typename std::enable_if, T>::value, bool>::type - CompareMember(const T &lhs, const T &rhs) { + template + static inline typename std::enable_if::value && EigenIO::is_tensor::value, bool>::type + CompareMember(const T1 &lhs, const T2 &rhs) { // First check whether dimensions match (Eigen tensor library will assert if they don't match) - bool bReturnValue = true; - for( auto i = 0 ; bReturnValue && i < T::NumIndices ; i++ ) + bool bReturnValue = (T1::NumIndices == T2::NumIndices); + for( auto i = 0 ; bReturnValue && i < T1::NumIndices ; i++ ) bReturnValue = ( lhs.dimension(i)) == rhs.dimension(i); if( bReturnValue ) { - Eigen::Tensor bResult = (lhs == rhs).all(); + Eigen::Tensor bResult = (lhs == rhs).all(); bReturnValue = bResult(0); } return bReturnValue; diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 342fc214..cb4f65d5 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -80,21 +80,21 @@ double d = 2*M_PI; bool b = false; template -void ioTest(const std::string &filename, const O &object, const std::string &name) +void ioTest(const std::string &filename, const O &object, const std::string &name, const char * tag = "testobject" ) { std::cout << "IO test: " << name << " -> " << filename << " ..."; // writer needs to be destroyed so that writing physically happens { W writer(filename); - write(writer, "testobject", object); + write(writer, tag , object); } std::cout << " done. reading..."; R reader(filename); std::unique_ptr buf( new O ); // In case object too big for stack - read(reader, "testobject", *buf); + read(reader, tag, *buf); bool good = Serializable::CompareMember(object, *buf); if (!good) { std::cout << " failure!" << std::endl; @@ -107,10 +107,8 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam #ifdef HAVE_HDF5 typedef std::complex TestScalar; -using TensorSingle = Eigen::TensorFixedSize>; -using TensorSimple = Eigen::Tensor, 6>; -typedef Eigen::Tensor TensorRank5UShort; -typedef Eigen::Tensor TensorRank5IntAlt; +typedef Eigen::TensorFixedSize> TensorRank5UShort; +typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; typedef Eigen::Tensor TensorRank3; typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; typedef std::vector aTensor_9_4_2; @@ -143,8 +141,8 @@ public: , DistilParameterValues{2,3,1,4,5,1} , Perambulator(2,3,1,4,5,1) , Perambulator2(7,1,6,1,5,1) - , tensorRank5UShort{5,4,3,2,1} , tensorRank3(7,3,2) + , atensor_9_4_2(3) { Grid_complex Flag{1,-3.1415927}; SequentialInit(Perambulator, Flag); @@ -162,7 +160,8 @@ public: }; #define TensorWriteReadInnerNoInit( T ) \ - ioTest("iotest_"s + std::to_string(++TestNum) + "_" #T ".h5", t, #T); + filename = "iotest_"s + std::to_string(++TestNum) + "_" #T ".h5"; \ + ioTest(filename, t, #T, #T); #define TensorWriteReadInner( T ) SequentialInit( t ); TensorWriteReadInnerNoInit( T ) #define TensorWriteRead( T ) { T t ; TensorWriteReadInner( T ) } #define TensorWriteReadV(T, ... ) { T t( __VA_ARGS__ ); TensorWriteReadInner( T ) } @@ -170,12 +169,34 @@ public: void EigenHdf5IOTest(void) { - unsigned int TestNum = 0; using namespace std::string_literals; + unsigned int TestNum = 0; + std::string filename; + using TensorSingle = Eigen::TensorFixedSize>; TensorWriteRead( TensorSingle ) + using TensorSimple = Eigen::Tensor, 6>; TensorWriteReadV( TensorSimple, 1, 1, 1, 1, 1, 1 ) TensorWriteReadV( TensorRank3, 6, 3, 2 ) TensorWriteRead ( Tensor_9_4_2 ) + { + TensorRank5UShort t; + TensorWriteReadInner ( TensorRank5UShort ); + std::cout << " Testing alternate memory order read ... "; + TensorRank5UShortAlt t2; + Hdf5Reader reader(filename); + read(reader, "TensorRank5UShort", t2); + bool good = true; + for_all( t2, [&](unsigned short c, unsigned short n, + const std::array &Dims ) { + good = good && ( c == n ); + } ); + if (!good) { + std::cout << " failure!" << std::endl; + dump_tensor(t2,"t2"); + exit(EXIT_FAILURE); + } + std::cout << " done." << std::endl; + } TensorWriteRead ( LSCTensor ) TensorWriteReadLarge( PerambIOTestClass ) #ifdef DEBUG From 4522f1e831a2710fe733398ab63624abea36422f Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 19 Feb 2019 17:22:30 +0000 Subject: [PATCH 107/347] separated final 2pt Contraction --- Hadrons/Modules/MDistil/BContraction.hpp | 23 ++- Hadrons/Modules/MDistil/Baryon2pt.cc | 7 + Hadrons/Modules/MDistil/Baryon2pt.hpp | 187 +++++++++++++++++++++++ Hadrons/Modules/MDistil/Distil.hpp | 8 + Hadrons/modules.inc | 2 + tests/hadrons/Test_hadrons_distil.cc | 17 +++ 6 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 Hadrons/Modules/MDistil/Baryon2pt.cc create mode 100644 Hadrons/Modules/MDistil/Baryon2pt.hpp diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 75c505cc..3e8c523f 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -72,6 +72,14 @@ protected: GridCartesian * grid3d; }; +/*class BFieldIO: Serializable{ +public: + using BaryonTensorSet = Eigen::Tensor; + GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, + BaryonTensorSet, BField + ); +};*/ + MODULE_REGISTER_TMP(BContraction, TBContraction, MDistil); /****************************************************************************** @@ -120,6 +128,7 @@ void TBContraction::execute(void) int N_3 = three.size(); int parity = par().parity; + const std::string &output{par().output}; LOG(Message) << "Computing distillation baryon fields" << std::endl; LOG(Message) << "One: '" << par().one << "' Two: '" << par().two << "' Three: '" << par().three << "'" << std::endl; @@ -133,7 +142,7 @@ void TBContraction::execute(void) grid3d = MakeLowerDimGrid(grid4d); int Nmom=1; int Nt=64; - std::vector BField(Nmom*Nt*N_1*N_2*N_3); + // std::vector BField(Nmom*Nt*N_1*N_2*N_3); int Bindex; int Nc=3; //Num colours @@ -223,7 +232,15 @@ void TBContraction::execute(void) } } - int Npairs = 0; + BFieldIO BField_save; + BField_save.BField = BField3; + + std::string filename ="./" + output + ".h5"; + std::cout << "Writing to file " << filename << std::endl; + Hdf5Writer writer(filename); + write(writer,"BaryonField",BField_save.BField); + + /* int Npairs = 0; char left[] = "uud"; char right[] = "uud"; std::vector pairs(6); @@ -261,7 +278,7 @@ void TBContraction::execute(void) for (int t=0 ; t < Nt ; t++){ std::cout << "C2(is=" << is << ",t=" << t << ") = " << corr(0,is,t) << std::endl; } - } + }*/ } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Baryon2pt.cc b/Hadrons/Modules/MDistil/Baryon2pt.cc new file mode 100644 index 00000000..58de7d33 --- /dev/null +++ b/Hadrons/Modules/MDistil/Baryon2pt.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TBaryon2pt; diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp new file mode 100644 index 00000000..e97dab16 --- /dev/null +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -0,0 +1,187 @@ +#ifndef Hadrons_MDistil_Baryon2pt_hpp_ +#define Hadrons_MDistil_Baryon2pt_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation +#include +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * Baryon2pt * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class Baryon2ptPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(Baryon2ptPar, + std::string, inputL, + std::string, inputR, + std::string, output + ); +}; + +template +class TBaryon2pt: public Module +{ +public: + // constructor + TBaryon2pt(const std::string name); + // destructor + virtual ~TBaryon2pt(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +class C2IO: Serializable{ +public: +using C2Set = Eigen::Tensor; + GRID_SERIALIZABLE_CLASS_MEMBERS(C2IO, + C2Set, C2 + ); +}; + +MODULE_REGISTER_TMP(Baryon2pt, TBaryon2pt, MDistil); + +/****************************************************************************** + * TBaryon2pt implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TBaryon2pt::TBaryon2pt(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TBaryon2pt::getInput(void) +{ + std::vector in; + + return in; +} + +template +std::vector TBaryon2pt::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TBaryon2pt::setup(void) +{ + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TBaryon2pt::execute(void) +{ + + const std::string &inputL{par().inputL}; + const std::string &inputR{par().inputR}; + const std::string &output{par().output}; + + int Nmom=1; + int Nt=64; + int Nc=3; //Num colours + std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; + std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + int Ngamma=3; + + int N_1=20; + int N_2=20; + int N_3=20; + + // using BaryonTensorSet = Eigen::Tensor; + + BFieldIO BFieldL; + BFieldL.BField.resize(Nmom,4*Ngamma,Nt,N_1,N_2,N_3); + + std::string filenameL ="./" + inputL + ".h5"; + std::cout << "Reading from file " << filenameL << std::endl; + Hdf5Reader readerL(filenameL); + read(readerL,"BaryonField",BFieldL.BField); + + BFieldIO BFieldR; + BFieldR.BField.resize(Nmom,4*Ngamma,Nt,N_1,N_2,N_3); + + std::string filenameR ="./" + inputR + ".h5"; + std::cout << "Reading from file " << filenameR << std::endl; + Hdf5Reader readerR(filenameR); + read(readerR,"BaryonField",BFieldR.BField); + + Eigen::Tensor corr(Nmom,Nt); + + int Npairs = 0; + char left[] = "uud"; + char right[] = "uud"; + std::vector pairs(6); + for (int ie=0, i=0 ; ie < 6 ; ie++){ + if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]){ + pairs[i] = ie; + i++; + Npairs++; + } + } + pairs.resize(Npairs); + std::cout << Npairs << " pairs: " << pairs << std::endl; + for (int imom=0 ; imom < Nmom ; imom++){ + for (int t=0 ; t < Nt ; t++){ + corr(imom,t) = 0.; + } + } + + int tsrc=0; + + for (int ipair=0 ; ipair < Npairs ; ipair++){ + Eigen::array, 3> product_dims = { Eigen::IndexPair(0,epsilon[pairs[ipair]][0]),Eigen::IndexPair(1,epsilon[pairs[ipair]][1]) ,Eigen::IndexPair(2,epsilon[pairs[ipair]][2]) }; + for (int imom=0 ; imom < Nmom ; imom++){ + std::cout << imom << std::endl; + Eigen::Tensor B5L = BFieldL.BField.chip(imom,0); + Eigen::Tensor B5R = BFieldR.BField.chip(imom,0); + for (int is=0 ; is < 4 ; is++){ + Eigen::Tensor B4L = B5L.chip(is,0); + Eigen::Tensor B4R = B5R.chip(is,0); + for (int t=0 ; t < Nt ; t++){ + Eigen::Tensor B3L = B4L.chip(t,0); + Eigen::Tensor B3R = B4R.chip(tsrc,0); + Eigen::Tensor C2 = B3L.contract(B3R,product_dims); + corr(imom,t) += (double)epsilon_sgn[pairs[ipair]]*C2(0); + } + } + } + } + for (int t=0 ; t < Nt ; t++){ + std::cout << "C2(t=" << t << ") = " << corr(0,t) << std::endl; + } + + C2IO C2_save; + C2_save.C2 = corr; + + std::string filename ="./" + output + ".h5"; + std::cout << "Writing to file " << filename << std::endl; + Hdf5Writer writer(filename); + write(writer,"C2",C2_save.C2); +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_Baryon2pt_hpp_ diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index f2ada50f..d6673dd0 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -200,6 +200,14 @@ public: }; +class BFieldIO: Serializable{ +public: + using BaryonTensorSet = Eigen::Tensor; + GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, + BaryonTensorSet, BField + ); +}; + END_MODULE_NAMESPACE // Grid /****************************************************************************** diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 6d5ce763..441df51b 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -10,6 +10,7 @@ modules_cc =\ Modules/MSolver/RBPrecCG.cc \ Modules/MDistil/BContraction.cc \ Modules/MDistil/LapEvec.cc \ + Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambLight.cc \ Modules/MDistil/DistilVectors.cc \ Modules/MContraction/WeakHamiltonianEye.cc \ @@ -88,6 +89,7 @@ modules_hpp =\ Modules/MDistil/LapEvec.hpp \ Modules/MDistil/Distil.hpp \ Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/Baryon2pt.hpp \ Modules/MDistil/BContraction.hpp \ Modules/MDistil/PerambLight.hpp \ Modules/MContraction/WeakHamiltonian.hpp \ diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 8d865d20..bfa8c305 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -282,6 +282,19 @@ void test_BaryonFieldRho(Application &application) BContractionPar.mom={"0 0 0"}; application.createModule("BaryonFieldRho",BContractionPar); } +///////////////////////////////////////////////////////////// +// BaryonContraction +///////////////////////////////////////////////////////////// + +void test_Baryon2pt(Application &application) +{ + // DistilVectors parameters + MDistil::Baryon2pt::Par Baryon2ptPar; + Baryon2ptPar.inputL="BaryonFieldPhi"; + Baryon2ptPar.inputR="BaryonFieldRho"; + Baryon2ptPar.output="C2_baryon"; + application.createModule("C2_b",Baryon2ptPar); +} bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { @@ -743,6 +756,10 @@ int main(int argc, char *argv[]) test_MesonField( application ); test_MesonFieldRho( application ); break; + case 9: // 3 + test_Global( application ); + test_Baryon2pt( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From 982a24514bb6eb251d043472419b90b5ad13d00c Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 19 Feb 2019 17:37:21 +0000 Subject: [PATCH 108/347] Binary IO also implemented and tested --- Grid/serialisation/BinaryIO.h | 49 ++++++++++++++++++++++++++++++++++ tests/IO/Test_serialisation.cc | 6 +++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Grid/serialisation/BinaryIO.h b/Grid/serialisation/BinaryIO.h index 757753c7..bf011454 100644 --- a/Grid/serialisation/BinaryIO.h +++ b/Grid/serialisation/BinaryIO.h @@ -51,6 +51,8 @@ namespace Grid { template void writeDefault(const std::string &s, const std::vector &x); void writeDefault(const std::string &s, const char *x); + template + void writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements); private: std::ofstream file_; }; @@ -66,6 +68,8 @@ namespace Grid { void readDefault(const std::string &s, U &output); template void readDefault(const std::string &s, std::vector &output); + template + void readMultiDim(const std::string &s, std::vector &buf, std::vector &dim); private: std::ifstream file_; }; @@ -92,6 +96,27 @@ namespace Grid { } } + template + void BinaryWriter::writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements) + { + uint64_t rank = static_cast( Dimensions.size() ); + uint64_t tmp = 1; + for( auto i = 0 ; i < rank ; i++ ) + tmp *= Dimensions[i]; + assert( tmp == NumElements && "Dimensions don't match size of data being written" ); + // Total number of elements + write("", tmp); + // Number of dimensions + write("", rank); + // Followed by each dimension + for( auto i = 0 ; i < rank ; i++ ) { + tmp = Dimensions[i]; + write("", tmp); + } + for( auto i = 0; i < NumElements; ++i) + write("", pDataRowMajor[i]); + } + // Reader template implementation //////////////////////////////////////////// template void BinaryReader::readDefault(const std::string &s, U &output) @@ -114,6 +139,30 @@ namespace Grid { read("", output[i]); } } + + template + void BinaryReader::readMultiDim(const std::string &s, std::vector &buf, std::vector &dim) + { + // Number of elements + uint64_t NumElements; + read("", NumElements); + // Number of dimensions + uint64_t rank; + read("", rank); + // Followed by each dimension + uint64_t count = 1; + dim.resize(rank); + uint64_t tmp; + for( auto i = 0 ; i < rank ; i++ ) { + read("", tmp); + dim[i] = tmp; + count *= tmp; + } + assert( count == NumElements && "Dimensions don't match size of data being read" ); + buf.resize(count); + for( auto i = 0; i < count; ++i) + read("", buf[i]); + } } #endif diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index cb4f65d5..01672e8b 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -159,9 +159,11 @@ public: } }; +#define RDR_ Hdf5Reader +#define WTR_ Hdf5Writer #define TensorWriteReadInnerNoInit( T ) \ filename = "iotest_"s + std::to_string(++TestNum) + "_" #T ".h5"; \ - ioTest(filename, t, #T, #T); + ioTest(filename, t, #T, #T); #define TensorWriteReadInner( T ) SequentialInit( t ); TensorWriteReadInnerNoInit( T ) #define TensorWriteRead( T ) { T t ; TensorWriteReadInner( T ) } #define TensorWriteReadV(T, ... ) { T t( __VA_ARGS__ ); TensorWriteReadInner( T ) } @@ -183,7 +185,7 @@ void EigenHdf5IOTest(void) TensorWriteReadInner ( TensorRank5UShort ); std::cout << " Testing alternate memory order read ... "; TensorRank5UShortAlt t2; - Hdf5Reader reader(filename); + RDR_ reader(filename); read(reader, "TensorRank5UShort", t2); bool good = true; for_all( t2, [&](unsigned short c, unsigned short n, From f70c5b004ad8cbe7115a4b59f78ab872d754599f Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Wed, 20 Feb 2019 12:56:13 +0000 Subject: [PATCH 109/347] some cleanup in Baryon2pt --- Hadrons/Modules/MDistil/BContraction.hpp | 62 +++++------------------- Hadrons/Modules/MDistil/Baryon2pt.hpp | 32 +++++++----- Hadrons/Modules/MDistil/Distil.hpp | 2 +- tests/hadrons/Test_hadrons_distil.cc | 2 + 4 files changed, 34 insertions(+), 64 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 3e8c523f..62300c3a 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -18,18 +18,17 @@ BEGIN_HADRONS_NAMESPACE ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) - - // general baryon tensor set based on Eigen tensors and Grid-allocated memory // Dimensions: // 0 - ext - external field (momentum, EM field, ...) - // 1 - str - spin-color structure + // 1 - str - dirac structure // 2 - t - timeslice - // 3 - i - left distillation mode index - // 4 - j - middle distillation mode index - // 5 - k - left distillation mode index + // 3 - s - free spin index + // 4 - i - left distillation mode index + // 5 - j - middle distillation mode index + // 6 - k - left distillation mode index // template - // using BaryonTensorSet = Eigen::TensorMap>; + // using BaryonTensorSet = Eigen::TensorMap>; class BContractionPar: Serializable @@ -74,7 +73,7 @@ protected: /*class BFieldIO: Serializable{ public: - using BaryonTensorSet = Eigen::Tensor; + using BaryonTensorSet = Eigen::Tensor; GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, BaryonTensorSet, BField ); @@ -183,9 +182,9 @@ void TBContraction::execute(void) Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 }; std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; - using BaryonTensorSet = Eigen::Tensor; + using BaryonTensorSet = Eigen::Tensor; int Ngamma=3; - BaryonTensorSet BField3(Nmom,4*Ngamma,Nt,N_1,N_2,N_3); + BaryonTensorSet BField3(Nmom,Ngamma,Nt,4,N_1,N_2,N_3); Eigen::Tensor corr(Nmom,4,Nt); @@ -216,7 +215,7 @@ void TBContraction::execute(void) tmp111 = 0.5*(double)parity*(tmp111 + tmp222); // P_\pm * ... diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); for (int is=0 ; is < 4 ; is++){ - BField3(imom,is+4*ig,t,i1,i2,i3)+=(double)epsilon_sgn[ie]*tmp111()(is)()*diquark2; + BField3(imom,ig,t,is,i1,i2,i3)+=(double)epsilon_sgn[ie]*tmp111()(is)()*diquark2; } } } @@ -228,7 +227,7 @@ void TBContraction::execute(void) } for (int is=0 ; is < 4 ; is++){ for (int t=0 ; t < Nt ; t++){ - std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField3(0,is,t,0,0,0) << std::endl; + std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField3(0,0,t,is,0,0,0) << std::endl; } } @@ -240,45 +239,6 @@ void TBContraction::execute(void) Hdf5Writer writer(filename); write(writer,"BaryonField",BField_save.BField); - /* int Npairs = 0; - char left[] = "uud"; - char right[] = "uud"; - std::vector pairs(6); - for (int ie=0, i=0 ; ie < 6 ; ie++){ - if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]){ - pairs[i] = ie; - i++; - Npairs++; - } - } - pairs.resize(Npairs); - std::cout << Npairs << " pairs: " << pairs << std::endl; - for (int imom=0 ; imom < Nmom ; imom++){ - for (int is=0 ; is < 4 ; is++){ - for (int t=0 ; t < Nt ; t++){ - corr(imom,is,t) = 0.; - } - } - } - for (int ipair=0 ; ipair < Npairs ; ipair++){ - Eigen::array, 3> product_dims = { Eigen::IndexPair(0,epsilon[pairs[ipair]][0]),Eigen::IndexPair(1,epsilon[pairs[ipair]][1]) ,Eigen::IndexPair(2,epsilon[pairs[ipair]][2]) }; - for (int imom=0 ; imom < Nmom ; imom++){ - Eigen::Tensor B5 = BField3.chip(imom,0); - for (int is=0 ; is < 4 ; is++){ - Eigen::Tensor B4 = B5.chip(is,0); - for (int t=0 ; t < Nt ; t++){ - Eigen::Tensor B3 = B4.chip(t,0); - Eigen::Tensor C2 = B3.contract(B3,product_dims); - corr(imom,is,t) += (double)epsilon_sgn[pairs[ipair]]*C2(0); - } - } - } - } - for (int is=0 ; is < 4 ; is++){ - for (int t=0 ; t < Nt ; t++){ - std::cout << "C2(is=" << is << ",t=" << t << ") = " << corr(0,is,t) << std::endl; - } - }*/ } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index e97dab16..75a23e7a 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -24,6 +24,8 @@ public: GRID_SERIALIZABLE_CLASS_MEMBERS(Baryon2ptPar, std::string, inputL, std::string, inputR, + std::string, quarksL, + std::string, quarksR, std::string, output ); }; @@ -95,6 +97,8 @@ void TBaryon2pt::execute(void) const std::string &inputL{par().inputL}; const std::string &inputR{par().inputR}; + const std::string &inputL{par().quarksL}; + const std::string &inputR{par().quarksR}; const std::string &output{par().output}; int Nmom=1; @@ -108,10 +112,10 @@ void TBaryon2pt::execute(void) int N_2=20; int N_3=20; - // using BaryonTensorSet = Eigen::Tensor; + // using BaryonTensorSet = Eigen::Tensor; BFieldIO BFieldL; - BFieldL.BField.resize(Nmom,4*Ngamma,Nt,N_1,N_2,N_3); + BFieldL.BField.resize(Nmom,Ngamma,Nt,4,N_1,N_2,N_3); std::string filenameL ="./" + inputL + ".h5"; std::cout << "Reading from file " << filenameL << std::endl; @@ -119,7 +123,7 @@ void TBaryon2pt::execute(void) read(readerL,"BaryonField",BFieldL.BField); BFieldIO BFieldR; - BFieldR.BField.resize(Nmom,4*Ngamma,Nt,N_1,N_2,N_3); + BFieldR.BField.resize(Nmom,Ngamma,Nt,4,N_1,N_2,N_3); std::string filenameR ="./" + inputR + ".h5"; std::cout << "Reading from file " << filenameR << std::endl; @@ -153,16 +157,20 @@ void TBaryon2pt::execute(void) Eigen::array, 3> product_dims = { Eigen::IndexPair(0,epsilon[pairs[ipair]][0]),Eigen::IndexPair(1,epsilon[pairs[ipair]][1]) ,Eigen::IndexPair(2,epsilon[pairs[ipair]][2]) }; for (int imom=0 ; imom < Nmom ; imom++){ std::cout << imom << std::endl; - Eigen::Tensor B5L = BFieldL.BField.chip(imom,0); - Eigen::Tensor B5R = BFieldR.BField.chip(imom,0); - for (int is=0 ; is < 4 ; is++){ - Eigen::Tensor B4L = B5L.chip(is,0); - Eigen::Tensor B4R = B5R.chip(is,0); + Eigen::Tensor B6L = BFieldL.BField.chip(imom,0); + Eigen::Tensor B6R = BFieldR.BField.chip(imom,0); + for (int ig=0 ; ig < Ngamma ; ig++){ + Eigen::Tensor B5L = B6L.chip(ig,0); + Eigen::Tensor B5R = B6R.chip(ig,0); for (int t=0 ; t < Nt ; t++){ - Eigen::Tensor B3L = B4L.chip(t,0); - Eigen::Tensor B3R = B4R.chip(tsrc,0); - Eigen::Tensor C2 = B3L.contract(B3R,product_dims); - corr(imom,t) += (double)epsilon_sgn[pairs[ipair]]*C2(0); + Eigen::Tensor B4L = B5L.chip(t,0); + Eigen::Tensor B4R = B5R.chip(tsrc,0); + for (int is=0 ; is < 4 ; is++){ + Eigen::Tensor B3L = B4L.chip(is,0); + Eigen::Tensor B3R = B4R.chip(is,0); + Eigen::Tensor C2 = B3L.contract(B3R,product_dims); + corr(imom,t) += (double)epsilon_sgn[pairs[ipair]]*C2(0); + } } } } diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index d6673dd0..af7e2786 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -202,7 +202,7 @@ public: class BFieldIO: Serializable{ public: - using BaryonTensorSet = Eigen::Tensor; + using BaryonTensorSet = Eigen::Tensor; GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, BaryonTensorSet, BField ); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index bfa8c305..5b43fac0 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -292,6 +292,8 @@ void test_Baryon2pt(Application &application) MDistil::Baryon2pt::Par Baryon2ptPar; Baryon2ptPar.inputL="BaryonFieldPhi"; Baryon2ptPar.inputR="BaryonFieldRho"; + Baryon2ptPar.quarksL="uud"; + Baryon2ptPar.quarksR="uud"; Baryon2ptPar.output="C2_baryon"; application.createModule("C2_b",Baryon2ptPar); } From 5d6462b706507dbd1c29503f8239b4eaecd7f0da Mon Sep 17 00:00:00 2001 From: ferben Date: Thu, 21 Feb 2019 11:13:10 +0000 Subject: [PATCH 110/347] bugfix --- Hadrons/Modules/MDistil/Baryon2pt.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index 75a23e7a..3c26bbe4 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -97,8 +97,8 @@ void TBaryon2pt::execute(void) const std::string &inputL{par().inputL}; const std::string &inputR{par().inputR}; - const std::string &inputL{par().quarksL}; - const std::string &inputR{par().quarksR}; + const std::string &quarksL{par().quarksL}; + const std::string &quarksR{par().quarksR}; const std::string &output{par().output}; int Nmom=1; @@ -166,8 +166,8 @@ void TBaryon2pt::execute(void) Eigen::Tensor B4L = B5L.chip(t,0); Eigen::Tensor B4R = B5R.chip(tsrc,0); for (int is=0 ; is < 4 ; is++){ - Eigen::Tensor B3L = B4L.chip(is,0); - Eigen::Tensor B3R = B4R.chip(is,0); + Eigen::Tensor B3L = B4L.chip(is,0); + Eigen::Tensor B3R = B4R.chip(is,0); Eigen::Tensor C2 = B3L.contract(B3R,product_dims); corr(imom,t) += (double)epsilon_sgn[pairs[ipair]]*C2(0); } From 34b9450fc99bba494fa6f6bbd2fa1f37c99bb501 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 21 Feb 2019 14:22:48 +0000 Subject: [PATCH 111/347] Gotten rid of c++17 --- Grid/serialisation/BaseIO.h | 119 +++++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 29 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 92c02476..a6746864 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -168,6 +168,32 @@ namespace Grid { //template struct Traits::value, void>::type> : Traits {}; } + // for_all helper function to call the lambda + template + typename std::enable_if::value, void>::type + for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, + std::array::rank_non_trivial> &MyIndex) + { + lambda( scalar, Seq++, MyIndex ); + } + + // for_all helper function to call the lambda + template + typename std::enable_if::value, void>::type + for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index Seq, + std::array::rank_non_trivial> &MyIndex) + { + using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later + const auto InnerRank = EigenIO::Traits::rank_non_trivial; + const auto rank{ETensor::NumIndices}; + for( typename EigenIO::Traits::scalar_type &Source : scalar ) { + lambda(Source, Seq++, MyIndex ); + // Now increment SubIndex + for( auto i = InnerRank - 1; i != -1 && ++MyIndex[rank + i] == EigenIO::Traits::DimensionNT(i); i-- ) + MyIndex[i] = 0; + } + } + // Calls a lamda (passing index and sequence number) for every member of an Eigen::Tensor // For efficiency, iteration proceeds in memory order, // ... but parameters guaranteed to be the same regardless of memory order @@ -208,17 +234,7 @@ namespace Grid { Index Seq = 0; Scalar * pScalar = ET.data(); for( std::size_t j = 0; j < NumScalars; j++ ) { - // if constexpr is C++ 17 ... but otherwise need two specialisations (Container vs Scalar) - if constexpr ( EigenIO::is_scalar::value ) { - lambda( * pScalar, Seq++, MyIndex ); - } else { - for( typename EigenIO::Traits::scalar_type &Source : * pScalar ) { - lambda(Source, Seq++, MyIndex ); - // Now increment SubIndex - for( auto i = rank + InnerRank - 1; i != rank - 1 && ++MyIndex[i] == Dims[i]; i-- ) - MyIndex[i] = 0; - } - } + for_all_do_lambda( lambda, * pScalar, Seq, MyIndex ); // Now increment the index to pass to the lambda (bearing in mind we're walking in memory order) if( ETensor::Options & Eigen::RowMajor ) { for( auto i = rank - 1; i != -1 && ++MyIndex[i] == Dims[i]; i-- ) @@ -376,6 +392,36 @@ namespace Grid { typename std::enable_if::value, void>::type write(const std::string &s, const ETensor &output); + // Helper functions for Scalar vs Container specialisations + template + inline typename std::enable_if::value, const typename EigenIO::Traits::scalar_type *>::type + getFirstScalar(const ETensor &output) + { + return output.data(); + } + + template + inline typename std::enable_if::value, const typename EigenIO::Traits::scalar_type *>::type + getFirstScalar(const ETensor &output) + { + return output.data()->begin(); + } + + template + inline typename std::enable_if::value, void>::type + copyScalars(typename EigenIO::Traits::scalar_type * &pCopy, const S &Source) + { + * pCopy ++ = Source; + } + + template + inline typename std::enable_if::value, void>::type + copyScalars(typename EigenIO::Traits::scalar_type * &pCopy, const S &Source) + { + for( const typename EigenIO::Traits::scalar_type &item : Source ) + * pCopy ++ = item; + } + void scientificFormat(const bool set); bool isScientific(void); void setPrecision(const unsigned int prec); @@ -417,12 +463,23 @@ namespace Grid { template typename std::enable_if::value, void>::type Reshape(ETensor &t, const std::array &dims ); - /*template - typename std::enable_if::value, std::size_t>::type - DimSize(ETensor &t, std::size_t dim ); - template - typename std::enable_if::value, std::size_t>::type - DimSize(ETensor &t, std::size_t dim );*/ + + // Helper functions for Scalar vs Container specialisations + template + inline typename std::enable_if::value, void>::type + copyScalars(S &Dest, const typename EigenIO::Traits::scalar_type * &pSource) + { + Dest = * pSource ++; + } + + template + inline typename std::enable_if::value, void>::type + copyScalars(S &Dest, const typename EigenIO::Traits::scalar_type * &pSource) + { + for( typename EigenIO::Traits::scalar_type &item : Dest ) + item = * pSource ++; + } + protected: template void fromString(U &output, const std::string &s); @@ -498,7 +555,7 @@ namespace Grid { // Eigen::Tensors of Grid tensors (iScalar, iVector, iMatrix) template - template + template typename std::enable_if::value, void>::type Writer::write(const std::string &s, const ETensor &output) { @@ -528,10 +585,11 @@ namespace Grid { Scalar * pCopyBuffer = nullptr; const Index TotalNumElements = NumElements * Traits::count; if( !CopyData ) { - if constexpr ( ContainerRank == 0 ) + /*if constexpr ( ContainerRank == 0 ) pWriteBuffer = output.data(); else - pWriteBuffer = output.data()->begin(); + pWriteBuffer = output.data()->begin();*/ + pWriteBuffer = getFirstScalar( output ); } else { // Regardless of the Eigen::Tensor storage order, the copy will be Row Major pCopyBuffer = static_cast(malloc(TotalNumElements * sizeof(Scalar))); @@ -540,12 +598,14 @@ namespace Grid { std::array MyIndex; for( auto &idx : MyIndex ) idx = 0; for( auto n = 0; n < NumElements; n++ ) { - if constexpr ( ContainerRank == 0 ) - * pCopy ++ = output( MyIndex ); + const Container & c = output( MyIndex ); + /*if constexpr ( ContainerRank == 0 ) + * pCopy ++ = c; else { - for( const Scalar &Source : output( MyIndex ) ) + for( const Scalar &Source : c ) * pCopy ++ = Source; - } + }*/ + copyScalars( pCopy, c ); // Now increment the index for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) MyIndex[i] = 0; @@ -656,8 +716,8 @@ namespace Grid { using Scalar = typename Traits::scalar_type; // read the (flat) data and dimensionality - std::vector dimData; - std::vector buf; + std::vector dimData; + std::vector buf; upcast->readMultiDim( s, buf, dimData ); // Make sure that the number of elements read matches dimensions read std::size_t NumElements = 1; @@ -717,15 +777,16 @@ namespace Grid { // Copy the data into the tensor ETDims MyIndex; for( auto &d : MyIndex ) d = 0; - std::size_t idx = 0; + const Scalar * pSource = &buf[0]; for( auto n = 0 ; n < NumElements ; n++ ) { Container & c = output( MyIndex ); - if constexpr ( EigenIO::is_scalar::value ) { + /*if constexpr ( EigenIO::is_scalar::value ) { c = buf[idx++]; } else { for( Scalar & s : c ) s = buf[idx++]; - } + }*/ + copyScalars( c, pSource ); // Now increment the index for( int i = ETensor::NumDimensions - 1; i >= 0 && ++MyIndex[i] == dims[i]; i-- ) MyIndex[i] = 0; From 752530f35282af7fa8f7f1ebc92eb40106b049ac Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 21 Feb 2019 14:43:07 +0000 Subject: [PATCH 112/347] Gotten rid of c++17 in Test_serialisation.cc --- tests/IO/Test_serialisation.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 01672e8b..ae4b5b69 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -98,7 +98,7 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam bool good = Serializable::CompareMember(object, *buf); if (!good) { std::cout << " failure!" << std::endl; - if constexpr (EigenIO::is_tensor::value) + if (EigenIO::is_tensor::value) dump_tensor(*buf,"???"); exit(EXIT_FAILURE); } From c640923159110208b222d4975e95a288433997fe Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 21 Feb 2019 15:48:52 +0000 Subject: [PATCH 113/347] Fixed reference to depth from test --- tests/hadrons/Test_hadrons_distil.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 5b43fac0..655be9cf 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -491,7 +491,6 @@ template void DebugGridTensorTest_print( int i ) { std::cout << i << " : " << EigenIO::is_tensor::value - << ", depth " << EigenIO::Traits::depth << ", rank " << EigenIO::Traits::rank << ", rank_non_trivial " << EigenIO::Traits::rank_non_trivial << ", count " << EigenIO::Traits::count From 55886cf9db1ea7da7ce813dcc9cbc0f83aff929e Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 21 Feb 2019 16:14:13 +0000 Subject: [PATCH 114/347] ran make_module_list.sh --- Hadrons/Modules.hpp | 141 +++++++++++------------ Hadrons/modules.inc | 270 ++++++++++++++++++++++---------------------- 2 files changed, 206 insertions(+), 205 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 7105e743..6c05dc46 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,79 +1,80 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include #include -#include +#include #include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include +#include #include #include -#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 441df51b..17f54adb 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,159 +1,159 @@ modules_cc =\ - Modules/MUtilities/TestSeqConserved.cc \ + Modules/MContraction/WeakHamiltonianEye.cc \ + Modules/MContraction/Baryon.cc \ + Modules/MContraction/Meson.cc \ + Modules/MContraction/WeakNeutral4ptDisc.cc \ + Modules/MContraction/WeakHamiltonianNonEye.cc \ + Modules/MContraction/A2AAslashField.cc \ + Modules/MContraction/WardIdentity.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/Gamma3pt.cc \ + Modules/MFermion/FreeProp.cc \ + Modules/MFermion/GaugeProp.cc \ + Modules/MSource/Momentum.cc \ + Modules/MSource/Point.cc \ + Modules/MSource/Wall.cc \ + Modules/MSource/SeqConserved.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Z2.cc \ + Modules/MSink/Point.cc \ + Modules/MSink/Smear.cc \ + Modules/MSolver/A2AVectors.cc \ + Modules/MSolver/A2AAslashVectors.cc \ + Modules/MSolver/RBPrecCG.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/GaugeFix.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ Modules/MUtilities/RandomVectors.cc \ Modules/MUtilities/TestSeqGamma.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MSolver/RBPrecCG.cc \ + Modules/MUtilities/TestSeqConserved.cc \ + Modules/MLoop/NoiseLoop.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/VPCounterTerms.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MScalar/ScalarVP.cc \ + Modules/MDistil/DistilVectors.cc \ Modules/MDistil/BContraction.cc \ Modules/MDistil/LapEvec.cc \ Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambLight.cc \ - Modules/MDistil/DistilVectors.cc \ - Modules/MContraction/WeakHamiltonianEye.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/Meson.cc \ - Modules/MContraction/WeakNeutral4ptDisc.cc \ - Modules/MContraction/WardIdentity.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/WeakHamiltonianNonEye.cc \ - Modules/MContraction/Baryon.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MAction/ZMobiusDWF.cc \ - Modules/MAction/WilsonClover.cc \ - Modules/MAction/Wilson.cc \ - Modules/MAction/DWF.cc \ - Modules/MAction/MobiusDWF.cc \ - Modules/MAction/ScaledDWF.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadA2AVectors.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadCosmHol.cc \ Modules/MNPR/Amputate.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MLoop/NoiseLoop.cc \ - Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/Div.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ - Modules/MScalarSUN/ShiftProbe.cc \ + Modules/MAction/Wilson.cc \ + Modules/MAction/MobiusDWF.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MAction/WilsonClover.cc \ + Modules/MAction/DWF.cc \ + Modules/MAction/ScaledDWF.cc \ Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/StochFreeField.cc \ - Modules/MScalarSUN/EMT.cc \ Modules/MScalarSUN/Grad.cc \ - Modules/MSink/Smear.cc \ - Modules/MSink/Point.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MFermion/FreeProp.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/ScalarVP.cc \ - Modules/MScalar/VPCounterTerms.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MSource/SeqConserved.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/Z2.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/Momentum.cc + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/ShiftProbe.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MScalarSUN/TwoPoint.cc \ + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/Div.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadCosmHol.cc \ + Modules/MIO/LoadA2AVectors.cc modules_hpp =\ - Modules/MUtilities/RandomVectors.hpp \ - Modules/MUtilities/TestSeqGamma.hpp \ - Modules/MUtilities/PrecisionCast.hpp \ - Modules/MUtilities/TestSeqConserved.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ - Modules/MSolver/Guesser.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MDistil/LapEvec.hpp \ - Modules/MDistil/Distil.hpp \ - Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/Baryon2pt.hpp \ - Modules/MDistil/BContraction.hpp \ - Modules/MDistil/PerambLight.hpp \ - Modules/MContraction/WeakHamiltonian.hpp \ - Modules/MContraction/WeakNeutral4ptDisc.hpp \ - Modules/MContraction/WeakHamiltonianEye.hpp \ - Modules/MContraction/DiscLoop.hpp \ Modules/MContraction/Baryon.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/A2AMesonField.hpp \ Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/WeakHamiltonianNonEye.hpp \ + Modules/MContraction/A2AMesonField.hpp \ Modules/MContraction/Meson.hpp \ + Modules/MContraction/WeakHamiltonian.hpp \ + Modules/MContraction/WeakHamiltonianNonEye.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/WeakNeutral4ptDisc.hpp \ + Modules/MContraction/Gamma3pt.hpp \ Modules/MContraction/WardIdentity.hpp \ - Modules/MAction/Wilson.hpp \ - Modules/MAction/WilsonClover.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/ScaledDWF.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MIO/LoadBinary.hpp \ - Modules/MIO/LoadCosmHol.hpp \ - Modules/MIO/LoadNersc.hpp \ - Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadEigenPack.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MLoop/NoiseLoop.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ - Modules/MScalarSUN/ShiftProbe.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ - Modules/MFermion/GaugeProp.hpp \ + Modules/MContraction/WeakHamiltonianEye.hpp \ Modules/MFermion/FreeProp.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/ScalarVP.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/ChargedProp.hpp \ - Modules/MScalar/VPCounterTerms.hpp \ - Modules/MSource/Momentum.hpp \ + Modules/MFermion/GaugeProp.hpp \ Modules/MSource/SeqGamma.hpp \ Modules/MSource/Point.hpp \ - Modules/MSource/Z2.hpp \ Modules/MSource/Wall.hpp \ - Modules/MSource/SeqConserved.hpp + Modules/MSource/Z2.hpp \ + Modules/MSource/SeqConserved.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MSink/Point.hpp \ + Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MSolver/Guesser.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSolver/A2AVectors.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MGauge/StoutSmearing.hpp \ + Modules/MGauge/Unit.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/GaugeFix.hpp \ + Modules/MGauge/FundtoHirep.hpp \ + Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ + Modules/MUtilities/PrecisionCast.hpp \ + Modules/MUtilities/RandomVectors.hpp \ + Modules/MUtilities/TestSeqGamma.hpp \ + Modules/MUtilities/TestSeqConserved.hpp \ + Modules/MLoop/NoiseLoop.hpp \ + Modules/MScalar/FreeProp.hpp \ + Modules/MScalar/VPCounterTerms.hpp \ + Modules/MScalar/ScalarVP.hpp \ + Modules/MScalar/Scalar.hpp \ + Modules/MScalar/ChargedProp.hpp \ + Modules/MDistil/Distil.hpp \ + Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/PerambLight.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/Baryon2pt.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MNPR/Amputate.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MAction/DWF.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MAction/Wilson.hpp \ + Modules/MAction/WilsonClover.hpp \ + Modules/MAction/ZMobiusDWF.hpp \ + Modules/MAction/ScaledDWF.hpp \ + Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/ShiftProbe.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MIO/LoadEigenPack.hpp \ + Modules/MIO/LoadNersc.hpp \ + Modules/MIO/LoadA2AVectors.hpp \ + Modules/MIO/LoadCosmHol.hpp \ + Modules/MIO/LoadCoarseEigenPack.hpp \ + Modules/MIO/LoadBinary.hpp From 292ff33f7f74419eda13dcde29ae4831f7aa6ab2 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 21 Feb 2019 16:51:05 +0000 Subject: [PATCH 115/347] Removed issue with std::string_literal --- tests/IO/Test_serialisation.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index ae4b5b69..6988017e 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -162,7 +162,7 @@ public: #define RDR_ Hdf5Reader #define WTR_ Hdf5Writer #define TensorWriteReadInnerNoInit( T ) \ - filename = "iotest_"s + std::to_string(++TestNum) + "_" #T ".h5"; \ + filename = "iotest_" + std::to_string(++TestNum) + "_" #T ".h5"; \ ioTest(filename, t, #T, #T); #define TensorWriteReadInner( T ) SequentialInit( t ); TensorWriteReadInnerNoInit( T ) #define TensorWriteRead( T ) { T t ; TensorWriteReadInner( T ) } @@ -171,7 +171,6 @@ public: void EigenHdf5IOTest(void) { - using namespace std::string_literals; unsigned int TestNum = 0; std::string filename; using TensorSingle = Eigen::TensorFixedSize>; From 44a2d4854ab94abf67c86fbed8c0682452259d34 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 22 Feb 2019 15:14:32 +0000 Subject: [PATCH 116/347] Ensured Hdf5 chunk size always less than 4GB --- Grid/serialisation/Hdf5IO.h | 36 ++++++++++++++++++++++++++-- tests/hadrons/Test_hadrons_distil.cc | 5 +++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index c1bb891c..8ec6a8f5 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -105,11 +105,23 @@ namespace Grid template <> void Hdf5Writer::writeDefault(const std::string &s, const std::string &x); + static hsize_t alignup(hsize_t n) + { + n--; // 1000 0011 --> 1000 0010 + n |= n >> 1; // 1000 0010 | 0100 0001 = 1100 0011 + n |= n >> 2; // 1100 0011 | 0011 0000 = 1111 0011 + n |= n >> 4; // 1111 0011 | 0000 1111 = 1111 1111 + n |= n >> 8; // ... (At this point all bits are 1, so further bitwise-or + n |= n >> 16; // operations produce no effect.) + n++; // 1111 1111 --> 1 0000 0000 + return n; + }; + template void Hdf5Writer::writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements) { // Hdf5 needs the dimensions as hsize_t - int rank = static_cast(Dimensions.size()); + const int rank = static_cast(Dimensions.size()); std::vector dim(rank); for(int i = 0; i < rank; i++) dim[i] = Dimensions[i]; @@ -119,9 +131,29 @@ namespace Grid size_t DataSize = NumElements * sizeof(U); if (DataSize > dataSetThres_) { + // Make sure the chunk size is < 4GB + const hsize_t MaxElements = ( sizeof( U ) == 1 ) ? 0xffffffff : 0x100000000 / sizeof( U ); + hsize_t ElementsPerChunk = 1; + bool bTooBig = false; + for( unsigned int i = rank - 1; i != -1; i-- ) { + if( bTooBig ) + // Chunk size is already as big as can be - remaining dimensions = 1 + dim[i] = 1; + else { + // Now make sure overall size is not too big + ElementsPerChunk *= dim[i]; + if( ElementsPerChunk >= MaxElements ) { + bTooBig = true; + hsize_t dividend = ElementsPerChunk / MaxElements; + hsize_t remainder = ElementsPerChunk % MaxElements; + if( remainder ) + dividend++; + dim[i] = dim[i] / dividend; + } + } + } H5NS::DataSet dataSet; H5NS::DSetCreatPropList plist; - plist.setChunk(rank, dim.data()); plist.setFletcher32(); dataSet = group_.createDataSet(s, Hdf5Type::type(), dataSpace, plist); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 655be9cf..40d6a4bf 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -655,7 +655,10 @@ int main(int argc, char *argv[]) << ", sizeof(size_t) = " << sizeof(size_t) << ", sizeof(std::size_t) = " << sizeof(std::size_t) << ", sizeof(std::streamsize) = " << sizeof(std::streamsize) - << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; + << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) + << ", sizeof(hsize_t) = " << sizeof(hsize_t) + << ", sizeof(unsigned long long) = " << sizeof(unsigned long long) + << std::endl; if( DebugEigenTest() ) return 0; if(DebugGridTensorTest()) return 0; #endif From 03d031d6232acc4910df14816bbac9ea9988acad Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 22 Feb 2019 16:30:22 +0000 Subject: [PATCH 117/347] tesserct test --- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 2 +- tests/hadrons/Test_hadrons_distil.cc | 18 +- tests/hadrons/Test_tesseract.cc | 803 ++++++++++++++++++++++++ 4 files changed, 815 insertions(+), 10 deletions(-) create mode 100644 tests/hadrons/Test_tesseract.cc diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 5dfc830b..fa62fe49 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -239,7 +239,7 @@ void TLapEvec::execute(void) auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); FieldMetaData header; - if((1)) { + if((0)) { const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(gridHD); pRNG4d.SeedFixedIntegers(seeds); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 00ac74cc..ba8f6ffa 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -212,7 +212,7 @@ void TPerambLight::execute(void) envGetTmp(GaugeField, Umu); FieldMetaData header; - if((1)){ + if((0)){ const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(grid4d); pRNG4d.SeedFixedIntegers(seeds); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 655be9cf..60729a28 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -42,8 +42,8 @@ void test_Global(Application &application) { // global parameters Application::GlobalPar globalPar; - globalPar.trajCounter.start = 1500; - globalPar.trajCounter.end = 1520; + globalPar.trajCounter.start = 1100; + globalPar.trajCounter.end = 1120; globalPar.trajCounter.step = 20; globalPar.runId = "test"; application.setPar(globalPar); @@ -60,8 +60,10 @@ void test_LapEvec(Application &application) application.createModule(szGaugeName); // Now make an instance of the LapEvec object MDistil::LapEvecPar p; - p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; - p.ConfigFileName="ckpoint_lat.3000"; + //p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; + //p.ConfigFileName="ckpoint_lat.3000"; + p.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; + p.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; p.gauge = szGaugeName; //p.EigenPackName = "ePack"; //p.Distil.TI = 8; @@ -91,8 +93,8 @@ void test_Perambulators(Application &application) MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="peramb.bin"; - PerambPar.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; - PerambPar.ConfigFileName="ckpoint_lat.3000"; + PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; + PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; PerambPar.UniqueIdentifier="full_dilution"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; @@ -138,8 +140,8 @@ void test_PerambulatorsS(Application &application) MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="perambS.bin"; - PerambPar.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; - PerambPar.ConfigFileName="ckpoint_lat.3000"; + PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; + PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; PerambPar.UniqueIdentifier="full_dilution"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; diff --git a/tests/hadrons/Test_tesseract.cc b/tests/hadrons/Test_tesseract.cc new file mode 100644 index 00000000..435e94e1 --- /dev/null +++ b/tests/hadrons/Test_tesseract.cc @@ -0,0 +1,803 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Tests/Hadrons/Test_hadrons_distil.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 +#include +#include + +using namespace Grid; +using namespace Hadrons; + +///////////////////////////////////////////////////////////// +// Test creation of laplacian eigenvectors +///////////////////////////////////////////////////////////// + +void test_Global(Application &application) +{ + // global parameters + Application::GlobalPar globalPar; + globalPar.trajCounter.start = 1100; + globalPar.trajCounter.end = 1120; + globalPar.trajCounter.step = 20; + globalPar.runId = "test"; + application.setPar(globalPar); +} + +///////////////////////////////////////////////////////////// +// Test creation of laplacian eigenvectors +///////////////////////////////////////////////////////////// + +void test_LapEvec(Application &application) +{ + const char szGaugeName[] = "gauge"; + // gauge field + application.createModule(szGaugeName); + // Now make an instance of the LapEvec object + MDistil::LapEvecPar p; + //p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; + //p.ConfigFileName="ckpoint_lat.3000"; + p.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; + p.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; + p.gauge = szGaugeName; + //p.EigenPackName = "ePack"; + //p.Distil.TI = 8; + //p.Distil.LI = 3; + //p.Distil.Nnoise = 2; + //p.Distil.tSrc = 0; + p.Stout.steps = 3; + p.Stout.parm = 0.2; + p.Cheby.PolyOrder = 11; + p.Cheby.alpha = 0.5; + p.Cheby.beta = 12.5; + p.Lanczos.Nvec = 6; + p.Lanczos.Nk = 15; + p.Lanczos.Np = 5; + p.Lanczos.MaxIt = 1000; + p.Lanczos.resid = 1e-5; + application.createModule("LapEvec",p); +} + +///////////////////////////////////////////////////////////// +// Perambulators +///////////////////////////////////////////////////////////// + +void test_Perambulators(Application &application) +{ + // PerambLight parameters + MDistil::PerambLight::Par PerambPar; + PerambPar.eigenPack="LapEvec"; + PerambPar.PerambFileName="peramb.bin"; + PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; + PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; + PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.Distil.tsrc = 0; + PerambPar.Distil.nnoise = 1; + PerambPar.Distil.LI=6; + PerambPar.Distil.SI=4; + PerambPar.Distil.TI=32; + PerambPar.nvec=6; + PerambPar.Distil.Ns=4; + PerambPar.Distil.Nt=32; + PerambPar.Distil.Nt_inv=1; + PerambPar.Solver.mass=0.5; + PerambPar.Solver.M5=1.8; + PerambPar.Ls=16; + PerambPar.Solver.CGPrecision=1e-2; + PerambPar.Solver.MaxIterations=10000; + application.createModule("Peramb",PerambPar); +} +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +void test_DistilVectors(Application &application) +{ + // DistilVectors parameters + MDistil::DistilVectors::Par DistilVecPar; + DistilVecPar.noise="Peramb_noise"; + DistilVecPar.perambulator="Peramb_perambulator_light"; + DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.tsrc = 0; + DistilVecPar.nnoise = 1; + DistilVecPar.LI=6; + DistilVecPar.SI=4; + DistilVecPar.TI=32; + DistilVecPar.nvec=6; + DistilVecPar.Ns=4; + DistilVecPar.Nt=32; + DistilVecPar.Nt_inv=1; + application.createModule("DistilVecs",DistilVecPar); +} +void test_PerambulatorsS(Application &application) +{ + // PerambLight parameters + MDistil::PerambLight::Par PerambPar; + PerambPar.eigenPack="LapEvec"; + PerambPar.PerambFileName="perambS.bin"; + PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; + PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; + PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.Distil.tsrc = 0; + PerambPar.Distil.nnoise = 1; + PerambPar.Distil.LI=3; + PerambPar.Distil.SI=4; + PerambPar.Distil.TI=8; + PerambPar.nvec=3; + PerambPar.Distil.Ns=4; + PerambPar.Distil.Nt=8; + PerambPar.Distil.Nt_inv=1; + PerambPar.Solver.mass=0.04; //strange mass??? + PerambPar.Solver.M5=1.8; + PerambPar.Ls=16; + PerambPar.Solver.CGPrecision=1e-8; + PerambPar.Solver.MaxIterations=10000; + application.createModule("PerambS",PerambPar); +} +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +void test_DistilVectorsS(Application &application) +{ + // DistilVectors parameters + MDistil::DistilVectors::Par DistilVecPar; + DistilVecPar.noise="PerambS_noise"; + DistilVecPar.perambulator="PerambS_perambulator_light"; + DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.tsrc = 0; + DistilVecPar.nnoise = 1; + DistilVecPar.LI=3; + DistilVecPar.SI=4; + DistilVecPar.TI=8; + DistilVecPar.nvec=3; + DistilVecPar.Ns=4; + DistilVecPar.Nt=8; + DistilVecPar.Nt_inv=1; + application.createModule("DistilVecsS",DistilVecPar); +} +///////////////////////////////////////////////////////////// +// MesonSink +///////////////////////////////////////////////////////////// + +void test_MesonSink(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="Peramb_unsmeared_sink"; + A2AMesonFieldPar.right="Peramb_unsmeared_sink"; + A2AMesonFieldPar.output="DistilFields"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonSink",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// MesonFields +///////////////////////////////////////////////////////////// + +void test_MesonFieldSL(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecsS_phi"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_phi"; + A2AMesonFieldPar.output="DistilFieldsS"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldS",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// MesonFields - phiphi +///////////////////////////////////////////////////////////// + +void test_MesonField(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_phi"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_phi"; + A2AMesonFieldPar.output="MesonSinksPhi"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonField",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// MesonFields - rhorho +///////////////////////////////////////////////////////////// + +void test_MesonFieldRho(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_rho"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.output="MesonSinksRho"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldRho",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// BaryonFields - phiphiphi +///////////////////////////////////////////////////////////// + +void test_BaryonFieldPhi(Application &application) +{ + // DistilVectors parameters + MDistil::BContraction::Par BContractionPar; + BContractionPar.one="DistilVecs_phi"; + BContractionPar.two="DistilVecs_phi"; + BContractionPar.three="DistilVecs_phi"; + BContractionPar.output="BaryonFieldPhi"; + BContractionPar.parity=1; + BContractionPar.mom={"0 0 0"}; + application.createModule("BaryonFieldPhi",BContractionPar); +} +///////////////////////////////////////////////////////////// +// BaryonFields - rhorhorho +///////////////////////////////////////////////////////////// + +void test_BaryonFieldRho(Application &application) +{ + // DistilVectors parameters + MDistil::BContraction::Par BContractionPar; + BContractionPar.one="DistilVecs_rho"; + BContractionPar.two="DistilVecs_rho"; + BContractionPar.three="DistilVecs_rho"; + BContractionPar.output="BaryonFieldRho"; + BContractionPar.parity=1; + BContractionPar.mom={"0 0 0"}; + application.createModule("BaryonFieldRho",BContractionPar); +} +///////////////////////////////////////////////////////////// +// BaryonContraction +///////////////////////////////////////////////////////////// + +void test_Baryon2pt(Application &application) +{ + // DistilVectors parameters + MDistil::Baryon2pt::Par Baryon2ptPar; + Baryon2ptPar.inputL="BaryonFieldPhi"; + Baryon2ptPar.inputR="BaryonFieldRho"; + Baryon2ptPar.quarksL="uud"; + Baryon2ptPar.quarksR="uud"; + Baryon2ptPar.output="C2_baryon"; + application.createModule("C2_b",Baryon2ptPar); +} + +///////////////////////////////////////////////////////////// +// emField +///////////////////////////////////////////////////////////// +void test_em(Application &application) +{ + MGauge::StochEm::Par StochEmPar; + StochEmPar.gauge=PhotonR::Gauge::feynman; + StochEmPar.zmScheme=PhotonR::ZmScheme::qedL; + application.createModule("Em",StochEmPar); +} +///////////////////////////////////////////////////////////// +// MesonA2ASlash +///////////////////////////////////////////////////////////// +void test_Aslash(Application &application) +{ + MContraction::A2AAslashField::Par A2AAslashFieldPar; + A2AAslashFieldPar.left="Peramb_unsmeared_sink"; + A2AAslashFieldPar.right="Peramb_unsmeared_sink"; + A2AAslashFieldPar.output="unsmeared_Aslash"; + A2AAslashFieldPar.emField={"Em"}; + A2AAslashFieldPar.cacheBlock=2; + A2AAslashFieldPar.block=4; + application.createModule("Aslash_field",A2AAslashFieldPar); +} + +bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) +{ + if( bGobbleWhiteSpace ) + while( std::isspace(static_cast(*pstr)) ) + pstr++; + const char * p = pstr; + bool bMinus = false; + char c = * p++; + if( c == '+' ) + c = * p++; + else if( c == '-' ) { + bMinus = true; + c = * p++; + } + int n = c - '0'; + if( n < 0 || n > 9 ) + return false; + while( * p >= '0' && * p <= '9' ) { + n = n * 10 + ( * p ) - '0'; + p++; + } + if( bMinus ) + n *= -1; + ri = n; + pstr = p; + return true; +} + +#ifdef DEBUG + +typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; + +template +void DebugShowTensor(T &x, const char * n) +{ + const MyTensor::Index s{x.size()}; + std::cout << n << ".size() = " << s << std::endl; + std::cout << n << ".NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; + std::cout << n << ".NumIndices = " << x.NumIndices << std::endl; + const auto d{x.dimensions()}; + //std::cout << n << ".dimensions().size() = " << d.size() << std::endl; + std::cout << "Dimensions are "; + for(auto i = 0; i < x.NumDimensions ; i++) + std::cout << "[" << d[i] << "]"; + std::cout << std::endl; + MyTensor::Index SizeCalculated{1}; + std::cout << "Dimensions again"; + for(int i=0 ; i < x.NumDimensions ; i++ ) { + std::cout << " : [" << i << /*", " << x.IndexNames[i] << */"]=" << x.dimension(i); + SizeCalculated *= d[i]; + } + std::cout << std::endl; + std::cout << "SizeCalculated = " << SizeCalculated << std::endl;\ + assert( SizeCalculated == s ); + // Initialise + assert( x.NumDimensions == 3 ); + for( int i = 0 ; i < d[0] ; i++ ) + for( int j = 0 ; j < d[1] ; j++ ) + for( int k = 0 ; k < d[2] ; k++ ) { + x(i,j,k) = std::complex(SizeCalculated, -SizeCalculated); + SizeCalculated--; + } + // Show raw data + std::cout << "Data follow : " << std::endl; + typename T::Scalar * p = x.data(); + for( auto i = 0 ; i < s ; i++ ) { + if( i ) std::cout << ", "; + std::cout << n << ".data()[" << i << "]=" << * p++; + } + std::cout << std::endl; +} + +// Test whether typedef and underlying types are the same + +void DebugTestTypeEqualities(void) +{ + Real r1; + RealD r2; + double r3; + const std::type_info &tr1{typeid(r1)}; + const std::type_info &tr2{typeid(r2)}; + const std::type_info &tr3{typeid(r3)}; + if( tr1 == tr2 && tr2 == tr3 ) + std::cout << "r1, r2 and r3 are the same type" << std::endl; + else + std::cout << "r1, r2 and r3 are different types" << std::endl; + std::cout << "r1 is a " << tr1.name() << std::endl; + std::cout << "r2 is a " << tr2.name() << std::endl; + std::cout << "r3 is a " << tr3.name() << std::endl; + + // These are the same + Complex c1; + std::complex c2; + const std::type_info &tc1{typeid(c1)}; + const std::type_info &tc2{typeid(c2)}; + const std::type_info &tc3{typeid(SpinVector::scalar_type)}; + if( tc1 == tc2 && tc2 == tc3) + std::cout << "c1, c2 and SpinVector::scalar_type are the same type" << std::endl; + else + std::cout << "c1, c2 and SpinVector::scalar_type are different types" << std::endl; + std::cout << "c1 is a " << tc1.name() << std::endl; + std::cout << "c2 is a " << tc2.name() << std::endl; + std::cout << "SpinVector::scalar_type is a " << tc3.name() << std::endl; + + // These are the same + SpinVector s1; + iSpinVector s2; + iScalar, Ns> > s3; + const std::type_info &ts1{typeid(s1)}; + const std::type_info &ts2{typeid(s2)}; + const std::type_info &ts3{typeid(s3)}; + if( ts1 == ts2 && ts2 == ts3 ) + std::cout << "s1, s2 and s3 are the same type" << std::endl; + else + std::cout << "s1, s2 and s3 are different types" << std::endl; + std::cout << "s1 is a " << ts1.name() << std::endl; + std::cout << "s2 is a " << ts2.name() << std::endl; + std::cout << "s3 is a " << ts3.name() << std::endl; + + // These are the same + SpinColourVector sc1; + iSpinColourVector sc2; + const std::type_info &tsc1{typeid(sc1)}; + const std::type_info &tsc2{typeid(sc2)}; + if( tsc1 == tsc2 ) + std::cout << "sc1 and sc2 are the same type" << std::endl; + else + std::cout << "sc1 and sc2 are different types" << std::endl; + std::cout << "sc1 is a " << tsc1.name() << std::endl; + std::cout << "sc2 is a " << tsc2.name() << std::endl; +} + +bool DebugEigenTest() +{ + { + Eigen::TensorFixedSize,Eigen::Sizes<3,4,5>> x; + DebugShowTensor(x, "fixed"); + } + const char pszTestFileName[] = "test_tensor.bin"; + std::array as={"Alpha", "Beta", "Gamma"}; + MyTensor x(as, 2,1,4); + DebugShowTensor(x, "x"); + x.WriteBinary(pszTestFileName); + DebugShowTensor(x, "x"); + // Test initialisation of an array of strings + for( auto a : as ) + std::cout << a << std::endl; + Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; + DebugShowTensor(p, "p"); + std::cout << "p.IndexNames follow" << std::endl; + for( auto a : p.IndexNames ) + std::cout << a << std::endl; + // Now see whether we can read a tensor back + std::array Names2={"Alpha", "Gamma", "Delta"}; + MyTensor y(Names2, 2,4,1); + y.ReadBinary(pszTestFileName); + DebugShowTensor(y, "y"); + + // Testing whether typedef produces the same type - yes it does + + DebugTestTypeEqualities(); + std::cout << std::endl; + + // How to access members of SpinColourVector + SpinColourVector sc; + for( int s = 0 ; s < Ns ; s++ ) { + auto cv{sc()(s)}; + iVector c2{sc()(s)}; + std::cout << " cv is a " << typeid(cv).name() << std::endl; + std::cout << " c2 is a " << typeid(c2).name() << std::endl; + for( int c = 0 ; c < Nc ; c++ ) { + Complex & z{cv(c)}; + std::cout << " sc[spin=" << s << ", colour=" << c << "] = " << z << std::endl; + } + } + // We could have removed the Lorentz index independently, but much easier to do as we do above + iVector,Ns> sc2{sc()}; + std::cout << "sc() is a " << typeid(sc()).name() << std::endl; + std::cout << "sc2 is a " << typeid(sc2 ).name() << std::endl; + + // Or you can access elements directly + std::complex z = sc()(0)(0); + std::cout << "z = " << z << std::endl; + sc()(3)(2) = std::complex{3.141,-3.141}; + std::cout << "sc()(3)(2) = " << sc()(3)(2) << std::endl; + + return true; +} + +template +void DebugGridTensorTest_print( int i ) +{ + std::cout << i << " : " << EigenIO::is_tensor::value + << ", rank " << EigenIO::Traits::rank + << ", rank_non_trivial " << EigenIO::Traits::rank_non_trivial + << ", count " << EigenIO::Traits::count + << ", scalar_size " << EigenIO::Traits::scalar_size + << ", size " << EigenIO::Traits::size + << std::endl; +} + +// begin() and end() are the minimum necessary to support range-for loops +// should really turn this into an iterator ... +template +class TestObject { +public: + using value_type = T; +private: + value_type * m_p; +public: + TestObject() { + m_p = reinterpret_cast(std::malloc(N * sizeof(value_type))); + } + ~TestObject() { std::free(m_p); } + inline value_type * begin(void) { return m_p; } + inline value_type * end(void) { return m_p + N; } +}; + +template +void EigenSliceExample() +{ + std::cout << "Eigen example, Options = " << Options << std::endl; + using T2 = Eigen::Tensor; + T2 a(4, 3); + a.setValues({{0, 100, 200}, {300, 400, 500}, + {600, 700, 800}, {900, 1000, 1100}}); + std::cout << "a\n" << a << std::endl; + DumpMemoryOrder( a, "a" ); + Eigen::array offsets = {0, 1}; + Eigen::array extents = {4, 2}; + T2 slice = a.slice(offsets, extents); + std::cout << "slice\n" << slice << std::endl; + DumpMemoryOrder( slice, "slice" ); + std::cout << "\n========================================" << std::endl; +} + +template +void EigenSliceExample2() +{ + using TestScalar = std::complex; + using T3 = Eigen::Tensor; + using T2 = Eigen::Tensor; + T3 a(2,3,4); + + std::cout << "Initialising a:"; + for_all( a, [&](TestScalar &c, float f, const std::array &Dims ){ + c = TestScalar{f,-f}; + std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; + } ); + std::cout << std::endl; + //std::cout << "Validating a:"; + float z = 0; + for( int i = 0 ; i < a.dimension(0) ; i++ ) + for( int j = 0 ; j < a.dimension(1) ; j++ ) + for( int k = 0 ; k < a.dimension(2) ; k++ ) { + TestScalar w{z, -z}; + //std::cout << " a(" << i << "," << j << "," << k << ")=" << w; + assert( a(i,j,k) == w ); + z++; + } + //std::cout << std::endl; + //std::cout << "a initialised to:\n" << a << std::endl; + DumpMemoryOrder( a, "a" ); + std::cout << "for_all(a):"; + for_all( a, [&](TestScalar c, typename T3::Index n, const std::array &Dims ){ + std::cout << " (" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")<" << n << ">=" << c; + } ); + std::cout << std::endl; + Eigen::array offsets = {0,1,1}; + Eigen::array extents = {1,2,2}; + T3 b; + b = a.slice( offsets, extents );//.reshape(NewExtents); + std::cout << "b = a.slice( offsets, extents ):\n" << b << std::endl; + DumpMemoryOrder( b, "b" ); + T2 c(3,4); + c = a.chip(0,1); + std::cout << "c = a.chip(0,0):\n" << c << std::endl; + DumpMemoryOrder( c, "c" ); + //T2 d = b.reshape(extents); + //std::cout << "b.reshape(extents) is:\n" << d << std::endl; + std::cout << "\n========================================" << std::endl; +} + +void DebugFelixTensorTest( void ) +{ + unsigned int Nmom = 2; + unsigned int Nt = 2; + unsigned int N_1 = 2; + unsigned int N_2 = 2; + unsigned int N_3 = 2; + using BaryonTensorSet = Eigen::Tensor; + BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); + std::vector Memory(Nmom * Nt * N_1 * N_2 * N_3 * 2); + using BaryonTensorMap = Eigen::TensorMap; + BaryonTensorMap BField4 (&Memory[0], Nmom,4,Nt,N_1,N_2,N_3); + + EigenSliceExample(); + EigenSliceExample<0>(); + EigenSliceExample2(); + EigenSliceExample2<0>(); +} + +bool DebugGridTensorTest( void ) +{ + DebugFelixTensorTest(); + typedef Complex t1; + typedef iScalar t2; + typedef iVector t3; + typedef iMatrix t4; + typedef iVector,4> t5; + typedef iScalar t6; + typedef iMatrix t7; + typedef iMatrix,4>,2> t8; + int i = 1; + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + + //using TOC7 = TestObject, 7>; + using TOC7 = t7; + TOC7 toc7; + constexpr std::complex Inc{1,-1}; + std::complex Start{Inc}; + for( auto &x : toc7 ) { + x = Start; + Start += Inc; + } + i = 0; + std::cout << "toc7:"; + for( auto x : toc7 ) std::cout << " [" << i++ << "]=" << x; + std::cout << std::endl; + + t2 o2; + auto a2 = TensorRemove(o2); + //t3 o3; + //t4 o4; + //auto a3 = TensorRemove(o3); + //auto a4 = TensorRemove(o4); + + return true; +} +#endif + +int main(int argc, char *argv[]) +{ +#ifdef DEBUG + // Debug only - test of Eigen::Tensor + std::cout << "sizeof(int) = " << sizeof(int) + << ", sizeof(long) = " << sizeof(long) + << ", sizeof(size_t) = " << sizeof(size_t) + << ", sizeof(std::size_t) = " << sizeof(std::size_t) + << ", sizeof(std::streamsize) = " << sizeof(std::streamsize) + << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; + if( DebugEigenTest() ) return 0; + if(DebugGridTensorTest()) return 0; +#endif + + // Decode command-line parameters. 1st one is which test to run + int iTestNum = -1; + + for(int i = 1 ; i < argc ; i++ ) { + std::cout << "argv[" << i << "]=\"" << argv[i] << "\"" << std::endl; + const char * p = argv[i]; + if( * p == '/' || * p == '-' ) { + p++; + char c = * p++; + switch(toupper(c)) { + case 'T': + if( bNumber( iTestNum, p ) ) { + std::cout << "Test " << iTestNum << " requested"; + if( * p ) + std::cout << " (ignoring trailer \"" << p << "\")"; + std::cout << std::endl; + } + else + std::cout << "Invalid test \"" << &argv[i][2] << "\"" << std::endl; + break; + default: + std::cout << "Ignoring switch \"" << &argv[i][1] << "\"" << std::endl; + break; + } + } + } + + // initialization ////////////////////////////////////////////////////////// + Grid_init(&argc, &argv); + HadronsLogError.Active(GridLogError.isActive()); + HadronsLogWarning.Active(GridLogWarning.isActive()); + HadronsLogMessage.Active(GridLogMessage.isActive()); + HadronsLogIterative.Active(GridLogIterative.isActive()); + HadronsLogDebug.Active(GridLogDebug.isActive()); + LOG(Message) << "Grid initialized" << std::endl; + + // run setup /////////////////////////////////////////////////////////////// + Application application; + + // For now perform free propagator test - replace this with distillation test(s) + LOG(Message) << "====== Creating xml for test " << iTestNum << " ======" << std::endl; + //const unsigned int nt = GridDefaultLatt()[Tp]; + + switch(iTestNum) { + case 1: + test_Global( application ); + test_LapEvec( application ); + break; + case 2: + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + break; + case 3: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + break; + default: // 4 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_MesonField( application ); + break; + case 5: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_PerambulatorsS( application ); + test_DistilVectorsS( application ); + test_MesonFieldSL( application ); + break; + case 6: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_MesonSink( application ); + break; + case 7: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_BaryonFieldPhi( application ); + test_BaryonFieldRho( application ); + break; + case 8: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_MesonField( application ); + test_MesonFieldRho( application ); + break; + case 9: // 3 + test_Global( application ); + test_Baryon2pt( application ); + break; + } + LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; + + // execution + application.saveParameterFile("test_hadrons_distil.xml"); + application.run(); + + // epilogue + LOG(Message) << "Grid is finalizing now" << std::endl; + Grid_finalize(); + + return EXIT_SUCCESS; +} From f9e505108b10580587743c4cc797961b67a4a15e Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 22 Feb 2019 16:31:17 +0000 Subject: [PATCH 118/347] test Aslash --- tests/hadrons/Test_hadrons_distil.cc | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 655be9cf..3aab094d 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -297,6 +297,33 @@ void test_Baryon2pt(Application &application) Baryon2ptPar.output="C2_baryon"; application.createModule("C2_b",Baryon2ptPar); } +///////////////////////////////////////////////////////////// +// emField +///////////////////////////////////////////////////////////// +void test_em(Application &application) +{ + MGauge::StochEm::Par StochEmPar; + StochEmPar.gauge=PhotonR::Gauge::feynman; + StochEmPar.zmScheme=PhotonR::ZmScheme::qedL; + application.createModule("Em",StochEmPar); +} + +///////////////////////////////////////////////////////////// +// MesonA2ASlash +///////////////////////////////////////////////////////////// + +void test_Aslash(Application &application) +{ + // DistilVectors parameters + MContraction::A2AAslashField::Par A2AAslashFieldPar; + A2AAslashFieldPar.left="Peramb_unsmeared_sink"; + A2AAslashFieldPar.right="Peramb_unsmeared_sink"; + A2AAslashFieldPar.output="unsmeared_Aslash"; + A2AAslashFieldPar.emField={"Em"}; + A2AAslashFieldPar.cacheBlock=2; + A2AAslashFieldPar.block=4; + application.createModule("Aslash_field",A2AAslashFieldPar); +} bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { @@ -761,6 +788,13 @@ int main(int argc, char *argv[]) test_Global( application ); test_Baryon2pt( application ); break; + case 10: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_em( application ); + test_Aslash( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From cad26a736e7744687044318e22b55e3a0209d30f Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 22 Feb 2019 17:05:16 +0000 Subject: [PATCH 119/347] quick&dirty fix for g5*field --- Hadrons/Modules.hpp | 137 ++++++------ Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 2 +- Hadrons/modules.inc | 264 ++++++++++++------------ tests/hadrons/Test_hadrons_distil.cc | 18 +- 5 files changed, 221 insertions(+), 202 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 6c05dc46..4f186a97 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,80 +1,81 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include +#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include -#include -#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index fa62fe49..5dfc830b 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -239,7 +239,7 @@ void TLapEvec::execute(void) auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); FieldMetaData header; - if((0)) { + if((1)) { const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(gridHD); pRNG4d.SeedFixedIntegers(seeds); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index ba8f6ffa..00ac74cc 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -212,7 +212,7 @@ void TPerambLight::execute(void) envGetTmp(GaugeField, Umu); FieldMetaData header; - if((0)){ + if((1)){ const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(grid4d); pRNG4d.SeedFixedIntegers(seeds); diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 17f54adb..f5a3cb78 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,159 +1,161 @@ modules_cc =\ - Modules/MContraction/WeakHamiltonianEye.cc \ - Modules/MContraction/Baryon.cc \ - Modules/MContraction/Meson.cc \ - Modules/MContraction/WeakNeutral4ptDisc.cc \ - Modules/MContraction/WeakHamiltonianNonEye.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MContraction/WardIdentity.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MFermion/FreeProp.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MSource/Momentum.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/SeqConserved.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/Z2.cc \ - Modules/MSink/Point.cc \ - Modules/MSink/Smear.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MUtilities/TestSeqConserved.cc \ Modules/MUtilities/RandomVectors.cc \ Modules/MUtilities/TestSeqGamma.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MUtilities/TestSeqConserved.cc \ - Modules/MLoop/NoiseLoop.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/VPCounterTerms.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MScalar/ScalarVP.cc \ - Modules/MDistil/DistilVectors.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/A2AVectors.cc \ + Modules/MSolver/A2AAslashVectors.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSolver/RBPrecCG.cc \ + Modules/MDistil/g5_multiply.cc \ Modules/MDistil/BContraction.cc \ Modules/MDistil/LapEvec.cc \ Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambLight.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MContraction/WeakHamiltonianEye.cc \ + Modules/MContraction/Gamma3pt.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/Meson.cc \ + Modules/MContraction/WeakNeutral4ptDisc.cc \ + Modules/MContraction/WardIdentity.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MContraction/WeakHamiltonianNonEye.cc \ + Modules/MContraction/Baryon.cc \ + Modules/MContraction/A2AAslashField.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MAction/WilsonClover.cc \ + Modules/MAction/Wilson.cc \ + Modules/MAction/DWF.cc \ + Modules/MAction/MobiusDWF.cc \ + Modules/MAction/ScaledDWF.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/GaugeFix.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadA2AVectors.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadCosmHol.cc \ Modules/MNPR/Amputate.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MAction/Wilson.cc \ - Modules/MAction/MobiusDWF.cc \ - Modules/MAction/ZMobiusDWF.cc \ - Modules/MAction/WilsonClover.cc \ - Modules/MAction/DWF.cc \ - Modules/MAction/ScaledDWF.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/ShiftProbe.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/StochFreeField.cc \ + Modules/MLoop/NoiseLoop.cc \ Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ Modules/MScalarSUN/Div.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MIO/LoadA2AVectors.cc + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/ShiftProbe.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/Grad.cc \ + Modules/MSink/Smear.cc \ + Modules/MSink/Point.cc \ + Modules/MFermion/GaugeProp.cc \ + Modules/MFermion/FreeProp.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/ScalarVP.cc \ + Modules/MScalar/VPCounterTerms.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MSource/SeqConserved.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Wall.cc \ + Modules/MSource/Z2.cc \ + Modules/MSource/Point.cc \ + Modules/MSource/Momentum.cc modules_hpp =\ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MContraction/WeakHamiltonian.hpp \ - Modules/MContraction/WeakHamiltonianNonEye.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/WeakNeutral4ptDisc.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/WardIdentity.hpp \ - Modules/MContraction/WeakHamiltonianEye.hpp \ - Modules/MFermion/FreeProp.hpp \ - Modules/MFermion/GaugeProp.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Point.hpp \ - Modules/MSource/Wall.hpp \ - Modules/MSource/Z2.hpp \ - Modules/MSource/SeqConserved.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ - Modules/MSolver/Guesser.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MUtilities/PrecisionCast.hpp \ Modules/MUtilities/RandomVectors.hpp \ Modules/MUtilities/TestSeqGamma.hpp \ + Modules/MUtilities/PrecisionCast.hpp \ Modules/MUtilities/TestSeqConserved.hpp \ - Modules/MLoop/NoiseLoop.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/VPCounterTerms.hpp \ - Modules/MScalar/ScalarVP.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/ChargedProp.hpp \ - Modules/MDistil/Distil.hpp \ + Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MSolver/Guesser.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSolver/A2AVectors.hpp \ Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/Distil.hpp \ + Modules/MDistil/g5_multiply.hpp \ Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/BContraction.hpp \ Modules/MDistil/Baryon2pt.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/PerambLight.hpp \ + Modules/MContraction/WeakHamiltonian.hpp \ + Modules/MContraction/WeakNeutral4ptDisc.hpp \ + Modules/MContraction/WeakHamiltonianEye.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/A2AMesonField.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/WeakHamiltonianNonEye.hpp \ + Modules/MContraction/Meson.hpp \ + Modules/MContraction/WardIdentity.hpp \ Modules/MAction/Wilson.hpp \ Modules/MAction/WilsonClover.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ + Modules/MAction/DWF.hpp \ Modules/MAction/ScaledDWF.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/ShiftProbe.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MIO/LoadEigenPack.hpp \ + Modules/MAction/ZMobiusDWF.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/Unit.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MGauge/StoutSmearing.hpp \ + Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ + Modules/MGauge/FundtoHirep.hpp \ + Modules/MGauge/GaugeFix.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ + Modules/MIO/LoadBinary.hpp \ + Modules/MIO/LoadCosmHol.hpp \ Modules/MIO/LoadNersc.hpp \ Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCosmHol.hpp \ Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadBinary.hpp + Modules/MIO/LoadEigenPack.hpp \ + Modules/MNPR/Amputate.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MLoop/NoiseLoop.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MScalarSUN/ShiftProbe.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MSink/Point.hpp \ + Modules/MFermion/GaugeProp.hpp \ + Modules/MFermion/FreeProp.hpp \ + Modules/MScalar/Scalar.hpp \ + Modules/MScalar/ScalarVP.hpp \ + Modules/MScalar/FreeProp.hpp \ + Modules/MScalar/ChargedProp.hpp \ + Modules/MScalar/VPCounterTerms.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Z2.hpp \ + Modules/MSource/Wall.hpp \ + Modules/MSource/SeqConserved.hpp diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 660626fd..49fd2a76 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -199,6 +199,21 @@ void test_MesonSink(Application &application) application.createModule("DistilMesonSink",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// +// g5*unsmeared +///////////////////////////////////////////////////////////// + +void test_g5_sinks(Application &application) +{ + // DistilVectors parameters + MDistil::g5_multiply::Par g5_multiplyPar; + g5_multiplyPar.input="Peramb_unsmeared_sink"; + g5_multiplyPar.nnoise = 1; + g5_multiplyPar.LI=5; + g5_multiplyPar.Ns=4; + g5_multiplyPar.Nt_inv=1; + application.createModule("g5phi",g5_multiplyPar); +} +///////////////////////////////////////////////////////////// // MesonFields ///////////////////////////////////////////////////////////// @@ -318,7 +333,7 @@ void test_Aslash(Application &application) { // DistilVectors parameters MContraction::A2AAslashField::Par A2AAslashFieldPar; - A2AAslashFieldPar.left="Peramb_unsmeared_sink"; + A2AAslashFieldPar.left="g5phi"; A2AAslashFieldPar.right="Peramb_unsmeared_sink"; A2AAslashFieldPar.output="unsmeared_Aslash"; A2AAslashFieldPar.emField={"Em"}; @@ -797,6 +812,7 @@ int main(int argc, char *argv[]) test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); + test_g5_sinks( application ); test_em( application ); test_Aslash( application ); break; From 3c9f2d41066b19ead21ef3d6f7d89747e4ab5360 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 25 Feb 2019 11:07:29 +0000 Subject: [PATCH 120/347] Chunking layout reasonably efficient. Looks for small prime factors of each dimension, falling back to approximate size if needed. --- Grid/serialisation/Hdf5IO.h | 78 ++++++++++++++++++++++++---------- tests/IO/Test_serialisation.cc | 19 +++------ 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 8ec6a8f5..324f796f 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -105,18 +106,17 @@ namespace Grid template <> void Hdf5Writer::writeDefault(const std::string &s, const std::string &x); - static hsize_t alignup(hsize_t n) - { - n--; // 1000 0011 --> 1000 0010 - n |= n >> 1; // 1000 0010 | 0100 0001 = 1100 0011 - n |= n >> 2; // 1100 0011 | 0011 0000 = 1111 0011 - n |= n >> 4; // 1111 0011 | 0000 1111 = 1111 1111 - n |= n >> 8; // ... (At this point all bits are 1, so further bitwise-or - n |= n >> 16; // operations produce no effect.) - n++; // 1111 1111 --> 1 0000 0000 - return n; + class SortNode { + public: + int index; + hsize_t dimsize; + //bool operator<(const SortNode &r) { return dimsize < r.dimsize || (dimsize == r.dimsize && index < r.index); } + //SortNode() = default; + SortNode(int Index, hsize_t DimSize) : index{Index}, dimsize{DimSize} {} }; + bool operator<(const SortNode &l, const SortNode &r) { return l.dimsize < r.dimsize || (l.dimsize == r.dimsize && l.index < r.index); } + template void Hdf5Writer::writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements) { @@ -125,30 +125,64 @@ namespace Grid std::vector dim(rank); for(int i = 0; i < rank; i++) dim[i] = Dimensions[i]; - // write to file + // write the entire dataset to file H5NS::DataSpace dataSpace(rank, dim.data()); size_t DataSize = NumElements * sizeof(U); if (DataSize > dataSetThres_) { - // Make sure the chunk size is < 4GB + // First few prime numbers from https://oeis.org/A000040 + static const unsigned short Primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, + 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, + 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, + 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271 }; + constexpr int NumPrimes = sizeof( Primes ) / sizeof( Primes[0] ); + // Make sure 1) each dimension; and 2) chunk size is < 4GB const hsize_t MaxElements = ( sizeof( U ) == 1 ) ? 0xffffffff : 0x100000000 / sizeof( U ); hsize_t ElementsPerChunk = 1; bool bTooBig = false; - for( unsigned int i = rank - 1; i != -1; i-- ) { + for( int i = rank - 1 ; i != -1 ; i-- ) { + auto &d = dim[i]; if( bTooBig ) - // Chunk size is already as big as can be - remaining dimensions = 1 - dim[i] = 1; + d = 1; // Chunk size is already as big as can be - remaining dimensions = 1 else { + // If individual dimension too big, reduce by prime factors if possible + for( int PrimeIdx = 0; d > MaxElements && PrimeIdx < NumPrimes; ) { + if( d % Primes[PrimeIdx] ) + PrimeIdx++; + else + d /= Primes[PrimeIdx]; + } + const char ErrorMsg[] = " dimension > 4GB without small prime factors. " + "Hdf5IO chunk size will be inefficient."; + if( d > MaxElements ) { + std::cout << GridLogMessage << "Individual" << ErrorMsg << std::endl; + hsize_t quotient = d / MaxElements; + if( d % MaxElements ) + quotient++; + d /= quotient; + } // Now make sure overall size is not too big - ElementsPerChunk *= dim[i]; - if( ElementsPerChunk >= MaxElements ) { + hsize_t OverflowCheck = ElementsPerChunk; + ElementsPerChunk *= d; + assert( OverflowCheck == ElementsPerChunk / d && "Product of dimensions overflowed hsize_t" ); + // If product of dimensions too big, reduce by prime factors + for( int PrimeIdx = 0; ElementsPerChunk > MaxElements && PrimeIdx < NumPrimes; ) { bTooBig = true; - hsize_t dividend = ElementsPerChunk / MaxElements; - hsize_t remainder = ElementsPerChunk % MaxElements; - if( remainder ) - dividend++; - dim[i] = dim[i] / dividend; + if( d % Primes[PrimeIdx] ) + PrimeIdx++; + else { + d /= Primes[PrimeIdx]; + ElementsPerChunk /= Primes[PrimeIdx]; + } + } + if( ElementsPerChunk > MaxElements ) { + std::cout << GridLogMessage << "Product of" << ErrorMsg << std::endl; + hsize_t quotient = ElementsPerChunk / MaxElements; + if( ElementsPerChunk % MaxElements ) + quotient++; + d /= quotient; + ElementsPerChunk /= quotient; } } } diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 6988017e..f14d118e 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -114,7 +114,7 @@ typedef Eigen::TensorFixedSize, Eigen::StorageOp typedef std::vector aTensor_9_4_2; typedef Eigen::TensorFixedSize> LSCTensor; #ifdef DEBUG -typedef Eigen::TensorFixedSize,2>,7>,3>, Eigen::Sizes<2,2,11,10,9>, Eigen::StorageOptions::RowMajor> LCMTensor; +typedef Eigen::TensorFixedSize,2>,7>,3>, Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor> LCMTensor; #endif class PerambIOTestClass: Serializable { @@ -132,9 +132,6 @@ public: , Tensor_9_4_2, tensor_9_4_2 , aTensor_9_4_2, atensor_9_4_2 , LSCTensor, MyLSCTensor -#ifdef DEBUG - , LCMTensor, MyLCMTensor -#endif ); PerambIOTestClass() : DistilParameterNames {"alpha", "beta", "gamma", "delta", "epsilon", "zeta"} @@ -153,23 +150,19 @@ public: for( auto &t : atensor_9_4_2 ) SequentialInit(t, Flag); SequentialInit( MyLSCTensor ); -#ifdef DEBUG - SequentialInit( MyLCMTensor ); -#endif } }; -#define RDR_ Hdf5Reader -#define WTR_ Hdf5Writer #define TensorWriteReadInnerNoInit( T ) \ - filename = "iotest_" + std::to_string(++TestNum) + "_" #T ".h5"; \ + filename = "iotest_" + std::to_string(++TestNum) + "_" #T + pszExtension; \ ioTest(filename, t, #T, #T); #define TensorWriteReadInner( T ) SequentialInit( t ); TensorWriteReadInnerNoInit( T ) #define TensorWriteRead( T ) { T t ; TensorWriteReadInner( T ) } #define TensorWriteReadV(T, ... ) { T t( __VA_ARGS__ ); TensorWriteReadInner( T ) } #define TensorWriteReadLarge( T ) { std::unique_ptr p{new T}; T &t{*p}; TensorWriteReadInnerNoInit(T) } -void EigenHdf5IOTest(void) +template +void EigenHdf5IOTest(const char * pszExtension) { unsigned int TestNum = 0; std::string filename; @@ -300,8 +293,10 @@ int main(int argc,char **argv) ioTest("iotest.h5", obj, "HDF5 (object) "); ioTest("iotest.h5", vec, "HDF5 (vector of objects)"); std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(); + EigenHdf5IOTest(".h5"); #endif + std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; + EigenHdf5IOTest(".bin"); std::cout << "\n==== vector flattening/reconstruction" << std::endl; typedef std::vector>> vec3d; From 76b6e8a01e751b91e7f0e035ca2fc5e21afa76b8 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 25 Feb 2019 11:18:25 +0000 Subject: [PATCH 121/347] first tesseract test --- Hadrons/Modules/MDistil/g5_multiply.cc | 7 ++ Hadrons/Modules/MDistil/g5_multiply.hpp | 121 ++++++++++++++++++++++++ tests/hadrons/Test_hadrons_distil.cc | 2 +- tests/hadrons/Test_tesseract.cc | 53 +---------- 4 files changed, 133 insertions(+), 50 deletions(-) create mode 100644 Hadrons/Modules/MDistil/g5_multiply.cc create mode 100644 Hadrons/Modules/MDistil/g5_multiply.hpp diff --git a/Hadrons/Modules/MDistil/g5_multiply.cc b/Hadrons/Modules/MDistil/g5_multiply.cc new file mode 100644 index 00000000..13d62eb0 --- /dev/null +++ b/Hadrons/Modules/MDistil/g5_multiply.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::Tg5_multiply; diff --git a/Hadrons/Modules/MDistil/g5_multiply.hpp b/Hadrons/Modules/MDistil/g5_multiply.hpp new file mode 100644 index 00000000..7c2ba42e --- /dev/null +++ b/Hadrons/Modules/MDistil/g5_multiply.hpp @@ -0,0 +1,121 @@ +#ifndef Hadrons_MDistil_g5_multiply_hpp_ +#define Hadrons_MDistil_g5_multiply_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * g5_multiply * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class g5_multiplyPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(g5_multiplyPar, + std::string, input, + int, nnoise, + int, LI, + int, Ns, + int, Nt_inv); +}; + +template +class Tg5_multiply: public Module +{ + public: + FERM_TYPE_ALIASES(FImpl,); + +public: + // constructor + Tg5_multiply(const std::string name); + // destructor + virtual ~Tg5_multiply(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(g5_multiply, Tg5_multiply, MDistil); + +/****************************************************************************** + * Tg5_multiply implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +Tg5_multiply::Tg5_multiply(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector Tg5_multiply::getInput(void) +{ + std::vector in; + + in.push_back(par().input); + + return in; +} + +template +std::vector Tg5_multiply::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void Tg5_multiply::setup(void) +{ + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int Nt_inv=par().Nt_inv; + + envCreate(std::vector, getName(), 1, + nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void Tg5_multiply::execute(void) +{ + auto &input_vector = envGet(std::vector, par().input); + auto &output_vector = envGet(std::vector, getName()); + + Gamma g5(Gamma::Algebra::Gamma5); + + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int Nt_inv=par().Nt_inv; + int Ntot = nnoise*LI*Ns*Nt_inv; + for (int i =0; i Date: Mon, 25 Feb 2019 11:35:33 +0000 Subject: [PATCH 122/347] Oops. Forgot to delete SortNode (prevented linking) --- Grid/serialisation/Hdf5IO.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 324f796f..15846e99 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -106,17 +106,6 @@ namespace Grid template <> void Hdf5Writer::writeDefault(const std::string &s, const std::string &x); - class SortNode { - public: - int index; - hsize_t dimsize; - //bool operator<(const SortNode &r) { return dimsize < r.dimsize || (dimsize == r.dimsize && index < r.index); } - //SortNode() = default; - SortNode(int Index, hsize_t DimSize) : index{Index}, dimsize{DimSize} {} - }; - - bool operator<(const SortNode &l, const SortNode &r) { return l.dimsize < r.dimsize || (l.dimsize == r.dimsize && l.index < r.index); } - template void Hdf5Writer::writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements) { From cfc14a7432d41b466676b3c6fc56a1ed30c315ee Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 25 Feb 2019 12:40:32 +0000 Subject: [PATCH 123/347] more adjustments to test --- tests/hadrons/Test_hadrons_distil.cc | 4 +++- tests/hadrons/Test_tesseract.cc | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 86a3435e..4e502e28 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -189,7 +189,8 @@ void test_MesonSink(Application &application) { // DistilVectors parameters MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="Peramb_unsmeared_sink"; + //A2AMesonFieldPar.left="Peramb_unsmeared_sink"; + A2AMesonFieldPar.left="g5phi"; A2AMesonFieldPar.right="Peramb_unsmeared_sink"; A2AMesonFieldPar.output="DistilFields"; A2AMesonFieldPar.gammas="all"; @@ -787,6 +788,7 @@ int main(int argc, char *argv[]) test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); + test_g5_sinks( application ); test_MesonSink( application ); break; case 7: // 3 diff --git a/tests/hadrons/Test_tesseract.cc b/tests/hadrons/Test_tesseract.cc index 7494e369..14f64346 100644 --- a/tests/hadrons/Test_tesseract.cc +++ b/tests/hadrons/Test_tesseract.cc @@ -199,6 +199,21 @@ void test_MesonSink(Application &application) application.createModule("DistilMesonSink",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// +// g5*unsmeared +///////////////////////////////////////////////////////////// + +void test_g5_sinks(Application &application) +{ + // DistilVectors parameters + MDistil::g5_multiply::Par g5_multiplyPar; + g5_multiplyPar.input="Peramb_unsmeared_sink"; + g5_multiplyPar.nnoise = 1; + g5_multiplyPar.LI=5; + g5_multiplyPar.Ns=4; + g5_multiplyPar.Nt_inv=1; + application.createModule("g5phi",g5_multiplyPar); +} +///////////////////////////////////////////////////////////// // MesonFields ///////////////////////////////////////////////////////////// From 9288019789fa75b4efee7b3f722b8fe69ebf2654 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 25 Feb 2019 14:10:24 +0000 Subject: [PATCH 124/347] Added Xml IO (has one deficiency: the format for multi-dimensional data is flat) --- Grid/serialisation/BaseIO.h | 27 +++++++------- Grid/serialisation/XmlIO.h | 65 +++++++++++++++++++++++++--------- tests/IO/Test_serialisation.cc | 10 +++--- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index a6746864..86dc1113 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -172,7 +172,7 @@ namespace Grid { template typename std::enable_if::value, void>::type for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, - std::array::rank_non_trivial> &MyIndex) + std::array::rank> &MyIndex) { lambda( scalar, Seq++, MyIndex ); } @@ -180,8 +180,8 @@ namespace Grid { // for_all helper function to call the lambda template typename std::enable_if::value, void>::type - for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index Seq, - std::array::rank_non_trivial> &MyIndex) + for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, + std::array::rank> &MyIndex) { using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later const auto InnerRank = EigenIO::Traits::rank_non_trivial; @@ -190,7 +190,7 @@ namespace Grid { lambda(Source, Seq++, MyIndex ); // Now increment SubIndex for( auto i = InnerRank - 1; i != -1 && ++MyIndex[rank + i] == EigenIO::Traits::DimensionNT(i); i-- ) - MyIndex[i] = 0; + MyIndex[rank + i] = 0; } } @@ -206,7 +206,7 @@ namespace Grid { assert( NumScalars > 0 ); using Index = typename ETensor::Index; Index ScalarElementCount{1}; - const auto InnerRank = EigenIO::Traits::rank_non_trivial; + const auto InnerRank = EigenIO::Traits::rank; const auto rank{ETensor::NumIndices}; std::array Dims; for(auto i = 0; i < rank; i++ ) { @@ -218,11 +218,11 @@ namespace Grid { } // Check that the number of containers is correct ... and we didn't lose anything in conversions assert( NumScalars == ScalarElementCount ); - // If the Scalar is actually a container, add the inner Scalar's non-trivial dimensions + // If the Scalar is actually a container, add the inner Scalar's dimensions size_t InnerScalarCount{1}; for(auto i = 0; i < InnerRank; i++ ) { - auto dim = EigenIO::Traits::DimensionNT(i); - assert( dim > 1 ); + auto dim = EigenIO::Traits::Dimension(i); + assert( dim > 0 ); Dims[rank + i] = static_cast(dim); assert( Dims[rank + i] == dim ); // check we didn't lose anything in the conversion InnerScalarCount *= dim; @@ -242,11 +242,12 @@ namespace Grid { } else { for( auto i = 0; i < rank && ++MyIndex[i] == Dims[i]; i++ ) MyIndex[i] = 0; - Seq = 0; + size_t NewSeq = 0; for( auto i = 0; i < rank + InnerRank ; i++ ) { - Seq *= Dims[i]; - Seq += MyIndex[i]; + NewSeq *= Dims[i]; + NewSeq += MyIndex[i]; } + Seq = static_cast( NewSeq ); } pScalar++; } @@ -271,7 +272,7 @@ namespace Grid { { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; - for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { c = Inc * static_cast::type>(n); } ); } @@ -291,7 +292,7 @@ namespace Grid { std::cout << pName; for( auto i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; std::cout << " in memory order:" << std::endl; - for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array &Dims ){ + for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array &Dims ){ std::cout << " "; for( auto dim : Dims ) std::cout << "[" << dim << "]"; diff --git a/Grid/serialisation/XmlIO.h b/Grid/serialisation/XmlIO.h index a26a33c5..38958ba0 100644 --- a/Grid/serialisation/XmlIO.h +++ b/Grid/serialisation/XmlIO.h @@ -57,6 +57,8 @@ namespace Grid void writeDefault(const std::string &s, const U &x); template void writeDefault(const std::string &s, const std::vector &x); + template + void writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements); std::string docString(void); std::string string(void); private: @@ -79,6 +81,8 @@ namespace Grid void readDefault(const std::string &s, U &output); template void readDefault(const std::string &s, std::vector &output); + template + void readMultiDim(const std::string &s, std::vector &buf, std::vector &dim); void readCurrentSubtree(std::string &s); private: void checkParse(const pugi::xml_parse_result &result, const std::string name); @@ -120,15 +124,28 @@ namespace Grid template void XmlWriter::writeDefault(const std::string &s, const std::vector &x) + { + std::vector dims(1); + dims[0] = x.size(); + writeMultiDim(s, dims, &x[0], dims[0]); + } + + template + void XmlWriter::writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements) { push(s); - for (auto &x_i: x) + if( Dimensions.size() > 1 ) { - write("elem", x_i); + for( auto d : Dimensions ) + write("dim", d); + } + while (NumElements--) + { + write("elem", *pDataRowMajor++); } pop(); } - + // Reader template implementation //////////////////////////////////////////// template void XmlReader::readDefault(const std::string &s, U &output) @@ -145,25 +162,39 @@ namespace Grid template void XmlReader::readDefault(const std::string &s, std::vector &output) { - std::string buf; - unsigned int i = 0; - + std::vector dims; + readMultiDim(s, output, dims); + assert( dims.size() == 1 && dims[0] == output.size() && "XmlIO: Expected 1D vector" ); + } + + template + void XmlReader::readMultiDim(const std::string &s, std::vector &buf, std::vector &dim) + { + unsigned int i = 0; + unsigned int Rank = 0; if (!push(s)) { std::cout << GridLogWarning << "XML: cannot open node '" << s << "'"; std::cout << std::endl; - - return; + } else { + while (node_.child("dim")) + { + dim.resize(Rank + 1); + read("dim", dim[Rank]); + node_.child("dim").set_name("dim-done"); + Rank++; + } + while (node_.child("elem")) + { + buf.resize(i + 1); + read("elem", buf[i]); + node_.child("elem").set_name("elem-done"); + i++; + } + pop(); + if( Rank == 0 ) + dim.push_back(i); } - while (node_.child("elem")) - { - output.resize(i + 1); - read("elem", output[i]); - node_.child("elem").set_name("elem-done"); - i++; - } - pop(); } - } #endif diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index f14d118e..305de088 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -86,7 +86,7 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam // writer needs to be destroyed so that writing physically happens { W writer(filename); - + writer.setPrecision(std::numeric_limits::digits10 + 1); write(writer, tag , object); } @@ -141,14 +141,14 @@ public: , tensorRank3(7,3,2) , atensor_9_4_2(3) { - Grid_complex Flag{1,-3.1415927}; + //Grid_complex Flag{1,-3.1415927}; // Gives errors on readback for text types + Grid_complex Flag{1,-1}; SequentialInit(Perambulator, Flag); SequentialInit(Perambulator2, Flag); SequentialInit(tensorRank5UShort); SequentialInit(tensorRank3, Flag); SequentialInit(tensor_9_4_2, Flag); - for( auto &t : atensor_9_4_2 ) - SequentialInit(t, Flag); + for( auto &t : atensor_9_4_2 ) SequentialInit(t, Flag); SequentialInit( MyLSCTensor ); } }; @@ -295,6 +295,8 @@ int main(int argc,char **argv) std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl; EigenHdf5IOTest(".h5"); #endif + std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; + EigenHdf5IOTest(".xml"); std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; EigenHdf5IOTest(".bin"); From c47c1a2472f10a793e1c2edfb10bfee6f21befc3 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 25 Feb 2019 15:36:11 +0000 Subject: [PATCH 125/347] started working on baryons - this time efficiently --- Grid/qcd/utils/A2Autils.h | 180 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 89b4d4bd..84c5fe60 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -101,6 +101,186 @@ public: #endif }; +/* +template +template +void A2Autils::BaryonField(TensorType &mat, + const FermionField *one, + const FermionField *two, + const FermionField *three, + std::vector gammaA, + std::vector gammaB, + const std::vector &mom, + int orthogdim, double *t_kernel, double *t_gsum) +{ + typedef typename FImpl::SiteSpinor vobj; + + typedef typename vobj::scalar_object sobj; + typedef typename vobj::scalar_type scalar_type; + typedef typename vobj::vector_type vector_type; + + typedef iSpinMatrix SpinMatrix_v; + typedef iSpinMatrix SpinMatrix_s; + + int oneBlock = mat.dimension(3); + int twoBlock = mat.dimension(4); + int threeBlock = mat.dimension(5); + + GridBase *grid = one[0]._grid; + + const int Nd = grid->_ndimension; + const int Nsimd = grid->Nsimd(); + + int Nt = grid->GlobalDimensions()[orthogdim]; + int Ngamma = gammaA.size(); + assert (Ngamma = gammaB.size()); // Only combinatin of two gammas gives correct operator! + int Nmom = mom.size(); + + int fd=grid->_fdimensions[orthogdim]; + int ld=grid->_ldimensions[orthogdim]; + int rd=grid->_rdimensions[orthogdim]; + + // will locally sum vectors first + // sum across these down to scalars + // splitting the SIMD + int MFrvol = rd*oneBlock*twoBlock*threeBlock*Nmom; + int MFlvol = ld*oneBlock*twoBlock*threeBlock*Nmom; + + Vector lvSum(MFrvol); + parallel_for (int r = 0; r < MFrvol; r++){ + lvSum[r] = zero; + } + + Vector lsSum(MFlvol); + parallel_for (int r = 0; r < MFlvol; r++){ + lsSum[r]=scalar_type(0.0); + } + + int e1= grid->_slice_nblock[orthogdim]; + int e2= grid->_slice_block [orthogdim]; + int stride=grid->_slice_stride[orthogdim]; + + // potentially wasting cores here if local time extent too small + if (t_kernel) *t_kernel = -usecond(); + parallel_for(int r=0;r_ostride[orthogdim]; // base offset for start of plane + + // first, the diquark two*gammaB*three + for(int n=0;n icoor(Nd); + std::vector extracted(Nsimd); + + for(int i=0;iiCoorFromIindex(icoor,idx); + + int ldx = rt+icoor[orthogdim]*rd; + + int ij_ldx = m+Nmom*i+Nmom*Lblock*j+Nmom*Lblock*Rblock*ldx; + + lsSum[ij_ldx]=lsSum[ij_ldx]+extracted[idx]; + + } + }}} + } + if (t_kernel) *t_kernel += usecond(); + assert(mat.dimension(0) == Nmom); + assert(mat.dimension(1) == Ngamma); + assert(mat.dimension(2) == Nt); + + // ld loop and local only?? + int pd = grid->_processors[orthogdim]; + int pc = grid->_processor_coor[orthogdim]; + parallel_for_nest2(int lt=0;ltGlobalSumVector(&mat(0,0,0,0,0),Nmom*Ngamma*Nt*Lblock*Rblock); + if (t_gsum) *t_gsum += usecond(); +} +*/ + template template void A2Autils::MesonField(TensorType &mat, From 7c7ffa3b106bd8a9d2bbc59d50ba5743e469f5d4 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 25 Feb 2019 15:38:47 +0000 Subject: [PATCH 126/347] Added text read/write --- Grid/serialisation/TextIO.h | 34 +++++++++++++++++++++++++++++++++- tests/IO/Test_serialisation.cc | 2 ++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Grid/serialisation/TextIO.h b/Grid/serialisation/TextIO.h index ada1aaf6..b9b13d07 100644 --- a/Grid/serialisation/TextIO.h +++ b/Grid/serialisation/TextIO.h @@ -51,6 +51,8 @@ namespace Grid void writeDefault(const std::string &s, const U &x); template void writeDefault(const std::string &s, const std::vector &x); + template + void writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements); private: void indent(void); private: @@ -69,6 +71,8 @@ namespace Grid void readDefault(const std::string &s, U &output); template void readDefault(const std::string &s, std::vector &output); + template + void readMultiDim(const std::string &s, std::vector &buf, std::vector &dim); private: void checkIndent(void); private: @@ -95,7 +99,18 @@ namespace Grid write(s, x[i]); } } - + + template + void TextWriter::writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements) + { + uint64_t Rank = Dimensions.size(); + write(s, Rank); + for( uint64_t d : Dimensions ) + write(s, d); + while( NumElements-- ) + write(s, *pDataRowMajor++); + } + // Reader template implementation //////////////////////////////////////////// template void TextReader::readDefault(const std::string &s, U &output) @@ -121,6 +136,23 @@ namespace Grid read("", output[i]); } } + + template + void TextReader::readMultiDim(const std::string &s, std::vector &buf, std::vector &dim) + { + const char sz[] = ""; + uint64_t Rank; + read(sz, Rank); + dim.resize( Rank ); + size_t NumElements = 1; + for( auto &d : dim ) { + read(sz, d); + NumElements *= d; + } + buf.resize( NumElements ); + for( auto i = 0; i < NumElements; i++ ) + read(s, buf[i]); + } } #endif diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 305de088..77f69b7c 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -299,6 +299,8 @@ int main(int argc,char **argv) EigenHdf5IOTest(".xml"); std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; EigenHdf5IOTest(".bin"); + std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl; + EigenHdf5IOTest(".dat"); std::cout << "\n==== vector flattening/reconstruction" << std::endl; typedef std::vector>> vec3d; From 578eb177e77694954388b4ab02fe9b9bf474e519 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 25 Feb 2019 22:03:21 +0000 Subject: [PATCH 127/347] Tweaked format and memory use on Xml format. Still crashes (out of memory) on large read on my laptop --- Grid/serialisation/TextIO.h | 4 +- Grid/serialisation/XmlIO.h | 76 ++++++++++++++++++++-------------- tests/IO/Test_serialisation.cc | 8 ++-- 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/Grid/serialisation/TextIO.h b/Grid/serialisation/TextIO.h index b9b13d07..a6f2d4cf 100644 --- a/Grid/serialisation/TextIO.h +++ b/Grid/serialisation/TextIO.h @@ -150,8 +150,8 @@ namespace Grid NumElements *= d; } buf.resize( NumElements ); - for( auto i = 0; i < NumElements; i++ ) - read(s, buf[i]); + for( auto &x : buf ) + read(s, x); } } diff --git a/Grid/serialisation/XmlIO.h b/Grid/serialisation/XmlIO.h index 38958ba0..af3d5d76 100644 --- a/Grid/serialisation/XmlIO.h +++ b/Grid/serialisation/XmlIO.h @@ -125,24 +125,28 @@ namespace Grid template void XmlWriter::writeDefault(const std::string &s, const std::vector &x) { - std::vector dims(1); - dims[0] = x.size(); - writeMultiDim(s, dims, &x[0], dims[0]); + push(s); + for( auto &u : x ) + { + write("elem", u); + } + pop(); } template void XmlWriter::writeMultiDim(const std::string &s, const std::vector & Dimensions, const U * pDataRowMajor, size_t NumElements) { push(s); - if( Dimensions.size() > 1 ) - { - for( auto d : Dimensions ) - write("dim", d); + size_t count = 1; + const size_t Rank = Dimensions.size(); + write("rank", Rank ); + for( auto d : Dimensions ) { + write("dim", d); + count *= d; } + assert( count == NumElements && "XmlIO : element count doesn't match dimensions" ); while (NumElements--) - { write("elem", *pDataRowMajor++); - } pop(); } @@ -162,38 +166,46 @@ namespace Grid template void XmlReader::readDefault(const std::string &s, std::vector &output) { - std::vector dims; - readMultiDim(s, output, dims); - assert( dims.size() == 1 && dims[0] == output.size() && "XmlIO: Expected 1D vector" ); - } - - template - void XmlReader::readMultiDim(const std::string &s, std::vector &buf, std::vector &dim) - { - unsigned int i = 0; - unsigned int Rank = 0; if (!push(s)) { std::cout << GridLogWarning << "XML: cannot open node '" << s << "'"; std::cout << std::endl; } else { - while (node_.child("dim")) + for(unsigned int i = 0; node_.child("elem"); ) { - dim.resize(Rank + 1); - read("dim", dim[Rank]); - node_.child("dim").set_name("dim-done"); - Rank++; - } - while (node_.child("elem")) - { - buf.resize(i + 1); - read("elem", buf[i]); + output.resize(i + 1); + read("elem", output[i++]); + node_.child("elem").set_name("elem-done"); + } + pop(); + } + } + + template + void XmlReader::readMultiDim(const std::string &s, std::vector &buf, std::vector &dim) + { + if (!push(s)) + { + std::cout << GridLogWarning << "XML: cannot open node '" << s << "'"; + std::cout << std::endl; + } else { + size_t Rank; + read("rank", Rank); + dim.resize( Rank ); + size_t NumElements = 1; + for( auto &d : dim ) + { + read("dim", d); + node_.child("dim").set_name("dim-done"); + NumElements *= d; + } + buf.resize( NumElements ); + for( auto &x : buf ) + { + read("elem", x); node_.child("elem").set_name("elem-done"); - i++; } pop(); - if( Rank == 0 ) - dim.push_back(i); } } } diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 77f69b7c..9381e629 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -113,7 +113,7 @@ typedef Eigen::Tensor TensorRank typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; typedef std::vector aTensor_9_4_2; typedef Eigen::TensorFixedSize> LSCTensor; -#ifdef DEBUG +#ifndef DEBUG typedef Eigen::TensorFixedSize,2>,7>,3>, Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor> LCMTensor; #endif @@ -193,7 +193,7 @@ void EigenHdf5IOTest(const char * pszExtension) } TensorWriteRead ( LSCTensor ) TensorWriteReadLarge( PerambIOTestClass ) -#ifdef DEBUG +#ifndef DEBUG std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; TensorWriteReadLarge ( LCMTensor ) // Also write > 4GB of complex numbers (I suspect this will fail inside Hdf5) @@ -295,12 +295,12 @@ int main(int argc,char **argv) std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl; EigenHdf5IOTest(".h5"); #endif - std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(".xml"); std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; EigenHdf5IOTest(".bin"); std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl; EigenHdf5IOTest(".dat"); + std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; + EigenHdf5IOTest(".xml"); std::cout << "\n==== vector flattening/reconstruction" << std::endl; typedef std::vector>> vec3d; From df065f1d57003253cef48b6dc9d5624b9ee0439e Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 26 Feb 2019 15:57:01 +0000 Subject: [PATCH 128/347] first test configs --- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 2 +- tests/hadrons/Test_24.cc | 772 ++++++++++++++++++++++++ 3 files changed, 774 insertions(+), 2 deletions(-) create mode 100644 tests/hadrons/Test_24.cc diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 5dfc830b..fa62fe49 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -239,7 +239,7 @@ void TLapEvec::execute(void) auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); FieldMetaData header; - if((1)) { + if((0)) { const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(gridHD); pRNG4d.SeedFixedIntegers(seeds); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 00ac74cc..ba8f6ffa 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -212,7 +212,7 @@ void TPerambLight::execute(void) envGetTmp(GaugeField, Umu); FieldMetaData header; - if((1)){ + if((0)){ const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(grid4d); pRNG4d.SeedFixedIntegers(seeds); diff --git a/tests/hadrons/Test_24.cc b/tests/hadrons/Test_24.cc new file mode 100644 index 00000000..e48ac602 --- /dev/null +++ b/tests/hadrons/Test_24.cc @@ -0,0 +1,772 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Tests/Hadrons/Test_hadrons_distil.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 +#include +#include + +using namespace Grid; +using namespace Hadrons; + +///////////////////////////////////////////////////////////// +// Test creation of laplacian eigenvectors +///////////////////////////////////////////////////////////// +int Nconf = 3160; + +void test_Global(Application &application) +{ + // global parameters + Application::GlobalPar globalPar; + globalPar.trajCounter.start = Nconf; + globalPar.trajCounter.end = Nconf + 20; + globalPar.trajCounter.step = 20; + globalPar.runId = "test"; + application.setPar(globalPar); +} + +///////////////////////////////////////////////////////////// +// Test creation of laplacian eigenvectors +///////////////////////////////////////////////////////////// + +void test_LapEvec(Application &application) +{ + const char szGaugeName[] = "gauge"; + // gauge field + application.createModule(szGaugeName); + // Now make an instance of the LapEvec object + MDistil::LapEvecPar p; + //p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; + //p.ConfigFileName="ckpoint_lat.3000"; + p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; + p.ConfigFileName="ckpoint_lat." + std::to_string(Nconf); + p.gauge = szGaugeName; + //p.EigenPackName = "ePack"; + //p.Distil.TI = 8; + //p.Distil.LI = 3; + //p.Distil.Nnoise = 2; + //p.Distil.tSrc = 0; + p.Stout.steps = 3; + p.Stout.parm = 0.2; + p.Cheby.PolyOrder = 11; + p.Cheby.alpha = 0.3; + p.Cheby.beta = 12.5; + p.Lanczos.Nvec = 50; + p.Lanczos.Nk = 60; + p.Lanczos.Np = 20; + p.Lanczos.MaxIt = 1000; + p.Lanczos.resid = 1e-8; + application.createModule("LapEvec",p); +} + +///////////////////////////////////////////////////////////// +// Perambulators +///////////////////////////////////////////////////////////// + +void test_Perambulators(Application &application) +{ + // PerambLight parameters + MDistil::PerambLight::Par PerambPar; + PerambPar.eigenPack="LapEvec"; + PerambPar.PerambFileName="peramb_" + std::to_string(Nconf) + ".bin"; + PerambPar.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; + PerambPar.ConfigFileName="ckpoint_lat." + std::to_string(Nconf); + PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.Distil.tsrc = 0; + PerambPar.Distil.nnoise = 1; + PerambPar.Distil.LI=50; + PerambPar.Distil.SI=4; + PerambPar.Distil.TI=64; + PerambPar.nvec=50; + PerambPar.Distil.Ns=4; + PerambPar.Distil.Nt=64; + PerambPar.Distil.Nt_inv=1; + PerambPar.Solver.mass=0.005; + PerambPar.Solver.M5=1.8; + PerambPar.Ls=16; + PerambPar.Solver.CGPrecision=1e-7; + PerambPar.Solver.MaxIterations=10000; + application.createModule("Peramb",PerambPar); +} +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +void test_DistilVectors(Application &application) +{ + // DistilVectors parameters + MDistil::DistilVectors::Par DistilVecPar; + DistilVecPar.noise="Peramb_noise"; + DistilVecPar.perambulator="Peramb_perambulator_light"; + DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.tsrc = 0; + DistilVecPar.nnoise = 1; + DistilVecPar.LI=50; + DistilVecPar.SI=4; + DistilVecPar.TI=64; + DistilVecPar.nvec=50; + DistilVecPar.Ns=4; + DistilVecPar.Nt=64; + DistilVecPar.Nt_inv=1; + application.createModule("DistilVecs",DistilVecPar); +} +void test_PerambulatorsS(Application &application) +{ + // PerambLight parameters + MDistil::PerambLight::Par PerambPar; + PerambPar.eigenPack="LapEvec"; + PerambPar.PerambFileName="perambS.bin"; + PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; + PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; + PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.Distil.tsrc = 0; + PerambPar.Distil.nnoise = 1; + PerambPar.Distil.LI=50; + PerambPar.Distil.SI=4; + PerambPar.Distil.TI=64; + PerambPar.nvec=50; + PerambPar.Distil.Ns=4; + PerambPar.Distil.Nt=64; + PerambPar.Distil.Nt_inv=1; + PerambPar.Solver.mass=0.04; //strange mass??? + PerambPar.Solver.M5=1.8; + PerambPar.Ls=16; + PerambPar.Solver.CGPrecision=1e-8; + PerambPar.Solver.MaxIterations=10000; + application.createModule("PerambS",PerambPar); +} +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +void test_DistilVectorsS(Application &application) +{ + // DistilVectors parameters + MDistil::DistilVectors::Par DistilVecPar; + DistilVecPar.noise="PerambS_noise"; + DistilVecPar.perambulator="PerambS_perambulator_light"; + DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.tsrc = 0; + DistilVecPar.nnoise = 1; + DistilVecPar.LI=50; + DistilVecPar.SI=4; + DistilVecPar.TI=64; + DistilVecPar.nvec=50; + DistilVecPar.Ns=4; + DistilVecPar.Nt=64; + DistilVecPar.Nt_inv=1; + application.createModule("DistilVecsS",DistilVecPar); +} +///////////////////////////////////////////////////////////// +// MesonSink +///////////////////////////////////////////////////////////// + +void test_MesonSink(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="Peramb_unsmeared_sink"; + A2AMesonFieldPar.right="Peramb_unsmeared_sink"; + A2AMesonFieldPar.output="DistilFields"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonSink",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// g5*unsmeared +///////////////////////////////////////////////////////////// +void test_g5_sinks(Application &application) +{ + MDistil::g5_multiply::Par g5_multiplyPar; + g5_multiplyPar.input="Peramb_unsmeared_sink"; + g5_multiplyPar.nnoise = 1; + g5_multiplyPar.LI=5; + g5_multiplyPar.Ns=4; + g5_multiplyPar.Nt_inv=1; + application.createModule("g5phi",g5_multiplyPar); +} +///////////////////////////////////////////////////////////// +// MesonFields +///////////////////////////////////////////////////////////// + +void test_MesonFieldSL(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecsS_phi"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_phi"; + A2AMesonFieldPar.output="DistilFieldsS"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldS",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// MesonFields - phiphi +///////////////////////////////////////////////////////////// + +void test_MesonField(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_phi"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_phi"; + A2AMesonFieldPar.output="MesonSinksPhi"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonField",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// MesonFields - rhorho +///////////////////////////////////////////////////////////// + +void test_MesonFieldRho(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_rho"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.output="MesonSinksRho"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldRho",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// +// BaryonFields - phiphiphi +///////////////////////////////////////////////////////////// + +void test_BaryonFieldPhi(Application &application) +{ + // DistilVectors parameters + MDistil::BContraction::Par BContractionPar; + BContractionPar.one="DistilVecs_phi"; + BContractionPar.two="DistilVecs_phi"; + BContractionPar.three="DistilVecs_phi"; + BContractionPar.output="BaryonFieldPhi"; + BContractionPar.parity=1; + BContractionPar.mom={"0 0 0"}; + application.createModule("BaryonFieldPhi",BContractionPar); +} +///////////////////////////////////////////////////////////// +// BaryonFields - rhorhorho +///////////////////////////////////////////////////////////// + +void test_BaryonFieldRho(Application &application) +{ + // DistilVectors parameters + MDistil::BContraction::Par BContractionPar; + BContractionPar.one="DistilVecs_rho"; + BContractionPar.two="DistilVecs_rho"; + BContractionPar.three="DistilVecs_rho"; + BContractionPar.output="BaryonFieldRho"; + BContractionPar.parity=1; + BContractionPar.mom={"0 0 0"}; + application.createModule("BaryonFieldRho",BContractionPar); +} +///////////////////////////////////////////////////////////// +// BaryonContraction +///////////////////////////////////////////////////////////// + +void test_Baryon2pt(Application &application) +{ + // DistilVectors parameters + MDistil::Baryon2pt::Par Baryon2ptPar; + Baryon2ptPar.inputL="BaryonFieldPhi"; + Baryon2ptPar.inputR="BaryonFieldRho"; + Baryon2ptPar.quarksL="uud"; + Baryon2ptPar.quarksR="uud"; + Baryon2ptPar.output="C2_baryon"; + application.createModule("C2_b",Baryon2ptPar); +} + +///////////////////////////////////////////////////////////// +// emField +///////////////////////////////////////////////////////////// +void test_em(Application &application) +{ + MGauge::StochEm::Par StochEmPar; + StochEmPar.gauge=PhotonR::Gauge::feynman; + StochEmPar.zmScheme=PhotonR::ZmScheme::qedL; + application.createModule("Em",StochEmPar); +} +///////////////////////////////////////////////////////////// +// MesonA2ASlash +///////////////////////////////////////////////////////////// +void test_Aslash(Application &application) +{ + MContraction::A2AAslashField::Par A2AAslashFieldPar; + A2AAslashFieldPar.left="Peramb_unsmeared_sink"; + A2AAslashFieldPar.right="Peramb_unsmeared_sink"; + A2AAslashFieldPar.output="unsmeared_Aslash"; + A2AAslashFieldPar.emField={"Em"}; + A2AAslashFieldPar.cacheBlock=2; + A2AAslashFieldPar.block=4; + application.createModule("Aslash_field",A2AAslashFieldPar); +} + +bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) +{ + if( bGobbleWhiteSpace ) + while( std::isspace(static_cast(*pstr)) ) + pstr++; + const char * p = pstr; + bool bMinus = false; + char c = * p++; + if( c == '+' ) + c = * p++; + else if( c == '-' ) { + bMinus = true; + c = * p++; + } + int n = c - '0'; + if( n < 0 || n > 9 ) + return false; + while( * p >= '0' && * p <= '9' ) { + n = n * 10 + ( * p ) - '0'; + p++; + } + if( bMinus ) + n *= -1; + ri = n; + pstr = p; + return true; +} + +#ifdef DEBUG + +typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; + +template +void DebugShowTensor(T &x, const char * n) +{ + const MyTensor::Index s{x.size()}; + std::cout << n << ".size() = " << s << std::endl; + std::cout << n << ".NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; + std::cout << n << ".NumIndices = " << x.NumIndices << std::endl; + const auto d{x.dimensions()}; + //std::cout << n << ".dimensions().size() = " << d.size() << std::endl; + std::cout << "Dimensions are "; + for(auto i = 0; i < x.NumDimensions ; i++) + std::cout << "[" << d[i] << "]"; + std::cout << std::endl; + MyTensor::Index SizeCalculated{1}; + std::cout << "Dimensions again"; + for(int i=0 ; i < x.NumDimensions ; i++ ) { + std::cout << " : [" << i << /*", " << x.IndexNames[i] << */"]=" << x.dimension(i); + SizeCalculated *= d[i]; + } + std::cout << std::endl; + std::cout << "SizeCalculated = " << SizeCalculated << std::endl;\ + assert( SizeCalculated == s ); + // Initialise + assert( x.NumDimensions == 3 ); + for( int i = 0 ; i < d[0] ; i++ ) + for( int j = 0 ; j < d[1] ; j++ ) + for( int k = 0 ; k < d[2] ; k++ ) { + x(i,j,k) = std::complex(SizeCalculated, -SizeCalculated); + SizeCalculated--; + } + // Show raw data + std::cout << "Data follow : " << std::endl; + typename T::Scalar * p = x.data(); + for( auto i = 0 ; i < s ; i++ ) { + if( i ) std::cout << ", "; + std::cout << n << ".data()[" << i << "]=" << * p++; + } + std::cout << std::endl; +} + +// Test whether typedef and underlying types are the same + +void DebugTestTypeEqualities(void) +{ + Real r1; + RealD r2; + double r3; + const std::type_info &tr1{typeid(r1)}; + const std::type_info &tr2{typeid(r2)}; + const std::type_info &tr3{typeid(r3)}; + if( tr1 == tr2 && tr2 == tr3 ) + std::cout << "r1, r2 and r3 are the same type" << std::endl; + else + std::cout << "r1, r2 and r3 are different types" << std::endl; + std::cout << "r1 is a " << tr1.name() << std::endl; + std::cout << "r2 is a " << tr2.name() << std::endl; + std::cout << "r3 is a " << tr3.name() << std::endl; + + // These are the same + Complex c1; + std::complex c2; + const std::type_info &tc1{typeid(c1)}; + const std::type_info &tc2{typeid(c2)}; + const std::type_info &tc3{typeid(SpinVector::scalar_type)}; + if( tc1 == tc2 && tc2 == tc3) + std::cout << "c1, c2 and SpinVector::scalar_type are the same type" << std::endl; + else + std::cout << "c1, c2 and SpinVector::scalar_type are different types" << std::endl; + std::cout << "c1 is a " << tc1.name() << std::endl; + std::cout << "c2 is a " << tc2.name() << std::endl; + std::cout << "SpinVector::scalar_type is a " << tc3.name() << std::endl; + + // These are the same + SpinVector s1; + iSpinVector s2; + iScalar, Ns> > s3; + const std::type_info &ts1{typeid(s1)}; + const std::type_info &ts2{typeid(s2)}; + const std::type_info &ts3{typeid(s3)}; + if( ts1 == ts2 && ts2 == ts3 ) + std::cout << "s1, s2 and s3 are the same type" << std::endl; + else + std::cout << "s1, s2 and s3 are different types" << std::endl; + std::cout << "s1 is a " << ts1.name() << std::endl; + std::cout << "s2 is a " << ts2.name() << std::endl; + std::cout << "s3 is a " << ts3.name() << std::endl; + + // These are the same + SpinColourVector sc1; + iSpinColourVector sc2; + const std::type_info &tsc1{typeid(sc1)}; + const std::type_info &tsc2{typeid(sc2)}; + if( tsc1 == tsc2 ) + std::cout << "sc1 and sc2 are the same type" << std::endl; + else + std::cout << "sc1 and sc2 are different types" << std::endl; + std::cout << "sc1 is a " << tsc1.name() << std::endl; + std::cout << "sc2 is a " << tsc2.name() << std::endl; +} + +bool DebugEigenTest() +{ + { + Eigen::TensorFixedSize,Eigen::Sizes<3,4,5>> x; + DebugShowTensor(x, "fixed"); + } + const char pszTestFileName[] = "test_tensor.bin"; + std::array as={"Alpha", "Beta", "Gamma"}; + MyTensor x(as, 2,1,4); + DebugShowTensor(x, "x"); + x.WriteBinary(pszTestFileName); + DebugShowTensor(x, "x"); + // Test initialisation of an array of strings + for( auto a : as ) + std::cout << a << std::endl; + Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; + DebugShowTensor(p, "p"); + std::cout << "p.IndexNames follow" << std::endl; + for( auto a : p.IndexNames ) + std::cout << a << std::endl; + // Now see whether we can read a tensor back + std::array Names2={"Alpha", "Gamma", "Delta"}; + MyTensor y(Names2, 2,4,1); + y.ReadBinary(pszTestFileName); + DebugShowTensor(y, "y"); + + // Testing whether typedef produces the same type - yes it does + + DebugTestTypeEqualities(); + std::cout << std::endl; + + // How to access members of SpinColourVector + SpinColourVector sc; + for( int s = 0 ; s < Ns ; s++ ) { + auto cv{sc()(s)}; + iVector c2{sc()(s)}; + std::cout << " cv is a " << typeid(cv).name() << std::endl; + std::cout << " c2 is a " << typeid(c2).name() << std::endl; + for( int c = 0 ; c < Nc ; c++ ) { + Complex & z{cv(c)}; + std::cout << " sc[spin=" << s << ", colour=" << c << "] = " << z << std::endl; + } + } + // We could have removed the Lorentz index independently, but much easier to do as we do above + iVector,Ns> sc2{sc()}; + std::cout << "sc() is a " << typeid(sc()).name() << std::endl; + std::cout << "sc2 is a " << typeid(sc2 ).name() << std::endl; + + // Or you can access elements directly + std::complex z = sc()(0)(0); + std::cout << "z = " << z << std::endl; + sc()(3)(2) = std::complex{3.141,-3.141}; + std::cout << "sc()(3)(2) = " << sc()(3)(2) << std::endl; + + return true; +} + +template +void DebugGridTensorTest_print( int i ) +{ + std::cout << i << " : " << EigenIO::is_tensor::value + << ", rank " << EigenIO::Traits::rank + << ", rank_non_trivial " << EigenIO::Traits::rank_non_trivial + << ", count " << EigenIO::Traits::count + << ", scalar_size " << EigenIO::Traits::scalar_size + << ", size " << EigenIO::Traits::size + << std::endl; +} + +// begin() and end() are the minimum necessary to support range-for loops +// should really turn this into an iterator ... +template +class TestObject { +public: + using value_type = T; +private: + value_type * m_p; +public: + TestObject() { + m_p = reinterpret_cast(std::malloc(N * sizeof(value_type))); + } + ~TestObject() { std::free(m_p); } + inline value_type * begin(void) { return m_p; } + inline value_type * end(void) { return m_p + N; } +}; + +template +void EigenSliceExample() +{ + std::cout << "Eigen example, Options = " << Options << std::endl; + using T2 = Eigen::Tensor; + T2 a(4, 3); + a.setValues({{0, 100, 200}, {300, 400, 500}, + {600, 700, 800}, {900, 1000, 1100}}); + std::cout << "a\n" << a << std::endl; + DumpMemoryOrder( a, "a" ); + Eigen::array offsets = {0, 1}; + Eigen::array extents = {4, 2}; + T2 slice = a.slice(offsets, extents); + std::cout << "slice\n" << slice << std::endl; + DumpMemoryOrder( slice, "slice" ); + std::cout << "\n========================================" << std::endl; +} + +template +void EigenSliceExample2() +{ + using TestScalar = std::complex; + using T3 = Eigen::Tensor; + using T2 = Eigen::Tensor; + T3 a(2,3,4); + + std::cout << "Initialising a:"; + for_all( a, [&](TestScalar &c, float f, const std::array &Dims ){ + c = TestScalar{f,-f}; + std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; + } ); + std::cout << std::endl; + //std::cout << "Validating a:"; + float z = 0; + for( int i = 0 ; i < a.dimension(0) ; i++ ) + for( int j = 0 ; j < a.dimension(1) ; j++ ) + for( int k = 0 ; k < a.dimension(2) ; k++ ) { + TestScalar w{z, -z}; + //std::cout << " a(" << i << "," << j << "," << k << ")=" << w; + assert( a(i,j,k) == w ); + z++; + } + //std::cout << std::endl; + //std::cout << "a initialised to:\n" << a << std::endl; + DumpMemoryOrder( a, "a" ); + std::cout << "for_all(a):"; + for_all( a, [&](TestScalar c, typename T3::Index n, const std::array &Dims ){ + std::cout << " (" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")<" << n << ">=" << c; + } ); + std::cout << std::endl; + Eigen::array offsets = {0,1,1}; + Eigen::array extents = {1,2,2}; + T3 b; + b = a.slice( offsets, extents );//.reshape(NewExtents); + std::cout << "b = a.slice( offsets, extents ):\n" << b << std::endl; + DumpMemoryOrder( b, "b" ); + T2 c(3,4); + c = a.chip(0,1); + std::cout << "c = a.chip(0,0):\n" << c << std::endl; + DumpMemoryOrder( c, "c" ); + //T2 d = b.reshape(extents); + //std::cout << "b.reshape(extents) is:\n" << d << std::endl; + std::cout << "\n========================================" << std::endl; +} + +void DebugFelixTensorTest( void ) +{ + unsigned int Nmom = 2; + unsigned int Nt = 2; + unsigned int N_1 = 2; + unsigned int N_2 = 2; + unsigned int N_3 = 2; + using BaryonTensorSet = Eigen::Tensor; + BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); + std::vector Memory(Nmom * Nt * N_1 * N_2 * N_3 * 2); + using BaryonTensorMap = Eigen::TensorMap; + BaryonTensorMap BField4 (&Memory[0], Nmom,4,Nt,N_1,N_2,N_3); + + EigenSliceExample(); + EigenSliceExample<0>(); + EigenSliceExample2(); + EigenSliceExample2<0>(); +} + +bool DebugGridTensorTest( void ) +{ + DebugFelixTensorTest(); + typedef Complex t1; + typedef iScalar t2; + typedef iVector t3; + typedef iMatrix t4; + typedef iVector,4> t5; + typedef iScalar t6; + typedef iMatrix t7; + typedef iMatrix,4>,2> t8; + int i = 1; + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + DebugGridTensorTest_print( i++ ); + + //using TOC7 = TestObject, 7>; + using TOC7 = t7; + TOC7 toc7; + constexpr std::complex Inc{1,-1}; + std::complex Start{Inc}; + for( auto &x : toc7 ) { + x = Start; + Start += Inc; + } + i = 0; + std::cout << "toc7:"; + for( auto x : toc7 ) std::cout << " [" << i++ << "]=" << x; + std::cout << std::endl; + + t2 o2; + auto a2 = TensorRemove(o2); + //t3 o3; + //t4 o4; + //auto a3 = TensorRemove(o3); + //auto a4 = TensorRemove(o4); + + return true; +} +#endif + +int main(int argc, char *argv[]) +{ +#ifdef DEBUG + // Debug only - test of Eigen::Tensor + std::cout << "sizeof(int) = " << sizeof(int) + << ", sizeof(long) = " << sizeof(long) + << ", sizeof(size_t) = " << sizeof(size_t) + << ", sizeof(std::size_t) = " << sizeof(std::size_t) + << ", sizeof(std::streamsize) = " << sizeof(std::streamsize) + << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; + if( DebugEigenTest() ) return 0; + if(DebugGridTensorTest()) return 0; +#endif + + // Decode command-line parameters. 1st one is which test to run + int iTestNum = -1; + + for(int i = 1 ; i < argc ; i++ ) { + std::cout << "argv[" << i << "]=\"" << argv[i] << "\"" << std::endl; + const char * p = argv[i]; + if( * p == '/' || * p == '-' ) { + p++; + char c = * p++; + switch(toupper(c)) { + case 'T': + if( bNumber( iTestNum, p ) ) { + std::cout << "Test " << iTestNum << " requested"; + if( * p ) + std::cout << " (ignoring trailer \"" << p << "\")"; + std::cout << std::endl; + } + else + std::cout << "Invalid test \"" << &argv[i][2] << "\"" << std::endl; + break; + default: + std::cout << "Ignoring switch \"" << &argv[i][1] << "\"" << std::endl; + break; + } + } + } + + // initialization ////////////////////////////////////////////////////////// + Grid_init(&argc, &argv); + HadronsLogError.Active(GridLogError.isActive()); + HadronsLogWarning.Active(GridLogWarning.isActive()); + HadronsLogMessage.Active(GridLogMessage.isActive()); + HadronsLogIterative.Active(GridLogIterative.isActive()); + HadronsLogDebug.Active(GridLogDebug.isActive()); + LOG(Message) << "Grid initialized" << std::endl; + + // run setup /////////////////////////////////////////////////////////////// + Application application; + + // For now perform free propagator test - replace this with distillation test(s) + LOG(Message) << "====== Creating xml for test " << iTestNum << " ======" << std::endl; + //const unsigned int nt = GridDefaultLatt()[Tp]; + + switch(iTestNum) { + default: + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_MesonSink( application ); + test_g5_sinks( application ); + test_em( application ); + test_Aslash( application ); + test_DistilVectors( application ); + test_MesonField( application ); + test_MesonFieldRho( application ); + break; + } + LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; + + // execution + application.saveParameterFile("test_hadrons_distil.xml"); + application.run(); + + // epilogue + LOG(Message) << "Grid is finalizing now" << std::endl; + Grid_finalize(); + + return EXIT_SUCCESS; +} From f168a9e7eeaf86fceea73f3af48b136d07e7fe95 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 26 Feb 2019 16:41:52 +0000 Subject: [PATCH 129/347] continued with baryons --- Grid/qcd/utils/A2Autils.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 84c5fe60..60f27475 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -100,7 +100,6 @@ public: int orthogdim); #endif }; - /* template template @@ -122,6 +121,8 @@ void A2Autils::BaryonField(TensorType &mat, typedef iSpinMatrix SpinMatrix_v; typedef iSpinMatrix SpinMatrix_s; + typedef iSpinColourMatrix SpinColourMatrix_v; + int oneBlock = mat.dimension(3); int twoBlock = mat.dimension(4); int threeBlock = mat.dimension(5); @@ -180,13 +181,16 @@ void A2Autils::BaryonField(TensorType &mat, auto three_k = three[j]._odata[ss]; - SpinMatrix_v vv; + SpinColourMatrix_v vv; for(int s1=0;s1 Date: Wed, 27 Feb 2019 17:51:25 +0000 Subject: [PATCH 130/347] baryons... --- Grid/qcd/utils/A2Autils.h | 63 +++++++++++++++++++++++---------------- tests/hadrons/Test_24.cc | 2 +- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 60f27475..17fe208d 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -144,17 +144,23 @@ void A2Autils::BaryonField(TensorType &mat, // will locally sum vectors first // sum across these down to scalars // splitting the SIMD - int MFrvol = rd*oneBlock*twoBlock*threeBlock*Nmom; - int MFlvol = ld*oneBlock*twoBlock*threeBlock*Nmom; + int MFrvol = rd*twoBlock*threeBlock*Nmom; + int MFlvol = ld*twoBlock*threeBlock*Nmom; - Vector lvSum(MFrvol); - parallel_for (int r = 0; r < MFrvol; r++){ - lvSum[r] = zero; + Vector> lvSum(3); + for (int ic=0;ic<3;ic++){ + lvSum[ic].resize(MFrvol); + parallel_for (int r = 0; r < MFrvol; r++){ + lvSum[ic][r] = zero; + } } - Vector lsSum(MFlvol); - parallel_for (int r = 0; r < MFlvol; r++){ - lsSum[r]=scalar_type(0.0); + Vector> lsSum(3); + for (int ic=0;ic<3;ic++){ + lsSum[ic].resize(MFlvol); + parallel_for (int r = 0; r < MFlvol; r++){ + lsSum[ic][r] = scalar_type(0.0); + } } int e1= grid->_slice_nblock[orthogdim]; @@ -181,24 +187,26 @@ void A2Autils::BaryonField(TensorType &mat, auto three_k = three[j]._odata[ss]; - SpinColourMatrix_v vv; + Vector vv(3); for(int s1=0;s1::BaryonField(TensorType &mat, } + for ( int ic=0;ic<3;ic++){ // Sum across simd lanes in the plane, breaking out orthog dir. parallel_for(int rt=0;rt icoor(Nd); std::vector extracted(Nsimd); - for(int i=0;i::BaryonField(TensorType &mat, int ij_ldx = m+Nmom*i+Nmom*Lblock*j+Nmom*Lblock*Rblock*ldx; - lsSum[ij_ldx]=lsSum[ij_ldx]+extracted[idx]; + lsSum[ic][ij_ldx]=lsSum[ic][ij_ldx]+extracted[idx]; } }}} } + + if (t_kernel) *t_kernel += usecond(); assert(mat.dimension(0) == Nmom); assert(mat.dimension(1) == Ngamma); assert(mat.dimension(2) == Nt); + TensorType diquark; // Need this instead of mat!!! + // ld loop and local only?? int pd = grid->_processors[orthogdim]; int pc = grid->_processor_coor[orthogdim]; @@ -248,21 +261,21 @@ void A2Autils::BaryonField(TensorType &mat, for(int pt=0;pt::BaryonField(TensorType &mat, } } } - + } //////////////////////////////////////////////////////////////////// // This global sum is taking as much as 50% of time on 16 nodes // Vector size is 7 x 16 x 32 x 16 x 16 x sizeof(complex) = 2MB - 60MB depending on volume diff --git a/tests/hadrons/Test_24.cc b/tests/hadrons/Test_24.cc index e48ac602..63b3986d 100644 --- a/tests/hadrons/Test_24.cc +++ b/tests/hadrons/Test_24.cc @@ -207,7 +207,7 @@ void test_g5_sinks(Application &application) MDistil::g5_multiply::Par g5_multiplyPar; g5_multiplyPar.input="Peramb_unsmeared_sink"; g5_multiplyPar.nnoise = 1; - g5_multiplyPar.LI=5; + g5_multiplyPar.LI=50; g5_multiplyPar.Ns=4; g5_multiplyPar.Nt_inv=1; application.createModule("g5phi",g5_multiplyPar); From 18b603c5aef7124583b411a4aa1df9158bf5ded5 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 28 Feb 2019 10:27:05 +0000 Subject: [PATCH 131/347] simple but hopefully efficient baryon field --- Grid/qcd/utils/A2Autils.h | 178 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 17fe208d..749987c0 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -100,6 +100,184 @@ public: int orthogdim); #endif }; +/* +template +void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, + const FermionField *one, + const FermionField *two, + const FermionField *three, + const std::vector &mom, + int orthogdim) +{ + typedef typename FImpl::SiteSpinor vobj; + + typedef typename vobj::scalar_object sobj; + typedef typename vobj::scalar_type scalar_type; + typedef typename vobj::vector_type vector_type; + + typedef iSpinVector SpinVector_v; + typedef iSpinVector SpinVector_s; + + int oneBlock = mat.dimension(2); + int twoBlock = mat.dimension(3); + int threeBlock = mat.dimension(4); + + GridBase *grid = wi[0]._grid; + + const int nd = grid->_ndimension; + const int Nsimd = grid->Nsimd(); + + int Nt = grid->GlobalDimensions()[orthogdim]; + int Nmom = mom.size(); + + int fd=grid->_fdimensions[orthogdim]; + int ld=grid->_ldimensions[orthogdim]; + int rd=grid->_rdimensions[orthogdim]; + + // will locally sum vectors first + // sum across these down to scalars + // splitting the SIMD + int MFrvol = rd*oneBlock*twoBlock*threeBlock*Nmom; + int MFlvol = ld*oneBlock*twoBlock*threeBlock*Nmom; + + Vector lvSum(MFrvol); + parallel_for (int r = 0; r < MFrvol; r++){ + lvSum[r] = zero; + } + + Vector lsSum(MFlvol); + parallel_for (int r = 0; r < MFlvol; r++){ + lsSum[r]=scalar_type(0.0); + } + + int e1= grid->_slice_nblock[orthogdim]; + int e2= grid->_slice_block [orthogdim]; + int stride=grid->_slice_stride[orthogdim]; + + parallel_for(int r=0;r_ostride[orthogdim]; // base offset for start of plane + + for(int n=0;n C gamma_5 = - i gamma_1 gamma_3 + auto gv3 = Gamma(Gamma::Algebra::SigmaXZ) * v3; + SpinVector_v vv; + + vv()()() = v1()()(0) * v2()()(1) * gv3()()(2) //Cross product + - v1()()(0) * v2()()(2) * gv3()()(1) + + v1()()(1) * v2()()(2) * gv3()()(0) + - v1()()(1) * v2()()(0) * gv3()()(2) + + v1()()(2) * v2()()(0) * gv3()()(1) + - v1()()(2) * v2()()(1) * gv3()()(0); + + + // After getting the sitewise product do the mom phase loop + int base = Nmom*i+Nmom*Lblock*j+Nmom*Lblock*Rblock*r; + for ( int m=0;m icoor(nd); + iScalar temp; + std::vector > extracted(Nsimd); + + for(int i=0;iiCoorFromIindex(icoor,idx); + + int ldx = rt+icoor[orthogdim]*rd; + + int ij_ldx = m+Nmom*i + Nmom*oneBlock * j + Nmom*oneBlock * twoBlock * k + Nmom*oneBlock * twoBlock *threeBlock * ldx; + + lsSum[ij_ldx]=lsSum[ij_ldx]+extracted[idx]._internal; + + } + }}}} + } + + assert(mat.dimension(0) == Nmom); + assert(mat.dimension(1) == Nt); + + int pd = grid->_processors[orthogdim]; + int pc = grid->_processor_coor[orthogdim]; + parallel_for_nest2(int lt=0;ltGlobalSumVector(&mat(0,0,0,0,0,0),Nmom*Nt*oneBlock*twoBlock*threeBlock); +} +*/ + + + + /* template template From 6d9f3779134db2bbd35b3044ce0b9b4348592b23 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 28 Feb 2019 11:05:31 +0000 Subject: [PATCH 132/347] added parity --- Grid/qcd/utils/A2Autils.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 749987c0..4caf0d5f 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -102,13 +102,16 @@ public: }; /* template -void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, +void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, const FermionField *one, const FermionField *two, const FermionField *three, const std::vector &mom, + int parity, int orthogdim) { + assert(parity == 1 || parity == -1); + typedef typename FImpl::SiteSpinor vobj; typedef typename vobj::scalar_object sobj; @@ -166,6 +169,7 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, for(int i=0;i::NucleonFieldMom(Eigen::Tensor &mat, auto gv3 = Gamma(Gamma::Algebra::SigmaXZ) * v3; SpinVector_v vv; - vv()()() = v1()()(0) * v2()()(1) * gv3()()(2) //Cross product - - v1()()(0) * v2()()(2) * gv3()()(1) - + v1()()(1) * v2()()(2) * gv3()()(0) - - v1()()(1) * v2()()(0) * gv3()()(2) - + v1()()(2) * v2()()(0) * gv3()()(1) - - v1()()(2) * v2()()(1) * gv3()()(0); + vv()()() = pv1()()(0) * v2()()(1) * gv3()()(2) //Cross product + - pv1()()(0) * v2()()(2) * gv3()()(1) + + pv1()()(1) * v2()()(2) * gv3()()(0) + - pv1()()(1) * v2()()(0) * gv3()()(2) + + pv1()()(2) * v2()()(0) * gv3()()(1) + - pv1()()(2) * v2()()(1) * gv3()()(0); // After getting the sitewise product do the mom phase loop From 880427133976b70901d77ccc4feb7797022743d2 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 28 Feb 2019 16:32:40 +0000 Subject: [PATCH 133/347] efficient baryons compile! --- Grid/qcd/utils/A2Autils.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 4caf0d5f..11e00c49 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -41,12 +41,21 @@ public: const std::vector &mom, int orthogdim); + + static void NucleonFieldMom(Eigen::Tensor &mat, + const FermionField *one, + const FermionField *two, + const FermionField *three, + const std::vector &mom, + int parity, + int orthogdim); + static void PionFieldXX(Eigen::Tensor &mat, const FermionField *wi, const FermionField *vj, int orthogdim, int g5); - + static void PionFieldWV(Eigen::Tensor &mat, const FermionField *wi, const FermionField *vj, @@ -100,7 +109,7 @@ public: int orthogdim); #endif }; -/* + template void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, const FermionField *one, @@ -277,7 +286,7 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, grid->GlobalSumVector(&mat(0,0,0,0,0,0),Nmom*Nt*oneBlock*twoBlock*threeBlock); } -*/ + From 3b05f91f5c20cace648dbf7eee99b8c506eccecc Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 28 Feb 2019 19:04:44 +0000 Subject: [PATCH 134/347] Prototype for template traits recommendations --- Grid/simd/Grid_vector_types.h | 20 ++-- Grid/tensors/Tensor_class.h | 6 +- Grid/tensors/Tensor_traits.h | 194 ++++++++++++++++------------------ 3 files changed, 107 insertions(+), 113 deletions(-) diff --git a/Grid/simd/Grid_vector_types.h b/Grid/simd/Grid_vector_types.h index c67e74cb..b34e056d 100644 --- a/Grid/simd/Grid_vector_types.h +++ b/Grid/simd/Grid_vector_types.h @@ -89,17 +89,25 @@ template using NotEnableIf = Invoke struct is_complex : public std::false_type {}; -template <> struct is_complex > : public std::true_type {}; -template <> struct is_complex > : public std::true_type {}; +template <> struct is_complex : public std::true_type {}; +template <> struct is_complex : public std::true_type {}; -template using IfReal = Invoke::value, int> >; +template struct is_real : public std::false_type {}; +template struct is_real::value, + void>::type> : public std::true_type {}; + +template struct is_integer : public std::false_type {}; +template struct is_integer::value, + void>::type> : public std::true_type {}; + +template using IfReal = Invoke::value, int> >; template using IfComplex = Invoke::value, int> >; -template using IfInteger = Invoke::value, int> >; +template using IfInteger = Invoke::value, int> >; template using IfSame = Invoke::value, int> >; -template using IfNotReal = Invoke::value, int> >; +template using IfNotReal = Invoke::value, int> >; template using IfNotComplex = Invoke::value, int> >; -template using IfNotInteger = Invoke::value, int> >; +template using IfNotInteger = Invoke::value, int> >; template using IfNotSame = Invoke::value, int> >; //////////////////////////////////////////////////////// diff --git a/Grid/tensors/Tensor_class.h b/Grid/tensors/Tensor_class.h index 6dd9cbcc..c87503c6 100644 --- a/Grid/tensors/Tensor_class.h +++ b/Grid/tensors/Tensor_class.h @@ -62,7 +62,7 @@ class iScalar { // get double precision version typedef iScalar::DoublePrecision> DoublePrecision; - enum { TensorLevel = GridTypeMapper::TensorLevel + 1 }; + static constexpr int TensorLevel = GridTypeMapper::TensorLevel + 1; // Scalar no action // template using tensor_reduce_level = typename @@ -248,7 +248,7 @@ class iVector { return *this; } - enum { TensorLevel = GridTypeMapper::TensorLevel + 1 }; + static constexpr int TensorLevel = GridTypeMapper::TensorLevel + 1; iVector(const Zero &z) { *this = zero; }; iVector() = default; /* @@ -390,7 +390,7 @@ class iMatrix { typedef iScalar tensor_reduced; typedef iMatrix scalar_object; - enum { TensorLevel = GridTypeMapper::TensorLevel + 1 }; + static constexpr int TensorLevel = GridTypeMapper::TensorLevel + 1; iMatrix(const Zero &z) { *this = zero; }; iMatrix() = default; diff --git a/Grid/tensors/Tensor_traits.h b/Grid/tensors/Tensor_traits.h index c1ef397a..d4854768 100644 --- a/Grid/tensors/Tensor_traits.h +++ b/Grid/tensors/Tensor_traits.h @@ -26,6 +26,21 @@ Author: Christopher Kelly namespace Grid { + // Forward declarations + template class iScalar; + template class iVector; + template class iMatrix; + + // These are the Grid tensors + template struct isGridTensor + : public std::false_type { static constexpr bool notvalue = true; }; + template struct isGridTensor> + : public std::true_type { static constexpr bool notvalue = false; }; + template struct isGridTensor> + : public std::true_type { static constexpr bool notvalue = false; }; + template struct isGridTensor> + : public std::true_type { static constexpr bool notvalue = false; }; + ////////////////////////////////////////////////////////////////////////////////// // Want to recurse: GridTypeMapper >::scalar_type == ComplexD. // Use of a helper class like this allows us to template specialise and "dress" @@ -40,25 +55,31 @@ namespace Grid { // to study C++11's type_traits.h file. (std::enable_if >) // ////////////////////////////////////////////////////////////////////////////////// - - template class GridTypeMapper { - public: - typedef typename T::scalar_type scalar_type; - typedef typename T::vector_type vector_type; - typedef typename T::vector_typeD vector_typeD; - typedef typename T::tensor_reduced tensor_reduced; - typedef typename T::scalar_object scalar_object; - typedef typename T::Complexified Complexified; - typedef typename T::Realified Realified; - typedef typename T::DoublePrecision DoublePrecision; - enum { TensorLevel = T::TensorLevel }; + + // This saves repeating common properties for supported Grid Scalar types + template struct GridTypeMapper_Base {}; + // TensorLevel How many nested grid tensors + // Rank Rank of the grid tensor + // count Total number of elements, i.e. product of dimensions + // scalar_size Size of the underlying fundamental object (tensor_reduced) in bytes + // size Total size of all elements in bytes + // Dimension(dim) Size of dimension dim + template struct GridTypeMapper_Base { + static constexpr int TensorLevel = 0; + static constexpr int Rank = 0; + static constexpr std::size_t count = 1; + static constexpr std::size_t scalar_size = sizeof(T); + static constexpr std::size_t size = scalar_size * count; + static constexpr int Dimension(unsigned int dim) { return 0; } }; ////////////////////////////////////////////////////////////////////////////////// // Recursion stops with these template specialisations ////////////////////////////////////////////////////////////////////////////////// - template<> class GridTypeMapper { - public: + + template struct GridTypeMapper {}; + + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef RealF scalar_type; typedef RealF vector_type; typedef RealD vector_typeD; @@ -67,10 +88,8 @@ namespace Grid { typedef ComplexF Complexified; typedef RealF Realified; typedef RealD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef RealD scalar_type; typedef RealD vector_type; typedef RealD vector_typeD; @@ -79,10 +98,8 @@ namespace Grid { typedef ComplexD Complexified; typedef RealD Realified; typedef RealD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexF scalar_type; typedef ComplexF vector_type; typedef ComplexD vector_typeD; @@ -91,10 +108,8 @@ namespace Grid { typedef ComplexF Complexified; typedef RealF Realified; typedef ComplexD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexD scalar_type; typedef ComplexD vector_type; typedef ComplexD vector_typeD; @@ -103,10 +118,8 @@ namespace Grid { typedef ComplexD Complexified; typedef RealD Realified; typedef ComplexD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef Integer scalar_type; typedef Integer vector_type; typedef Integer vector_typeD; @@ -115,11 +128,9 @@ namespace Grid { typedef void Complexified; typedef void Realified; typedef void DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef RealF scalar_type; typedef vRealF vector_type; typedef vRealD vector_typeD; @@ -128,10 +139,8 @@ namespace Grid { typedef vComplexF Complexified; typedef vRealF Realified; typedef vRealD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef RealD scalar_type; typedef vRealD vector_type; typedef vRealD vector_typeD; @@ -140,10 +149,8 @@ namespace Grid { typedef vComplexD Complexified; typedef vRealD Realified; typedef vRealD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexF scalar_type; typedef vComplexH vector_type; typedef vComplexD vector_typeD; @@ -152,10 +159,8 @@ namespace Grid { typedef vComplexH Complexified; typedef vRealH Realified; typedef vComplexD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexF scalar_type; typedef vComplexF vector_type; typedef vComplexD vector_typeD; @@ -164,10 +169,8 @@ namespace Grid { typedef vComplexF Complexified; typedef vRealF Realified; typedef vComplexD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexD scalar_type; typedef vComplexD vector_type; typedef vComplexD vector_typeD; @@ -176,10 +179,8 @@ namespace Grid { typedef vComplexD Complexified; typedef vRealD Realified; typedef vComplexD DoublePrecision; - enum { TensorLevel = 0 }; }; - template<> class GridTypeMapper { - public: + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef Integer scalar_type; typedef vInteger vector_type; typedef vInteger vector_typeD; @@ -188,57 +189,49 @@ namespace Grid { typedef void Complexified; typedef void Realified; typedef void DoublePrecision; - enum { TensorLevel = 0 }; }; - // First some of my own traits - template struct isGridTensor { - static const bool value = true; - static const bool notvalue = false; +#define GridTypeMapper_RepeatedTypes \ + typedef typename ObjectTraits::scalar_type scalar_type; \ + typedef typename ObjectTraits::vector_type vector_type; \ + typedef typename ObjectTraits::vector_typeD vector_typeD; \ + typedef typename ObjectTraits::tensor_reduced tensor_reduced; \ + typedef typename ObjectTraits::scalar_object scalar_object; \ + typedef typename ObjectTraits::Complexified Complexified; \ + typedef typename ObjectTraits::Realified Realified; \ + typedef typename ObjectTraits::DoublePrecision DoublePrecision; \ + static constexpr int TensorLevel = BaseTraits::TensorLevel + 1; \ + static constexpr std::size_t scalar_size = BaseTraits::scalar_size; \ + static constexpr std::size_t size = scalar_size * count + + template struct GridTypeMapper> { + using ObjectTraits = iScalar; + using BaseTraits = GridTypeMapper; + static constexpr int Rank = 1 + BaseTraits::Rank; + static constexpr std::size_t count = 1 * BaseTraits::count; + static constexpr int Dimension(unsigned int dim) { + return ( dim == 0 ) ? 1 : BaseTraits::Dimension(dim - 1); } + GridTypeMapper_RepeatedTypes; }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; + + template struct GridTypeMapper> { + using ObjectTraits = iVector; + using BaseTraits = GridTypeMapper; + static constexpr int Rank = 1 + BaseTraits::Rank; + static constexpr std::size_t count = N * BaseTraits::count; + static constexpr int Dimension(unsigned int dim) { + return ( dim == 0 ) ? N : BaseTraits::Dimension(dim - 1); } + GridTypeMapper_RepeatedTypes; }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; - }; - template<> struct isGridTensor { - static const bool value = false; - static const bool notvalue = true; + + template struct GridTypeMapper> { + using ObjectTraits = iMatrix; + using BaseTraits = GridTypeMapper; + static constexpr int Rank = 2 + BaseTraits::Rank; + static constexpr std::size_t count = N * N * BaseTraits::count; + static constexpr int Dimension(unsigned int dim) { + return ( dim == 0 || dim == 1 ) ? N : BaseTraits::Dimension(dim - 2); } + GridTypeMapper_RepeatedTypes; }; // Match the index @@ -263,20 +256,13 @@ namespace Grid { typedef T type; }; - //Query if a tensor or Lattice is SIMD vector or scalar - template - class isSIMDvectorized{ - template - static typename std::enable_if< !std::is_same< typename GridTypeMapper::type>::scalar_type, - typename GridTypeMapper::type>::vector_type>::value, char>::type test(void *); + //Query whether a tensor or Lattice is SIMD vector or scalar + template struct isSIMDvectorized : public std::false_type {}; + template struct isSIMDvectorized::type>::scalar_type, + typename GridTypeMapper::type>::vector_type>::value, void>::type> + : public std::true_type {}; - template - static double test(...); - - public: - enum {value = sizeof(test(0)) == sizeof(char) }; - }; - //Get the precision of a Lattice, tensor or scalar type in units of sizeof(float) template class getPrecision{ From 91be0285077a1e2623ce2a22a0da5fe400b0eab0 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 28 Feb 2019 19:06:25 +0000 Subject: [PATCH 135/347] Still one issue on write --- Grid/serialisation/BaseIO.h | 214 ++++++++++++++------------------- Grid/serialisation/Hdf5IO.h | 4 +- tests/IO/Test_serialisation.cc | 58 +++++---- 3 files changed, 127 insertions(+), 149 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 86dc1113..09cd1181 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -73,7 +73,7 @@ namespace Grid { template struct is_tensor_of_container::value && is_container::value, void>::type> : public std::true_type {}; // Is this a fixed-size Eigen tensor - template struct is_tensor_fixed : public std::false_type {}; + template struct is_tensor_fixed : public std::false_type {}; template struct is_tensor_fixed> : public std::true_type {}; @@ -89,86 +89,81 @@ namespace Grid { && !is_tensor_fixed::value, void>::type> : public std::true_type {}; // These traits describe the Eigen tensor scalar and container objects supported for IO - // Containers arbitrarily deeply nested compositions of fixed size objects: - // ... grid tensors (iScalar, iVector, and iMatrix) and fixed size arrays + // Containers are arbitrarily deeply nested compositions of fixed size objects, + // ... grid tensors (iScalar, iVector, and iMatrix) and std::array + // EigenIO::Traits are not defined for Eigen tensors, but rather their top-level scalar + // This is because Eigen tensors have a dynamic size flavour, but the scalars are all fixed size + // This allows the traits to all be defined as constexpr template struct Traits {}; // C needed for specialisation // This defines the bottom level - i.e. it's a description of the underlying scalar template struct Traits::value, void>::type> { using scalar_type = T; // Type of the underlying scalar using scalar_real = typename RealType::type; // real type underlying scalar_type static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) - static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 + //static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) static constexpr std::size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes static constexpr std::size_t size = scalar_size * count; // total size of elements in bytes static constexpr std::size_t Dimension(unsigned int dim) { return 0; } // Dimension size - static constexpr std::size_t DimensionNT(unsigned int dim) { return 0; } // non-trivial dim size + //static constexpr std::size_t DimensionNT(unsigned int dim) { return 0; } // non-trivial dim size // e.g. iScalar> - // depth = 2 - // rank = 1 + // rank = 2 // rank_non_trivial = 0 // count = 1 - // e.g. iVector,4> - // depth = 2 + // e.g. iVector,1> // rank = 3 + // rank_non_trivial = 2 + // count = 9 + // e.g. iScalar,4>> + // rank = 4 // rank_non_trivial = 3 // count = 36 - // e.g. iScalar,3>> - // depth = 3 - // rank = 3 - // rank_non_trivial = 3 - // count = 48 }; template struct Traits> { using scalar_type = typename Traits::scalar_type; using scalar_real = typename RealType::type; static constexpr unsigned int rank = 1 + Traits::rank; - static constexpr unsigned int rank_non_trivial = 0 + Traits::rank_non_trivial; + //static constexpr unsigned int rank_non_trivial = 0 + Traits::rank_non_trivial; static constexpr unsigned int count = 1 * Traits::count; static constexpr std::size_t scalar_size = Traits::scalar_size; static constexpr std::size_t size = scalar_size * count; static constexpr std::size_t Dimension(unsigned int dim) { return ( dim == 0 ) ? 1 : Traits::Dimension(dim - 1); } - static constexpr std::size_t DimensionNT(unsigned int dim) { - return Traits::DimensionNT(dim); } + //static constexpr std::size_t DimensionNT(unsigned int dim) { + //return Traits::DimensionNT(dim); } }; template struct Traits> { using scalar_type = typename Traits::scalar_type; using scalar_real = typename RealType::type; static constexpr unsigned int rank = 1 + Traits::rank; - static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + Traits::rank_non_trivial; + //static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + Traits::rank_non_trivial; static constexpr unsigned int count = N * Traits::count; static constexpr std::size_t scalar_size = Traits::scalar_size; static constexpr std::size_t size = scalar_size * count; static constexpr std::size_t Dimension(unsigned int dim) { return ( dim == 0 ) ? N : Traits::Dimension(dim - 1); } - static constexpr std::size_t DimensionNT(unsigned int dim) { - return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 ) ? N : Traits::DimensionNT(dim - 1); - } + //static constexpr std::size_t DimensionNT(unsigned int dim) { + //return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 ) ? N : Traits::DimensionNT(dim - 1); + //} }; template struct Traits> { using scalar_type = typename Traits::scalar_type; using scalar_real = typename RealType::type; static constexpr unsigned int rank = 2 + Traits::rank; - static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + Traits::rank_non_trivial; + //static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + Traits::rank_non_trivial; static constexpr unsigned int count = N * N * Traits::count; static constexpr std::size_t scalar_size = Traits::scalar_size; static constexpr std::size_t size = scalar_size * count; static constexpr std::size_t Dimension(unsigned int dim) { return ( dim == 0 || dim == 1 ) ? N : Traits::Dimension(dim - 2); } - static constexpr std::size_t DimensionNT(unsigned int dim) { - return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 || dim == 1 ) ? N : Traits::DimensionNT(dim - 2); - } + //static constexpr std::size_t DimensionNT(unsigned int dim) { + //return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 || dim == 1 ) ? N : Traits::DimensionNT(dim - 2); + //} }; template struct Traits> : Traits> {}; - // Tensors have the same traits as their top-level scalar - // Shouldn't be necessary ... but I make the mistake of getting traits of the tensor so often - // that I am tempted to define this. - // HOWEVER, Eigen tensors have a dynamic size flavour, but the scalars are (currently) all fixed size - //template struct Traits::value, void>::type> : Traits {}; } - // for_all helper function to call the lambda + // for_all helper function to call the lambda for scalar template typename std::enable_if::value, void>::type for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, @@ -177,19 +172,19 @@ namespace Grid { lambda( scalar, Seq++, MyIndex ); } - // for_all helper function to call the lambda + // for_all helper function to call the lambda for container template typename std::enable_if::value, void>::type - for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, + for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &container, typename ETensor::Index &Seq, std::array::rank> &MyIndex) { - using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later - const auto InnerRank = EigenIO::Traits::rank_non_trivial; + using Traits = EigenIO::Traits; const auto rank{ETensor::NumIndices}; - for( typename EigenIO::Traits::scalar_type &Source : scalar ) { + const auto InnerRank = Traits::rank; + for( typename Traits::scalar_type &Source : container ) { lambda(Source, Seq++, MyIndex ); // Now increment SubIndex - for( auto i = InnerRank - 1; i != -1 && ++MyIndex[rank + i] == EigenIO::Traits::DimensionNT(i); i-- ) + for( auto i = InnerRank - 1; i != -1 && ++MyIndex[rank + i] == Traits::Dimension(i); i-- ) MyIndex[rank + i] = 0; } } @@ -257,23 +252,43 @@ namespace Grid { // Would have preferred to define template variables for this, but that's c++ 17 template typename std::enable_if::value && !EigenIO::is_complex::scalar_type>::value, void>::type - SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = 1 ) + SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = 1, + unsigned short Precision = 0 ) { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; - for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { - c = Inc * static_cast(n); + for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + scalar_type x = Inc * static_cast(n); + if( Precision ) { + std::stringstream s; + s << std::scientific << std::setprecision(Precision) << x; + s >> x; + } + c = x; } ); } template typename std::enable_if::value && EigenIO::is_complex::scalar_type>::value, void>::type - SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc={1,-1}) + SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc={1,-1}, + unsigned short Precision = 0 ) { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { - c = Inc * static_cast::type>(n); + auto re = Inc.real(); + auto im = Inc.imag(); + re *= n; + im *= n; + if( Precision ) { + std::stringstream s; + s << std::scientific << std::setprecision(Precision) << re; + s >> re; + s.clear(); + s << im; + s >> im; + } + c = scalar_type(re,im); } ); } @@ -561,7 +576,7 @@ namespace Grid { Writer::write(const std::string &s, const ETensor &output) { using Index = typename ETensor::Index; - using Container = typename ETensor::Scalar; // NB: could be same as Scalar + using Container = typename ETensor::Scalar; // NB: could be same as scalar using Traits = EigenIO::Traits; using Scalar = typename Traits::scalar_type; // type of the underlying scalar constexpr unsigned int TensorRank{ETensor::NumIndices}; @@ -711,85 +726,49 @@ namespace Grid { typename std::enable_if::value, void>::type Reader::read(const std::string &s, ETensor &output) { - // alias to element type - using Container = typename ETensor::Scalar; + using Index = typename ETensor::Index; + using Container = typename ETensor::Scalar; // NB: could be same as scalar using Traits = EigenIO::Traits; - using Scalar = typename Traits::scalar_type; + using Scalar = typename Traits::scalar_type; // type of the underlying scalar + constexpr unsigned int TensorRank{ETensor::NumIndices}; + constexpr unsigned int ContainerRank{Traits::rank}; // Only non-zero for containers + constexpr unsigned int TotalRank{TensorRank + ContainerRank}; + using ETDims = std::array; // Dimensions of the tensor // read the (flat) data and dimensionality std::vector dimData; std::vector buf; upcast->readMultiDim( s, buf, dimData ); + assert(dimData.size() == TotalRank && "EigenIO: Tensor rank mismatch" ); // Make sure that the number of elements read matches dimensions read std::size_t NumElements = 1; - std::size_t RankRequired = 0; - std::vector dimNonTrivial; - dimNonTrivial.reserve(dimData.size()); - for( auto d : dimData ) { + for( auto d : dimData ) NumElements *= d; - if( d > 1 ) { - RankRequired++; - dimNonTrivial.push_back(d); - } - } - assert( NumElements == buf.size() && "Number of elements read back <> product of dimensions" ); + assert( NumElements == buf.size() && "EigenIO: Number of elements != product of dimensions" ); // If our scalar object is a Container, make sure it's dimensions match what we read back - const auto InnerRank{Traits::rank_non_trivial}; - if ( InnerRank > 0 ) { - assert( RankRequired >= InnerRank && "Tensor Container too complex for data" ); - for( auto i = InnerRank - 1 ; i != -1 ; i-- ) { - auto d = dimNonTrivial[--RankRequired]; - assert( d == Traits::DimensionNT(i) && "Tensor Container dimensions don't match data" ); - NumElements /= d; - dimNonTrivial.pop_back(); - } - } - // Make sure our object has the right rank - assert( ETensor::NumDimensions >= RankRequired ); - bool bShapeOK = true; - std::size_t RankNonTrivial = 0; + for( auto i = 0 ; i < ContainerRank ; i++ ) + assert( dimData[TensorRank+i] == Traits::Dimension(i) && "Tensor Container dimensions don't match data" ); + // Now see whether the tensor is the right shape, or can be made to be const auto & dims{output.dimensions()}; - using ETDims = std::array; - ETDims dimsNew; - // Make sure fixed dimension objects have allocated memory - /*if constexpr( EigenIO::is_tensor_fixed::value ) { - for( auto &d : dimsNew ) d = 0; - output( dimsNew ) = 0; - }*/ - for( auto i = 0, j = 0 ; bShapeOK && i < ETensor::NumDimensions ; i++ ) { - auto d = dims[i]; - if( d < 1 ) + bool bShapeOK = (output.data() != nullptr); + for( auto i = 0; bShapeOK && i < TensorRank ; i++ ) + if( dims[i] != dimData[i] ) bShapeOK = false; - else if( d > 1 ) { - RankNonTrivial++; - if( d != dimNonTrivial[j] ) - bShapeOK = false; - j++; - } - dimsNew[i] = d; - } - //if( RankNonTrivial == 0 ) RankNonTrivial++; // Make the tensor the same size as the data read - if ( !bShapeOK || RankNonTrivial != RankRequired ) { - for( auto i = 0 ; i < ETensor::NumDimensions ; i++ ) - dimsNew[i] = ( i < RankRequired ) ? dimNonTrivial[i] : 1; - Reshape(output, dimsNew); + ETDims MyIndex; + if( !bShapeOK ) { + for( auto i = 0 ; i < TensorRank ; i++ ) + MyIndex[i] = dimData[i]; + Reshape(output, MyIndex); } // Copy the data into the tensor - ETDims MyIndex; for( auto &d : MyIndex ) d = 0; const Scalar * pSource = &buf[0]; for( auto n = 0 ; n < NumElements ; n++ ) { Container & c = output( MyIndex ); - /*if constexpr ( EigenIO::is_scalar::value ) { - c = buf[idx++]; - } else { - for( Scalar & s : c ) - s = buf[idx++]; - }*/ copyScalars( c, pSource ); // Now increment the index - for( int i = ETensor::NumDimensions - 1; i >= 0 && ++MyIndex[i] == dims[i]; i-- ) + for( int i = TensorRank - 1; i != -1 && ++MyIndex[i] == dims[i]; i-- ) MyIndex[i] = 0; } } @@ -811,22 +790,6 @@ namespace Grid { t.resize( dims ); } - /*template - template - typename std::enable_if::value, std::size_t>::type - Reader::DimSize(ETensor &t, std::size_t dim ) - { - return 0;//ETensor::Dimension[dim]; - } - - template - template - typename std::enable_if::value, std::size_t>::type - Reader::DimSize(ETensor &t, std::size_t dim ) - { - return t.dimension(dim); - }*/ - template template void Reader::fromString(U &output, const std::string &s) @@ -880,21 +843,24 @@ namespace Grid { for( auto i = 0 ; bReturnValue && i < T1::NumIndices ; i++ ) bReturnValue = ( lhs.dimension(i)) == rhs.dimension(i); if( bReturnValue ) { - Eigen::Tensor bResult = (lhs == rhs).all(); - bReturnValue = bResult(0); + using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + for_all( lhs, [&](scalar_type &c, typename T1::Index n, const std::array &Dims ) { + scalar_type x = c - rhs[Dims]; + if( x < 1e-10 ) + bReturnValue = false; + } ); } return bReturnValue; } template - static inline typename std::enable_if, T>::value, bool>::type + static inline typename std::enable_if::value, bool>::type CompareMember(const std::vector &lhs, const std::vector &rhs) { const auto NumElements{lhs.size()}; bool bResult = ( NumElements == rhs.size() ); - for( auto i = 0 ; i < NumElements && bResult ; i++ ) { - Eigen::Tensor b = (lhs[i] == rhs[i]).all(); - bResult = b(0); - } + for( auto i = 0 ; i < NumElements && bResult ; i++ ) + bResult = CompareMember(lhs[i], rhs[i]); return bResult; } diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 15846e99..0876de02 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -143,9 +143,9 @@ namespace Grid d /= Primes[PrimeIdx]; } const char ErrorMsg[] = " dimension > 4GB without small prime factors. " - "Hdf5IO chunk size will be inefficient."; + "Hdf5IO chunk size will be inefficient. NB Serialisation is not intended for large datasets - please consider alternatives."; if( d > MaxElements ) { - std::cout << GridLogMessage << "Individual" << ErrorMsg << std::endl; + std::cout << GridLogWarning << "Individual" << ErrorMsg << std::endl; hsize_t quotient = d / MaxElements; if( d % MaxElements ) quotient++; diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 9381e629..c8d7906f 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -80,17 +80,19 @@ double d = 2*M_PI; bool b = false; template -void ioTest(const std::string &filename, const O &object, const std::string &name, const char * tag = "testobject" ) +void ioTest(const std::string &filename, const O &object, const std::string &name, + const char * tag = "testobject", unsigned short Precision = 0 ) { std::cout << "IO test: " << name << " -> " << filename << " ..."; // writer needs to be destroyed so that writing physically happens { W writer(filename); - writer.setPrecision(std::numeric_limits::digits10 + 1); + if( Precision ) + writer.setPrecision(Precision); write(writer, tag , object); } - std::cout << " done. reading..."; + std::cout << " done. reading ..."; R reader(filename); std::unique_ptr buf( new O ); // In case object too big for stack @@ -99,7 +101,7 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam if (!good) { std::cout << " failure!" << std::endl; if (EigenIO::is_tensor::value) - dump_tensor(*buf,"???"); + dump_tensor(*buf); exit(EXIT_FAILURE); } std::cout << " done." << std::endl; @@ -108,16 +110,17 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam #ifdef HAVE_HDF5 typedef std::complex TestScalar; typedef Eigen::TensorFixedSize> TensorRank5UShort; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; +typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; typedef Eigen::Tensor TensorRank3; typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; typedef std::vector aTensor_9_4_2; typedef Eigen::TensorFixedSize> LSCTensor; -#ifndef DEBUG +#ifndef NO_STRESS_TESTS typedef Eigen::TensorFixedSize,2>,7>,3>, Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor> LCMTensor; #endif class PerambIOTestClass: Serializable { + Grid_complex Flag; public: using PerambTensor = Eigen::Tensor; GRID_SERIALIZABLE_CLASS_MEMBERS(PerambIOTestClass @@ -134,45 +137,52 @@ public: , LSCTensor, MyLSCTensor ); PerambIOTestClass() - : DistilParameterNames {"alpha", "beta", "gamma", "delta", "epsilon", "zeta"} + : DistilParameterNames {"do", "androids", "dream", "of", "electric", "sheep?"} , DistilParameterValues{2,3,1,4,5,1} , Perambulator(2,3,1,4,5,1) , Perambulator2(7,1,6,1,5,1) , tensorRank3(7,3,2) , atensor_9_4_2(3) + //, Flag(1,-3.1415927) + , Flag(1,-1) { - //Grid_complex Flag{1,-3.1415927}; // Gives errors on readback for text types - Grid_complex Flag{1,-1}; SequentialInit(Perambulator, Flag); SequentialInit(Perambulator2, Flag); SequentialInit(tensorRank5UShort); SequentialInit(tensorRank3, Flag); SequentialInit(tensor_9_4_2, Flag); for( auto &t : atensor_9_4_2 ) SequentialInit(t, Flag); - SequentialInit( MyLSCTensor ); + SequentialInit( MyLSCTensor, Flag ); } }; #define TensorWriteReadInnerNoInit( T ) \ filename = "iotest_" + std::to_string(++TestNum) + "_" #T + pszExtension; \ ioTest(filename, t, #T, #T); -#define TensorWriteReadInner( T ) SequentialInit( t ); TensorWriteReadInnerNoInit( T ) +#define TensorWriteReadInner( T ) SequentialInit( t, Flag, Precision ); TensorWriteReadInnerNoInit( T ) #define TensorWriteRead( T ) { T t ; TensorWriteReadInner( T ) } #define TensorWriteReadV(T, ... ) { T t( __VA_ARGS__ ); TensorWriteReadInner( T ) } #define TensorWriteReadLarge( T ) { std::unique_ptr p{new T}; T &t{*p}; TensorWriteReadInnerNoInit(T) } template -void EigenHdf5IOTest(const char * pszExtension) +void EigenHdf5IOTest(const char * pszExtension, unsigned short Precision = 0) { unsigned int TestNum = 0; std::string filename; - using TensorSingle = Eigen::TensorFixedSize>; - TensorWriteRead( TensorSingle ) + { + int Flag = 7; + unsigned short Precision = 0; + using TensorSingle = Eigen::TensorFixedSize>; + TensorWriteRead( TensorSingle ) + } + TestScalar Flag{1,-3.1415927}; using TensorSimple = Eigen::Tensor, 6>; TensorWriteReadV( TensorSimple, 1, 1, 1, 1, 1, 1 ) TensorWriteReadV( TensorRank3, 6, 3, 2 ) TensorWriteRead ( Tensor_9_4_2 ) { + unsigned short Flag = 1; + unsigned short Precision = 0; TensorRank5UShort t; TensorWriteReadInner ( TensorRank5UShort ); std::cout << " Testing alternate memory order read ... "; @@ -193,7 +203,7 @@ void EigenHdf5IOTest(const char * pszExtension) } TensorWriteRead ( LSCTensor ) TensorWriteReadLarge( PerambIOTestClass ) -#ifndef DEBUG +#ifndef NO_STRESS_TESTS std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; TensorWriteReadLarge ( LCMTensor ) // Also write > 4GB of complex numbers (I suspect this will fail inside Hdf5) @@ -285,22 +295,24 @@ int main(int argc,char **argv) ioTest("iotest.dat", obj, "text (object) "); ioTest("iotest.dat", vec, "text (vector of objects)"); //// text - ioTest("iotest.json", obj, "JSON (object) "); - ioTest("iotest.json", vec, "JSON (vector of objects)"); + //ioTest("iotest.json", obj, "JSON (object) "); + //ioTest("iotest.json", vec, "JSON (vector of objects)"); //// HDF5 #ifdef HAVE_HDF5 ioTest("iotest.h5", obj, "HDF5 (object) "); ioTest("iotest.h5", vec, "HDF5 (vector of objects)"); +#endif + std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl; + EigenHdf5IOTest(".dat", 6); + std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; + EigenHdf5IOTest(".xml", 4); + std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; + EigenHdf5IOTest(".bin"); +#ifdef HAVE_HDF5 std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl; EigenHdf5IOTest(".h5"); #endif - std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(".bin"); - std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(".dat"); - std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(".xml"); std::cout << "\n==== vector flattening/reconstruction" << std::endl; typedef std::vector>> vec3d; From a344a2227e9b4247c79fa1a2ede6f6d99d7196e5 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 28 Feb 2019 20:30:16 +0000 Subject: [PATCH 136/347] Fixing build errors --- Grid/qcd/utils/A2Autils.h | 10 +++++++--- Grid/serialisation/BaseIO.h | 15 +++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 11e00c49..b3914f33 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -134,8 +134,10 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, int twoBlock = mat.dimension(3); int threeBlock = mat.dimension(4); - GridBase *grid = wi[0]._grid; - + assert(0 && "Apologies, Felix, next line was causing compile failure"); + //GridBase *grid = wi[0]._grid; + GridBase *grid = nullptr; + const int nd = grid->_ndimension; const int Nsimd = grid->Nsimd(); @@ -200,7 +202,9 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, // After getting the sitewise product do the mom phase loop - int base = Nmom*i+Nmom*Lblock*j+Nmom*Lblock*Rblock*r; + assert(0 && "Apologies, Felix, next line was causing compile failure"); + //int base = Nmom*i+Nmom*Lblock*j+Nmom*Lblock*Rblock*r; + int base = 0; for ( int m=0;m; - using scalar_type = typename Traits::scalar_type; - for_all( lhs, [&](scalar_type &c, typename T1::Index n, const std::array &Dims ) { - scalar_type x = c - rhs[Dims]; - if( x < 1e-10 ) - bReturnValue = false; - } ); + Eigen::Tensor bResult = (lhs == rhs).all(); + bReturnValue = bResult(0); } return bReturnValue; } @@ -865,13 +860,13 @@ namespace Grid { } template - static inline typename std::enable_if, T>::value, void>::type + static inline typename std::enable_if::value, void>::type WriteMember(std::ostream &os, const T &object) { os << object; } template - static inline typename std::enable_if, T>::value, void>::type + static inline typename std::enable_if::value, void>::type WriteMember(std::ostream &os, const T &object) { os << "Eigen::Tensor"; } From 76536493894a6a50457d95ff2c5388e0f2ddd033 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 1 Mar 2019 12:57:41 +0000 Subject: [PATCH 137/347] baryons working now --- Grid/qcd/utils/A2Autils.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index b3914f33..d5feb275 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -134,9 +134,7 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, int twoBlock = mat.dimension(3); int threeBlock = mat.dimension(4); - assert(0 && "Apologies, Felix, next line was causing compile failure"); - //GridBase *grid = wi[0]._grid; - GridBase *grid = nullptr; + GridBase *grid = one[0]._grid; const int nd = grid->_ndimension; const int Nsimd = grid->Nsimd(); @@ -202,9 +200,7 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, // After getting the sitewise product do the mom phase loop - assert(0 && "Apologies, Felix, next line was causing compile failure"); - //int base = Nmom*i+Nmom*Lblock*j+Nmom*Lblock*Rblock*r; - int base = 0; + int base = Nmom*i+Nmom*oneBlock*j+Nmom*oneBlock*twoBlock*k+Nmom*oneBlock*twoBlock*threeBlock*r; for ( int m=0;m Date: Fri, 1 Mar 2019 14:44:39 +0000 Subject: [PATCH 138/347] baryons work now??? --- Grid/qcd/utils/A2Autils.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index d5feb275..da4fac22 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -178,7 +178,7 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, for(int i=0;i::NucleonFieldMom(Eigen::Tensor &mat, // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 auto gv3 = Gamma(Gamma::Algebra::SigmaXZ) * v3; SpinVector_v vv; - - vv()()() = pv1()()(0) * v2()()(1) * gv3()()(2) //Cross product - - pv1()()(0) * v2()()(2) * gv3()()(1) - + pv1()()(1) * v2()()(2) * gv3()()(0) - - pv1()()(1) * v2()()(0) * gv3()()(2) - + pv1()()(2) * v2()()(0) * gv3()()(1) - - pv1()()(2) * v2()()(1) * gv3()()(0); + for(int s1=0;s1 Date: Sat, 2 Mar 2019 00:24:37 +0000 Subject: [PATCH 139/347] Fixed issues with Eigen Tensor serialisation. Fixed issues with precision to text streams --- Grid/serialisation/BaseIO.h | 18 ++++--- tests/IO/Test_serialisation.cc | 86 +++++++++++++++++----------------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 289e7902..30d7a42d 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -282,7 +282,7 @@ namespace Grid { im *= n; if( Precision ) { std::stringstream s; - s << std::scientific << std::setprecision(Precision) << re; + s << std::setprecision(Precision) << re; s >> re; s.clear(); s << im; @@ -741,13 +741,16 @@ namespace Grid { upcast->readMultiDim( s, buf, dimData ); assert(dimData.size() == TotalRank && "EigenIO: Tensor rank mismatch" ); // Make sure that the number of elements read matches dimensions read - std::size_t NumElements = 1; - for( auto d : dimData ) - NumElements *= d; - assert( NumElements == buf.size() && "EigenIO: Number of elements != product of dimensions" ); + std::size_t NumContainers = 1; + for( auto i = 0 ; i < TensorRank ; i++ ) + NumContainers *= dimData[i]; // If our scalar object is a Container, make sure it's dimensions match what we read back - for( auto i = 0 ; i < ContainerRank ; i++ ) + std::size_t ElementsPerContainer = 1; + for( auto i = 0 ; i < ContainerRank ; i++ ) { assert( dimData[TensorRank+i] == Traits::Dimension(i) && "Tensor Container dimensions don't match data" ); + ElementsPerContainer *= dimData[TensorRank+i]; + } + assert( NumContainers * ElementsPerContainer == buf.size() && "EigenIO: Number of elements != product of dimensions" ); // Now see whether the tensor is the right shape, or can be made to be const auto & dims{output.dimensions()}; bool bShapeOK = (output.data() != nullptr); @@ -764,13 +767,14 @@ namespace Grid { // Copy the data into the tensor for( auto &d : MyIndex ) d = 0; const Scalar * pSource = &buf[0]; - for( auto n = 0 ; n < NumElements ; n++ ) { + for( std::size_t n = 0 ; n < NumContainers ; n++ ) { Container & c = output( MyIndex ); copyScalars( c, pSource ); // Now increment the index for( int i = TensorRank - 1; i != -1 && ++MyIndex[i] == dims[i]; i-- ) MyIndex[i] = 0; } + assert( pSource == &buf[NumContainers * ElementsPerContainer] ); } template diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index c8d7906f..f3df9bec 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -4,11 +4,12 @@ Source file: ./tests/Test_serialisation.cc - Copyright (C) 2015-2016 + Copyright (C) 2015-2019 Author: Guido Cossu Author: Antonin Portelli Author: Peter Boyle +Author: Michael Marshall 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 @@ -107,7 +108,6 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam std::cout << " done." << std::endl; } -#ifdef HAVE_HDF5 typedef std::complex TestScalar; typedef Eigen::TensorFixedSize> TensorRank5UShort; typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; @@ -115,9 +115,6 @@ typedef Eigen::Tensor TensorRank typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; typedef std::vector aTensor_9_4_2; typedef Eigen::TensorFixedSize> LSCTensor; -#ifndef NO_STRESS_TESTS -typedef Eigen::TensorFixedSize,2>,7>,3>, Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor> LCMTensor; -#endif class PerambIOTestClass: Serializable { Grid_complex Flag; @@ -156,35 +153,41 @@ public: } }; -#define TensorWriteReadInnerNoInit( T ) \ - filename = "iotest_" + std::to_string(++TestNum) + "_" #T + pszExtension; \ - ioTest(filename, t, #T, #T); -#define TensorWriteReadInner( T ) SequentialInit( t, Flag, Precision ); TensorWriteReadInnerNoInit( T ) -#define TensorWriteRead( T ) { T t ; TensorWriteReadInner( T ) } -#define TensorWriteReadV(T, ... ) { T t( __VA_ARGS__ ); TensorWriteReadInner( T ) } -#define TensorWriteReadLarge( T ) { std::unique_ptr p{new T}; T &t{*p}; TensorWriteReadInnerNoInit(T) } +#define TEST_PARAMS( T ) #T, Flag, Precision, filename, pszExtension, TestNum + +template +void EigenTensorTestSingle(const char * MyTypeName, typename EigenIO::Traits::scalar_type Flag, + unsigned short Precision, std::string &filename, const char * pszExtension, unsigned int &TestNum, + IndexTypes... otherDims) +{ + using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + std::unique_ptr pTensor{new T(otherDims...)}; + SequentialInit( * pTensor, Flag, Precision ); + filename = "iotest_" + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; + ioTest(filename, * pTensor, MyTypeName, MyTypeName); +} template -void EigenHdf5IOTest(const char * pszExtension, unsigned short Precision = 0) +void EigenTensorTest(const char * pszExtension, unsigned short Precision = 0) { unsigned int TestNum = 0; std::string filename; { int Flag = 7; - unsigned short Precision = 0; using TensorSingle = Eigen::TensorFixedSize>; - TensorWriteRead( TensorSingle ) + EigenTensorTestSingle(TEST_PARAMS( TensorSingle )); } TestScalar Flag{1,-3.1415927}; using TensorSimple = Eigen::Tensor, 6>; - TensorWriteReadV( TensorSimple, 1, 1, 1, 1, 1, 1 ) - TensorWriteReadV( TensorRank3, 6, 3, 2 ) - TensorWriteRead ( Tensor_9_4_2 ) + using I = typename TensorSimple::Index; + EigenTensorTestSingle( TEST_PARAMS( TensorSimple ), 1, 1, 1, 1, 1, 1 ); + EigenTensorTestSingle( TEST_PARAMS( TensorRank3 ), 6, 3, 2 ); + EigenTensorTestSingle(TEST_PARAMS( Tensor_9_4_2 )); { unsigned short Flag = 1; - unsigned short Precision = 0; TensorRank5UShort t; - TensorWriteReadInner ( TensorRank5UShort ); + EigenTensorTestSingle(TEST_PARAMS( TensorRank5UShort )); std::cout << " Testing alternate memory order read ... "; TensorRank5UShortAlt t2; RDR_ reader(filename); @@ -201,23 +204,20 @@ void EigenHdf5IOTest(const char * pszExtension, unsigned short Precision = 0) } std::cout << " done." << std::endl; } - TensorWriteRead ( LSCTensor ) - TensorWriteReadLarge( PerambIOTestClass ) -#ifndef NO_STRESS_TESTS - std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; - TensorWriteReadLarge ( LCMTensor ) - // Also write > 4GB of complex numbers (I suspect this will fail inside Hdf5) + EigenTensorTestSingle(TEST_PARAMS( LSCTensor )); { - static constexpr size_t Num = 0x11000000; - std::cout << "Stress test: " << Num * sizeof( Grid_complex ) / 1024 / 1024 - << " MB array of complex" << std::endl; - using Stress = std::vector>; - Stress t (Num); - TensorWriteReadInnerNoInit( Stress ); + static const char MyTypeName[] = "PerambIOTestClass"; + std::unique_ptr pObj{new PerambIOTestClass()}; + filename = "iotest_" + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; + ioTest(filename, * pObj, MyTypeName, MyTypeName); } +#ifdef STRESS_TESTS + using LCMTensor = Eigen::TensorFixedSize,2>,7>,3>, + Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor>; + std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; + EigenTensorTestSingle(TEST_PARAMS( LCMTensor )); #endif } -#endif template void tensorConvTestFn(GridSerialRNG &rng, const std::string label) @@ -295,24 +295,22 @@ int main(int argc,char **argv) ioTest("iotest.dat", obj, "text (object) "); ioTest("iotest.dat", vec, "text (vector of objects)"); //// text - //ioTest("iotest.json", obj, "JSON (object) "); - //ioTest("iotest.json", vec, "JSON (vector of objects)"); + ioTest("iotest.json", obj, "JSON (object) "); + ioTest("iotest.json", vec, "JSON (vector of objects)"); //// HDF5 #ifdef HAVE_HDF5 ioTest("iotest.h5", obj, "HDF5 (object) "); ioTest("iotest.h5", vec, "HDF5 (vector of objects)"); -#endif - std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(".dat", 6); - std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(".xml", 4); - std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(".bin"); -#ifdef HAVE_HDF5 std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl; - EigenHdf5IOTest(".h5"); + EigenTensorTest(".h5"); #endif + std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; + EigenTensorTest(".bin"); + std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; + EigenTensorTest(".xml", 6); + std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl; + EigenTensorTest(".dat", 5); std::cout << "\n==== vector flattening/reconstruction" << std::endl; typedef std::vector>> vec3d; From d56d8c923f2be1a7e387ea68d2c1c7ce0c898245 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Sat, 2 Mar 2019 00:36:53 +0000 Subject: [PATCH 140/347] Replaced an error in A2AUtils.h that was stopping the build with an assert() --- Grid/qcd/utils/A2Autils.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index da4fac22..55ace9c0 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -178,7 +178,9 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, for(int i=0;i Date: Mon, 4 Mar 2019 17:31:21 +0000 Subject: [PATCH 141/347] baryons should compile now... --- Grid/qcd/utils/A2Autils.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 55ace9c0..f7d4cc32 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -178,29 +178,28 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, for(int i=0;i C gamma_5 = - i gamma_1 gamma_3 + auto v2g = v2*Gamma(Gamma::Algebra::SigmaXZ); for(int k=0;k C gamma_5 = - i gamma_1 gamma_3 - auto gv3 = Gamma(Gamma::Algebra::SigmaXZ) * v3; SpinVector_v vv; for(int s1=0;s1 Date: Tue, 5 Mar 2019 12:01:55 +0000 Subject: [PATCH 142/347] added output for source meson fields on all tsrc --- Hadrons/Modules/MDistil/DistilVectors.hpp | 42 +++++++++++++++++------ tests/hadrons/Test_hadrons_distil.cc | 18 ++++++++++ tests/hadrons/Test_tesseract.cc | 18 ++++++++++ 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 775719c7..970e8679 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -85,7 +85,7 @@ std::vector TDistilVectors::getInput(void) template std::vector TDistilVectors::getOutput(void) { - std::vector out = {getName() + "_rho", getName() + "_phi"}; + std::vector out = {getName() + "_rho", getName() + "_phi", getName() + "_rho_all_tsrc"}; return out; } @@ -106,6 +106,8 @@ void TDistilVectors::setup(void) nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); envCreate(std::vector, getName() + "_phi", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); + envCreate(std::vector, getName() + "_rho_all_tsrc", 1, + nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); GridCartesian * grid4d = env().getGrid(); @@ -138,6 +140,7 @@ void TDistilVectors::execute(void) auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); + auto &rho_all = envGet(std::vector, getName() + "_rho_all_tsrc"); envGetTmp(LatticeSpinColourVector, tmp2); @@ -189,16 +192,38 @@ void TDistilVectors::execute(void) } } } - SpinColourVector scv0; - std::vector siteFirst(grid4d->Nd(),0); - peekSite(scv0, rho[vecindex], siteFirst); - auto & cplx0 = scv0()(0)(0); - std::cout << "site0[rho(vecindex = " << vecindex << ")] = " << cplx0 << std::endl; } } } } + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < Ns; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + rho_all[vecindex] = zero; + tmp3d_nospin = zero; + for (int it = 0; it < Nt; it++){ + t_inv = it; + if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); + tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; + tmp3d=zero; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=zero; + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); + rho_all[vecindex] += tmp2; + } + } + } + } + } + } + } + } for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { @@ -214,11 +239,6 @@ void TDistilVectors::execute(void) } InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); } - SpinColourVector scv0; - std::vector siteFirst(grid4d->Nd(),0); - peekSite(scv0, phi[vecindex], siteFirst); - auto & cplx0 = scv0()(0)(0); - std::cout << "site0[phi(vecindex = " << vecindex << ")] = " << cplx0 << std::endl; } } } diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 4e502e28..a56d3ae3 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -269,6 +269,23 @@ void test_MesonFieldRho(Application &application) application.createModule("DistilMesonFieldRho",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// +// MesonFields - rhorhoall +///////////////////////////////////////////////////////////// + +void test_MesonFieldRhoAll(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_rho_all_tsrc"; + A2AMesonFieldPar.right="DistilVecs_rho_all_tsrc"; + A2AMesonFieldPar.output="MesonSinksRhoAll"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldRhoAll",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// // BaryonFields - phiphiphi ///////////////////////////////////////////////////////////// @@ -774,6 +791,7 @@ int main(int argc, char *argv[]) test_Perambulators( application ); test_DistilVectors( application ); test_MesonField( application ); + test_MesonFieldRhoAll( application ); break; case 5: // 3 test_Global( application ); diff --git a/tests/hadrons/Test_tesseract.cc b/tests/hadrons/Test_tesseract.cc index 14f64346..bb4fc0bb 100644 --- a/tests/hadrons/Test_tesseract.cc +++ b/tests/hadrons/Test_tesseract.cc @@ -268,6 +268,23 @@ void test_MesonFieldRho(Application &application) application.createModule("DistilMesonFieldRho",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// +// MesonFields - rhorhoAll +///////////////////////////////////////////////////////////// + +void test_MesonFieldRhoAll(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs_rho_all_tsrc"; + A2AMesonFieldPar.right="DistilVecs_rho_all_tsrc"; + A2AMesonFieldPar.output="MesonSinksRhoAll"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldRhoAll",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// // BaryonFields - phiphiphi ///////////////////////////////////////////////////////////// @@ -757,6 +774,7 @@ int main(int argc, char *argv[]) test_DistilVectors( application ); test_MesonField( application ); test_MesonFieldRho( application ); + test_MesonFieldRhoAll( application ); break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From 7718ee199af2afe7d11f308d942c9e8af0488244 Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 5 Mar 2019 17:16:42 +0000 Subject: [PATCH 143/347] efficient baryon test program --- Grid/qcd/utils/A2Autils.h | 26 +++++++++++------- Hadrons/Modules.hpp | 1 + Hadrons/modules.inc | 2 ++ tests/hadrons/Test_hadrons_distil.cc | 40 ++++++++++++++++++++++++++++ tests/hadrons/Test_tesseract.cc | 32 ++++++++++++++++++++++ 5 files changed, 92 insertions(+), 9 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index f7d4cc32..1a1d2207 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -185,21 +185,29 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, auto v2 = conjugate(two[j]._odata[ss]); // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 - auto v2g = v2*Gamma(Gamma::Algebra::SigmaXZ); + //auto v2g = v2*Gamma(Gamma::Algebra::SigmaXZ); + auto v2g=v2; for(int k=0;k::NucleonFieldMom(Eigen::Tensor &mat, for ( int m=0;m::NucleonFieldMom(Eigen::Tensor &mat, parallel_for(int rt=0;rt icoor(nd); - iScalar temp; - std::vector > extracted(Nsimd); + std::vector extracted(Nsimd); for(int i=0;i::NucleonFieldMom(Eigen::Tensor &mat, int ij_rdx = m+Nmom*i + Nmom*oneBlock * j + Nmom*oneBlock * twoBlock * k + Nmom*oneBlock * twoBlock *threeBlock * rt; - temp._internal = lvSum[ij_rdx]; - extract(temp,extracted); + extract(lvSum[ij_rdx],extracted); for(int idx=0;idx::NucleonFieldMom(Eigen::Tensor &mat, int ij_ldx = m+Nmom*i + Nmom*oneBlock * j + Nmom*oneBlock * twoBlock * k + Nmom*oneBlock * twoBlock *threeBlock * ldx; - lsSum[ij_ldx]=lsSum[ij_ldx]+extracted[idx]._internal; + lsSum[ij_ldx]=lsSum[ij_ldx]+extracted[idx]; } }}}} diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 4f186a97..ffbf34ce 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index f5a3cb78..475ce214 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -10,6 +10,7 @@ modules_cc =\ Modules/MSolver/RBPrecCG.cc \ Modules/MDistil/g5_multiply.cc \ Modules/MDistil/BContraction.cc \ + Modules/MDistil/BC2.cc \ Modules/MDistil/LapEvec.cc \ Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambLight.cc \ @@ -94,6 +95,7 @@ modules_hpp =\ Modules/MDistil/Baryon2pt.hpp \ Modules/MDistil/BContraction.hpp \ Modules/MDistil/PerambLight.hpp \ + Modules/MDistil/BC2.hpp \ Modules/MContraction/WeakHamiltonian.hpp \ Modules/MContraction/WeakNeutral4ptDisc.hpp \ Modules/MContraction/WeakHamiltonianEye.hpp \ diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index a56d3ae3..c09b3d91 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -286,6 +286,38 @@ void test_MesonFieldRhoAll(Application &application) application.createModule("DistilMesonFieldRhoAll",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// +// BaryonFields - phiphiphi - efficient +///////////////////////////////////////////////////////////// + +void test_BaryonFieldPhi2(Application &application) +{ + // DistilVectors parameters + MDistil::BC2::Par BC2Par; + BC2Par.one="DistilVecs_phi"; + BC2Par.two="DistilVecs_phi"; + BC2Par.three="DistilVecs_phi"; + BC2Par.output="BaryonFieldPhi2"; + BC2Par.parity=1; + BC2Par.mom={"0 0 0"}; + application.createModule("BaryonFieldPhi2",BC2Par); +} +///////////////////////////////////////////////////////////// +// BaryonFields - rhorhorho - efficient +///////////////////////////////////////////////////////////// + +void test_BaryonFieldRho2(Application &application) +{ + // DistilVectors parameters + MDistil::BC2::Par BC2Par; + BC2Par.one="DistilVecs_rho"; + BC2Par.two="DistilVecs_rho"; + BC2Par.three="DistilVecs_rho"; + BC2Par.output="BaryonFieldRho2"; + BC2Par.parity=1; + BC2Par.mom={"0 0 0"}; + application.createModule("BaryonFieldRho2",BC2Par); +} +///////////////////////////////////////////////////////////// // BaryonFields - phiphiphi ///////////////////////////////////////////////////////////// @@ -836,6 +868,14 @@ int main(int argc, char *argv[]) test_em( application ); test_Aslash( application ); break; + case 11: // 3 + test_Global( application ); + test_LapEvec( application ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_BaryonFieldPhi2( application ); + test_BaryonFieldRho2( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; diff --git a/tests/hadrons/Test_tesseract.cc b/tests/hadrons/Test_tesseract.cc index bb4fc0bb..a683e26f 100644 --- a/tests/hadrons/Test_tesseract.cc +++ b/tests/hadrons/Test_tesseract.cc @@ -285,6 +285,38 @@ void test_MesonFieldRhoAll(Application &application) application.createModule("DistilMesonFieldRhoAll",A2AMesonFieldPar); } ///////////////////////////////////////////////////////////// +// BaryonFields - phiphiphi - efficient +///////////////////////////////////////////////////////////// + +void test_BaryonFieldPhi2(Application &application) +{ + // DistilVectors parameters + MDistil::BC2::Par BC2Par; + BC2Par.one="DistilVecs_phi"; + BC2Par.two="DistilVecs_phi"; + BC2Par.three="DistilVecs_phi"; + BC2Par.output="BaryonFieldPhi2"; + BC2Par.parity=1; + BC2Par.mom={"0 0 0"}; + application.createModule("BaryonFieldPhi2",BC2Par); +} +///////////////////////////////////////////////////////////// +// BaryonFields - rhorhorho - efficient +///////////////////////////////////////////////////////////// + +void test_BaryonFieldRho2(Application &application) +{ + // DistilVectors parameters + MDistil::BC2::Par BC2Par; + BC2Par.one="DistilVecs_rho"; + BC2Par.two="DistilVecs_rho"; + BC2Par.three="DistilVecs_rho"; + BC2Par.output="BaryonFieldRho2"; + BC2Par.parity=1; + BC2Par.mom={"0 0 0"}; + application.createModule("BaryonFieldRho2",BC2Par); +} +///////////////////////////////////////////////////////////// // BaryonFields - phiphiphi ///////////////////////////////////////////////////////////// From 4a00513e65e0ca46150a6c69c38433b5fdfc327d Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 6 Mar 2019 11:16:22 +0000 Subject: [PATCH 144/347] Moving Eigen trensor utilities to separate (optional) header --- Grid/serialisation/BaseIO.h | 215 ---------------------------- Grid/util/Eigen.h | 248 +++++++++++++++++++++++++++++++++ tests/IO/Test_serialisation.cc | 1 + 3 files changed, 249 insertions(+), 215 deletions(-) create mode 100644 Grid/util/Eigen.h diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 30d7a42d..bf66436a 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -163,221 +163,6 @@ namespace Grid { template struct Traits> : Traits> {}; } - // for_all helper function to call the lambda for scalar - template - typename std::enable_if::value, void>::type - for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, - std::array::rank> &MyIndex) - { - lambda( scalar, Seq++, MyIndex ); - } - - // for_all helper function to call the lambda for container - template - typename std::enable_if::value, void>::type - for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &container, typename ETensor::Index &Seq, - std::array::rank> &MyIndex) - { - using Traits = EigenIO::Traits; - const auto rank{ETensor::NumIndices}; - const auto InnerRank = Traits::rank; - for( typename Traits::scalar_type &Source : container ) { - lambda(Source, Seq++, MyIndex ); - // Now increment SubIndex - for( auto i = InnerRank - 1; i != -1 && ++MyIndex[rank + i] == Traits::Dimension(i); i-- ) - MyIndex[rank + i] = 0; - } - } - - // Calls a lamda (passing index and sequence number) for every member of an Eigen::Tensor - // For efficiency, iteration proceeds in memory order, - // ... but parameters guaranteed to be the same regardless of memory order - template - typename std::enable_if::value, void>::type - for_all( ETensor &ET, Lambda lambda ) - { - using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later - const std::size_t NumScalars = ET.size(); - assert( NumScalars > 0 ); - using Index = typename ETensor::Index; - Index ScalarElementCount{1}; - const auto InnerRank = EigenIO::Traits::rank; - const auto rank{ETensor::NumIndices}; - std::array Dims; - for(auto i = 0; i < rank; i++ ) { - auto dim = ET.dimension(i); - assert( dim > 0 ); - Dims[i] = static_cast(dim); - assert( Dims[i] == dim ); // check we didn't lose anything in the conversion - ScalarElementCount *= Dims[i]; - } - // Check that the number of containers is correct ... and we didn't lose anything in conversions - assert( NumScalars == ScalarElementCount ); - // If the Scalar is actually a container, add the inner Scalar's dimensions - size_t InnerScalarCount{1}; - for(auto i = 0; i < InnerRank; i++ ) { - auto dim = EigenIO::Traits::Dimension(i); - assert( dim > 0 ); - Dims[rank + i] = static_cast(dim); - assert( Dims[rank + i] == dim ); // check we didn't lose anything in the conversion - InnerScalarCount *= dim; - } - assert(EigenIO::Traits::count == InnerScalarCount); - assert(EigenIO::Traits::size == sizeof( Scalar )); - std::array MyIndex; - for( auto &idx : MyIndex ) idx = 0; - Index Seq = 0; - Scalar * pScalar = ET.data(); - for( std::size_t j = 0; j < NumScalars; j++ ) { - for_all_do_lambda( lambda, * pScalar, Seq, MyIndex ); - // Now increment the index to pass to the lambda (bearing in mind we're walking in memory order) - if( ETensor::Options & Eigen::RowMajor ) { - for( auto i = rank - 1; i != -1 && ++MyIndex[i] == Dims[i]; i-- ) - MyIndex[i] = 0; - } else { - for( auto i = 0; i < rank && ++MyIndex[i] == Dims[i]; i++ ) - MyIndex[i] = 0; - size_t NewSeq = 0; - for( auto i = 0; i < rank + InnerRank ; i++ ) { - NewSeq *= Dims[i]; - NewSeq += MyIndex[i]; - } - Seq = static_cast( NewSeq ); - } - pScalar++; - } - } - - // Sequential initialisation of tensors - // Would have preferred to define template variables for this, but that's c++ 17 - template - typename std::enable_if::value && !EigenIO::is_complex::scalar_type>::value, void>::type - SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = 1, - unsigned short Precision = 0 ) - { - using Traits = EigenIO::Traits; - using scalar_type = typename Traits::scalar_type; - for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { - scalar_type x = Inc * static_cast(n); - if( Precision ) { - std::stringstream s; - s << std::scientific << std::setprecision(Precision) << x; - s >> x; - } - c = x; - } ); - } - - template - typename std::enable_if::value && EigenIO::is_complex::scalar_type>::value, void>::type - SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc={1,-1}, - unsigned short Precision = 0 ) - { - using Traits = EigenIO::Traits; - using scalar_type = typename Traits::scalar_type; - for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { - auto re = Inc.real(); - auto im = Inc.imag(); - re *= n; - im *= n; - if( Precision ) { - std::stringstream s; - s << std::setprecision(Precision) << re; - s >> re; - s.clear(); - s << im; - s >> im; - } - c = scalar_type(re,im); - } ); - } - - // Helper to dump a tensor -#ifdef DEBUG -#define dump_tensor(args...) dump_tensor_func(args) - template - typename std::enable_if::value, void>::type - dump_tensor_func(T &t, const char * pName = nullptr) - { - using Traits = typename EigenIO::Traits; - const auto rank{T::NumIndices}; - const auto &dims = t.dimensions(); - std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; - if( pName ) - std::cout << pName; - for( auto i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; - std::cout << " in memory order:" << std::endl; - for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array &Dims ){ - std::cout << " "; - for( auto dim : Dims ) - std::cout << "[" << dim << "]"; - std::cout << " = " << c << std::endl; - } ); - std::cout << "========================================" << std::endl; - } - - template - typename std::enable_if::value, void>::type - dump_tensor_func(T &t, const char * pName = nullptr) - { - std::cout << "Dumping non-tensor object "; - if( pName ) - std::cout << pName; - std::cout << "=" << t; - } - - // Helper to dump a tensor in memory order - // Kind of superfluous given the above ... just keeping in case I need to fall back to this -#define DumpMemoryOrder(args...) DumpMemoryOrder_func(args) - template - typename std::enable_if::value, void>::type - DumpMemoryOrder_func(T &t, const char * pName = nullptr) - { - const auto rank = t.rank(); - const auto &dims = t.dimensions(); - std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; - if( pName ) - std::cout << pName; - for( auto d : dims ) std::cout << "[" << d << "]"; - std::cout << " in memory order:" << std::endl; - const typename T::Scalar * p = t.data(); - const auto size = t.size(); - const typename T::Scalar * pEnd = p + size; - if( rank <= 2 ) { - for( unsigned int i = 0 ; i < t.size() ; i++ ) - std::cout << "[" << i << "]=" << *p++ << " "; - std::cout << std::endl; - } else { - const auto innersize = dims[rank-2] * dims[rank-1]; - using Index = typename T::Index; - std::vector idx(rank - 2); - for( auto &i : idx ) i = 0; - Index idxCounter = 0; - while( p < pEnd ) { - if( T::Options & Eigen::RowMajor ) { - if( pName ) - std::cout << pName; - idxCounter = 0; - for(auto i = 0 ; i < rank - 2 ; i++) - std::cout << "[" << idx[i] << "]:"; - } - for( unsigned int i = 0 ; i < innersize ; i++ ) - std::cout << " [" << idxCounter++ << "]=" << *p++; - if( T::Options & Eigen::RowMajor ) - std::cout << std::endl; - // Now increment MyIndex - for( auto i = rank - 3; i != -1 && ++idx[i] == dims[i]; i-- ) - idx[i] = 0; - } - if( ! ( T::Options & Eigen::RowMajor ) ) - std::cout << std::endl; - } - } -#else -#define dump_tensor(args...) -#define DumpMemoryOrder(args...) -#endif - // Abstract writer/reader classes //////////////////////////////////////////// // static polymorphism implemented using CRTP idiom class Serializable; diff --git a/Grid/util/Eigen.h b/Grid/util/Eigen.h new file mode 100644 index 00000000..01e7b64b --- /dev/null +++ b/Grid/util/Eigen.h @@ -0,0 +1,248 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Grid/util/Eigen.h + + Copyright (C) 2019 + + Author: Michael Marshall + + 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 */ +#ifndef GRID_UTIL_EIGEN_H +#define GRID_UTIL_EIGEN_H +#include + +namespace Grid { + // for_all helper function to call the lambda for scalar + template + typename std::enable_if::value, void>::type + for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, + std::array::rank> &MyIndex) + { + lambda( scalar, Seq++, MyIndex ); + } + + // for_all helper function to call the lambda for container + template + typename std::enable_if::value, void>::type + for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &container, typename ETensor::Index &Seq, + std::array::rank> &MyIndex) + { + using Traits = EigenIO::Traits; + const auto rank{ETensor::NumIndices}; + const auto InnerRank = Traits::rank; + for( typename Traits::scalar_type &Source : container ) { + lambda(Source, Seq++, MyIndex ); + // Now increment SubIndex + for( auto i = InnerRank - 1; i != -1 && ++MyIndex[rank + i] == Traits::Dimension(i); i-- ) + MyIndex[rank + i] = 0; + } + } + + // Calls a lamda (passing index and sequence number) for every member of an Eigen::Tensor + // For efficiency, iteration proceeds in memory order, + // ... but parameters guaranteed to be the same regardless of memory order + template + typename std::enable_if::value, void>::type + for_all( ETensor &ET, Lambda lambda ) + { + using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later + const std::size_t NumScalars = ET.size(); + assert( NumScalars > 0 ); + using Index = typename ETensor::Index; + Index ScalarElementCount{1}; + const auto InnerRank = EigenIO::Traits::rank; + const auto rank{ETensor::NumIndices}; + std::array Dims; + for(auto i = 0; i < rank; i++ ) { + auto dim = ET.dimension(i); + assert( dim > 0 ); + Dims[i] = static_cast(dim); + assert( Dims[i] == dim ); // check we didn't lose anything in the conversion + ScalarElementCount *= Dims[i]; + } + // Check that the number of containers is correct ... and we didn't lose anything in conversions + assert( NumScalars == ScalarElementCount ); + // If the Scalar is actually a container, add the inner Scalar's dimensions + size_t InnerScalarCount{1}; + for(auto i = 0; i < InnerRank; i++ ) { + auto dim = EigenIO::Traits::Dimension(i); + assert( dim > 0 ); + Dims[rank + i] = static_cast(dim); + assert( Dims[rank + i] == dim ); // check we didn't lose anything in the conversion + InnerScalarCount *= dim; + } + assert(EigenIO::Traits::count == InnerScalarCount); + assert(EigenIO::Traits::size == sizeof( Scalar )); + std::array MyIndex; + for( auto &idx : MyIndex ) idx = 0; + Index Seq = 0; + Scalar * pScalar = ET.data(); + for( std::size_t j = 0; j < NumScalars; j++ ) { + for_all_do_lambda( lambda, * pScalar, Seq, MyIndex ); + // Now increment the index to pass to the lambda (bearing in mind we're walking in memory order) + if( ETensor::Options & Eigen::RowMajor ) { + for( auto i = rank - 1; i != -1 && ++MyIndex[i] == Dims[i]; i-- ) + MyIndex[i] = 0; + } else { + for( auto i = 0; i < rank && ++MyIndex[i] == Dims[i]; i++ ) + MyIndex[i] = 0; + size_t NewSeq = 0; + for( auto i = 0; i < rank + InnerRank ; i++ ) { + NewSeq *= Dims[i]; + NewSeq += MyIndex[i]; + } + Seq = static_cast( NewSeq ); + } + pScalar++; + } + } + + // Sequential initialisation of tensors + // Would have preferred to define template variables for this, but that's c++ 17 + template + typename std::enable_if::value && !EigenIO::is_complex::scalar_type>::value, void>::type + SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = 1, + unsigned short Precision = 0 ) + { + using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + scalar_type x = Inc * static_cast(n); + if( Precision ) { + std::stringstream s; + s << std::scientific << std::setprecision(Precision) << x; + s >> x; + } + c = x; + } ); + } + + template + typename std::enable_if::value && EigenIO::is_complex::scalar_type>::value, void>::type + SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc={1,-1}, + unsigned short Precision = 0 ) + { + using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + auto re = Inc.real(); + auto im = Inc.imag(); + re *= n; + im *= n; + if( Precision ) { + std::stringstream s; + s << std::setprecision(Precision) << re; + s >> re; + s.clear(); + s << im; + s >> im; + } + c = scalar_type(re,im); + } ); + } + + // Helper to dump a tensor +#ifdef DEBUG +#define dump_tensor(args...) dump_tensor_func(args) + template + typename std::enable_if::value, void>::type + dump_tensor_func(T &t, const char * pName = nullptr) + { + using Traits = typename EigenIO::Traits; + const auto rank{T::NumIndices}; + const auto &dims = t.dimensions(); + std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; + if( pName ) + std::cout << pName; + for( auto i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; + std::cout << " in memory order:" << std::endl; + for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array &Dims ){ + std::cout << " "; + for( auto dim : Dims ) + std::cout << "[" << dim << "]"; + std::cout << " = " << c << std::endl; + } ); + std::cout << "========================================" << std::endl; + } + + template + typename std::enable_if::value, void>::type + dump_tensor_func(T &t, const char * pName = nullptr) + { + std::cout << "Dumping non-tensor object "; + if( pName ) + std::cout << pName; + std::cout << "=" << t; + } + + // Helper to dump a tensor in memory order + // Kind of superfluous given the above ... just keeping in case I need to fall back to this +#define DumpMemoryOrder(args...) DumpMemoryOrder_func(args) + template + typename std::enable_if::value, void>::type + DumpMemoryOrder_func(T &t, const char * pName = nullptr) + { + const auto rank = t.rank(); + const auto &dims = t.dimensions(); + std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; + if( pName ) + std::cout << pName; + for( auto d : dims ) std::cout << "[" << d << "]"; + std::cout << " in memory order:" << std::endl; + const typename T::Scalar * p = t.data(); + const auto size = t.size(); + const typename T::Scalar * pEnd = p + size; + if( rank <= 2 ) { + for( unsigned int i = 0 ; i < t.size() ; i++ ) + std::cout << "[" << i << "]=" << *p++ << " "; + std::cout << std::endl; + } else { + const auto innersize = dims[rank-2] * dims[rank-1]; + using Index = typename T::Index; + std::vector idx(rank - 2); + for( auto &i : idx ) i = 0; + Index idxCounter = 0; + while( p < pEnd ) { + if( T::Options & Eigen::RowMajor ) { + if( pName ) + std::cout << pName; + idxCounter = 0; + for(auto i = 0 ; i < rank - 2 ; i++) + std::cout << "[" << idx[i] << "]:"; + } + for( unsigned int i = 0 ; i < innersize ; i++ ) + std::cout << " [" << idxCounter++ << "]=" << *p++; + if( T::Options & Eigen::RowMajor ) + std::cout << std::endl; + // Now increment MyIndex + for( auto i = rank - 3; i != -1 && ++idx[i] == dims[i]; i-- ) + idx[i] = 0; + } + if( ! ( T::Options & Eigen::RowMajor ) ) + std::cout << std::endl; + } + } +#else +#define dump_tensor(args...) +#define DumpMemoryOrder(args...) +#endif +} +#endif diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index f3df9bec..fd6326a8 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -29,6 +29,7 @@ Author: Michael Marshall *************************************************************************************/ /* END LEGAL */ #include +#include using namespace Grid; using namespace Grid::QCD; From aa24f04911a2f826ccb32c1ed4181eb2af7f504c Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Wed, 6 Mar 2019 12:55:05 +0000 Subject: [PATCH 145/347] Changed EigenIO to use GridTypeMapper type traits --- Grid/serialisation/BaseIO.h | 64 +++++++++++++++++----------------- Grid/util/Eigen.h | 37 ++++++++++---------- tests/IO/Test_serialisation.cc | 28 +++++++++++---- 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index bf66436a..4d9bd9e0 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -37,16 +37,16 @@ Author: Guido Cossu namespace Grid { // TODO Support Grid::complex from GPU port - template using Grid_complex = std::complex; + //template using Grid_complex = std::complex; // Returns original type, except for Grid_complex, where it returns the underlying type - template struct RealType { using type = T; }; - template struct RealType> { using type = T; }; + //template struct RealType { using type = T; }; + //template struct RealType> { using type = T; }; namespace EigenIO { - template struct is_complex : public std::false_type {}; - template struct is_complex> - : std::integral_constant::value> {}; + //template struct is_complex : public std::false_type {}; + //template struct is_complex> + //: std::integral_constant::value> {}; // Eigen tensors can be composed of arithmetic scalar and complex types template struct is_scalar : std::integral_constant struct is_container : public std::false_type {}; - template struct is_container> : public std::true_type {}; - template struct is_container> : public std::true_type {}; - template struct is_container> : public std::true_type {}; - template struct is_container> : public std::true_type {}; + //template struct is_container : public std::false_type {}; + //template struct is_container> : public std::true_type {}; + //template struct is_container> : public std::true_type {}; + //template struct is_container> : public std::true_type {}; + //template struct is_container> : public std::true_type {}; // Is this an Eigen tensor template struct is_tensor : std::integral_constant struct is_tensor_of_container : public std::false_type {}; - template struct is_tensor_of_container::value && is_container::value, void>::type> : public std::true_type {}; + template struct is_tensor_of_container::value && isGridTensor::value, void>::type> : public std::true_type {}; // Is this a fixed-size Eigen tensor template struct is_tensor_fixed : public std::false_type {}; @@ -94,11 +94,11 @@ namespace Grid { // EigenIO::Traits are not defined for Eigen tensors, but rather their top-level scalar // This is because Eigen tensors have a dynamic size flavour, but the scalars are all fixed size // This allows the traits to all be defined as constexpr - template struct Traits {}; // C needed for specialisation + /*template struct Traits {}; // C needed for specialisation // This defines the bottom level - i.e. it's a description of the underlying scalar template struct Traits::value, void>::type> { using scalar_type = T; // Type of the underlying scalar - using scalar_real = typename RealType::type; // real type underlying scalar_type + using scalar_real = typename RealPart::type; // real type underlying scalar_type static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) //static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) @@ -121,7 +121,7 @@ namespace Grid { }; template struct Traits> { using scalar_type = typename Traits::scalar_type; - using scalar_real = typename RealType::type; + using scalar_real = typename RealPart::type; static constexpr unsigned int rank = 1 + Traits::rank; //static constexpr unsigned int rank_non_trivial = 0 + Traits::rank_non_trivial; static constexpr unsigned int count = 1 * Traits::count; @@ -134,7 +134,7 @@ namespace Grid { }; template struct Traits> { using scalar_type = typename Traits::scalar_type; - using scalar_real = typename RealType::type; + using scalar_real = typename RealPart::type; static constexpr unsigned int rank = 1 + Traits::rank; //static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + Traits::rank_non_trivial; static constexpr unsigned int count = N * Traits::count; @@ -148,7 +148,7 @@ namespace Grid { }; template struct Traits> { using scalar_type = typename Traits::scalar_type; - using scalar_real = typename RealType::type; + using scalar_real = typename RealPart::type; static constexpr unsigned int rank = 2 + Traits::rank; //static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + Traits::rank_non_trivial; static constexpr unsigned int count = N * N * Traits::count; @@ -160,7 +160,7 @@ namespace Grid { //return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 || dim == 1 ) ? N : Traits::DimensionNT(dim - 2); //} }; - template struct Traits> : Traits> {}; + template struct Traits> : Traits> {};*/ } // Abstract writer/reader classes //////////////////////////////////////////// @@ -195,14 +195,14 @@ namespace Grid { // Helper functions for Scalar vs Container specialisations template - inline typename std::enable_if::value, const typename EigenIO::Traits::scalar_type *>::type + inline typename std::enable_if::value, const typename GridTypeMapper::scalar_type *>::type getFirstScalar(const ETensor &output) { return output.data(); } template - inline typename std::enable_if::value, const typename EigenIO::Traits::scalar_type *>::type + inline typename std::enable_if::value, const typename GridTypeMapper::scalar_type *>::type getFirstScalar(const ETensor &output) { return output.data()->begin(); @@ -210,16 +210,16 @@ namespace Grid { template inline typename std::enable_if::value, void>::type - copyScalars(typename EigenIO::Traits::scalar_type * &pCopy, const S &Source) + copyScalars(typename GridTypeMapper::scalar_type * &pCopy, const S &Source) { * pCopy ++ = Source; } template - inline typename std::enable_if::value, void>::type - copyScalars(typename EigenIO::Traits::scalar_type * &pCopy, const S &Source) + inline typename std::enable_if::value, void>::type + copyScalars(typename GridTypeMapper::scalar_type * &pCopy, const S &Source) { - for( const typename EigenIO::Traits::scalar_type &item : Source ) + for( const typename GridTypeMapper::scalar_type &item : Source ) * pCopy ++ = item; } @@ -268,16 +268,16 @@ namespace Grid { // Helper functions for Scalar vs Container specialisations template inline typename std::enable_if::value, void>::type - copyScalars(S &Dest, const typename EigenIO::Traits::scalar_type * &pSource) + copyScalars(S &Dest, const typename GridTypeMapper::scalar_type * &pSource) { Dest = * pSource ++; } template - inline typename std::enable_if::value, void>::type - copyScalars(S &Dest, const typename EigenIO::Traits::scalar_type * &pSource) + inline typename std::enable_if::value, void>::type + copyScalars(S &Dest, const typename GridTypeMapper::scalar_type * &pSource) { - for( typename EigenIO::Traits::scalar_type &item : Dest ) + for( typename GridTypeMapper::scalar_type &item : Dest ) item = * pSource ++; } @@ -362,10 +362,10 @@ namespace Grid { { using Index = typename ETensor::Index; using Container = typename ETensor::Scalar; // NB: could be same as scalar - using Traits = EigenIO::Traits; + using Traits = GridTypeMapper; using Scalar = typename Traits::scalar_type; // type of the underlying scalar constexpr unsigned int TensorRank{ETensor::NumIndices}; - constexpr unsigned int ContainerRank{Traits::rank}; // Only non-zero for containers + constexpr unsigned int ContainerRank{Traits::Rank}; // Only non-zero for containers constexpr unsigned int TotalRank{TensorRank + ContainerRank}; const Index NumElements{output.size()}; assert( NumElements > 0 ); @@ -513,10 +513,10 @@ namespace Grid { { using Index = typename ETensor::Index; using Container = typename ETensor::Scalar; // NB: could be same as scalar - using Traits = EigenIO::Traits; + using Traits = GridTypeMapper; using Scalar = typename Traits::scalar_type; // type of the underlying scalar constexpr unsigned int TensorRank{ETensor::NumIndices}; - constexpr unsigned int ContainerRank{Traits::rank}; // Only non-zero for containers + constexpr unsigned int ContainerRank{Traits::Rank}; // Only non-zero for containers constexpr unsigned int TotalRank{TensorRank + ContainerRank}; using ETDims = std::array; // Dimensions of the tensor diff --git a/Grid/util/Eigen.h b/Grid/util/Eigen.h index 01e7b64b..7f5fa915 100644 --- a/Grid/util/Eigen.h +++ b/Grid/util/Eigen.h @@ -27,6 +27,7 @@ /* END LEGAL */ #ifndef GRID_UTIL_EIGEN_H #define GRID_UTIL_EIGEN_H +#include #include namespace Grid { @@ -34,7 +35,7 @@ namespace Grid { template typename std::enable_if::value, void>::type for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, - std::array::rank> &MyIndex) + std::array::Rank> &MyIndex) { lambda( scalar, Seq++, MyIndex ); } @@ -43,11 +44,11 @@ namespace Grid { template typename std::enable_if::value, void>::type for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &container, typename ETensor::Index &Seq, - std::array::rank> &MyIndex) + std::array::Rank> &MyIndex) { - using Traits = EigenIO::Traits; + using Traits = GridTypeMapper; const auto rank{ETensor::NumIndices}; - const auto InnerRank = Traits::rank; + const auto InnerRank = Traits::Rank; for( typename Traits::scalar_type &Source : container ) { lambda(Source, Seq++, MyIndex ); // Now increment SubIndex @@ -68,7 +69,7 @@ namespace Grid { assert( NumScalars > 0 ); using Index = typename ETensor::Index; Index ScalarElementCount{1}; - const auto InnerRank = EigenIO::Traits::rank; + const auto InnerRank = GridTypeMapper::Rank; const auto rank{ETensor::NumIndices}; std::array Dims; for(auto i = 0; i < rank; i++ ) { @@ -83,14 +84,14 @@ namespace Grid { // If the Scalar is actually a container, add the inner Scalar's dimensions size_t InnerScalarCount{1}; for(auto i = 0; i < InnerRank; i++ ) { - auto dim = EigenIO::Traits::Dimension(i); + auto dim = GridTypeMapper::Dimension(i); assert( dim > 0 ); Dims[rank + i] = static_cast(dim); assert( Dims[rank + i] == dim ); // check we didn't lose anything in the conversion InnerScalarCount *= dim; } - assert(EigenIO::Traits::count == InnerScalarCount); - assert(EigenIO::Traits::size == sizeof( Scalar )); + assert(GridTypeMapper::count == InnerScalarCount); + assert(GridTypeMapper::size == sizeof( Scalar )); std::array MyIndex; for( auto &idx : MyIndex ) idx = 0; Index Seq = 0; @@ -118,13 +119,13 @@ namespace Grid { // Sequential initialisation of tensors // Would have preferred to define template variables for this, but that's c++ 17 template - typename std::enable_if::value && !EigenIO::is_complex::scalar_type>::value, void>::type - SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = 1, + typename std::enable_if::value && !is_complex::scalar_type>::value, void>::type + SequentialInit( ETensor &ET, typename GridTypeMapper::scalar_type Inc = 1, unsigned short Precision = 0 ) { - using Traits = EigenIO::Traits; + using Traits = GridTypeMapper; using scalar_type = typename Traits::scalar_type; - for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { scalar_type x = Inc * static_cast(n); if( Precision ) { std::stringstream s; @@ -136,13 +137,13 @@ namespace Grid { } template - typename std::enable_if::value && EigenIO::is_complex::scalar_type>::value, void>::type - SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc={1,-1}, + typename std::enable_if::value && is_complex::scalar_type>::value, void>::type + SequentialInit( ETensor &ET, typename GridTypeMapper::scalar_type Inc={1,-1}, unsigned short Precision = 0 ) { - using Traits = EigenIO::Traits; + using Traits = GridTypeMapper; using scalar_type = typename Traits::scalar_type; - for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { auto re = Inc.real(); auto im = Inc.imag(); re *= n; @@ -166,7 +167,7 @@ namespace Grid { typename std::enable_if::value, void>::type dump_tensor_func(T &t, const char * pName = nullptr) { - using Traits = typename EigenIO::Traits; + using Traits = GridTypeMapper; const auto rank{T::NumIndices}; const auto &dims = t.dimensions(); std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; @@ -174,7 +175,7 @@ namespace Grid { std::cout << pName; for( auto i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; std::cout << " in memory order:" << std::endl; - for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array &Dims ){ + for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array &Dims ){ std::cout << " "; for( auto dim : Dims ) std::cout << "[" << dim << "]"; diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index fd6326a8..2ba16361 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -109,16 +109,16 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam std::cout << " done." << std::endl; } -typedef std::complex TestScalar; -typedef Eigen::TensorFixedSize> TensorRank5UShort; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; +typedef ComplexD TestScalar; +typedef Eigen::TensorFixedSize> TensorRank5UShort; +typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; typedef Eigen::Tensor TensorRank3; typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; typedef std::vector aTensor_9_4_2; typedef Eigen::TensorFixedSize> LSCTensor; class PerambIOTestClass: Serializable { - Grid_complex Flag; + ComplexD Flag; public: using PerambTensor = Eigen::Tensor; GRID_SERIALIZABLE_CLASS_MEMBERS(PerambIOTestClass @@ -157,11 +157,11 @@ public: #define TEST_PARAMS( T ) #T, Flag, Precision, filename, pszExtension, TestNum template -void EigenTensorTestSingle(const char * MyTypeName, typename EigenIO::Traits::scalar_type Flag, +void EigenTensorTestSingle(const char * MyTypeName, typename GridTypeMapper::scalar_type Flag, unsigned short Precision, std::string &filename, const char * pszExtension, unsigned int &TestNum, IndexTypes... otherDims) { - using Traits = EigenIO::Traits; + using Traits = GridTypeMapper; using scalar_type = typename Traits::scalar_type; std::unique_ptr pTensor{new T(otherDims...)}; SequentialInit( * pTensor, Flag, Precision ); @@ -176,7 +176,7 @@ void EigenTensorTest(const char * pszExtension, unsigned short Precision = 0) std::string filename; { int Flag = 7; - using TensorSingle = Eigen::TensorFixedSize>; + using TensorSingle = Eigen::TensorFixedSize>; EigenTensorTestSingle(TEST_PARAMS( TensorSingle )); } TestScalar Flag{1,-3.1415927}; @@ -240,6 +240,20 @@ void tensorConvTestFn(GridSerialRNG &rng, const std::string label) int main(int argc,char **argv) { + { + LSCTensor Bingo; + constexpr Complex Flag{1,-3.1415927}; + Complex z{0}; + SpinColourVector * pV = Bingo.data(); + for( std::size_t i = Bingo.size(); i--; ) { + for( typename GridTypeMapper::scalar_type &s : *pV++ ) { + s = z; + z += Flag; + } + } + dump_tensor( Bingo ); + } + Grid_init(&argc,&argv); std::cout << std::boolalpha << "==== basic IO" << std::endl; // display true / false for boolean From d716f8a0c99f56c9d2d232e2896a694d7a899627 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 6 Mar 2019 13:55:36 +0000 Subject: [PATCH 146/347] new module for baryon contraction --- Hadrons/Modules/MDistil/BC2.cc | 7 ++ Hadrons/Modules/MDistil/BC2.hpp | 189 ++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 Hadrons/Modules/MDistil/BC2.cc create mode 100644 Hadrons/Modules/MDistil/BC2.hpp diff --git a/Hadrons/Modules/MDistil/BC2.cc b/Hadrons/Modules/MDistil/BC2.cc new file mode 100644 index 00000000..c44d8745 --- /dev/null +++ b/Hadrons/Modules/MDistil/BC2.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TBC2; diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp new file mode 100644 index 00000000..cae594cd --- /dev/null +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -0,0 +1,189 @@ +#ifndef Hadrons_MDistil_BC2_hpp_ +#define Hadrons_MDistil_BC2_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * BC2 * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + + // general baryon tensor set based on Eigen tensors and Grid-allocated memory + // Dimensions: + // 0 - ext - external field (momentum, EM field, ...) + // 1 - str - dirac structure + // 2 - t - timeslice + // 3 - s - free spin index + // 4 - i - left distillation mode index + // 5 - j - middle distillation mode index + // 6 - k - left distillation mode index + // template + // using BaryonTensorSet = Eigen::TensorMap>; + +class BC2Par: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(BC2Par, + std::string, one, + std::string, two, + std::string, three, + std::string, output, + int, parity, + std::vector, mom); +}; + +template +class TBC2: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl,); +public: + // constructor + TBC2(const std::string name); + // destructor + virtual ~TBC2(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +private: + bool hasPhase_{false}; + std::string momphName_; + std::vector gamma12_; + std::vector gamma23_; + std::vector> mom_; +protected: + GridCartesian * grid4d; + GridCartesian * grid3d; +}; + +MODULE_REGISTER_TMP(BC2, TBC2, MDistil); + +/****************************************************************************** + * TBC2 implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TBC2::TBC2(const std::string name) +: Module(name) +, momphName_(name + "_momph") +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TBC2::getInput(void) +{ + std::vector in = {par().one, par().two, par().three}; + + return in; +} + +template +std::vector TBC2::getOutput(void) +{ + std::vector out = {}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TBC2::setup(void) +{ + for (auto &pstr: par().mom) + { + auto p = strToVec(pstr); + + if (p.size() != env().getNd() - 1) + { + HADRONS_ERROR(Size, "Momentum has " + std::to_string(p.size()) + " components instead of " + std::to_string(env().getNd() - 1)); + } + mom_.push_back(p); + } + envCache(std::vector, momphName_, 1, + par().mom.size(), envGetGrid(ComplexField)); + + envTmpLat(ComplexField, "coor"); +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TBC2::execute(void) +{ + + auto &one = envGet(std::vector, par().one); + auto &two = envGet(std::vector, par().two); + auto &three = envGet(std::vector, par().three); + + int N_1 = one.size(); + int N_2 = two.size(); + int N_3 = three.size(); + + LOG(Message) << "Computing distillation baryon fields" << std::endl; + LOG(Message) << "One: '" << par().one << "' Two: '" << par().two << "' Three: '" << par().three << "'" << std::endl; + LOG(Message) << "Momenta:" << std::endl; + for (auto &p: mom_) + { + LOG(Message) << " " << p << std::endl; + } + + + int Nmom=1; + int Nt=8; + + int parity = 1; + int orthogDim=3; + + auto &ph = envGet(std::vector, momphName_); + + if (!hasPhase_) + { + startTimer("Momentum phases"); + for (unsigned int j = 0; j < Nmom; ++j) + { + Complex i(0.0,1.0); + std::vector p; + + envGetTmp(ComplexField, coor); + ph[j] = zero; + for(unsigned int mu = 0; mu < mom_[j].size(); mu++) + { + LatticeCoordinate(coor, mu); + ph[j] = ph[j] + (mom_[j][mu]/env().getDim(mu))*coor; + } + ph[j] = exp((Real)(2*M_PI)*i*ph[j]); + } + hasPhase_ = true; + stopTimer("Momentum phases"); +} + envCache(std::vector, momphName_, 1, mom_.size(), envGetGrid(ComplexField)); + + Eigen::Tensor m(Nmom,Nt,N_1,N_2,N_3,4); + A2Autils::NucleonFieldMom(m, &one[0], &two[0], &three[0], ph, parity, orthogDim); + for (int is=0 ; is < 4 ; is++){ + for (int t=0 ; t < Nt ; t++){ + std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << m(0,t,is,0,0,0) << std::endl; + } + } + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_BC2_hpp_ From 584fa0a6333e4eef08f7aece76a07bdee4b18c65 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 7 Mar 2019 12:53:34 +0000 Subject: [PATCH 147/347] Changes after review with Peter --- Grid/serialisation/BaseIO.h | 159 +++++++---------------------- Grid/simd/Grid_vector_types.h | 2 + Grid/tensors/Tensor_class.h | 178 ++++++--------------------------- Grid/tensors/Tensor_traits.h | 115 +++++++++++---------- Grid/util/Eigen.h | 30 +++--- tests/IO/Test_serialisation.cc | 8 +- 6 files changed, 149 insertions(+), 343 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 4d9bd9e0..043762ea 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -36,41 +36,41 @@ Author: Guido Cossu #include namespace Grid { - // TODO Support Grid::complex from GPU port - //template using Grid_complex = std::complex; - - // Returns original type, except for Grid_complex, where it returns the underlying type - //template struct RealType { using type = T; }; - //template struct RealType> { using type = T; }; - namespace EigenIO { - //template struct is_complex : public std::false_type {}; - //template struct is_complex> - //: std::integral_constant::value> {}; + // EigenIO works for scalars that are not just Grid supported scalars + template struct is_complex : public std::false_type {}; + // Support all complex types (not just Grid complex types) - even if the definitions overlap (!) + template struct is_complex< T , typename + std::enable_if< ::Grid::is_complex< T >::value>::type> : public std::true_type {}; + template struct is_complex, typename + std::enable_if>::value>::type> : public std::true_type {}; - // Eigen tensors can be composed of arithmetic scalar and complex types - template struct is_scalar : std::integral_constant::value || is_complex::value> {}; - - // Eigen tensors can also be composed of a limited number of containers - // NB: grid tensors (iScalar, iVector, iMatrix) have stricter limits on complex types - //template struct is_container : public std::false_type {}; - //template struct is_container> : public std::true_type {}; - //template struct is_container> : public std::true_type {}; - //template struct is_container> : public std::true_type {}; - //template struct is_container> : public std::true_type {}; + // Helpers to support I/O for Eigen tensors of arithmetic scalars, complex types, or Grid tensors + template struct is_scalar : public std::false_type {}; + template struct is_scalar::value || is_complex::value>::type> : public std::true_type {}; // Is this an Eigen tensor template struct is_tensor : std::integral_constant, T>::value> {}; // Is this an Eigen tensor of a supported scalar - template struct is_tensor_of_scalar : public std::false_type {}; - template struct is_tensor_of_scalar::value && is_scalar::value, void>::type> : public std::true_type {}; + template struct is_tensor_of_scalar : public std::false_type {}; + template struct is_tensor_of_scalar::value && is_scalar::value>::type> : public std::true_type {}; // Is this an Eigen tensor of a supported container - template struct is_tensor_of_container : public std::false_type {}; - template struct is_tensor_of_container::value && isGridTensor::value, void>::type> : public std::true_type {}; + template struct is_tensor_of_container : public std::false_type {}; + template struct is_tensor_of_container::value && isGridTensor::value>::type> : public std::true_type {}; + + // Traits are the default for scalars, or come from GridTypeMapper for GridTensors + template struct Traits {}; + template struct Traits::value>::type> : public GridTypeMapper_Base { + using scalar_type = typename T::Scalar; + static constexpr bool is_complex = ::Grid::EigenIO::is_complex::value; + }; + template struct Traits::value>::type> : public GridTypeMapper { + using scalar_type = typename GridTypeMapper::scalar_type; + static constexpr bool is_complex = ::Grid::EigenIO::is_complex::value; + }; // Is this a fixed-size Eigen tensor template struct is_tensor_fixed : public std::false_type {}; @@ -84,83 +84,9 @@ namespace Grid { : public std::true_type {}; // Is this a variable-size Eigen tensor - template struct is_tensor_variable : public std::false_type {}; + template struct is_tensor_variable : public std::false_type {}; template struct is_tensor_variable::value - && !is_tensor_fixed::value, void>::type> : public std::true_type {}; - - // These traits describe the Eigen tensor scalar and container objects supported for IO - // Containers are arbitrarily deeply nested compositions of fixed size objects, - // ... grid tensors (iScalar, iVector, and iMatrix) and std::array - // EigenIO::Traits are not defined for Eigen tensors, but rather their top-level scalar - // This is because Eigen tensors have a dynamic size flavour, but the scalars are all fixed size - // This allows the traits to all be defined as constexpr - /*template struct Traits {}; // C needed for specialisation - // This defines the bottom level - i.e. it's a description of the underlying scalar - template struct Traits::value, void>::type> { - using scalar_type = T; // Type of the underlying scalar - using scalar_real = typename RealPart::type; // real type underlying scalar_type - static constexpr unsigned int rank = 0; // The rank of the grid tensor (i.e. how many indices used) - //static constexpr unsigned int rank_non_trivial = 0; // As per rank, but excludes those of dimension 1 - static constexpr unsigned int count = 1; // total number of elements (i.e. product of dimensions) - static constexpr std::size_t scalar_size = sizeof(T); // Size of the underlying scalar in bytes - static constexpr std::size_t size = scalar_size * count; // total size of elements in bytes - static constexpr std::size_t Dimension(unsigned int dim) { return 0; } // Dimension size - //static constexpr std::size_t DimensionNT(unsigned int dim) { return 0; } // non-trivial dim size - // e.g. iScalar> - // rank = 2 - // rank_non_trivial = 0 - // count = 1 - // e.g. iVector,1> - // rank = 3 - // rank_non_trivial = 2 - // count = 9 - // e.g. iScalar,4>> - // rank = 4 - // rank_non_trivial = 3 - // count = 36 - }; - template struct Traits> { - using scalar_type = typename Traits::scalar_type; - using scalar_real = typename RealPart::type; - static constexpr unsigned int rank = 1 + Traits::rank; - //static constexpr unsigned int rank_non_trivial = 0 + Traits::rank_non_trivial; - static constexpr unsigned int count = 1 * Traits::count; - static constexpr std::size_t scalar_size = Traits::scalar_size; - static constexpr std::size_t size = scalar_size * count; - static constexpr std::size_t Dimension(unsigned int dim) { - return ( dim == 0 ) ? 1 : Traits::Dimension(dim - 1); } - //static constexpr std::size_t DimensionNT(unsigned int dim) { - //return Traits::DimensionNT(dim); } - }; - template struct Traits> { - using scalar_type = typename Traits::scalar_type; - using scalar_real = typename RealPart::type; - static constexpr unsigned int rank = 1 + Traits::rank; - //static constexpr unsigned int rank_non_trivial = (N>1 ? 1 : 0) + Traits::rank_non_trivial; - static constexpr unsigned int count = N * Traits::count; - static constexpr std::size_t scalar_size = Traits::scalar_size; - static constexpr std::size_t size = scalar_size * count; - static constexpr std::size_t Dimension(unsigned int dim) { - return ( dim == 0 ) ? N : Traits::Dimension(dim - 1); } - //static constexpr std::size_t DimensionNT(unsigned int dim) { - //return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 ) ? N : Traits::DimensionNT(dim - 1); - //} - }; - template struct Traits> { - using scalar_type = typename Traits::scalar_type; - using scalar_real = typename RealPart::type; - static constexpr unsigned int rank = 2 + Traits::rank; - //static constexpr unsigned int rank_non_trivial = (N>1 ? 2 : 0) + Traits::rank_non_trivial; - static constexpr unsigned int count = N * N * Traits::count; - static constexpr std::size_t scalar_size = Traits::scalar_size; - static constexpr std::size_t size = scalar_size * count; - static constexpr std::size_t Dimension(unsigned int dim) { - return ( dim == 0 || dim == 1 ) ? N : Traits::Dimension(dim - 2); } - //static constexpr std::size_t DimensionNT(unsigned int dim) { - //return ( N == 1 ) ? Traits::DimensionNT(dim) : ( dim == 0 || dim == 1 ) ? N : Traits::DimensionNT(dim - 2); - //} - }; - template struct Traits> : Traits> {};*/ + && !is_tensor_fixed::value>::type> : public std::true_type {}; } // Abstract writer/reader classes //////////////////////////////////////////// @@ -177,11 +103,10 @@ namespace Grid { void push(const std::string &s); void pop(void); template - typename std::enable_if::value, void>::type + typename std::enable_if::value>::type write(const std::string& s, const U &output); template - typename std::enable_if::value - && !EigenIO::is_tensor::value, void>::type + typename std::enable_if::value && !EigenIO::is_tensor::value>::type write(const std::string& s, const U &output); template void write(const std::string &s, const iScalar &output); @@ -190,19 +115,21 @@ namespace Grid { template void write(const std::string &s, const iMatrix &output); template - typename std::enable_if::value, void>::type + typename std::enable_if::value>::type write(const std::string &s, const ETensor &output); // Helper functions for Scalar vs Container specialisations template - inline typename std::enable_if::value, const typename GridTypeMapper::scalar_type *>::type + inline typename std::enable_if::value, + const typename ETensor::Scalar *>::type getFirstScalar(const ETensor &output) { return output.data(); } template - inline typename std::enable_if::value, const typename GridTypeMapper::scalar_type *>::type + inline typename std::enable_if::value, + const typename EigenIO::Traits::scalar_type *>::type getFirstScalar(const ETensor &output) { return output.data()->begin(); @@ -210,7 +137,7 @@ namespace Grid { template inline typename std::enable_if::value, void>::type - copyScalars(typename GridTypeMapper::scalar_type * &pCopy, const S &Source) + copyScalars(S * &pCopy, const S &Source) { * pCopy ++ = Source; } @@ -268,7 +195,7 @@ namespace Grid { // Helper functions for Scalar vs Container specialisations template inline typename std::enable_if::value, void>::type - copyScalars(S &Dest, const typename GridTypeMapper::scalar_type * &pSource) + copyScalars(S &Dest, const S * &pSource) { Dest = * pSource ++; } @@ -362,7 +289,7 @@ namespace Grid { { using Index = typename ETensor::Index; using Container = typename ETensor::Scalar; // NB: could be same as scalar - using Traits = GridTypeMapper; + using Traits = EigenIO::Traits; using Scalar = typename Traits::scalar_type; // type of the underlying scalar constexpr unsigned int TensorRank{ETensor::NumIndices}; constexpr unsigned int ContainerRank{Traits::Rank}; // Only non-zero for containers @@ -386,10 +313,6 @@ namespace Grid { Scalar * pCopyBuffer = nullptr; const Index TotalNumElements = NumElements * Traits::count; if( !CopyData ) { - /*if constexpr ( ContainerRank == 0 ) - pWriteBuffer = output.data(); - else - pWriteBuffer = output.data()->begin();*/ pWriteBuffer = getFirstScalar( output ); } else { // Regardless of the Eigen::Tensor storage order, the copy will be Row Major @@ -400,12 +323,6 @@ namespace Grid { for( auto &idx : MyIndex ) idx = 0; for( auto n = 0; n < NumElements; n++ ) { const Container & c = output( MyIndex ); - /*if constexpr ( ContainerRank == 0 ) - * pCopy ++ = c; - else { - for( const Scalar &Source : c ) - * pCopy ++ = Source; - }*/ copyScalars( pCopy, c ); // Now increment the index for( int i = output.NumDimensions - 1; i >= 0 && ++MyIndex[i] == output.dimension(i); i-- ) @@ -513,7 +430,7 @@ namespace Grid { { using Index = typename ETensor::Index; using Container = typename ETensor::Scalar; // NB: could be same as scalar - using Traits = GridTypeMapper; + using Traits = EigenIO::Traits; using Scalar = typename Traits::scalar_type; // type of the underlying scalar constexpr unsigned int TensorRank{ETensor::NumIndices}; constexpr unsigned int ContainerRank{Traits::Rank}; // Only non-zero for containers diff --git a/Grid/simd/Grid_vector_types.h b/Grid/simd/Grid_vector_types.h index b34e056d..3dfa2b18 100644 --- a/Grid/simd/Grid_vector_types.h +++ b/Grid/simd/Grid_vector_types.h @@ -865,8 +865,10 @@ template struct is_simd : public std::false_type {}; template <> struct is_simd : public std::true_type {}; template <> struct is_simd : public std::true_type {}; +template <> struct is_simd : public std::true_type {}; template <> struct is_simd : public std::true_type {}; template <> struct is_simd : public std::true_type {}; +template <> struct is_simd : public std::true_type {}; template <> struct is_simd : public std::true_type {}; template using IfSimd = Invoke::value, int> >; diff --git a/Grid/tensors/Tensor_class.h b/Grid/tensors/Tensor_class.h index c87503c6..a5778d34 100644 --- a/Grid/tensors/Tensor_class.h +++ b/Grid/tensors/Tensor_class.h @@ -42,27 +42,26 @@ namespace Grid { // class GridTensorBase {}; +// Too late to remove these traits from Grid Tensors, so inherit from GridTypeMapper +#define GridVector_CopyTraits \ + using element = vtype; \ + using scalar_type = typename Traits::scalar_type; \ + using vector_type = typename Traits::vector_type; \ + using vector_typeD = typename Traits::vector_typeD; \ + using tensor_reduced = typename Traits::tensor_reduced; \ + using scalar_object = typename Traits::scalar_object; \ + using Complexified = typename Traits::Complexified; \ + using Realified = typename Traits::Realified; \ + using DoublePrecision = typename Traits::DoublePrecision; \ + static constexpr int TensorLevel = Traits::TensorLevel + template class iScalar { public: vtype _internal; - typedef vtype element; - typedef typename GridTypeMapper::scalar_type scalar_type; - typedef typename GridTypeMapper::vector_type vector_type; - typedef typename GridTypeMapper::vector_typeD vector_typeD; - typedef typename GridTypeMapper::tensor_reduced tensor_reduced_v; - typedef typename GridTypeMapper::scalar_object recurse_scalar_object; - typedef iScalar tensor_reduced; - typedef iScalar scalar_object; - // substitutes a real or complex version with same tensor structure - typedef iScalar::Complexified> Complexified; - typedef iScalar::Realified> Realified; - - // get double precision version - typedef iScalar::DoublePrecision> DoublePrecision; - - static constexpr int TensorLevel = GridTypeMapper::TensorLevel + 1; + using Traits = GridTypeMapper >; + GridVector_CopyTraits; // Scalar no action // template using tensor_reduce_level = typename @@ -173,37 +172,10 @@ class iScalar { return stream; }; - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline begin() const { return &_internal; } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline begin() const { return _internal.begin(); } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return (&_internal) + 1; } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal.begin() + sizeof(_internal)/sizeof(scalar_type); } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline begin() { return &_internal; } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline begin() { return _internal.begin(); } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return (&_internal) + 1; } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal.begin() + sizeof(_internal)/sizeof(scalar_type); } + strong_inline const scalar_type * begin() const { return reinterpret_cast(&_internal); } + strong_inline scalar_type * begin() { return reinterpret_cast< scalar_type *>(&_internal); } + strong_inline const scalar_type * end() const { return begin() + Traits::count; } + strong_inline scalar_type * end() { return begin() + Traits::count; } }; /////////////////////////////////////////////////////////// // Allows to turn scalar>>> back to double. @@ -224,22 +196,9 @@ class iVector { public: vtype _internal[N]; - typedef vtype element; - typedef typename GridTypeMapper::scalar_type scalar_type; - typedef typename GridTypeMapper::vector_type vector_type; - typedef typename GridTypeMapper::vector_typeD vector_typeD; - typedef typename GridTypeMapper::tensor_reduced tensor_reduced_v; - typedef typename GridTypeMapper::scalar_object recurse_scalar_object; - typedef iScalar tensor_reduced; - typedef iVector scalar_object; + using Traits = GridTypeMapper >; + GridVector_CopyTraits; - // substitutes a real or complex version with same tensor structure - typedef iVector::Complexified, N> Complexified; - typedef iVector::Realified, N> Realified; - - // get double precision version - typedef iVector::DoublePrecision, N> DoublePrecision; - template ::value, T>::type * = nullptr> strong_inline auto operator=(T arg) -> iVector { @@ -248,7 +207,6 @@ class iVector { return *this; } - static constexpr int TensorLevel = GridTypeMapper::TensorLevel + 1; iVector(const Zero &z) { *this = zero; }; iVector() = default; /* @@ -334,37 +292,10 @@ class iVector { // return _internal[i]; // } - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline begin() const { return _internal; } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline begin() const { return _internal[0].begin(); } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal + N; } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal[0].begin() + sizeof(_internal)/sizeof(scalar_type); } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline begin() { return _internal; } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline begin() { return _internal[0].begin(); } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal + N; } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal[0].begin() + sizeof(_internal)/sizeof(scalar_type); } + strong_inline const scalar_type * begin() const { return reinterpret_cast(_internal); } + strong_inline scalar_type * begin() { return reinterpret_cast< scalar_type *>(_internal); } + strong_inline const scalar_type * end() const { return begin() + Traits::count; } + strong_inline scalar_type * end() { return begin() + Traits::count; } }; template @@ -372,25 +303,8 @@ class iMatrix { public: vtype _internal[N][N]; - typedef vtype element; - typedef typename GridTypeMapper::scalar_type scalar_type; - typedef typename GridTypeMapper::vector_type vector_type; - typedef typename GridTypeMapper::vector_typeD vector_typeD; - typedef typename GridTypeMapper::tensor_reduced tensor_reduced_v; - typedef typename GridTypeMapper::scalar_object recurse_scalar_object; - - // substitutes a real or complex version with same tensor structure - typedef iMatrix::Complexified, N> Complexified; - typedef iMatrix::Realified, N> Realified; - - // get double precision version - typedef iMatrix::DoublePrecision, N> DoublePrecision; - - // Tensor removal - typedef iScalar tensor_reduced; - typedef iMatrix scalar_object; - - static constexpr int TensorLevel = GridTypeMapper::TensorLevel + 1; + using Traits = GridTypeMapper >; + GridVector_CopyTraits; iMatrix(const Zero &z) { *this = zero; }; iMatrix() = default; @@ -521,37 +435,10 @@ class iMatrix { // return _internal[i][j]; // } - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline begin() const { return _internal[0]; } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline begin() const { return _internal[0][0].begin(); } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal[0] + N * N; } - - template - typename std::enable_if::value, const scalar_type *>::type - strong_inline end() const { return _internal[0][0].begin() + sizeof(_internal)/sizeof(scalar_type); } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline begin() { return _internal[0]; } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline begin() { return _internal[0][0].begin(); } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal[0] + N * N; } - - template - typename std::enable_if::value, scalar_type *>::type - strong_inline end() { return _internal[0][0].begin() + sizeof(_internal)/sizeof(scalar_type); } + strong_inline const scalar_type * begin() const { return reinterpret_cast(_internal[0]); } + strong_inline scalar_type * begin() { return reinterpret_cast< scalar_type *>(_internal[0]); } + strong_inline const scalar_type * end() const { return begin() + Traits::count; } + strong_inline scalar_type * end() { return begin() + Traits::count; } }; template @@ -574,6 +461,3 @@ void vprefetch(const iMatrix &vv) { } } #endif - - - diff --git a/Grid/tensors/Tensor_traits.h b/Grid/tensors/Tensor_traits.h index d4854768..9cb93e17 100644 --- a/Grid/tensors/Tensor_traits.h +++ b/Grid/tensors/Tensor_traits.h @@ -5,6 +5,7 @@ Author: Azusa Yamaguchi Author: Peter Boyle Author: Christopher Kelly +Author: Michael Marshall 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 @@ -32,14 +33,10 @@ namespace Grid { template class iMatrix; // These are the Grid tensors - template struct isGridTensor - : public std::false_type { static constexpr bool notvalue = true; }; - template struct isGridTensor> - : public std::true_type { static constexpr bool notvalue = false; }; - template struct isGridTensor> - : public std::true_type { static constexpr bool notvalue = false; }; - template struct isGridTensor> - : public std::true_type { static constexpr bool notvalue = false; }; + template struct isGridTensor : public std::false_type { static constexpr bool notvalue = true; }; + template struct isGridTensor> : public std::true_type { static constexpr bool notvalue = false; }; + template struct isGridTensor> : public std::true_type { static constexpr bool notvalue = false; }; + template struct isGridTensor> : public std::true_type { static constexpr bool notvalue = false; }; ////////////////////////////////////////////////////////////////////////////////// // Want to recurse: GridTypeMapper >::scalar_type == ComplexD. @@ -57,20 +54,15 @@ namespace Grid { ////////////////////////////////////////////////////////////////////////////////// // This saves repeating common properties for supported Grid Scalar types - template struct GridTypeMapper_Base {}; // TensorLevel How many nested grid tensors // Rank Rank of the grid tensor // count Total number of elements, i.e. product of dimensions - // scalar_size Size of the underlying fundamental object (tensor_reduced) in bytes - // size Total size of all elements in bytes // Dimension(dim) Size of dimension dim - template struct GridTypeMapper_Base { + struct GridTypeMapper_Base { static constexpr int TensorLevel = 0; static constexpr int Rank = 0; static constexpr std::size_t count = 1; - static constexpr std::size_t scalar_size = sizeof(T); - static constexpr std::size_t size = scalar_size * count; - static constexpr int Dimension(unsigned int dim) { return 0; } + static constexpr int Dimension(int dim) { return 0; } }; ////////////////////////////////////////////////////////////////////////////////// @@ -79,7 +71,7 @@ namespace Grid { template struct GridTypeMapper {}; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef RealF scalar_type; typedef RealF vector_type; typedef RealD vector_typeD; @@ -89,7 +81,7 @@ namespace Grid { typedef RealF Realified; typedef RealD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef RealD scalar_type; typedef RealD vector_type; typedef RealD vector_typeD; @@ -99,7 +91,7 @@ namespace Grid { typedef RealD Realified; typedef RealD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexF scalar_type; typedef ComplexF vector_type; typedef ComplexD vector_typeD; @@ -109,7 +101,7 @@ namespace Grid { typedef RealF Realified; typedef ComplexD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexD scalar_type; typedef ComplexD vector_type; typedef ComplexD vector_typeD; @@ -119,7 +111,7 @@ namespace Grid { typedef RealD Realified; typedef ComplexD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef Integer scalar_type; typedef Integer vector_type; typedef Integer vector_typeD; @@ -130,7 +122,7 @@ namespace Grid { typedef void DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef RealF scalar_type; typedef vRealF vector_type; typedef vRealD vector_typeD; @@ -140,7 +132,7 @@ namespace Grid { typedef vRealF Realified; typedef vRealD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef RealD scalar_type; typedef vRealD vector_type; typedef vRealD vector_typeD; @@ -150,7 +142,17 @@ namespace Grid { typedef vRealD Realified; typedef vRealD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { + typedef RealF scalar_type; + typedef vRealH vector_type; + typedef vRealD vector_typeD; + typedef vRealH tensor_reduced; + typedef RealF scalar_object; + typedef vComplexH Complexified; + typedef vRealH Realified; + typedef vRealD DoublePrecision; + }; + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexF scalar_type; typedef vComplexH vector_type; typedef vComplexD vector_typeD; @@ -160,7 +162,7 @@ namespace Grid { typedef vRealH Realified; typedef vComplexD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexF scalar_type; typedef vComplexF vector_type; typedef vComplexD vector_typeD; @@ -170,7 +172,7 @@ namespace Grid { typedef vRealF Realified; typedef vComplexD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef ComplexD scalar_type; typedef vComplexD vector_type; typedef vComplexD vector_typeD; @@ -180,7 +182,7 @@ namespace Grid { typedef vRealD Realified; typedef vComplexD DoublePrecision; }; - template<> struct GridTypeMapper : public GridTypeMapper_Base { + template<> struct GridTypeMapper : public GridTypeMapper_Base { typedef Integer scalar_type; typedef vInteger vector_type; typedef vInteger vector_typeD; @@ -192,46 +194,49 @@ namespace Grid { }; #define GridTypeMapper_RepeatedTypes \ - typedef typename ObjectTraits::scalar_type scalar_type; \ - typedef typename ObjectTraits::vector_type vector_type; \ - typedef typename ObjectTraits::vector_typeD vector_typeD; \ - typedef typename ObjectTraits::tensor_reduced tensor_reduced; \ - typedef typename ObjectTraits::scalar_object scalar_object; \ - typedef typename ObjectTraits::Complexified Complexified; \ - typedef typename ObjectTraits::Realified Realified; \ - typedef typename ObjectTraits::DoublePrecision DoublePrecision; \ - static constexpr int TensorLevel = BaseTraits::TensorLevel + 1; \ - static constexpr std::size_t scalar_size = BaseTraits::scalar_size; \ - static constexpr std::size_t size = scalar_size * count + using BaseTraits = GridTypeMapper; \ + using scalar_type = typename BaseTraits::scalar_type; \ + using vector_type = typename BaseTraits::vector_type; \ + using vector_typeD = typename BaseTraits::vector_typeD; \ + static constexpr int TensorLevel = BaseTraits::TensorLevel + 1 template struct GridTypeMapper> { - using ObjectTraits = iScalar; - using BaseTraits = GridTypeMapper; - static constexpr int Rank = 1 + BaseTraits::Rank; - static constexpr std::size_t count = 1 * BaseTraits::count; - static constexpr int Dimension(unsigned int dim) { - return ( dim == 0 ) ? 1 : BaseTraits::Dimension(dim - 1); } GridTypeMapper_RepeatedTypes; + using tensor_reduced = iScalar; + using scalar_object = iScalar; + using Complexified = iScalar; + using Realified = iScalar; + using DoublePrecision = iScalar; + static constexpr int Rank = BaseTraits::Rank + 1; + static constexpr std::size_t count = BaseTraits::count; + static constexpr int Dimension(int dim) { + return ( dim == 0 ) ? 1 : BaseTraits::Dimension(dim - 1); } }; template struct GridTypeMapper> { - using ObjectTraits = iVector; - using BaseTraits = GridTypeMapper; - static constexpr int Rank = 1 + BaseTraits::Rank; - static constexpr std::size_t count = N * BaseTraits::count; - static constexpr int Dimension(unsigned int dim) { - return ( dim == 0 ) ? N : BaseTraits::Dimension(dim - 1); } GridTypeMapper_RepeatedTypes; + using tensor_reduced = iScalar; + using scalar_object = iVector; + using Complexified = iVector; + using Realified = iVector; + using DoublePrecision = iVector; + static constexpr int Rank = BaseTraits::Rank + 1; + static constexpr std::size_t count = BaseTraits::count * N; + static constexpr int Dimension(int dim) { + return ( dim == 0 ) ? N : BaseTraits::Dimension(dim - 1); } }; template struct GridTypeMapper> { - using ObjectTraits = iMatrix; - using BaseTraits = GridTypeMapper; - static constexpr int Rank = 2 + BaseTraits::Rank; - static constexpr std::size_t count = N * N * BaseTraits::count; - static constexpr int Dimension(unsigned int dim) { - return ( dim == 0 || dim == 1 ) ? N : BaseTraits::Dimension(dim - 2); } GridTypeMapper_RepeatedTypes; + using tensor_reduced = iScalar; + using scalar_object = iMatrix; + using Complexified = iMatrix; + using Realified = iMatrix; + using DoublePrecision = iMatrix; + static constexpr int Rank = BaseTraits::Rank + 2; + static constexpr std::size_t count = BaseTraits::count * N * N; + static constexpr int Dimension(int dim) { + return ( dim == 0 || dim == 1 ) ? N : BaseTraits::Dimension(dim - 2); } }; // Match the index diff --git a/Grid/util/Eigen.h b/Grid/util/Eigen.h index 7f5fa915..9af510fe 100644 --- a/Grid/util/Eigen.h +++ b/Grid/util/Eigen.h @@ -35,7 +35,7 @@ namespace Grid { template typename std::enable_if::value, void>::type for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, - std::array::Rank> &MyIndex) + std::array::Rank> &MyIndex) { lambda( scalar, Seq++, MyIndex ); } @@ -44,9 +44,9 @@ namespace Grid { template typename std::enable_if::value, void>::type for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &container, typename ETensor::Index &Seq, - std::array::Rank> &MyIndex) + std::array::Rank> &MyIndex) { - using Traits = GridTypeMapper; + using Traits = EigenIO::Traits; const auto rank{ETensor::NumIndices}; const auto InnerRank = Traits::Rank; for( typename Traits::scalar_type &Source : container ) { @@ -65,11 +65,12 @@ namespace Grid { for_all( ETensor &ET, Lambda lambda ) { using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later + using Traits = EigenIO::Traits; const std::size_t NumScalars = ET.size(); assert( NumScalars > 0 ); using Index = typename ETensor::Index; Index ScalarElementCount{1}; - const auto InnerRank = GridTypeMapper::Rank; + const auto InnerRank = Traits::Rank; const auto rank{ETensor::NumIndices}; std::array Dims; for(auto i = 0; i < rank; i++ ) { @@ -84,14 +85,13 @@ namespace Grid { // If the Scalar is actually a container, add the inner Scalar's dimensions size_t InnerScalarCount{1}; for(auto i = 0; i < InnerRank; i++ ) { - auto dim = GridTypeMapper::Dimension(i); + auto dim = Traits::Dimension(i); assert( dim > 0 ); Dims[rank + i] = static_cast(dim); assert( Dims[rank + i] == dim ); // check we didn't lose anything in the conversion InnerScalarCount *= dim; } - assert(GridTypeMapper::count == InnerScalarCount); - assert(GridTypeMapper::size == sizeof( Scalar )); + assert(Traits::count == InnerScalarCount); std::array MyIndex; for( auto &idx : MyIndex ) idx = 0; Index Seq = 0; @@ -119,11 +119,10 @@ namespace Grid { // Sequential initialisation of tensors // Would have preferred to define template variables for this, but that's c++ 17 template - typename std::enable_if::value && !is_complex::scalar_type>::value, void>::type - SequentialInit( ETensor &ET, typename GridTypeMapper::scalar_type Inc = 1, - unsigned short Precision = 0 ) + typename std::enable_if::value && !EigenIO::Traits::is_complex>::type + SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = 1, unsigned short Precision = 0 ) { - using Traits = GridTypeMapper; + using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { scalar_type x = Inc * static_cast(n); @@ -137,11 +136,10 @@ namespace Grid { } template - typename std::enable_if::value && is_complex::scalar_type>::value, void>::type - SequentialInit( ETensor &ET, typename GridTypeMapper::scalar_type Inc={1,-1}, - unsigned short Precision = 0 ) + typename std::enable_if::value && EigenIO::Traits::is_complex>::type + SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc={1,-1}, unsigned short Precision = 0 ) { - using Traits = GridTypeMapper; + using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { auto re = Inc.real(); @@ -167,7 +165,7 @@ namespace Grid { typename std::enable_if::value, void>::type dump_tensor_func(T &t, const char * pName = nullptr) { - using Traits = GridTypeMapper; + using Traits = EigenIO::Traits; const auto rank{T::NumIndices}; const auto &dims = t.dimensions(); std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 2ba16361..56cdcb4b 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -110,8 +110,8 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam } typedef ComplexD TestScalar; -typedef Eigen::TensorFixedSize> TensorRank5UShort; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; +typedef Eigen::TensorFixedSize> TensorRank5UShort; +typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; typedef Eigen::Tensor TensorRank3; typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; typedef std::vector aTensor_9_4_2; @@ -157,11 +157,11 @@ public: #define TEST_PARAMS( T ) #T, Flag, Precision, filename, pszExtension, TestNum template -void EigenTensorTestSingle(const char * MyTypeName, typename GridTypeMapper::scalar_type Flag, +void EigenTensorTestSingle(const char * MyTypeName, typename EigenIO::Traits::scalar_type Flag, unsigned short Precision, std::string &filename, const char * pszExtension, unsigned int &TestNum, IndexTypes... otherDims) { - using Traits = GridTypeMapper; + using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; std::unique_ptr pTensor{new T(otherDims...)}; SequentialInit( * pTensor, Flag, Precision ); From f9e273d4bf5c4a2c35fe258ae6851e6b09bc6a59 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Thu, 7 Mar 2019 14:33:04 +0000 Subject: [PATCH 148/347] Making sure same as Traits-recommend --- Grid/simd/Grid_vector_types.h | 1 + Grid/tensors/Tensor_class.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Grid/simd/Grid_vector_types.h b/Grid/simd/Grid_vector_types.h index 3dfa2b18..707af211 100644 --- a/Grid/simd/Grid_vector_types.h +++ b/Grid/simd/Grid_vector_types.h @@ -10,6 +10,7 @@ Author: Azusa Yamaguchi Author: Guido Cossu Author: Peter Boyle Author: neo +Author: Michael Marshall 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 diff --git a/Grid/tensors/Tensor_class.h b/Grid/tensors/Tensor_class.h index a5778d34..d59640df 100644 --- a/Grid/tensors/Tensor_class.h +++ b/Grid/tensors/Tensor_class.h @@ -5,6 +5,7 @@ Copyright (C) 2015 Author: Azusa Yamaguchi Author: Peter Boyle +Author: Michael Marshall 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 From 93dfbfbfcd2b58eeeee57e9fba3bd8a93e59aec2 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 7 Mar 2019 15:33:50 +0000 Subject: [PATCH 149/347] added module to compute perambulator from a solve --- Hadrons/Modules.hpp | 147 ++++++------- Hadrons/Modules/MDistil/BC2.hpp | 1 + Hadrons/Modules/MDistil/Distil.hpp | 15 ++ Hadrons/Modules/MDistil/LapEvec.hpp | 4 +- Hadrons/Modules/MDistil/PerambLight.hpp | 4 +- Hadrons/modules.inc | 270 ++++++++++++------------ tests/hadrons/Test_hadrons_distil.cc | 41 ++++ 7 files changed, 272 insertions(+), 210 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index ffbf34ce..075d6f41 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,82 +1,83 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp index cae594cd..f83962f6 100644 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -125,6 +125,7 @@ template void TBC2::execute(void) { + auto &one = envGet(std::vector, par().one); auto &two = envGet(std::vector, par().two); auto &three = envGet(std::vector, par().three); diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index af7e2786..747d5f51 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -513,6 +513,21 @@ inline void RotateEigen(std::vector & evec) } } +struct DistilParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, TI, + int, LI, + int, nnoise, + int, tsrc, + int, SI, + int, Ns, + int, Nt, + int, Nt_inv) + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} +}; + + END_MODULE_NAMESPACE END_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index fa62fe49..22ca8526 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -316,6 +316,7 @@ void TLapEvec::execute(void) std::cout << GridLogMessage << " Compute eigenpack, Timeslice = " << t << std::endl; std::cout << GridLogMessage << "------------------------------------------------------------" << std::endl; + std::cout << "T: " << t << " / " << Ntfirst + Ntlocal << std::endl; eig[t].resize(LPar.Nk+LPar.Np,gridLD); // Construct smearing operator @@ -356,7 +357,7 @@ void TLapEvec::execute(void) // Now rotate the eigenvectors into our phase convention RotateEigen( eig[t].evec ); - if((1)) { // Debugging only + if((0)) { // Debugging only // Write the eigenvectors and eigenvalues to disk //std::cout << GridLogMessage << "Writing eigenvalues/vectors to " << pszEigenPack << std::endl; eig[t].record.operatorXml = DefaultOperatorXml; @@ -365,6 +366,7 @@ void TLapEvec::execute(void) //std::cout << GridLogMessage << "Written eigenvectors" << std::endl; } } + std::cout << "T: " << t << " / " << Ntfirst + Ntlocal << std::endl; for (int i=0;i DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} }; - + */ struct SolverParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(SolverParameters, double, CGPrecision, diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 475ce214..d994d41e 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,163 +1,165 @@ modules_cc =\ + Modules/MScalarSUN/ShiftProbe.cc \ + Modules/MScalarSUN/Grad.cc \ + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/Div.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/TwoPoint.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/VPCounterTerms.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MScalar/ScalarVP.cc \ + Modules/MLoop/NoiseLoop.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadCosmHol.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadA2AVectors.cc \ + Modules/MSink/Smear.cc \ + Modules/MSink/Point.cc \ + Modules/MFermion/FreeProp.cc \ + Modules/MFermion/GaugeProp.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/GaugeFix.cc \ + Modules/MUtilities/TestSeqGamma.cc \ Modules/MUtilities/TestSeqConserved.cc \ Modules/MUtilities/RandomVectors.cc \ - Modules/MUtilities/TestSeqGamma.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MSolver/RBPrecCG.cc \ + Modules/MDistil/PerambFromSolve.cc \ Modules/MDistil/g5_multiply.cc \ - Modules/MDistil/BContraction.cc \ - Modules/MDistil/BC2.cc \ Modules/MDistil/LapEvec.cc \ - Modules/MDistil/Baryon2pt.cc \ - Modules/MDistil/PerambLight.cc \ Modules/MDistil/DistilVectors.cc \ - Modules/MContraction/WeakHamiltonianEye.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MContraction/DiscLoop.cc \ + Modules/MDistil/BContraction.cc \ + Modules/MDistil/Baryon2pt.cc \ + Modules/MDistil/BC2.cc \ + Modules/MDistil/PerambLight.cc \ + Modules/MSource/Momentum.cc \ + Modules/MSource/Z2.cc \ + Modules/MSource/Point.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Wall.cc \ + Modules/MSource/SeqConserved.cc \ Modules/MContraction/Meson.cc \ - Modules/MContraction/WeakNeutral4ptDisc.cc \ + Modules/MContraction/A2AAslashField.cc \ Modules/MContraction/WardIdentity.cc \ - Modules/MContraction/A2AMesonField.cc \ Modules/MContraction/WeakHamiltonianNonEye.cc \ Modules/MContraction/Baryon.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MAction/ZMobiusDWF.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/WeakHamiltonianEye.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MContraction/WeakNeutral4ptDisc.cc \ + Modules/MContraction/Gamma3pt.cc \ + Modules/MAction/MobiusDWF.cc \ Modules/MAction/WilsonClover.cc \ Modules/MAction/Wilson.cc \ Modules/MAction/DWF.cc \ - Modules/MAction/MobiusDWF.cc \ Modules/MAction/ScaledDWF.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadA2AVectors.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MNPR/Amputate.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MSolver/A2AVectors.cc \ + Modules/MSolver/RBPrecCG.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/A2AAslashVectors.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MLoop/NoiseLoop.cc \ - Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/Div.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ - Modules/MScalarSUN/ShiftProbe.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/StochFreeField.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MSink/Smear.cc \ - Modules/MSink/Point.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MFermion/FreeProp.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/ScalarVP.cc \ - Modules/MScalar/VPCounterTerms.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MSource/SeqConserved.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/Z2.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/Momentum.cc + Modules/MNPR/Amputate.cc modules_hpp =\ - Modules/MUtilities/RandomVectors.hpp \ - Modules/MUtilities/TestSeqGamma.hpp \ - Modules/MUtilities/PrecisionCast.hpp \ - Modules/MUtilities/TestSeqConserved.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ - Modules/MSolver/Guesser.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MDistil/LapEvec.hpp \ - Modules/MDistil/Distil.hpp \ - Modules/MDistil/g5_multiply.hpp \ - Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/Baryon2pt.hpp \ - Modules/MDistil/BContraction.hpp \ - Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/BC2.hpp \ - Modules/MContraction/WeakHamiltonian.hpp \ - Modules/MContraction/WeakNeutral4ptDisc.hpp \ - Modules/MContraction/WeakHamiltonianEye.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/WeakHamiltonianNonEye.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MContraction/WardIdentity.hpp \ - Modules/MAction/Wilson.hpp \ - Modules/MAction/WilsonClover.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/ScaledDWF.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MIO/LoadBinary.hpp \ - Modules/MIO/LoadCosmHol.hpp \ - Modules/MIO/LoadNersc.hpp \ - Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadEigenPack.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MLoop/NoiseLoop.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TrMag.hpp \ Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrMag.hpp \ Modules/MScalarSUN/ShiftProbe.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ - Modules/MFermion/GaugeProp.hpp \ - Modules/MFermion/FreeProp.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MScalar/FreeProp.hpp \ Modules/MScalar/Scalar.hpp \ Modules/MScalar/ScalarVP.hpp \ - Modules/MScalar/FreeProp.hpp \ Modules/MScalar/ChargedProp.hpp \ Modules/MScalar/VPCounterTerms.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Point.hpp \ + Modules/MLoop/NoiseLoop.hpp \ + Modules/MIO/LoadEigenPack.hpp \ + Modules/MIO/LoadA2AVectors.hpp \ + Modules/MIO/LoadCoarseEigenPack.hpp \ + Modules/MIO/LoadCosmHol.hpp \ + Modules/MIO/LoadBinary.hpp \ + Modules/MIO/LoadNersc.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MSink/Point.hpp \ + Modules/MFermion/FreeProp.hpp \ + Modules/MFermion/GaugeProp.hpp \ + Modules/MGauge/FundtoHirep.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/StoutSmearing.hpp \ + Modules/MGauge/Unit.hpp \ + Modules/MGauge/GaugeFix.hpp \ + Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MUtilities/TestSeqGamma.hpp \ + Modules/MUtilities/RandomVectors.hpp \ + Modules/MUtilities/TestSeqConserved.hpp \ + Modules/MUtilities/PrecisionCast.hpp \ + Modules/MDistil/PerambLight.hpp \ + Modules/MDistil/Distil.hpp \ + Modules/MDistil/BC2.hpp \ + Modules/MDistil/g5_multiply.hpp \ + Modules/MDistil/PerambFromSolve.hpp \ + Modules/MDistil/Baryon2pt.hpp \ + Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/DistilVectors.hpp \ + Modules/MSource/SeqConserved.hpp \ Modules/MSource/Z2.hpp \ Modules/MSource/Wall.hpp \ - Modules/MSource/SeqConserved.hpp + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/WeakHamiltonianEye.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MContraction/Meson.hpp \ + Modules/MContraction/WeakHamiltonian.hpp \ + Modules/MContraction/WeakNeutral4ptDisc.hpp \ + Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/WeakHamiltonianNonEye.hpp \ + Modules/MContraction/WardIdentity.hpp \ + Modules/MContraction/A2AMesonField.hpp \ + Modules/MAction/WilsonClover.hpp \ + Modules/MAction/ScaledDWF.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MAction/Wilson.hpp \ + Modules/MAction/DWF.hpp \ + Modules/MAction/ZMobiusDWF.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/A2AVectors.hpp \ + Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/Guesser.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MNPR/Amputate.hpp diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index c09b3d91..5852b854 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -49,6 +49,32 @@ void test_Global(Application &application) application.setPar(globalPar); } + +///////////////////////////////////////////////////////////// +// Test creation Solver +///////////////////////////////////////////////////////////// + +void test_SolverS(Application &application) +{ + std::string boundary = "1 1 1 -1"; + + + MAction::DWF::Par actionPar; + actionPar.gauge = "gauge"; + actionPar.Ls = 16; + actionPar.M5 = 1.8; + actionPar.mass = 0.005; + actionPar.boundary = boundary; + actionPar.twist = "0. 0. 0. 0."; + application.createModule("DWF_s", actionPar); + + + MSolver::RBPrecCG::Par solverPar; + solverPar.action = "DWF_s"; + solverPar.residual = 1.0e-7; + solverPar.maxIteration = 10000; + application.createModule("CG_s", solverPar); +} ///////////////////////////////////////////////////////////// // Test creation of laplacian eigenvectors ///////////////////////////////////////////////////////////// @@ -393,6 +419,21 @@ void test_Aslash(Application &application) application.createModule("Aslash_field",A2AAslashFieldPar); } +///////////////////////////////////////////////////////////// +// MesonA2ASlashSequential +///////////////////////////////////////////////////////////// + +void test_AslashSeq(Application &application) +{ + // DistilVectors parameters + MSolver::A2AAslashVectors::Par A2AAslashVectorsPar; + A2AAslashVectorsPar.vector="Peramb_unsmeared_sink"; + A2AAslashVectorsPar.emField="Em"; + A2AAslashVectorsPar.solver="CG_s"; + A2AAslashVectorsPar.output="Aslash_seq"; + application.createModule("Aslash_seq",A2AAslashVectorsPar); +} + bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { if( bGobbleWhiteSpace ) From 1538bf8c34a1f1af63ccec2c7b4277f8031ef927 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 7 Mar 2019 17:36:22 +0000 Subject: [PATCH 150/347] added everythong to compute sequential aslash fields --- Hadrons/Modules.hpp | 1 + Hadrons/Modules/MDistil/DistilVectors.hpp | 35 +---------- Hadrons/modules.inc | 2 + tests/hadrons/Test_hadrons_distil.cc | 76 +++++++++++++++++++++-- 4 files changed, 76 insertions(+), 38 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 075d6f41..a250d869 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 970e8679..2efd10fa 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -85,7 +85,7 @@ std::vector TDistilVectors::getInput(void) template std::vector TDistilVectors::getOutput(void) { - std::vector out = {getName() + "_rho", getName() + "_phi", getName() + "_rho_all_tsrc"}; + std::vector out = {getName() + "_rho", getName() + "_phi"}; return out; } @@ -106,9 +106,6 @@ void TDistilVectors::setup(void) nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); envCreate(std::vector, getName() + "_phi", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); - envCreate(std::vector, getName() + "_rho_all_tsrc", 1, - nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); - GridCartesian * grid4d = env().getGrid(); std::vector latt_size = GridDefaultLatt(); @@ -140,8 +137,6 @@ void TDistilVectors::execute(void) auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &rho = envGet(std::vector, getName() + "_rho"); auto &phi = envGet(std::vector, getName() + "_phi"); - auto &rho_all = envGet(std::vector, getName() + "_rho_all_tsrc"); - envGetTmp(LatticeSpinColourVector, tmp2); envGetTmp(LatticeColourVector, tmp_nospin); @@ -197,34 +192,6 @@ void TDistilVectors::execute(void) } } - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < Ns; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; - rho_all[vecindex] = zero; - tmp3d_nospin = zero; - for (int it = 0; it < Nt; it++){ - t_inv = it; - if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); - tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; - tmp3d=zero; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=zero; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); - rho_all[vecindex] += tmp2; - } - } - } - } - } - } - } - } - for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index d994d41e..eb4c6d88 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -47,6 +47,7 @@ modules_cc =\ Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/BC2.cc \ Modules/MDistil/PerambLight.cc \ + Modules/MDistil/DistilSink.cc \ Modules/MSource/Momentum.cc \ Modules/MSource/Z2.cc \ Modules/MSource/Point.cc \ @@ -130,6 +131,7 @@ modules_hpp =\ Modules/MDistil/LapEvec.hpp \ Modules/MDistil/BContraction.hpp \ Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/DistilSink.hpp \ Modules/MSource/SeqConserved.hpp \ Modules/MSource/Z2.hpp \ Modules/MSource/Wall.hpp \ diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 5852b854..cb97d727 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -178,7 +178,7 @@ void test_PerambulatorsS(Application &application) PerambPar.Distil.Ns=4; PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; - PerambPar.Solver.mass=0.04; //strange mass??? + PerambPar.Solver.mass=0.005; //strange mass??? PerambPar.Solver.M5=1.8; PerambPar.Ls=16; PerambPar.Solver.CGPrecision=1e-8; @@ -200,10 +200,10 @@ void test_DistilVectorsS(Application &application) DistilVecPar.nnoise = 1; DistilVecPar.LI=3; DistilVecPar.SI=4; - DistilVecPar.TI=8; + DistilVecPar.TI=32; DistilVecPar.nvec=3; DistilVecPar.Ns=4; - DistilVecPar.Nt=8; + DistilVecPar.Nt=32; DistilVecPar.Nt_inv=1; application.createModule("DistilVecsS",DistilVecPar); } @@ -427,12 +427,69 @@ void test_AslashSeq(Application &application) { // DistilVectors parameters MSolver::A2AAslashVectors::Par A2AAslashVectorsPar; - A2AAslashVectorsPar.vector="Peramb_unsmeared_sink"; + A2AAslashVectorsPar.vector="PerambS_unsmeared_sink"; A2AAslashVectorsPar.emField="Em"; A2AAslashVectorsPar.solver="CG_s"; A2AAslashVectorsPar.output="Aslash_seq"; application.createModule("Aslash_seq",A2AAslashVectorsPar); } +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +void test_DistilVectorsAslashSeq(Application &application) +{ + // DistilVectors parameters + MDistil::DistilSink::Par DistilSinkPar; + DistilSinkPar.perambulator="PerambAslashS"; + DistilSinkPar.eigenPack="LapEvec"; + DistilSinkPar.tsrc = 0; + DistilSinkPar.nnoise = 1; + DistilSinkPar.LI=3; + DistilSinkPar.SI=4; + DistilSinkPar.TI=8; + DistilSinkPar.nvec=3; + DistilSinkPar.Ns=4; + DistilSinkPar.Nt=8; + DistilSinkPar.Nt_inv=1; + application.createModule("DistilVecsAslashSeq",DistilSinkPar); +} +void test_PerambulatorsSolve(Application &application) +{ + // PerambLight parameters + MDistil::PerambFromSolve::Par PerambFromSolvePar; + PerambFromSolvePar.eigenPack="LapEvec"; + PerambFromSolvePar.solve="Aslash_seq"; + PerambFromSolvePar.PerambFileName="perambAslashS.bin"; + PerambFromSolvePar.Distil.tsrc = 0; + PerambFromSolvePar.Distil.nnoise = 1; + PerambFromSolvePar.Distil.LI=3; + PerambFromSolvePar.Distil.SI=4; + PerambFromSolvePar.Distil.TI=8; + PerambFromSolvePar.nvec=3; + PerambFromSolvePar.Distil.Ns=4; + PerambFromSolvePar.Distil.Nt=8; + PerambFromSolvePar.Distil.Nt_inv=1; + application.createModule("PerambAslashS",PerambFromSolvePar); +} +///////////////////////////////////////////////////////////// +// MesonFields - aslaaaash +///////////////////////////////////////////////////////////// + +void test_MesonFieldAslashSeq(Application &application) +{ + // DistilVectors parameters + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecsAslashSeq"; + //A2AMesonFieldPar.right="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecsAslashSeq"; + A2AMesonFieldPar.output="MesonSinksAslashSeq"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldAslashSeq",A2AMesonFieldPar); +} bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { @@ -917,6 +974,17 @@ int main(int argc, char *argv[]) test_BaryonFieldPhi2( application ); test_BaryonFieldRho2( application ); break; + case 12: // 3 + test_Global( application ); + test_SolverS( application ); + test_LapEvec( application ); + test_PerambulatorsS( application ); + test_em( application ); + test_AslashSeq( application ); + test_PerambulatorsSolve( application ); + test_DistilVectorsAslashSeq( application ); + test_MesonFieldAslashSeq( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From dde118fed93104b6dd06689fbc9842f4be3c2133 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 7 Mar 2019 17:36:53 +0000 Subject: [PATCH 151/347] added everythong to compute sequential aslash fields --- Hadrons/Modules/MDistil/DistilSink.cc | 7 + Hadrons/Modules/MDistil/DistilSink.hpp | 183 ++++++++++++++++++++ Hadrons/Modules/MDistil/PerambFromSolve.cc | 7 + Hadrons/Modules/MDistil/PerambFromSolve.hpp | 178 +++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 Hadrons/Modules/MDistil/DistilSink.cc create mode 100644 Hadrons/Modules/MDistil/DistilSink.hpp create mode 100644 Hadrons/Modules/MDistil/PerambFromSolve.cc create mode 100644 Hadrons/Modules/MDistil/PerambFromSolve.hpp diff --git a/Hadrons/Modules/MDistil/DistilSink.cc b/Hadrons/Modules/MDistil/DistilSink.cc new file mode 100644 index 00000000..eb151e32 --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilSink.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TDistilSink; diff --git a/Hadrons/Modules/MDistil/DistilSink.hpp b/Hadrons/Modules/MDistil/DistilSink.hpp new file mode 100644 index 00000000..ccd8a8aa --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilSink.hpp @@ -0,0 +1,183 @@ +#ifndef Hadrons_MDistil_DistilSink_hpp_ +#define Hadrons_MDistil_DistilSink_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation +#include + +BEGIN_HADRONS_NAMESPACE + + +/****************************************************************************** + * DistilSink * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class DistilSinkPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilSinkPar, + std::string, perambulator, + std::string, eigenPack, + bool, multiFile, + int, tsrc, + int, nnoise, + int, LI, + int, SI, + int, TI, + int, nvec, + int, Ns, + int, Nt, + int, Nt_inv); +}; + +template +class TDistilSink: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl,); +public: + // constructor + TDistilSink(const std::string name); + // destructor + virtual ~TDistilSink(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(DistilSink, TDistilSink, MDistil); + +/****************************************************************************** + * TDistilSink implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TDistilSink::TDistilSink(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TDistilSink::getInput(void) +{ + std::vector in; + + in.push_back(par().perambulator); + in.push_back(par().eigenPack); + + return in; +} + +template +std::vector TDistilSink::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TDistilSink::setup(void) +{ + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int Nt_inv=par().Nt_inv; + + envCreate(std::vector, getName(), 1, + nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); + + GridCartesian * grid4d = env().getGrid(); + std::vector latt_size = GridDefaultLatt(); + std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); + std::vector mpi_layout = GridDefaultMpi(); + std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); + latt_size[Nd-1] = 1; + simd_layout_3.push_back( 1 ); + mpi_layout[Nd-1] = 1; + GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + + + envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TDistilSink::execute(void) +{ + + auto &perambulator = envGet(Perambulator, par().perambulator); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + auto &phi = envGet(std::vector, getName()); + + envGetTmp(LatticeSpinColourVector, tmp2); + envGetTmp(LatticeColourVector, tmp_nospin); + envGetTmp(LatticeSpinColourVector, tmp3d); + envGetTmp(LatticeColourVector, tmp3d_nospin); + envGetTmp(LatticeSpinColourVector, sink_tslice); + envGetTmp(LatticeColourVector, evec3d); + + GridCartesian * grid4d = env().getGrid(); + + int Ntlocal = grid4d->LocalDimensions()[3]; + int Ntfirst = grid4d->LocalStarts()[3]; + + int tsrc=par().tsrc; + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI + int Nt=par().Nt; + int TI=par().TI; + int nvec=par().nvec; + + bool full_tdil=(TI==Nt); + + int vecindex; + int t_inv; + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < Ns; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + phi[vecindex] = zero; + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + sink_tslice=zero; + for (int ivec = 0; ivec < nvec; ivec++) { + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); + } + InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); + } + } + } + } + } + + std::cout << "size phi" << phi.size() << std::endl; + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_DistilSink_hpp_ diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.cc b/Hadrons/Modules/MDistil/PerambFromSolve.cc new file mode 100644 index 00000000..80d89aa2 --- /dev/null +++ b/Hadrons/Modules/MDistil/PerambFromSolve.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TPerambFromSolve; diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp new file mode 100644 index 00000000..9cc263e8 --- /dev/null +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -0,0 +1,178 @@ +#ifndef Hadrons_MDistil_PerambFromSolve_hpp_ +#define Hadrons_MDistil_PerambFromSolve_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * PerambFromSolve * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) +/* +struct DistilParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, TI, + int, LI, + int, nnoise, + int, tsrc, + int, SI, + int, Ns, + int, Nt, + int, Nt_inv) + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} +}; +*/ + +class PerambFromSolvePar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(PerambFromSolvePar, + std::string, eigenPack, + std::string, PerambFileName, + std::string, solve, + int, nvec, + DistilParameters, Distil); +}; + +template +class TPerambFromSolve: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl,); +public: + // constructor + TPerambFromSolve(const std::string name); + // destructor + virtual ~TPerambFromSolve(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(PerambFromSolve, TPerambFromSolve, MDistil); + +/****************************************************************************** + * TPerambFromSolve implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TPerambFromSolve::TPerambFromSolve(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TPerambFromSolve::getInput(void) +{ + std::vector in; + + in.push_back(par().solve); + in.push_back(par().eigenPack); + + return in; +} + +template +std::vector TPerambFromSolve::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TPerambFromSolve::setup(void) +{ + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; + const int LI{Distil.LI}; + const int nnoise{Distil.nnoise}; + const int Nt_inv{Distil.Nt_inv}; + const int Ns{Distil.Ns}; + std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + + envCreate(Perambulator, getName(), 1, + sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); + envCreate(std::vector, getName() + "_noise", 1, + nvec*Distil.Ns*Distil.Nt*Distil.nnoise); + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TPerambFromSolve::execute(void) +{ + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; + const int LI{Distil.LI}; + const int TI{Distil.TI}; + const int nnoise{Distil.nnoise}; + const int Nt{Distil.Nt}; + const int Nt_inv{Distil.Nt_inv}; + const int tsrc{Distil.tsrc}; + const int Ns{Distil.Ns}; + const bool full_tdil{TI==Nt}; + const bool exact_distillation{full_tdil && LI==nvec}; + auto &perambulator = envGet(Perambulator, + getName()); + auto &solve = envGet(std::vector, par().solve); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + + envGetTmp(LatticeColourVector, result_nospin); + envGetTmp(LatticeColourVector, result_3d); + envGetTmp(LatticeColourVector, evec3d); + + GridCartesian * grid4d = env().getGrid(); + + int Ntlocal = grid4d->LocalDimensions()[3]; + int Ntfirst = grid4d->LocalStarts()[3]; + + const std::string &PerambFileName{par().PerambFileName}; + + + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < Ns; ds++) { + for (int is = 0; is < Ns; is++) { + result_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); + for (int ivec = 0; ivec < nvec; ivec++) { + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); + std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; + } + } + } + } + } + } + } + + if(PerambFileName.length()) + perambulator.WriteBinary(PerambFileName); + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_PerambFromSolve_hpp_ From e63019ac50b45c46203b678652c9f8349f0b1f43 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 8 Mar 2019 00:01:45 +0000 Subject: [PATCH 152/347] Tensor serialisation is fully functional --- Grid/serialisation/BaseIO.h | 4 +- Grid/serialisation/XmlIO.h | 40 ++++++- Grid/util/{Eigen.h => EigenUtil.h} | 169 ++++++++++------------------- tests/IO/Test_serialisation.cc | 28 ++--- 4 files changed, 107 insertions(+), 134 deletions(-) rename Grid/util/{Eigen.h => EigenUtil.h} (50%) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 043762ea..2a780fb4 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -316,7 +316,7 @@ namespace Grid { pWriteBuffer = getFirstScalar( output ); } else { // Regardless of the Eigen::Tensor storage order, the copy will be Row Major - pCopyBuffer = static_cast(malloc(TotalNumElements * sizeof(Scalar))); + pCopyBuffer = new Scalar[TotalNumElements]; pWriteBuffer = pCopyBuffer; Scalar * pCopy = pCopyBuffer; std::array MyIndex; @@ -330,7 +330,7 @@ namespace Grid { } } upcast->template writeMultiDim(s, TotalDims, pWriteBuffer, TotalNumElements); - if( pCopyBuffer ) free( pCopyBuffer ); + if( pCopyBuffer ) delete [] pCopyBuffer; } template diff --git a/Grid/serialisation/XmlIO.h b/Grid/serialisation/XmlIO.h index af3d5d76..40d5f2bb 100644 --- a/Grid/serialisation/XmlIO.h +++ b/Grid/serialisation/XmlIO.h @@ -138,15 +138,30 @@ namespace Grid { push(s); size_t count = 1; - const size_t Rank = Dimensions.size(); + const int Rank = static_cast( Dimensions.size() ); write("rank", Rank ); + std::vector MyIndex( Rank ); for( auto d : Dimensions ) { write("dim", d); count *= d; } assert( count == NumElements && "XmlIO : element count doesn't match dimensions" ); - while (NumElements--) + static const char sName[] = "tensor"; + for( int i = 0 ; i < Rank ; i++ ) { + MyIndex[i] = 0; + push(sName); + } + while (NumElements--) { write("elem", *pDataRowMajor++); + int i; + for( i = Rank - 1 ; i != -1 && ++MyIndex[i] == Dimensions[i] ; i-- ) + MyIndex[i] = 0; + int Rollover = Rank - 1 - i; + for( i = 0 ; i < Rollover ; i++ ) + pop(); + for( i = 0 ; NumElements && i < Rollover ; i++ ) + push(sName); + } pop(); } @@ -189,7 +204,9 @@ namespace Grid std::cout << GridLogWarning << "XML: cannot open node '" << s << "'"; std::cout << std::endl; } else { - size_t Rank; + static const char sName[] = "tensor"; + static const char sNameDone[] = "tensor-done"; + int Rank; read("rank", Rank); dim.resize( Rank ); size_t NumElements = 1; @@ -200,10 +217,27 @@ namespace Grid NumElements *= d; } buf.resize( NumElements ); + std::vector MyIndex( Rank ); + for( int i = 0 ; i < Rank ; i++ ) { + MyIndex[i] = 0; + push(sName); + } + for( auto &x : buf ) { + NumElements--; read("elem", x); node_.child("elem").set_name("elem-done"); + int i; + for( i = Rank - 1 ; i != -1 && ++MyIndex[i] == dim[i] ; i-- ) + MyIndex[i] = 0; + int Rollover = Rank - 1 - i; + for( i = 0 ; i < Rollover ; i++ ) { + node_.set_name(sNameDone); + pop(); + } + for( i = 0 ; NumElements && i < Rollover ; i++ ) + push(sName); } pop(); } diff --git a/Grid/util/Eigen.h b/Grid/util/EigenUtil.h similarity index 50% rename from Grid/util/Eigen.h rename to Grid/util/EigenUtil.h index 9af510fe..c6f10276 100644 --- a/Grid/util/Eigen.h +++ b/Grid/util/EigenUtil.h @@ -2,7 +2,7 @@ Grid physics library, www.github.com/paboyle/Grid - Source file: Grid/util/Eigen.h + Source file: Grid/util/EigenUtil.h Copyright (C) 2019 @@ -25,8 +25,8 @@ See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#ifndef GRID_UTIL_EIGEN_H -#define GRID_UTIL_EIGEN_H +#ifndef GRID_UTIL_EIGENUTIL_H +#define GRID_UTIL_EIGENUTIL_H #include #include @@ -35,25 +35,28 @@ namespace Grid { template typename std::enable_if::value, void>::type for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, - std::array::Rank> &MyIndex) + const std::array &MyIndex, + const std::array::Rank> &DimGridTensor, + std::array::Rank> &GridTensorIndex) { - lambda( scalar, Seq++, MyIndex ); + lambda( scalar, Seq++, MyIndex, GridTensorIndex ); } // for_all helper function to call the lambda for container template typename std::enable_if::value, void>::type for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &container, typename ETensor::Index &Seq, - std::array::Rank> &MyIndex) + const std::array &MyIndex, + const std::array::Rank> &DimGridTensor, + std::array::Rank> &GridTensorIndex) { using Traits = EigenIO::Traits; - const auto rank{ETensor::NumIndices}; const auto InnerRank = Traits::Rank; for( typename Traits::scalar_type &Source : container ) { - lambda(Source, Seq++, MyIndex ); + lambda(Source, Seq++, MyIndex, GridTensorIndex ); // Now increment SubIndex - for( auto i = InnerRank - 1; i != -1 && ++MyIndex[rank + i] == Traits::Dimension(i); i-- ) - MyIndex[rank + i] = 0; + for( auto i = InnerRank - 1; i != -1 && ++GridTensorIndex[i] == DimGridTensor[i]; i-- ) + GridTensorIndex[i] = 0; } } @@ -65,58 +68,51 @@ namespace Grid { for_all( ETensor &ET, Lambda lambda ) { using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later - using Traits = EigenIO::Traits; - const std::size_t NumScalars = ET.size(); - assert( NumScalars > 0 ); using Index = typename ETensor::Index; + using Traits = EigenIO::Traits; + // Check that the number of elements in the container is the product of tensor dimensions + const Index NumScalars = ET.size(); + assert( NumScalars > 0 && "EigenUtil: tensor has no elements" ); Index ScalarElementCount{1}; - const auto InnerRank = Traits::Rank; const auto rank{ETensor::NumIndices}; - std::array Dims; - for(auto i = 0; i < rank; i++ ) { - auto dim = ET.dimension(i); - assert( dim > 0 ); - Dims[i] = static_cast(dim); - assert( Dims[i] == dim ); // check we didn't lose anything in the conversion - ScalarElementCount *= Dims[i]; + std::array DimTensor, MyIndex; + for(int i = 0; i < rank; i++ ) { + DimTensor[i] = ET.dimension(i); + ScalarElementCount *= DimTensor[i]; + MyIndex[i] = 0; } - // Check that the number of containers is correct ... and we didn't lose anything in conversions - assert( NumScalars == ScalarElementCount ); - // If the Scalar is actually a container, add the inner Scalar's dimensions - size_t InnerScalarCount{1}; - for(auto i = 0; i < InnerRank; i++ ) { - auto dim = Traits::Dimension(i); - assert( dim > 0 ); - Dims[rank + i] = static_cast(dim); - assert( Dims[rank + i] == dim ); // check we didn't lose anything in the conversion - InnerScalarCount *= dim; + assert( NumScalars == ScalarElementCount && "EigenUtil: tensor size not product of dimensions" ); + // Save the GridTensor dimensions + const auto InnerRank{Traits::Rank}; + std::array DimGridTensor, GridTensorIndex; + for(int i = 0; i < InnerRank; i++ ) { + DimGridTensor[i] = Traits::Dimension(i); + GridTensorIndex[i] = 0; } - assert(Traits::count == InnerScalarCount); - std::array MyIndex; - for( auto &idx : MyIndex ) idx = 0; + // Now walk the tensor in memory order Index Seq = 0; Scalar * pScalar = ET.data(); - for( std::size_t j = 0; j < NumScalars; j++ ) { - for_all_do_lambda( lambda, * pScalar, Seq, MyIndex ); + for( Index j = 0; j < NumScalars; j++ ) { + for_all_do_lambda( lambda, * pScalar, Seq, MyIndex, DimGridTensor, GridTensorIndex ); // Now increment the index to pass to the lambda (bearing in mind we're walking in memory order) if( ETensor::Options & Eigen::RowMajor ) { - for( auto i = rank - 1; i != -1 && ++MyIndex[i] == Dims[i]; i-- ) + for( auto i = rank - 1; i != -1 && ++MyIndex[i] == DimTensor[i]; i-- ) MyIndex[i] = 0; } else { - for( auto i = 0; i < rank && ++MyIndex[i] == Dims[i]; i++ ) + for( auto i = 0; i < rank && ++MyIndex[i] == DimTensor[i]; i++ ) MyIndex[i] = 0; - size_t NewSeq = 0; - for( auto i = 0; i < rank + InnerRank ; i++ ) { - NewSeq *= Dims[i]; - NewSeq += MyIndex[i]; + Seq = 0; + for( auto i = 0; i < rank; i++ ) { + Seq *= DimTensor[i]; + Seq += MyIndex[i]; } - Seq = static_cast( NewSeq ); + Seq *= Traits::count; } pScalar++; } } - // Sequential initialisation of tensors + // Sequential initialisation of tensors up to specified precision // Would have preferred to define template variables for this, but that's c++ 17 template typename std::enable_if::value && !EigenIO::Traits::is_complex>::type @@ -124,11 +120,13 @@ namespace Grid { { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; - for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + using Index = typename ETensor::Index; + for_all( ET, [&](scalar_type &c, Index n, const std::array &TensorIndex, + const std::array &ScalarIndex ) { scalar_type x = Inc * static_cast(n); if( Precision ) { std::stringstream s; - s << std::scientific << std::setprecision(Precision) << x; + s << std::setprecision(Precision) << x; s >> x; } c = x; @@ -141,7 +139,9 @@ namespace Grid { { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; - for_all( ET, [&](scalar_type &c, typename ETensor::Index n, const std::array &Dims ) { + using Index = typename ETensor::Index; + for_all( ET, [&](scalar_type &c, Index n, const std::array &TensorIndex, + const std::array &ScalarIndex ) { auto re = Inc.real(); auto im = Inc.imag(); re *= n; @@ -159,24 +159,28 @@ namespace Grid { } // Helper to dump a tensor -#ifdef DEBUG -#define dump_tensor(args...) dump_tensor_func(args) template typename std::enable_if::value, void>::type - dump_tensor_func(T &t, const char * pName = nullptr) + dump_tensor(T &t, const char * pName = nullptr) { using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + using Index = typename T::Index; const auto rank{T::NumIndices}; const auto &dims = t.dimensions(); - std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; + std::cout << "Dumping rank " << rank + Traits::Rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; if( pName ) std::cout << pName; - for( auto i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; + for( int i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; + for( int i = 0 ; i < Traits::Rank; i++ ) std::cout << "(" << Traits::Dimension(i) << ")"; std::cout << " in memory order:" << std::endl; - for_all( t, [&](typename Traits::scalar_type &c, typename T::Index index, const std::array &Dims ){ + for_all( t, [&](scalar_type &c, Index n, const std::array &TensorIndex, + const std::array &ScalarIndex ){ std::cout << " "; - for( auto dim : Dims ) + for( auto dim : TensorIndex ) std::cout << "[" << dim << "]"; + for( auto dim : ScalarIndex ) + std::cout << "(" << dim << ")"; std::cout << " = " << c << std::endl; } ); std::cout << "========================================" << std::endl; @@ -184,64 +188,11 @@ namespace Grid { template typename std::enable_if::value, void>::type - dump_tensor_func(T &t, const char * pName = nullptr) + dump_tensor(T &t, const char * pName = nullptr) { std::cout << "Dumping non-tensor object "; - if( pName ) - std::cout << pName; + if( pName ) std::cout << pName; std::cout << "=" << t; } - - // Helper to dump a tensor in memory order - // Kind of superfluous given the above ... just keeping in case I need to fall back to this -#define DumpMemoryOrder(args...) DumpMemoryOrder_func(args) - template - typename std::enable_if::value, void>::type - DumpMemoryOrder_func(T &t, const char * pName = nullptr) - { - const auto rank = t.rank(); - const auto &dims = t.dimensions(); - std::cout << "Dumping rank " << rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; - if( pName ) - std::cout << pName; - for( auto d : dims ) std::cout << "[" << d << "]"; - std::cout << " in memory order:" << std::endl; - const typename T::Scalar * p = t.data(); - const auto size = t.size(); - const typename T::Scalar * pEnd = p + size; - if( rank <= 2 ) { - for( unsigned int i = 0 ; i < t.size() ; i++ ) - std::cout << "[" << i << "]=" << *p++ << " "; - std::cout << std::endl; - } else { - const auto innersize = dims[rank-2] * dims[rank-1]; - using Index = typename T::Index; - std::vector idx(rank - 2); - for( auto &i : idx ) i = 0; - Index idxCounter = 0; - while( p < pEnd ) { - if( T::Options & Eigen::RowMajor ) { - if( pName ) - std::cout << pName; - idxCounter = 0; - for(auto i = 0 ; i < rank - 2 ; i++) - std::cout << "[" << idx[i] << "]:"; - } - for( unsigned int i = 0 ; i < innersize ; i++ ) - std::cout << " [" << idxCounter++ << "]=" << *p++; - if( T::Options & Eigen::RowMajor ) - std::cout << std::endl; - // Now increment MyIndex - for( auto i = rank - 3; i != -1 && ++idx[i] == dims[i]; i-- ) - idx[i] = 0; - } - if( ! ( T::Options & Eigen::RowMajor ) ) - std::cout << std::endl; - } - } -#else -#define dump_tensor(args...) -#define DumpMemoryOrder(args...) -#endif } #endif diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 56cdcb4b..229e7f12 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -29,7 +29,7 @@ Author: Michael Marshall *************************************************************************************/ /* END LEGAL */ #include -#include +#include using namespace Grid; using namespace Grid::QCD; @@ -158,8 +158,8 @@ public: template void EigenTensorTestSingle(const char * MyTypeName, typename EigenIO::Traits::scalar_type Flag, - unsigned short Precision, std::string &filename, const char * pszExtension, unsigned int &TestNum, - IndexTypes... otherDims) + unsigned short Precision, std::string &filename, const char * pszExtension, + unsigned int &TestNum, IndexTypes... otherDims) { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; @@ -187,15 +187,17 @@ void EigenTensorTest(const char * pszExtension, unsigned short Precision = 0) EigenTensorTestSingle(TEST_PARAMS( Tensor_9_4_2 )); { unsigned short Flag = 1; - TensorRank5UShort t; EigenTensorTestSingle(TEST_PARAMS( TensorRank5UShort )); std::cout << " Testing alternate memory order read ... "; TensorRank5UShortAlt t2; RDR_ reader(filename); read(reader, "TensorRank5UShort", t2); bool good = true; - for_all( t2, [&](unsigned short c, unsigned short n, - const std::array &Dims ) { + using Index = typename TensorRank5UShortAlt::Index; + // NB: I can't call + for_all( t2, [&](unsigned short c, Index n, + const std::array &TensorIndex, + const std::array::Rank> &GridTensorIndex ){ good = good && ( c == n ); } ); if (!good) { @@ -240,20 +242,6 @@ void tensorConvTestFn(GridSerialRNG &rng, const std::string label) int main(int argc,char **argv) { - { - LSCTensor Bingo; - constexpr Complex Flag{1,-3.1415927}; - Complex z{0}; - SpinColourVector * pV = Bingo.data(); - for( std::size_t i = Bingo.size(); i--; ) { - for( typename GridTypeMapper::scalar_type &s : *pV++ ) { - s = z; - z += Flag; - } - } - dump_tensor( Bingo ); - } - Grid_init(&argc,&argv); std::cout << std::boolalpha << "==== basic IO" << std::endl; // display true / false for boolean From 2d659015ffbaabc1905ee1e85b270a7715242d72 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Fri, 8 Mar 2019 00:30:43 +0000 Subject: [PATCH 153/347] Serialisation is fully functional. Ready for review. --- tests/IO/Test_serialisation.cc | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 229e7f12..7a6b33cd 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -156,6 +156,7 @@ public: #define TEST_PARAMS( T ) #T, Flag, Precision, filename, pszExtension, TestNum +// Perform an I/O test for a single Eigen tensor (of any type) template void EigenTensorTestSingle(const char * MyTypeName, typename EigenIO::Traits::scalar_type Flag, unsigned short Precision, std::string &filename, const char * pszExtension, @@ -169,9 +170,11 @@ void EigenTensorTestSingle(const char * MyTypeName, typename EigenIO::Traits: ioTest(filename, * pTensor, MyTypeName, MyTypeName); } +// Perform a series of I/O tests for Eigen tensors, including a serialisable object template void EigenTensorTest(const char * pszExtension, unsigned short Precision = 0) { + // Perform a series of tests on progressively more complex tensors unsigned int TestNum = 0; std::string filename; { @@ -185,6 +188,8 @@ void EigenTensorTest(const char * pszExtension, unsigned short Precision = 0) EigenTensorTestSingle( TEST_PARAMS( TensorSimple ), 1, 1, 1, 1, 1, 1 ); EigenTensorTestSingle( TEST_PARAMS( TensorRank3 ), 6, 3, 2 ); EigenTensorTestSingle(TEST_PARAMS( Tensor_9_4_2 )); + EigenTensorTestSingle(TEST_PARAMS( LSCTensor )); + // Now see whether we could write out a tensor in one memory order and read back in the other { unsigned short Flag = 1; EigenTensorTestSingle(TEST_PARAMS( TensorRank5UShort )); @@ -207,18 +212,21 @@ void EigenTensorTest(const char * pszExtension, unsigned short Precision = 0) } std::cout << " done." << std::endl; } - EigenTensorTestSingle(TEST_PARAMS( LSCTensor )); + // Now test a serialisable object containing a number of tensors { static const char MyTypeName[] = "PerambIOTestClass"; std::unique_ptr pObj{new PerambIOTestClass()}; filename = "iotest_" + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; ioTest(filename, * pObj, MyTypeName, MyTypeName); } -#ifdef STRESS_TESTS - using LCMTensor = Eigen::TensorFixedSize,2>,7>,3>, - Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor>; - std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; - EigenTensorTestSingle(TEST_PARAMS( LCMTensor )); + // Stress test. Too large for the XML or text readers and writers! +#ifdef STRESS_TEST + if( typeid( WTR_ ).name() == typeid( Hdf5Writer ).name() || typeid( WTR_ ).name() == typeid( BinaryWriter ).name() ) { + using LCMTensor=Eigen::TensorFixedSize,2>,7>,3>, + Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor>; + std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; + EigenTensorTestSingle(TEST_PARAMS( LCMTensor )); + } #endif } From 4a70b2ffd44447e5b10b37504d5e7c90500958de Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 8 Mar 2019 12:23:22 +0000 Subject: [PATCH 154/347] Aslash insertions work now? --- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 44 ++++++++++++++++++--- Hadrons/Modules/MDistil/PerambLight.hpp | 2 +- tests/hadrons/Test_hadrons_distil.cc | 43 ++++++++++---------- 4 files changed, 64 insertions(+), 27 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 22ca8526..b9b28cfe 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -239,7 +239,7 @@ void TLapEvec::execute(void) auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); FieldMetaData header; - if((0)) { + if((1)) { const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(gridHD); pRNG4d.SeedFixedIntegers(seeds); diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 9cc263e8..413a5d26 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -50,11 +50,10 @@ class TPerambFromSolve: public Module { public: FERM_TYPE_ALIASES(FImpl,); -public: // constructor TPerambFromSolve(const std::string name); // destructor - virtual ~TPerambFromSolve(void) {}; + virtual ~TPerambFromSolve(void); // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); @@ -62,6 +61,12 @@ public: virtual void setup(void); // execution virtual void execute(void); +protected: + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; +protected: + virtual void Cleanup(void); + }; MODULE_REGISTER_TMP(PerambFromSolve, TPerambFromSolve, MDistil); @@ -72,8 +77,15 @@ MODULE_REGISTER_TMP(PerambFromSolve, TPerambFromSolve, MDistil); // constructor ///////////////////////////////////////////////////////////////// template TPerambFromSolve::TPerambFromSolve(const std::string name) -: Module(name) +:grid3d{nullptr}, grid4d{nullptr}, Module(name) {} +//destructor +template +TPerambFromSolve::~TPerambFromSolve(void) +{ + Cleanup(); +}; + // dependencies/products /////////////////////////////////////////////////////// template @@ -99,7 +111,9 @@ std::vector TPerambFromSolve::getOutput(void) template void TPerambFromSolve::setup(void) { - const int nvec{par().nvec}; + Cleanup(); + + const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; const int LI{Distil.LI}; const int nnoise{Distil.nnoise}; @@ -107,13 +121,33 @@ void TPerambFromSolve::setup(void) const int Ns{Distil.Ns}; std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + grid4d = env().getGrid(); + grid3d = MakeLowerDimGrid(grid4d); + + envCreate(Perambulator, getName(), 1, sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); - + + envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + envTmpLat(LatticeColourVector, "result_nospin"); + + } +template +void TPerambFromSolve::Cleanup(void) +{ + if( grid3d != nullptr ) { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; +} + + // execution /////////////////////////////////////////////////////////////////// template void TPerambFromSolve::execute(void) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index b11c0e5a..78af7757 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -212,7 +212,7 @@ void TPerambLight::execute(void) envGetTmp(GaugeField, Umu); FieldMetaData header; - if((0)){ + if((1)){ const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(grid4d); pRNG4d.SeedFixedIntegers(seeds); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index cb97d727..301a7d3f 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -203,7 +203,7 @@ void test_DistilVectorsS(Application &application) DistilVecPar.TI=32; DistilVecPar.nvec=3; DistilVecPar.Ns=4; - DistilVecPar.Nt=32; + DistilVecPar.Nt=8; DistilVecPar.Nt_inv=1; application.createModule("DistilVecsS",DistilVecPar); } @@ -430,10 +430,31 @@ void test_AslashSeq(Application &application) A2AAslashVectorsPar.vector="PerambS_unsmeared_sink"; A2AAslashVectorsPar.emField="Em"; A2AAslashVectorsPar.solver="CG_s"; - A2AAslashVectorsPar.output="Aslash_seq"; + A2AAslashVectorsPar.output="AslashSeq"; application.createModule("Aslash_seq",A2AAslashVectorsPar); } ///////////////////////////////////////////////////////////// +// Aslash_perambulators +///////////////////////////////////////////////////////////// +void test_PerambulatorsSolve(Application &application) +{ + // PerambLight parameters + MDistil::PerambFromSolve::Par PerambFromSolvePar; + PerambFromSolvePar.eigenPack="LapEvec"; + PerambFromSolvePar.solve="Aslash_seq"; + PerambFromSolvePar.PerambFileName="perambAslashS.bin"; + PerambFromSolvePar.Distil.tsrc = 0; + PerambFromSolvePar.Distil.nnoise = 1; + PerambFromSolvePar.Distil.LI=3; + PerambFromSolvePar.Distil.SI=4; + PerambFromSolvePar.Distil.TI=8; + PerambFromSolvePar.nvec=3; + PerambFromSolvePar.Distil.Ns=4; + PerambFromSolvePar.Distil.Nt=8; + PerambFromSolvePar.Distil.Nt_inv=1; + application.createModule("PerambAslashS",PerambFromSolvePar); +} +///////////////////////////////////////////////////////////// // DistilVectors ///////////////////////////////////////////////////////////// @@ -454,24 +475,6 @@ void test_DistilVectorsAslashSeq(Application &application) DistilSinkPar.Nt_inv=1; application.createModule("DistilVecsAslashSeq",DistilSinkPar); } -void test_PerambulatorsSolve(Application &application) -{ - // PerambLight parameters - MDistil::PerambFromSolve::Par PerambFromSolvePar; - PerambFromSolvePar.eigenPack="LapEvec"; - PerambFromSolvePar.solve="Aslash_seq"; - PerambFromSolvePar.PerambFileName="perambAslashS.bin"; - PerambFromSolvePar.Distil.tsrc = 0; - PerambFromSolvePar.Distil.nnoise = 1; - PerambFromSolvePar.Distil.LI=3; - PerambFromSolvePar.Distil.SI=4; - PerambFromSolvePar.Distil.TI=8; - PerambFromSolvePar.nvec=3; - PerambFromSolvePar.Distil.Ns=4; - PerambFromSolvePar.Distil.Nt=8; - PerambFromSolvePar.Distil.Nt_inv=1; - application.createModule("PerambAslashS",PerambFromSolvePar); -} ///////////////////////////////////////////////////////////// // MesonFields - aslaaaash ///////////////////////////////////////////////////////////// From 64ba6646376e5c132cbe184c228c6ffc07ddf046 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 8 Mar 2019 12:25:00 +0000 Subject: [PATCH 155/347] changed debug options --- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index b9b28cfe..22ca8526 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -239,7 +239,7 @@ void TLapEvec::execute(void) auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); FieldMetaData header; - if((1)) { + if((0)) { const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(gridHD); pRNG4d.SeedFixedIntegers(seeds); diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 78af7757..b11c0e5a 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -212,7 +212,7 @@ void TPerambLight::execute(void) envGetTmp(GaugeField, Umu); FieldMetaData header; - if((1)){ + if((0)){ const std::vector seeds({1, 2, 3, 4, 5}); GridParallelRNG pRNG4d(grid4d); pRNG4d.SeedFixedIntegers(seeds); From 2df396380dd1013f7779e0fdc440cfe894774ecf Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 8 Mar 2019 16:28:21 +0000 Subject: [PATCH 156/347] solver is now external --- Hadrons/Modules/MDistil/PerambLight.hpp | 62 ++++++++++++++++++------- tests/hadrons/Test_hadrons_distil.cc | 22 +++++---- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 78af7757..20645025 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -34,7 +34,7 @@ struct DistilParameters: Serializable { template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} }; */ -struct SolverParameters: Serializable { +/*struct SolverParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(SolverParameters, double, CGPrecision, int, MaxIterations, @@ -42,7 +42,7 @@ struct SolverParameters: Serializable { double, M5) SolverParameters() = default; template SolverParameters(Reader& Reader){read(Reader,"Solver",*this);} -}; +};*/ class PerambLightPar: Serializable { @@ -55,9 +55,10 @@ public: std::string, UniqueIdentifier, bool, multiFile, int, nvec, - int, Ls, // For makeFiveDimGrid +// int, Ls, // For makeFiveDimGrid DistilParameters, Distil, - SolverParameters, Solver); + std::string, solver); +// SolverParameters, Solver); }; template @@ -65,6 +66,7 @@ class TPerambLight: public Module { public: FERM_TYPE_ALIASES(FImpl,); + SOLVER_TYPE_ALIASES(FImpl,); // constructor TPerambLight(const std::string name); // destructor @@ -80,9 +82,10 @@ protected: // These variables are created in setup() and freed in Cleanup() GridCartesian * grid3d; // Owned by me, so I must delete it GridCartesian * grid4d; // Owned by environment (so I won't delete it) - protected: virtual void Cleanup(void); +private: + unsigned int Ls_; }; MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); @@ -110,6 +113,7 @@ std::vector TPerambLight::getInput(void) std::vector in; in.push_back(par().eigenPack); + in.push_back(par().solver); return in; } @@ -164,6 +168,12 @@ void TPerambLight::setup(void) envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + + Ls_ = env().getObjectLs(par().solver); + envTmpLat(FermionField, "v4dtmp"); + envTmpLat(FermionField, "v5dtmp", Ls_); + envTmpLat(FermionField, "v5dtmp_sol", Ls_); + } // clean up any temporaries created by setup (that aren't stored in the environment) @@ -183,7 +193,7 @@ void TPerambLight::execute(void) { const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; - const SolverParameters & Solver{par().Solver}; + //const SolverParameters & Solver{par().Solver}; const int LI{Distil.LI}; //const int SI{Distil.SI}; const int TI{Distil.TI}; @@ -192,9 +202,15 @@ void TPerambLight::execute(void) const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; const int tsrc{Distil.tsrc}; const int Ns{Distil.Ns}; - - const Real mass{Solver.mass}; - const Real M5 {Solver.M5}; + + auto &solver=envGet(Solver, par().solver); + auto &mat = solver.getFMat(); + envGetTmp(FermionField, v4dtmp); + envGetTmp(FermionField, v5dtmp); + envGetTmp(FermionField, v5dtmp_sol); + + //const Real mass{Solver.mass}; + //const Real M5 {Solver.M5}; const bool full_tdil{TI==Nt}; const bool exact_distillation{full_tdil && LI==nvec}; @@ -250,7 +266,7 @@ void TPerambLight::execute(void) //std::cout << pszGaugeConfigFile << std::endl; //GridSerialRNG sRNG; sRNG.SeedUniqueString(std::string(pszGaugeConfigFile)); GridSerialRNG sRNG; - sRNG.SeedUniqueString(ConfigFileName + "_" + std::to_string(mass) + "_" + UniqueIdentifier); + sRNG.SeedUniqueString(ConfigFileName + "_" + UniqueIdentifier); Real rn; for (int inoise=0;inoise::execute(void) GridRedBlackCartesian RBGrid(grid4d); std::cout << "init RBG done" << std::endl; - const int Ls{par().Ls}; - const double CGPrecision{Solver.CGPrecision}; - const int MaxIterations {Solver.MaxIterations}; + //const int Ls{par().Ls}; + //const double CGPrecision{Solver.CGPrecision}; + //const int MaxIterations {Solver.MaxIterations}; { - GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,grid4d); + /*GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,grid4d); GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,grid4d); typedef DomainWallFermionR FermionAction; @@ -323,7 +339,7 @@ void TPerambLight::execute(void) LatticeSpinColourVector a(grid4d); LatticeColourVector b(grid4d); b= peekSpin(a,0); - +*/ int t_inv; for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { @@ -351,11 +367,21 @@ void TPerambLight::execute(void) } std::cout << "Inversion for noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; result=zero; - LatticeFermion src5(FGrid); + /*LatticeFermion src5(FGrid); LatticeFermion sol5(FGrid); Dop.ImportPhysicalFermionSource(dist_source,src5); SchurSolver(Dop,src5,sol5); Dop.ExportPhysicalFermionSolution(sol5,result); //These are the meson sinks + */ + v4dtmp = dist_source; + if (Ls_ == 1){ + solver(result, v4dtmp); + } else { + mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); + solver(v5dtmp_sol, v5dtmp); + mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); + result = v4dtmp; + } if ((1)) // comment out if unsmeared sink is too large??? unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; @@ -375,8 +401,8 @@ void TPerambLight::execute(void) } } // Kill our 5 dimensional grid (avoid leaks). Should really declare these objects temporary - delete FrbGrid; - delete FGrid; + //delete FrbGrid; + //delete FGrid; } std::cout << "perambulator done" << std::endl; perambulator.SliceShare( grid3d, grid4d ); diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 301a7d3f..9727568e 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -122,6 +122,7 @@ void test_Perambulators(Application &application) PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.solver="CG_s"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; PerambPar.Distil.LI=5; @@ -131,11 +132,11 @@ void test_Perambulators(Application &application) PerambPar.Distil.Ns=4; PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; - PerambPar.Solver.mass=0.005; - PerambPar.Solver.M5=1.8; - PerambPar.Ls=16; - PerambPar.Solver.CGPrecision=1e-8; - PerambPar.Solver.MaxIterations=10000; + //PerambPar.Solver.mass=0.005; + //PerambPar.Solver.M5=1.8; + //PerambPar.Ls=16; + //PerambPar.Solver.CGPrecision=1e-8; + //PerambPar.Solver.MaxIterations=10000; application.createModule("Peramb",PerambPar); } ///////////////////////////////////////////////////////////// @@ -169,6 +170,7 @@ void test_PerambulatorsS(Application &application) PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.solver="CG_s"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; PerambPar.Distil.LI=3; @@ -178,11 +180,11 @@ void test_PerambulatorsS(Application &application) PerambPar.Distil.Ns=4; PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; - PerambPar.Solver.mass=0.005; //strange mass??? - PerambPar.Solver.M5=1.8; - PerambPar.Ls=16; - PerambPar.Solver.CGPrecision=1e-8; - PerambPar.Solver.MaxIterations=10000; + //PerambPar.Solver.mass=0.005; //strange mass??? + //PerambPar.Solver.M5=1.8; + //PerambPar.Ls=16; + //PerambPar.Solver.CGPrecision=1e-8; + //PerambPar.Solver.MaxIterations=10000; application.createModule("PerambS",PerambPar); } ///////////////////////////////////////////////////////////// From 5fb2ee89bbbfb2261b9fceb61dc7a347387d1b5c Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 8 Mar 2019 16:50:21 +0000 Subject: [PATCH 157/347] modified test so that it runs --- tests/hadrons/Test_hadrons_distil.cc | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 9727568e..87aa9d95 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -173,10 +173,10 @@ void test_PerambulatorsS(Application &application) PerambPar.solver="CG_s"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=3; + PerambPar.Distil.LI=5; PerambPar.Distil.SI=4; PerambPar.Distil.TI=8; - PerambPar.nvec=3; + PerambPar.nvec=5; PerambPar.Distil.Ns=4; PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; @@ -200,10 +200,10 @@ void test_DistilVectorsS(Application &application) DistilVecPar.eigenPack="LapEvec"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; - DistilVecPar.LI=3; + DistilVecPar.LI=5; DistilVecPar.SI=4; DistilVecPar.TI=32; - DistilVecPar.nvec=3; + DistilVecPar.nvec=5; DistilVecPar.Ns=4; DistilVecPar.Nt=8; DistilVecPar.Nt_inv=1; @@ -447,10 +447,10 @@ void test_PerambulatorsSolve(Application &application) PerambFromSolvePar.PerambFileName="perambAslashS.bin"; PerambFromSolvePar.Distil.tsrc = 0; PerambFromSolvePar.Distil.nnoise = 1; - PerambFromSolvePar.Distil.LI=3; + PerambFromSolvePar.Distil.LI=5; PerambFromSolvePar.Distil.SI=4; PerambFromSolvePar.Distil.TI=8; - PerambFromSolvePar.nvec=3; + PerambFromSolvePar.nvec=5; PerambFromSolvePar.Distil.Ns=4; PerambFromSolvePar.Distil.Nt=8; PerambFromSolvePar.Distil.Nt_inv=1; @@ -468,10 +468,10 @@ void test_DistilVectorsAslashSeq(Application &application) DistilSinkPar.eigenPack="LapEvec"; DistilSinkPar.tsrc = 0; DistilSinkPar.nnoise = 1; - DistilSinkPar.LI=3; + DistilSinkPar.LI=5; DistilSinkPar.SI=4; DistilSinkPar.TI=8; - DistilSinkPar.nvec=3; + DistilSinkPar.nvec=5; DistilSinkPar.Ns=4; DistilSinkPar.Nt=8; DistilSinkPar.Nt_inv=1; @@ -911,17 +911,20 @@ int main(int argc, char *argv[]) break; case 2: test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); break; case 3: // 3 test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); break; default: // 4 test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); @@ -930,6 +933,7 @@ int main(int argc, char *argv[]) break; case 5: // 3 test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); @@ -939,6 +943,7 @@ int main(int argc, char *argv[]) break; case 6: // 3 test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_g5_sinks( application ); @@ -946,6 +951,7 @@ int main(int argc, char *argv[]) break; case 7: // 3 test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); @@ -954,6 +960,7 @@ int main(int argc, char *argv[]) break; case 8: // 3 test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); @@ -961,10 +968,12 @@ int main(int argc, char *argv[]) break; case 9: // 3 test_Global( application ); + test_SolverS( application ); test_Baryon2pt( application ); break; case 10: // 3 test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_g5_sinks( application ); @@ -973,6 +982,7 @@ int main(int argc, char *argv[]) break; case 11: // 3 test_Global( application ); + test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); From c2a3231cdf8d3079928e8d1ba078252e855916d8 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 11 Mar 2019 18:05:39 +0000 Subject: [PATCH 158/347] added testing module for multiple perambulators --- Hadrons/Modules.hpp | 141 +++---- .../Modules/MDistil/PerambMultipleSolves.cc | 7 + .../Modules/MDistil/PerambMultipleSolves.hpp | 345 ++++++++++++++++++ Hadrons/modules.inc | 264 +++++++------- 4 files changed, 556 insertions(+), 201 deletions(-) create mode 100644 Hadrons/Modules/MDistil/PerambMultipleSolves.cc create mode 100644 Hadrons/Modules/MDistil/PerambMultipleSolves.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index a250d869..109717cc 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,84 +1,85 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include +#include #include +#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include #include -#include +#include #include -#include -#include +#include +#include #include -#include -#include -#include +#include +#include +#include +#include #include +#include #include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/Modules/MDistil/PerambMultipleSolves.cc b/Hadrons/Modules/MDistil/PerambMultipleSolves.cc new file mode 100644 index 00000000..b5b92315 --- /dev/null +++ b/Hadrons/Modules/MDistil/PerambMultipleSolves.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TPerambMultipleSolves; diff --git a/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp b/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp new file mode 100644 index 00000000..b3605e25 --- /dev/null +++ b/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp @@ -0,0 +1,345 @@ +#ifndef Hadrons_MDistil_PerambMultipleSolves_hpp_ +#define Hadrons_MDistil_PerambMultipleSolves_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation +#include + +BEGIN_HADRONS_NAMESPACE + + +/****************************************************************************** + * PerambMultipleSolves * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class PerambMultipleSolvesPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(PerambMultipleSolvesPar, + std::string, eigenPack, + std::string, PerambFileName, + std::string, ConfigFileDir, + std::string, ConfigFileName, + std::string, UniqueIdentifier, + int, nsolves, + std::vector, nvecs, + int, nvec, + bool, multiFile, + DistilParameters, Distil, + std::string, solver); +}; + +template +class TPerambMultipleSolves: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl,); + SOLVER_TYPE_ALIASES(FImpl,); + // constructor + TPerambMultipleSolves(const std::string name); + // destructor + virtual ~TPerambMultipleSolves(void); + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +protected: + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) +protected: + virtual void Cleanup(void); +private: + unsigned int Ls_; +}; + +MODULE_REGISTER_TMP(PerambMultipleSolves, TPerambMultipleSolves, MDistil); + +// constructor ///////////////////////////////////////////////////////////////// +template +TPerambMultipleSolves::TPerambMultipleSolves(const std::string name) +: grid3d{nullptr}, grid4d{nullptr}, Module(name) +{} + +// destructor +template +TPerambMultipleSolves::~TPerambMultipleSolves(void) +{ + Cleanup(); +}; + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TPerambMultipleSolves::getInput(void) +{ + std::vector in; + + in.push_back(par().eigenPack); + in.push_back(par().solver); + + return in; +} + +template +std::vector TPerambMultipleSolves::getOutput(void) +{ + std::vector out; + const int nsolves{par().nsolves}; + std::vector nvecs{par().nvecs}; + + for(int i=0;i +void TPerambMultipleSolves::setup(void) +{ + /* Cleanup(); + + const int nvec{par().nvec}; + // auto &noise = envGet(std::vector>>, par().noise); + const int nsolves{par().nsolves}; + std::vector nvecs{par().nvecs}; + const DistilParameters & Distil{par().Distil}; + const int LI{Distil.LI}; + const int nnoise{Distil.nnoise}; + const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; + const int Ns{Distil.Ns}; + std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + + envCreate(std::vector, getName() + "_noise", 1, + nvec*Distil.Ns*Distil.Nt*Distil.nnoise); + for(int i=0;i, getName() + "_solve_"+std::to_string(nvecs[i]), 1, + nnoise*nvecs[i]*Ns*Nt_inv, envGetGrid(FermionField)); + } + grid4d = env().getGrid(); + grid3d = MakeLowerDimGrid(grid4d);//new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + + envTmpLat(GaugeField, "Umu"); + envTmpLat(LatticeSpinColourVector, "dist_source"); + envTmp(std::vector, "sources",nsolves); + envTmpLat(LatticeSpinColourVector, "tmp2"); + envTmpLat(LatticeSpinColourVector, "result"); + //envTmpLat(LatticeSpinColourVector, "result_single_component"); + envTmpLat(LatticeColourVector, "result_nospin"); + //envTmpLat(LatticeColourVector, "tmp_nospin"); + //envTmpLat(LatticeSpinVector, "peramb_tmp"); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + + Ls_ = env().getObjectLs(par().solver); + envTmpLat(FermionField, "v4dtmp"); + envTmpLat(FermionField, "v5dtmp", Ls_); + envTmpLat(FermionField, "v5dtmp_sol", Ls_); +*/ +} + +// clean up any temporaries created by setup (that aren't stored in the environment) +template +void TPerambMultipleSolves::Cleanup(void) +{ + if( grid3d != nullptr ) { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TPerambMultipleSolves::execute(void) +{ + /* const int nsolves{par().nsolves}; + const int nvec{par().nvec}; + std::vector nvecs{par().nvecs}; + const DistilParameters & Distil{par().Distil}; + //const SolverParameters & Solver{par().Solver}; + const int LI{Distil.LI}; + //const int SI{Distil.SI}; + const int TI{Distil.TI}; + const int nnoise{Distil.nnoise}; + const int Nt{Distil.Nt}; + const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; + const int tsrc{Distil.tsrc}; + const int Ns{Distil.Ns}; + + auto &solver=envGet(Solver, par().solver); + auto &mat = solver.getFMat(); + envGetTmp(FermionField, v4dtmp); + envGetTmp(FermionField, v5dtmp); + envGetTmp(FermionField, v5dtmp_sol); + + const bool full_tdil{TI==Nt}; + const bool exact_distillation{full_tdil && LI==nvec}; + + const std::string &ConfigFileDir{par().ConfigFileDir}; + const std::string &ConfigFileName{par().ConfigFileName}; + const std::string &UniqueIdentifier{par().UniqueIdentifier}; + + auto &noise = envGet(std::vector, getName() + "_noise"); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + std::vector> solves(nsolves); + for(int i=0;i, getName() +"_solve_"+std::to_string(nvecs[i])); + solves[i].resize(nnoise*nvecs[i]*Ns*Nt_inv); + solves[i]=unsmeared_sink; + } + + envGetTmp(GaugeField, Umu); + FieldMetaData header; + if((1)){ + const std::vector seeds({1, 2, 3, 4, 5}); + GridParallelRNG pRNG4d(grid4d); + pRNG4d.SeedFixedIntegers(seeds); + std::cout << GridLogMessage << "now hot config" << std::endl; + SU::HotConfiguration(pRNG4d, Umu); + std::cout << GridLogMessage << "hot cfg done." << std::endl; + + // Set up the SAME gauge field on every time plane + // int Nt = grid4d->gDimensions()[Tdir]; + Grid_unquiesce_nodes(); + + auto Usft = Umu; + Lattice > coor(grid4d); + LatticeCoordinate(coor,Tdir); + for(int t=1;t 7,0,1,2,3,4,5,6 t=1 + // 0,0,2,3,4,5,6,7 6,7,0,1,2,3,4,5 t=2 + // 0,0,0,3,4,5,6,7 5,6,7,0,1,2,3,4 t=3 + //... + + Usft = Cshift(Usft,Tdir,-1); + Umu = where(coor==t,Usft,Umu); + } + } else { + //std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); + std::string fileName(ConfigFileDir + ConfigFileName); + std::cout << GridLogMessage << "Loading NERSC configuration from '" << fileName << "'" << std::endl; + NerscIO::readConfiguration(Umu, header, fileName); + std::cout << GridLogMessage << "reading done." << std::endl; + } + + GridSerialRNG sRNG; + sRNG.SeedUniqueString(ConfigFileName + "_" + UniqueIdentifier); + Real rn; + + for (int inoise=0;inoise 0.5) ? -1 : 1; + } + } + } + } + } + + + envGetTmp(LatticeSpinColourVector, dist_source); + envGetTmp(LatticeSpinColourVector, tmp2); + envGetTmp(LatticeSpinColourVector, result); + envGetTmp(LatticeColourVector, result_nospin); + envGetTmp(LatticeSpinColourVector, tmp3d); + envGetTmp(LatticeColourVector, tmp3d_nospin); + envGetTmp(LatticeColourVector, result_3d); + envGetTmp(LatticeColourVector, evec3d); + envGetTmp(std::vector, sources); + + const int Ntlocal{grid4d->LocalDimensions()[3]}; + const int Ntfirst{grid4d->LocalStarts()[3]}; + + std::cout << "init RBG " << std::endl; + GridRedBlackCartesian RBGrid(grid4d); + std::cout << "init RBG done" << std::endl; + + { + + int t_inv; + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < Ns; ds++) { + std::cout << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; + dist_source = zero; + for (int isource = 0; isource < nsolves; isource ++){ + if(dk < nvecs[isource]) + sources[isource] = zero; + } + tmp3d_nospin = zero; + evec3d = zero; + for (int it = dt; it < Nt; it += TI){ + if (full_tdil) t_inv = tsrc; else t_inv = it; + if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += Ns){ // TODO: Also allow non-full spin dilution (re-define exact_distillation?) + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); + tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; + tmp3d=zero; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=zero; + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); + dist_source += tmp2; + for (int isource = 0; isource < nsolves; isource ++){ + if(dk < nvecs[isource]) + sources[isource] += tmp2; + } + } + } + } + } + std::cout << "Inversion for noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; + result=zero; + for (int isource = 0; isource < nsolves; isource ++){ + if(dk < nvecs[isource]){ + v4dtmp = sources[isource]; + if (Ls_ == 1){ + solver(result, v4dtmp); + } else { + mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); + solver(v5dtmp_sol, v5dtmp); + mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); + result = v4dtmp; + } + solves[isource][inoise+nnoise*(dk+nvecs[isource]*(dt+Nt_inv*ds))] = result; + } + } + } + } + } + } + + + } +*/ +} +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_PerambMultipleSolves_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index eb4c6d88..18b18676 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,167 +1,169 @@ modules_cc =\ - Modules/MScalarSUN/ShiftProbe.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ - Modules/MScalarSUN/Div.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/StochFreeField.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/VPCounterTerms.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MScalar/ScalarVP.cc \ - Modules/MLoop/NoiseLoop.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadA2AVectors.cc \ - Modules/MSink/Smear.cc \ - Modules/MSink/Point.cc \ - Modules/MFermion/FreeProp.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MUtilities/TestSeqGamma.cc \ Modules/MUtilities/TestSeqConserved.cc \ Modules/MUtilities/RandomVectors.cc \ + Modules/MUtilities/TestSeqGamma.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MDistil/PerambFromSolve.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/A2AVectors.cc \ + Modules/MSolver/A2AAslashVectors.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSolver/RBPrecCG.cc \ Modules/MDistil/g5_multiply.cc \ - Modules/MDistil/LapEvec.cc \ - Modules/MDistil/DistilVectors.cc \ Modules/MDistil/BContraction.cc \ - Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/BC2.cc \ + Modules/MDistil/LapEvec.cc \ + Modules/MDistil/PerambFromSolve.cc \ + Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambLight.cc \ Modules/MDistil/DistilSink.cc \ - Modules/MSource/Momentum.cc \ - Modules/MSource/Z2.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/SeqConserved.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MDistil/PerambMultipleSolves.cc \ + Modules/MContraction/WeakHamiltonianEye.cc \ + Modules/MContraction/Gamma3pt.cc \ + Modules/MContraction/DiscLoop.cc \ Modules/MContraction/Meson.cc \ - Modules/MContraction/A2AAslashField.cc \ + Modules/MContraction/WeakNeutral4ptDisc.cc \ Modules/MContraction/WardIdentity.cc \ + Modules/MContraction/A2AMesonField.cc \ Modules/MContraction/WeakHamiltonianNonEye.cc \ Modules/MContraction/Baryon.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/WeakHamiltonianEye.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/WeakNeutral4ptDisc.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MAction/MobiusDWF.cc \ + Modules/MContraction/A2AAslashField.cc \ + Modules/MAction/ZMobiusDWF.cc \ Modules/MAction/WilsonClover.cc \ Modules/MAction/Wilson.cc \ Modules/MAction/DWF.cc \ + Modules/MAction/MobiusDWF.cc \ Modules/MAction/ScaledDWF.cc \ - Modules/MAction/ZMobiusDWF.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/A2AAslashVectors.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/GaugeFix.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadA2AVectors.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadCosmHol.cc \ + Modules/MNPR/Amputate.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MNPR/Amputate.cc + Modules/MLoop/NoiseLoop.cc \ + Modules/MScalarSUN/TwoPoint.cc \ + Modules/MScalarSUN/Div.cc \ + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/ShiftProbe.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/Grad.cc \ + Modules/MSink/Smear.cc \ + Modules/MSink/Point.cc \ + Modules/MFermion/GaugeProp.cc \ + Modules/MFermion/FreeProp.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/ScalarVP.cc \ + Modules/MScalar/VPCounterTerms.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MSource/SeqConserved.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Wall.cc \ + Modules/MSource/Z2.cc \ + Modules/MSource/Point.cc \ + Modules/MSource/Momentum.cc modules_hpp =\ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/ShiftProbe.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/ScalarVP.hpp \ - Modules/MScalar/ChargedProp.hpp \ - Modules/MScalar/VPCounterTerms.hpp \ - Modules/MLoop/NoiseLoop.hpp \ - Modules/MIO/LoadEigenPack.hpp \ - Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadCosmHol.hpp \ - Modules/MIO/LoadBinary.hpp \ - Modules/MIO/LoadNersc.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ - Modules/MFermion/FreeProp.hpp \ - Modules/MFermion/GaugeProp.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MUtilities/TestSeqGamma.hpp \ Modules/MUtilities/RandomVectors.hpp \ - Modules/MUtilities/TestSeqConserved.hpp \ + Modules/MUtilities/TestSeqGamma.hpp \ Modules/MUtilities/PrecisionCast.hpp \ - Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/Distil.hpp \ - Modules/MDistil/BC2.hpp \ - Modules/MDistil/g5_multiply.hpp \ - Modules/MDistil/PerambFromSolve.hpp \ - Modules/MDistil/Baryon2pt.hpp \ + Modules/MUtilities/TestSeqConserved.hpp \ + Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MSolver/Guesser.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSolver/A2AVectors.hpp \ Modules/MDistil/LapEvec.hpp \ - Modules/MDistil/BContraction.hpp \ + Modules/MDistil/Distil.hpp \ + Modules/MDistil/g5_multiply.hpp \ Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/Baryon2pt.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/PerambLight.hpp \ Modules/MDistil/DistilSink.hpp \ - Modules/MSource/SeqConserved.hpp \ - Modules/MSource/Z2.hpp \ - Modules/MSource/Wall.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Point.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/WeakHamiltonianEye.hpp \ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/Meson.hpp \ + Modules/MDistil/PerambFromSolve.hpp \ + Modules/MDistil/BC2.hpp \ + Modules/MDistil/PerambMultipleSolves.hpp \ Modules/MContraction/WeakHamiltonian.hpp \ Modules/MContraction/WeakNeutral4ptDisc.hpp \ - Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/WeakHamiltonianEye.hpp \ Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/WeakHamiltonianNonEye.hpp \ - Modules/MContraction/WardIdentity.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MContraction/Gamma3pt.hpp \ Modules/MContraction/A2AMesonField.hpp \ - Modules/MAction/WilsonClover.hpp \ - Modules/MAction/ScaledDWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/WeakHamiltonianNonEye.hpp \ + Modules/MContraction/Meson.hpp \ + Modules/MContraction/WardIdentity.hpp \ Modules/MAction/Wilson.hpp \ + Modules/MAction/WilsonClover.hpp \ Modules/MAction/DWF.hpp \ + Modules/MAction/ScaledDWF.hpp \ Modules/MAction/ZMobiusDWF.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/Guesser.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/Unit.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MGauge/StoutSmearing.hpp \ + Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ + Modules/MGauge/FundtoHirep.hpp \ + Modules/MGauge/GaugeFix.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ + Modules/MIO/LoadBinary.hpp \ + Modules/MIO/LoadCosmHol.hpp \ + Modules/MIO/LoadNersc.hpp \ + Modules/MIO/LoadA2AVectors.hpp \ + Modules/MIO/LoadCoarseEigenPack.hpp \ + Modules/MIO/LoadEigenPack.hpp \ + Modules/MNPR/Amputate.hpp \ Modules/MNPR/FourQuark.hpp \ Modules/MNPR/Bilinear.hpp \ - Modules/MNPR/Amputate.hpp + Modules/MLoop/NoiseLoop.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MScalarSUN/ShiftProbe.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MSink/Point.hpp \ + Modules/MFermion/GaugeProp.hpp \ + Modules/MFermion/FreeProp.hpp \ + Modules/MScalar/Scalar.hpp \ + Modules/MScalar/ScalarVP.hpp \ + Modules/MScalar/FreeProp.hpp \ + Modules/MScalar/ChargedProp.hpp \ + Modules/MScalar/VPCounterTerms.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Z2.hpp \ + Modules/MSource/Wall.hpp \ + Modules/MSource/SeqConserved.hpp From a0405c6d842914062e48e1f811a02ca72d74c308 Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Tue, 12 Mar 2019 14:01:29 +0000 Subject: [PATCH 159/347] PerambMultipleSolves.hpp compiles (not had time to test) --- .../Modules/MDistil/PerambMultipleSolves.hpp | 13 +++----- tests/hadrons/Test_hadrons_distil.cc | 31 +++++++------------ 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp b/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp index b3605e25..65bc9055 100644 --- a/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp +++ b/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp @@ -109,7 +109,7 @@ std::vector TPerambMultipleSolves::getOutput(void) template void TPerambMultipleSolves::setup(void) { - /* Cleanup(); + Cleanup(); const int nvec{par().nvec}; // auto &noise = envGet(std::vector>>, par().noise); @@ -133,7 +133,8 @@ void TPerambMultipleSolves::setup(void) envTmpLat(GaugeField, "Umu"); envTmpLat(LatticeSpinColourVector, "dist_source"); - envTmp(std::vector, "sources",nsolves); + envTmp(std::vector, "sources", 1, + std::vector( nsolves, grid4d )); envTmpLat(LatticeSpinColourVector, "tmp2"); envTmpLat(LatticeSpinColourVector, "result"); //envTmpLat(LatticeSpinColourVector, "result_single_component"); @@ -149,7 +150,6 @@ void TPerambMultipleSolves::setup(void) envTmpLat(FermionField, "v4dtmp"); envTmpLat(FermionField, "v5dtmp", Ls_); envTmpLat(FermionField, "v5dtmp_sol", Ls_); -*/ } // clean up any temporaries created by setup (that aren't stored in the environment) @@ -167,7 +167,7 @@ void TPerambMultipleSolves::Cleanup(void) template void TPerambMultipleSolves::execute(void) { - /* const int nsolves{par().nsolves}; + const int nsolves{par().nsolves}; const int nvec{par().nvec}; std::vector nvecs{par().nvecs}; const DistilParameters & Distil{par().Distil}; @@ -199,7 +199,7 @@ void TPerambMultipleSolves::execute(void) std::vector> solves(nsolves); for(int i=0;i, getName() +"_solve_"+std::to_string(nvecs[i])); - solves[i].resize(nnoise*nvecs[i]*Ns*Nt_inv); + solves[i].resize(nnoise*nvecs[i]*Ns*Nt_inv, grid4d); solves[i]=unsmeared_sink; } @@ -333,10 +333,7 @@ void TPerambMultipleSolves::execute(void) } } } - - } -*/ } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 87aa9d95..8b0830e1 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -30,6 +30,7 @@ #include #include #include +#include using namespace Grid; using namespace Hadrons; @@ -688,13 +689,10 @@ bool DebugEigenTest() template void DebugGridTensorTest_print( int i ) { - std::cout << i << " : " << EigenIO::is_tensor::value - << ", rank " << EigenIO::Traits::rank - << ", rank_non_trivial " << EigenIO::Traits::rank_non_trivial - << ", count " << EigenIO::Traits::count - << ", scalar_size " << EigenIO::Traits::scalar_size - << ", size " << EigenIO::Traits::size - << std::endl; + // std::cout << i << " : " << EigenIO::is_tensor::value + // << ", Rank " << EigenIO::Traits::Rank + // << ", count " << EigenIO::Traits::count + // << std::endl; } // begin() and end() are the minimum necessary to support range-for loops @@ -723,12 +721,12 @@ void EigenSliceExample() a.setValues({{0, 100, 200}, {300, 400, 500}, {600, 700, 800}, {900, 1000, 1100}}); std::cout << "a\n" << a << std::endl; - DumpMemoryOrder( a, "a" ); + dump_tensor( a, "a" ); Eigen::array offsets = {0, 1}; Eigen::array extents = {4, 2}; T2 slice = a.slice(offsets, extents); std::cout << "slice\n" << slice << std::endl; - DumpMemoryOrder( slice, "slice" ); + dump_tensor( slice, "slice" ); std::cout << "\n========================================" << std::endl; } @@ -741,10 +739,7 @@ void EigenSliceExample2() T3 a(2,3,4); std::cout << "Initialising a:"; - for_all( a, [&](TestScalar &c, float f, const std::array &Dims ){ - c = TestScalar{f,-f}; - std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; - } ); + SequentialInit( a ); std::cout << std::endl; //std::cout << "Validating a:"; float z = 0; @@ -758,22 +753,18 @@ void EigenSliceExample2() } //std::cout << std::endl; //std::cout << "a initialised to:\n" << a << std::endl; - DumpMemoryOrder( a, "a" ); - std::cout << "for_all(a):"; - for_all( a, [&](TestScalar c, typename T3::Index n, const std::array &Dims ){ - std::cout << " (" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")<" << n << ">=" << c; - } ); + dump_tensor( a, "a" ); std::cout << std::endl; Eigen::array offsets = {0,1,1}; Eigen::array extents = {1,2,2}; T3 b; b = a.slice( offsets, extents );//.reshape(NewExtents); std::cout << "b = a.slice( offsets, extents ):\n" << b << std::endl; - DumpMemoryOrder( b, "b" ); + dump_tensor( b, "b" ); T2 c(3,4); c = a.chip(0,1); std::cout << "c = a.chip(0,0):\n" << c << std::endl; - DumpMemoryOrder( c, "c" ); + dump_tensor( c, "c" ); //T2 d = b.reshape(extents); //std::cout << "b.reshape(extents) is:\n" << d << std::endl; std::cout << "\n========================================" << std::endl; From 6bb9b67c93b2dbce1a45b2b566e8c9791488051d Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Mar 2019 12:09:12 +0000 Subject: [PATCH 160/347] externalised gauge field reading to hadrons module --- Hadrons/Modules/MDistil/LapEvec.hpp | 46 +-------- Hadrons/Modules/MDistil/PerambLight.hpp | 119 +----------------------- 2 files changed, 5 insertions(+), 160 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 22ca8526..829cfcf7 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -86,8 +86,8 @@ class LapEvecPar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, std::string, gauge, - std::string, ConfigFileDir, - std::string, ConfigFileName, + // std::string, ConfigFileDir, + // std::string, ConfigFileName, //,std::string, EigenPackName StoutParameters, Stout ,ChebyshevParameters, Cheby @@ -211,11 +211,6 @@ void TLapEvec::execute(void) { LOG(Message) << "execute() : start for " << getName() << std::endl; - // Alii for parameters - //const int &TI{par().Distil.TI}; - //const int &LI{par().Distil.LI}; - //const int &nnoise{par().Distil.Nnoise}; - //const int &tsrc{par().Distil.tSrc}; const ChebyshevParameters &ChebPar{par().Cheby}; const LanczosParameters &LPar{par().Lanczos}; const int &nvec{LPar.Nvec}; @@ -231,45 +226,8 @@ void TLapEvec::execute(void) //else //assert(nnoise>1); - const std::string &ConfigFileDir{par().ConfigFileDir}; - const std::string &ConfigFileName{par().ConfigFileName}; - - // Debugging only - //envGetTmp(GaugeField, Umu); auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); - FieldMetaData header; - if((0)) { - const std::vector seeds({1, 2, 3, 4, 5}); - GridParallelRNG pRNG4d(gridHD); - pRNG4d.SeedFixedIntegers(seeds); - std::cout << GridLogMessage << "now hot config" << std::endl; - SU::HotConfiguration(pRNG4d, Umu); - std::cout << GridLogMessage << "hot cfg done." << std::endl; - - // Set up the SAME gauge field on every time plane - Grid_unquiesce_nodes(); - Umu_smear = Umu; - Lattice > coor(gridHD); - LatticeCoordinate(coor,Tdir); - for(int t=1;t 7,0,1,2,3,4,5,6 t=1 - // 0,0,2,3,4,5,6,7 6,7,0,1,2,3,4,5 t=2 - // 0,0,0,3,4,5,6,7 5,6,7,0,1,2,3,4 t=3 - //... - - Umu_smear = Cshift(Umu_smear,Tdir,-1); - Umu = where(coor==t,Umu_smear,Umu); - } - // std::cout << "Umu is "< DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} -}; - */ -/*struct SolverParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(SolverParameters, - double, CGPrecision, - int, MaxIterations, - double, mass, - double, M5) - SolverParameters() = default; - template SolverParameters(Reader& Reader){read(Reader,"Solver",*this);} -};*/ class PerambLightPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, std::string, eigenPack, - std::string, PerambFileName, - std::string, ConfigFileDir, - std::string, ConfigFileName, + std::string, PerambFileName, //stem!!! std::string, UniqueIdentifier, bool, multiFile, int, nvec, -// int, Ls, // For makeFiveDimGrid DistilParameters, Distil, std::string, solver); -// SolverParameters, Solver); }; template @@ -132,7 +104,6 @@ void TPerambLight::setup(void) { Cleanup(); - // auto &noise = envGet(std::vector>>, par().noise); const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; const int LI{Distil.LI}; @@ -140,12 +111,7 @@ void TPerambLight::setup(void) const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; const int Ns{Distil.Ns}; std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - //std::complex z{0.6,-3.1}; - //envCreate(std::string, getName() + "_debug_delete_me", 1, "Bingonuts"); - //envCreate(std::complex, getName() + "_debug_delete_me_2", 1, 0.6); - //envCreate(std::complex, getName() + "_debug_delete_me_3", 1, z); - //envCreate(std::complex, getName() + "_debug_delete_me_4", 1, {0.6 COMMA -3.1}); - //envCreate(std::array, getName() + "_debug_delete_me_5", 1, {"One" COMMA "Two" COMMA "Three"}); + envCreate(Perambulator, getName() + "_perambulator_light", 1, sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, @@ -156,14 +122,10 @@ void TPerambLight::setup(void) grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d);//new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); - envTmpLat(GaugeField, "Umu"); envTmpLat(LatticeSpinColourVector, "dist_source"); envTmpLat(LatticeSpinColourVector, "tmp2"); envTmpLat(LatticeSpinColourVector, "result"); - //envTmpLat(LatticeSpinColourVector, "result_single_component"); envTmpLat(LatticeColourVector, "result_nospin"); - //envTmpLat(LatticeColourVector, "tmp_nospin"); - //envTmpLat(LatticeSpinVector, "peramb_tmp"); envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); @@ -193,7 +155,6 @@ void TPerambLight::execute(void) { const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; - //const SolverParameters & Solver{par().Solver}; const int LI{Distil.LI}; //const int SI{Distil.SI}; const int TI{Distil.TI}; @@ -209,64 +170,21 @@ void TPerambLight::execute(void) envGetTmp(FermionField, v5dtmp); envGetTmp(FermionField, v5dtmp_sol); - //const Real mass{Solver.mass}; - //const Real M5 {Solver.M5}; const bool full_tdil{TI==Nt}; const bool exact_distillation{full_tdil && LI==nvec}; - const std::string &ConfigFileDir{par().ConfigFileDir}; - const std::string &ConfigFileName{par().ConfigFileName}; const std::string &UniqueIdentifier{par().UniqueIdentifier}; - //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, getName() + "_noise"); auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); - envGetTmp(GaugeField, Umu); - FieldMetaData header; - if((0)){ - const std::vector seeds({1, 2, 3, 4, 5}); - GridParallelRNG pRNG4d(grid4d); - pRNG4d.SeedFixedIntegers(seeds); - std::cout << GridLogMessage << "now hot config" << std::endl; - SU::HotConfiguration(pRNG4d, Umu); - std::cout << GridLogMessage << "hot cfg done." << std::endl; - - // Set up the SAME gauge field on every time plane - // int Nt = grid4d->gDimensions()[Tdir]; - Grid_unquiesce_nodes(); - - auto Usft = Umu; - Lattice > coor(grid4d); - LatticeCoordinate(coor,Tdir); - for(int t=1;t 7,0,1,2,3,4,5,6 t=1 - // 0,0,2,3,4,5,6,7 6,7,0,1,2,3,4,5 t=2 - // 0,0,0,3,4,5,6,7 5,6,7,0,1,2,3,4 t=3 - //... - - Usft = Cshift(Usft,Tdir,-1); - Umu = where(coor==t,Usft,Umu); - } - } else { - //std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); - std::string fileName(ConfigFileDir + ConfigFileName); - std::cout << GridLogMessage << "Loading NERSC configuration from '" << fileName << "'" << std::endl; - NerscIO::readConfiguration(Umu, header, fileName); - std::cout << GridLogMessage << "reading done." << std::endl; - } - //Create Noises - //std::cout << pszGaugeConfigFile << std::endl; - //GridSerialRNG sRNG; sRNG.SeedUniqueString(std::string(pszGaugeConfigFile)); GridSerialRNG sRNG; - sRNG.SeedUniqueString(ConfigFileName + "_" + UniqueIdentifier); + sRNG.SeedUniqueString(UniqueIdentifier); //maybe add trajectory number?? Real rn; for (int inoise=0;inoise::execute(void) for (int is=0;is::execute(void) envGetTmp(LatticeSpinColourVector, dist_source); envGetTmp(LatticeSpinColourVector, tmp2); envGetTmp(LatticeSpinColourVector, result); - //envGetTmp(LatticeSpinColourVector, result_single_component); envGetTmp(LatticeColourVector, result_nospin); - //envGetTmp(LatticeColourVector, tmp_nospin); - //envGetTmp(LatticeSpinVector, peramb_tmp); envGetTmp(LatticeSpinColourVector, tmp3d); envGetTmp(LatticeColourVector, tmp3d_nospin); envGetTmp(LatticeColourVector, result_3d); @@ -321,25 +235,8 @@ void TPerambLight::execute(void) GridRedBlackCartesian RBGrid(grid4d); std::cout << "init RBG done" << std::endl; - //const int Ls{par().Ls}; - //const double CGPrecision{Solver.CGPrecision}; - //const int MaxIterations {Solver.MaxIterations}; { - /*GridCartesian * FGrid = SpaceTimeGrid::makeFiveDimGrid(Ls,grid4d); - GridRedBlackCartesian * FrbGrid = SpaceTimeGrid::makeFiveDimRedBlackGrid(Ls,grid4d); - - typedef DomainWallFermionR FermionAction; - - FermionAction Dop(Umu,*FGrid,*FrbGrid,*grid4d,RBGrid,mass,M5); - - MdagMLinearOperator HermOp(Dop); - ConjugateGradient CG(CGPrecision,MaxIterations); - SchurRedBlackDiagMooeeSolve SchurSolver(CG); - LatticeSpinColourVector a(grid4d); - LatticeColourVector b(grid4d); - b= peekSpin(a,0); -*/ int t_inv; for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { @@ -367,12 +264,6 @@ void TPerambLight::execute(void) } std::cout << "Inversion for noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; result=zero; - /*LatticeFermion src5(FGrid); - LatticeFermion sol5(FGrid); - Dop.ImportPhysicalFermionSource(dist_source,src5); - SchurSolver(Dop,src5,sol5); - Dop.ExportPhysicalFermionSolution(sol5,result); //These are the meson sinks - */ v4dtmp = dist_source; if (Ls_ == 1){ solver(result, v4dtmp); @@ -400,14 +291,10 @@ void TPerambLight::execute(void) } } } - // Kill our 5 dimensional grid (avoid leaks). Should really declare these objects temporary - //delete FrbGrid; - //delete FGrid; } std::cout << "perambulator done" << std::endl; perambulator.SliceShare( grid3d, grid4d ); - // THIS IS WHERE WE WANT TO SAVE THE PERAMBULATORS TO DISK if(PerambFileName.length()) perambulator.WriteBinary(PerambFileName); } From 5313e44d11ecc8f5ccf1407267ecd416cf6d8418 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Mar 2019 13:15:12 +0000 Subject: [PATCH 161/347] some cleanup --- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 17 ++++++----------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 829cfcf7..1e0f459b 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -335,7 +335,7 @@ void TLapEvec::execute(void) // Now write out the 4d eigenvectors eig4d.record.operatorXml = DefaultOperatorXml; eig4d.record.solverXml = DefaultsolverXml; - eig4d.write(sEigenPackName,false); + eig4d.write(sEigenPackName + "." + std::to_string(vm().getTrajectory()),false); // Close the local debugging log file if( ll ) { diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 413a5d26..63a98af5 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -201,7 +201,7 @@ void TPerambFromSolve::execute(void) } if(PerambFileName.length()) - perambulator.WriteBinary(PerambFileName); + perambulator.WriteBinary(PerambFileName + "." + std::to_string(vm().getTrajectory())); } diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 1d78b202..d7d599b9 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -184,7 +184,7 @@ void TPerambLight::execute(void) //Create Noises GridSerialRNG sRNG; - sRNG.SeedUniqueString(UniqueIdentifier); //maybe add trajectory number?? + sRNG.SeedUniqueString(UniqueIdentifier + std::to_string(vm().getTrajectory())); //maybe add more?? Real rn; for (int inoise=0;inoise::execute(void) } // Load perambulator if it exists on disk instead of creating it - const std::string &PerambFileName{par().PerambFileName}; + // Not sure this is how we want it - rather specify an input flag 'read' + // and assert that the file is there. +/* const std::string &PerambFileName{par().PerambFileName}; if( PerambFileName.length() ){ bool bExists = false; { @@ -217,7 +219,7 @@ void TPerambLight::execute(void) perambulator.ReadBinary(PerambFileName); return; } - } + }*/ envGetTmp(LatticeSpinColourVector, dist_source); envGetTmp(LatticeSpinColourVector, tmp2); @@ -231,10 +233,6 @@ void TPerambLight::execute(void) const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - std::cout << "init RBG " << std::endl; - GridRedBlackCartesian RBGrid(grid4d); - std::cout << "init RBG done" << std::endl; - { int t_inv; @@ -262,7 +260,6 @@ void TPerambLight::execute(void) } } } - std::cout << "Inversion for noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; result=zero; v4dtmp = dist_source; if (Ls_ == 1){ @@ -275,7 +272,6 @@ void TPerambLight::execute(void) } if ((1)) // comment out if unsmeared sink is too large??? unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; - std::cout << "Contraction of perambulator from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; for (int is = 0; is < Ns; is++) { result_nospin = peekSpin(result,is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { @@ -283,7 +279,6 @@ void TPerambLight::execute(void) for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); - std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; } } } @@ -296,7 +291,7 @@ void TPerambLight::execute(void) perambulator.SliceShare( grid3d, grid4d ); if(PerambFileName.length()) - perambulator.WriteBinary(PerambFileName); + perambulator.WriteBinary(PerambFileName + "." + std::to_string(vm().getTrajectory())); } END_MODULE_NAMESPACE From 0faf40e20779846efbd0caebe9dd345eafc3ec75 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Mar 2019 13:24:18 +0000 Subject: [PATCH 162/347] last commit did not compile - fxied this --- Hadrons/Modules/MDistil/PerambLight.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index d7d599b9..51f3b735 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -207,8 +207,8 @@ void TPerambLight::execute(void) // Load perambulator if it exists on disk instead of creating it // Not sure this is how we want it - rather specify an input flag 'read' // and assert that the file is there. -/* const std::string &PerambFileName{par().PerambFileName}; - if( PerambFileName.length() ){ + const std::string &PerambFileName{par().PerambFileName}; +/* if( PerambFileName.length() ){ bool bExists = false; { std::ifstream f(PerambFileName, std::ios::binary); From 50ca3101def433fbd755c59cd4548cc6b34dd406 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Mar 2019 17:25:55 +0000 Subject: [PATCH 163/347] bug in multiSolves and new test prog --- .../Modules/MDistil/PerambMultipleSolves.hpp | 88 +++----------- tests/hadrons/Test_hadrons_distil.cc | 112 ++++++++++++++++-- 2 files changed, 121 insertions(+), 79 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp b/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp index 65bc9055..845dca46 100644 --- a/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp +++ b/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp @@ -25,9 +25,6 @@ class PerambMultipleSolvesPar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambMultipleSolvesPar, std::string, eigenPack, - std::string, PerambFileName, - std::string, ConfigFileDir, - std::string, ConfigFileName, std::string, UniqueIdentifier, int, nsolves, std::vector, nvecs, @@ -102,6 +99,7 @@ std::vector TPerambMultipleSolves::getOutput(void) out.push_back(getName()+ "_solve_" +std::to_string(nvecs[i])); } + out.push_back(getName()+ "_noise"); return out; } @@ -112,7 +110,6 @@ void TPerambMultipleSolves::setup(void) Cleanup(); const int nvec{par().nvec}; - // auto &noise = envGet(std::vector>>, par().noise); const int nsolves{par().nsolves}; std::vector nvecs{par().nvecs}; const DistilParameters & Distil{par().Distil}; @@ -133,14 +130,11 @@ void TPerambMultipleSolves::setup(void) envTmpLat(GaugeField, "Umu"); envTmpLat(LatticeSpinColourVector, "dist_source"); - envTmp(std::vector, "sources", 1, - std::vector( nsolves, grid4d )); + //envTmp(std::vector, "sources", 1, + // std::vector( nsolves, grid4d )); envTmpLat(LatticeSpinColourVector, "tmp2"); envTmpLat(LatticeSpinColourVector, "result"); - //envTmpLat(LatticeSpinColourVector, "result_single_component"); envTmpLat(LatticeColourVector, "result_nospin"); - //envTmpLat(LatticeColourVector, "tmp_nospin"); - //envTmpLat(LatticeSpinVector, "peramb_tmp"); envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); @@ -171,7 +165,6 @@ void TPerambMultipleSolves::execute(void) const int nvec{par().nvec}; std::vector nvecs{par().nvecs}; const DistilParameters & Distil{par().Distil}; - //const SolverParameters & Solver{par().Solver}; const int LI{Distil.LI}; //const int SI{Distil.SI}; const int TI{Distil.TI}; @@ -190,8 +183,6 @@ void TPerambMultipleSolves::execute(void) const bool full_tdil{TI==Nt}; const bool exact_distillation{full_tdil && LI==nvec}; - const std::string &ConfigFileDir{par().ConfigFileDir}; - const std::string &ConfigFileName{par().ConfigFileName}; const std::string &UniqueIdentifier{par().UniqueIdentifier}; auto &noise = envGet(std::vector, getName() + "_noise"); @@ -203,44 +194,8 @@ void TPerambMultipleSolves::execute(void) solves[i]=unsmeared_sink; } - envGetTmp(GaugeField, Umu); - FieldMetaData header; - if((1)){ - const std::vector seeds({1, 2, 3, 4, 5}); - GridParallelRNG pRNG4d(grid4d); - pRNG4d.SeedFixedIntegers(seeds); - std::cout << GridLogMessage << "now hot config" << std::endl; - SU::HotConfiguration(pRNG4d, Umu); - std::cout << GridLogMessage << "hot cfg done." << std::endl; - - // Set up the SAME gauge field on every time plane - // int Nt = grid4d->gDimensions()[Tdir]; - Grid_unquiesce_nodes(); - - auto Usft = Umu; - Lattice > coor(grid4d); - LatticeCoordinate(coor,Tdir); - for(int t=1;t 7,0,1,2,3,4,5,6 t=1 - // 0,0,2,3,4,5,6,7 6,7,0,1,2,3,4,5 t=2 - // 0,0,0,3,4,5,6,7 5,6,7,0,1,2,3,4 t=3 - //... - - Usft = Cshift(Usft,Tdir,-1); - Umu = where(coor==t,Usft,Umu); - } - } else { - //std::string fileName( "/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/ckpoint_lat.3000" ); - std::string fileName(ConfigFileDir + ConfigFileName); - std::cout << GridLogMessage << "Loading NERSC configuration from '" << fileName << "'" << std::endl; - NerscIO::readConfiguration(Umu, header, fileName); - std::cout << GridLogMessage << "reading done." << std::endl; - } - GridSerialRNG sRNG; - sRNG.SeedUniqueString(ConfigFileName + "_" + UniqueIdentifier); + sRNG.SeedUniqueString(UniqueIdentifier + std::to_string(vm().getTrajectory())); Real rn; for (int inoise=0;inoise::execute(void) envGetTmp(LatticeColourVector, tmp3d_nospin); envGetTmp(LatticeColourVector, result_3d); envGetTmp(LatticeColourVector, evec3d); - envGetTmp(std::vector, sources); + //envGetTmp(std::vector, sources); const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - std::cout << "init RBG " << std::endl; - GridRedBlackCartesian RBGrid(grid4d); - std::cout << "init RBG done" << std::endl; - { int t_inv; @@ -287,10 +238,6 @@ void TPerambMultipleSolves::execute(void) for (int ds = 0; ds < Ns; ds++) { std::cout << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; dist_source = zero; - for (int isource = 0; isource < nsolves; isource ++){ - if(dk < nvecs[isource]) - sources[isource] = zero; - } tmp3d_nospin = zero; evec3d = zero; for (int it = dt; it < Nt; it += TI){ @@ -305,27 +252,23 @@ void TPerambMultipleSolves::execute(void) tmp2=zero; InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); dist_source += tmp2; - for (int isource = 0; isource < nsolves; isource ++){ - if(dk < nvecs[isource]) - sources[isource] += tmp2; - } } } } } std::cout << "Inversion for noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; result=zero; + v4dtmp = dist_source; + if (Ls_ == 1){ + solver(result, v4dtmp); + } else { + mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); + solver(v5dtmp_sol, v5dtmp); + mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); + result = v4dtmp; + } for (int isource = 0; isource < nsolves; isource ++){ if(dk < nvecs[isource]){ - v4dtmp = sources[isource]; - if (Ls_ == 1){ - solver(result, v4dtmp); - } else { - mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); - solver(v5dtmp_sol, v5dtmp); - mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); - result = v4dtmp; - } solves[isource][inoise+nnoise*(dk+nvecs[isource]*(dt+Nt_inv*ds))] = result; } } @@ -335,6 +278,9 @@ void TPerambMultipleSolves::execute(void) } } } + + + END_MODULE_NAMESPACE END_HADRONS_NAMESPACE diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 8b0830e1..daa765c8 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -87,10 +87,6 @@ void test_LapEvec(Application &application) application.createModule(szGaugeName); // Now make an instance of the LapEvec object MDistil::LapEvecPar p; - //p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; - //p.ConfigFileName="ckpoint_lat.3000"; - p.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; - p.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; p.gauge = szGaugeName; //p.EigenPackName = "ePack"; //p.Distil.TI = 8; @@ -120,8 +116,6 @@ void test_Perambulators(Application &application) MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="peramb.bin"; - PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; - PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; PerambPar.UniqueIdentifier="full_dilution"; PerambPar.solver="CG_s"; PerambPar.Distil.tsrc = 0; @@ -141,6 +135,104 @@ void test_Perambulators(Application &application) application.createModule("Peramb",PerambPar); } ///////////////////////////////////////////////////////////// +// Multiple Perambulators +///////////////////////////////////////////////////////////// + +void test_MultiPerambulators(Application &application) +{ + // PerambLight parameters + MDistil::PerambMultipleSolves::Par PerambPar; + PerambPar.eigenPack="LapEvec"; + PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.solver="CG_s"; + PerambPar.Distil.tsrc = 0; + PerambPar.Distil.nnoise = 1; + PerambPar.Distil.LI=10; + PerambPar.Distil.SI=4; + PerambPar.Distil.TI=8; + PerambPar.nvec=5; + PerambPar.nsolves=3; + PerambPar.nvecs={2,3,5}; + PerambPar.Distil.Ns=4; + PerambPar.Distil.Nt=8; + PerambPar.Distil.Nt_inv=1; + application.createModule("PerambMulti",PerambPar); + MDistil::PerambFromSolve::Par SolvePar; + SolvePar.eigenPack="LapEvec"; + SolvePar.PerambFileName="PerMulti2"; + SolvePar.solve = "PerambMulti_solve_2"; + SolvePar.Distil.nnoise = 1; + SolvePar.Distil.LI=2; + SolvePar.Distil.SI=4; + SolvePar.Distil.TI=8; + SolvePar.nvec=2; + SolvePar.Distil.Ns=4; + SolvePar.Distil.Nt=8; + SolvePar.Distil.Nt_inv=1; + application.createModule("PerambMulti2",SolvePar); + SolvePar.PerambFileName="PerMulti3"; + SolvePar.solve = "PerambMulti_solve_3"; + SolvePar.Distil.LI=3; + SolvePar.nvec=3; + application.createModule("PerambMulti3",SolvePar); + SolvePar.PerambFileName="PerMulti5"; + SolvePar.solve = "PerambMulti_solve_5"; + SolvePar.Distil.LI=5; + SolvePar.nvec=5; + application.createModule("PerambMulti5",SolvePar); + MDistil::DistilVectors::Par DistilVecPar; + DistilVecPar.noise="PerambMulti_noise"; + DistilVecPar.perambulator="PerambMulti2"; + DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.tsrc = 0; + DistilVecPar.nnoise = 1; + DistilVecPar.LI=2; + DistilVecPar.SI=4; + DistilVecPar.TI=8; + DistilVecPar.nvec=2; + DistilVecPar.Ns=4; + DistilVecPar.Nt=8; + DistilVecPar.Nt_inv=1; + application.createModule("DistilVecs2",DistilVecPar); + DistilVecPar.perambulator="PerambMulti3"; + DistilVecPar.LI=3; + DistilVecPar.nvec=3; + application.createModule("DistilVecs3",DistilVecPar); + DistilVecPar.perambulator="PerambMulti5"; + DistilVecPar.LI=5; + DistilVecPar.nvec=5; + application.createModule("DistilVecs5",DistilVecPar); + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs2_rho"; + A2AMesonFieldPar.right="DistilVecs2_rho"; + A2AMesonFieldPar.output="MesonSinksRho2"; + A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + application.createModule("DistilMesonFieldRho2",A2AMesonFieldPar); + A2AMesonFieldPar.left="DistilVecs2_phi"; + A2AMesonFieldPar.right="DistilVecs2_phi"; + A2AMesonFieldPar.output="MesonSinksPhi2"; + application.createModule("DistilMesonFieldPhi2",A2AMesonFieldPar); + A2AMesonFieldPar.left="DistilVecs3_rho"; + A2AMesonFieldPar.right="DistilVecs3_rho"; + A2AMesonFieldPar.output="MesonSinksRho3"; + application.createModule("DistilMesonFieldRho3",A2AMesonFieldPar); + A2AMesonFieldPar.left="DistilVecs3_phi"; + A2AMesonFieldPar.right="DistilVecs3_phi"; + A2AMesonFieldPar.output="MesonSinksPhi3"; + application.createModule("DistilMesonFieldPhi3",A2AMesonFieldPar); + A2AMesonFieldPar.left="DistilVecs5_rho"; + A2AMesonFieldPar.right="DistilVecs5_rho"; + A2AMesonFieldPar.output="MesonSinksRho5"; + application.createModule("DistilMesonFieldRho5",A2AMesonFieldPar); + A2AMesonFieldPar.left="DistilVecs5_phi"; + A2AMesonFieldPar.right="DistilVecs5_phi"; + A2AMesonFieldPar.output="MesonSinksPhi5"; + application.createModule("DistilMesonFieldPhi5",A2AMesonFieldPar); +} +///////////////////////////////////////////////////////////// // DistilVectors ///////////////////////////////////////////////////////////// @@ -168,8 +260,6 @@ void test_PerambulatorsS(Application &application) MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="perambS.bin"; - PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; - PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; PerambPar.UniqueIdentifier="full_dilution"; PerambPar.solver="CG_s"; PerambPar.Distil.tsrc = 0; @@ -991,6 +1081,12 @@ int main(int argc, char *argv[]) test_DistilVectorsAslashSeq( application ); test_MesonFieldAslashSeq( application ); break; + case 13: + test_Global( application ); + test_SolverS( application ); + test_LapEvec( application ); + test_MultiPerambulators( application ); + break; } LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; From d1fe4dce33be90911355292820914109e7f69d13 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 15 Mar 2019 10:28:02 +0000 Subject: [PATCH 164/347] new idea to get multiple perambulators --- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 14 +++++-- tests/hadrons/Test_hadrons_distil.cc | 43 +++++++++------------ 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 63a98af5..c9f62b6e 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -42,6 +42,8 @@ public: std::string, PerambFileName, std::string, solve, int, nvec, + int, nvec_reduced, + int, LI_reduced, DistilParameters, Distil); }; @@ -125,8 +127,12 @@ void TPerambFromSolve::setup(void) grid3d = MakeLowerDimGrid(grid4d); + const int nvec_reduced{par().nvec_reduced}; + const int LI_reduced{par().LI_reduced}; + //envCreate(Perambulator, getName(), 1, + // sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(Perambulator, getName(), 1, - sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); + sIndexNames,Distil.Nt,nvec_reduced,LI_reduced,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); @@ -152,6 +158,8 @@ void TPerambFromSolve::Cleanup(void) template void TPerambFromSolve::execute(void) { + const int nvec_reduced{par().nvec_reduced}; + const int LI_reduced{par().LI_reduced}; const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; const int LI{Distil.LI}; @@ -181,14 +189,14 @@ void TPerambFromSolve::execute(void) for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { + for (int dk = 0; dk < LI_reduced; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { for (int ds = 0; ds < Ns; ds++) { for (int is = 0; is < Ns; is++) { result_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); - for (int ivec = 0; ivec < nvec; ivec++) { + for (int ivec = 0; ivec < nvec_reduced; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index daa765c8..062c805e 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -141,48 +141,43 @@ void test_Perambulators(Application &application) void test_MultiPerambulators(Application &application) { // PerambLight parameters - MDistil::PerambMultipleSolves::Par PerambPar; + MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.PerambFileName="Peramb5"; PerambPar.solver="CG_s"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=10; + PerambPar.Distil.LI=5; PerambPar.Distil.SI=4; PerambPar.Distil.TI=8; PerambPar.nvec=5; - PerambPar.nsolves=3; - PerambPar.nvecs={2,3,5}; PerambPar.Distil.Ns=4; PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; - application.createModule("PerambMulti",PerambPar); + application.createModule("Peramb5",PerambPar); MDistil::PerambFromSolve::Par SolvePar; SolvePar.eigenPack="LapEvec"; - SolvePar.PerambFileName="PerMulti2"; - SolvePar.solve = "PerambMulti_solve_2"; + SolvePar.PerambFileName="Peramb2"; + SolvePar.solve = "Peramb5_unsmeared_sink"; SolvePar.Distil.nnoise = 1; - SolvePar.Distil.LI=2; + SolvePar.Distil.LI=5; SolvePar.Distil.SI=4; SolvePar.Distil.TI=8; - SolvePar.nvec=2; + SolvePar.nvec=5; + SolvePar.nvec_reduced=2; + SolvePar.LI_reduced=2; SolvePar.Distil.Ns=4; SolvePar.Distil.Nt=8; SolvePar.Distil.Nt_inv=1; - application.createModule("PerambMulti2",SolvePar); - SolvePar.PerambFileName="PerMulti3"; - SolvePar.solve = "PerambMulti_solve_3"; - SolvePar.Distil.LI=3; - SolvePar.nvec=3; - application.createModule("PerambMulti3",SolvePar); - SolvePar.PerambFileName="PerMulti5"; - SolvePar.solve = "PerambMulti_solve_5"; - SolvePar.Distil.LI=5; - SolvePar.nvec=5; - application.createModule("PerambMulti5",SolvePar); + application.createModule("Peramb2",SolvePar); + SolvePar.PerambFileName="Peramb3"; + SolvePar.nvec_reduced=3; + SolvePar.LI_reduced=3; + application.createModule("Peramb3",SolvePar); MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="PerambMulti_noise"; - DistilVecPar.perambulator="PerambMulti2"; + DistilVecPar.noise="Peramb5_noise"; + DistilVecPar.perambulator="Peramb2"; DistilVecPar.eigenPack="LapEvec"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; @@ -194,11 +189,11 @@ void test_MultiPerambulators(Application &application) DistilVecPar.Nt=8; DistilVecPar.Nt_inv=1; application.createModule("DistilVecs2",DistilVecPar); - DistilVecPar.perambulator="PerambMulti3"; + DistilVecPar.perambulator="Peramb3"; DistilVecPar.LI=3; DistilVecPar.nvec=3; application.createModule("DistilVecs3",DistilVecPar); - DistilVecPar.perambulator="PerambMulti5"; + DistilVecPar.perambulator="Peramb5_perambulator_light"; DistilVecPar.LI=5; DistilVecPar.nvec=5; application.createModule("DistilVecs5",DistilVecPar); From bff4eeec4147587d5480e9e293daeff6e97555fb Mon Sep 17 00:00:00 2001 From: Michael Marshall Date: Mon, 18 Mar 2019 12:15:25 +0000 Subject: [PATCH 165/347] Added disclaimer on half-precision types --- Grid/tensors/Tensor_traits.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Grid/tensors/Tensor_traits.h b/Grid/tensors/Tensor_traits.h index 9cb93e17..45a1d807 100644 --- a/Grid/tensors/Tensor_traits.h +++ b/Grid/tensors/Tensor_traits.h @@ -143,6 +143,7 @@ namespace Grid { typedef vRealD DoublePrecision; }; template<> struct GridTypeMapper : public GridTypeMapper_Base { + // Fixme this is incomplete until Grid supports fp16 or bfp16 arithmetic types typedef RealF scalar_type; typedef vRealH vector_type; typedef vRealD vector_typeD; @@ -153,6 +154,7 @@ namespace Grid { typedef vRealD DoublePrecision; }; template<> struct GridTypeMapper : public GridTypeMapper_Base { + // Fixme this is incomplete until Grid supports fp16 or bfp16 arithmetic types typedef ComplexF scalar_type; typedef vComplexH vector_type; typedef vComplexD vector_typeD; From 204cfa1c5a61d8123a6a6f3c08159a10d656a7c0 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 19 Mar 2019 07:28:29 +0000 Subject: [PATCH 166/347] Added documentation for Grid using Xcode --- documentation/GridXcFig1.png | Bin 0 -> 122927 bytes documentation/GridXcFig2.png | Bin 0 -> 259997 bytes documentation/GridXcFig3.png | Bin 0 -> 565606 bytes documentation/GridXcode.md | 416 +++++++++++++++++++++++++++++++++++ 4 files changed, 416 insertions(+) create mode 100644 documentation/GridXcFig1.png create mode 100644 documentation/GridXcFig2.png create mode 100644 documentation/GridXcFig3.png create mode 100644 documentation/GridXcode.md diff --git a/documentation/GridXcFig1.png b/documentation/GridXcFig1.png new file mode 100644 index 0000000000000000000000000000000000000000..73fd06258ff1c8a6d914c37ce10ab1d844c66c7a GIT binary patch literal 122927 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 z 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
12jX#fc`R4|on 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 CDpT#4;>`;L1O!YuSxHp{1T-TA1XOvn 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≈VdqZpi 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 w-YIULqs;wS#Z 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 z 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

$RO)rBl`VKOf!Gz*?#pDZnPy7tEx=_%mN9AH%7 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 + + + +Label +Grid.startup +ProgramArguments + +sh +-c +launchctl setenv Grid $HOME/src/Grid +launchctl setenv GridDebug mpidebug +launchctl setenv GridRelease mpirelease +launchctl setenv GridPre $HOME/bin +launchctl setenv GridPkg /opt/local + + +RunAtLoad + + + +``` + +These environment variables will be set each time your machine boots, so either reboot, or load them manually with: + + launchctl load ~/Library/LaunchAgents/environment.plist + launchctl start ~/Library/LaunchAgents/environment.plist + +NB: if they are already loaded, you will need to unload them first, with: + + launchctl stop ~/Library/LaunchAgents/environment.plist + launchctl unload ~/Library/LaunchAgents/environment.plist + +## 3. Install and build Open MPI -- ***optional*** + +Download the latest version of [Open MPI][OMPI] version 3.1 (I used 3.1.3). + +NB: Grid does not have any dependencies on fortran, however many standard scientific packages do, so you may as well download GNU fortran (e.g. MacPorts ``gfortran`` package) and build Open MPI like so: + +[OMPI]: https://www.open-mpi.org/software/ompi/v3.1/ + + ../configure CC=clang CXX=clang++ F77=gfortran FC=gfortran CXXFLAGS=-g --prefix=$GridPre/openmpi-3.1.3 + make -j 4 all install + +(If you don't want to bother with fortran bindings, just don't include the F77 and FC flags) + +## 4. Install and build Grid pre-requisites + +To simplify the installation of **Grid pre-requisites**, you can use your favourite package manager, e.g.: + +### 1. [MacPorts][MacPorts] + +[MacPorts]: https://www.macports.org "MacPorts package manager" + +Install [MacPorts][MacPorts] if you haven't done so already, and then install packages with: + + sudo port install + +These are the `portname`s for mandatory Grid libraries: + +* git +* gmp +* mpfr + +and these are the `portname`s for optional Grid libraries: + +* fftw-3 +* hdf5 +* lapack +* doxygen +* OpenBLAS + +***Please update this list with any packages I've missed! ... and double-check whether OpenBLAS is really for Grid*** + +### 2. [Homebrew][Homebrew] + +[Homebrew]: https://brew.sh "Homebrew package manager" + +Install [Homebrew][Homebrew] if you haven't done so already, and then install packages with: + + sudo brew install + +The same packages are available as from MacPorts. + +### Install LIME ***optional*** + +There isn't currently a port for [C-LIME][C-LIME], so download the source and then build it: + +[C-LIME]: https://usqcd-software.github.io/c-lime/ "C-language API for Lattice QCD Interchange Message Encapsulation / Large Internet Message Encapsulation" + +../configure --prefix=$GridPre/lime-1.3.2 CC=clang +make -j 4 +make install + +## 5. Install and Build Grid + +### Install Grid + +[Grid]: https://github.com/paboyle/Grid + +Start by cloning [Grid (from GitHub)][Grid] ***into the directory you specified in*** `$Grid`. Bear in mind that git will create the `Grid` subdirectory to place Grid in, so for example if `$Grid` is set to `~/src/Grid` then install Grid with: + + cd ~/src + +followed by either: + + git clone git@github.com:paboyle/Grid.git + +or + + git clone https://github.com/paboyle/Grid.git + +depending on whether you are using https or ssh. + +### Build Grid + +The Xcode build system supports ***debug*** and ***release*** configurations for each project (more configurations can be defined). We will create separate Grid build directories for each configuration, using the Grid **Autoconf** build system to make each configuration. NB: it is **not** necessary to run `make install` on them once they are built (IDE features such as *jump to definition* will work better of you don't). + +Below are shown the ``configure`` script invocations below for debug and release configurations, with and without MPI. You are unlikely to need all four combinations, so as a minimum, just build ``build_mpidebug`` (or ``build_debug`` if you don't want MPI). You probably want to build the corresponding release configuration (``build_mpirelease`` or ``build_release``), but this is optional. + +For each configuration, run the `configure` script (shown below) ***first***, then make the configuration with: + + make -j 4 + +NB: you **do not** need to run ``make install`` for these to work with Xcode. + +### 1. build_mpidebug + +This is the build for every day developing with Xcode. It uses the Xcode clang c++ compiler, with MPI and debug symbols. This is the version of Grid Xcode links to for **debug** configurations. + + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridMPIDebug --enable-comms=mpi-auto MPICXX=$GridPre/openmpi-3.1.3/bin/mpicxx CXXFLAGS=-g --enable-doxygen-doc + +### 2. build_mpirelease + +Much the same as `build_mpidebug`, except without debug symbols for **release** configurations (it can be handy to use `#ifdef DEBUG` while developing, and it's useful to be able to compile and test the alternate case). + + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridMPIRelease --enable-comms=mpi-auto MPICXX=$GridPre/openmpi-3.1.3/bin/mpicxx CXXFLAGS=-g --enable-doxygen-doc + +### 3. build_debug + +Debug configuration without MPI: + + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridDebug --enable-comms=none CXXFLAGS=-g --enable-doxygen-doc + +### 4. build_release + +Release configuration without MPI: + + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridRelease --enable-comms=none + +# Make a new application using Grid + +NB: Instead of following the instructions in this section, you can clone `HelloGrid` from the [University of Edinburgh GitLab site][HelloGrid]. + +[HelloGrid]: https://git.ecdf.ed.ac.uk/s1786208/HelloGrid + +## Make a new application + +To make a hello world application for Grid: + +* Start Xcode +* Click 'Create a new project' +* Click ‘macOS’, then in the ‘Application’ section choose ‘Command Line Tool’, then click ‘Next’ +* Choose options for your new project: + * Product Name: HelloGrid + * Team: None + * Organisation Name: sopa + * Organisation Identifier: uk.ac.ed.ph + * Language: C++ + * ... then click ‘Next’ +* Choose a location for your project, e.g. `$HOME/src`. NB: The project and all it’s files will be created inside `$HOME/src/HelloGrid`. If you are using Git, you can put the new project under Git source control immediately, if you like. Now click ‘Create’. + +## Configure your new application to use Grid + +Click the project name (`HelloGrid`) in the project navigator pane on the left (command-1 if it's not visible), then click the project name (`HelloGrid`) under `PROJECT` in the second pane. Click the `Build Settings` tab on the right, then under that click `All` and `Combined`. You should see: + +![Project settings](GridXcFig1.png) + +We now need to make changes to two sections (these are listed in alphabetical order), bearing in mind that if you are not using MPI (or you gave your build directories different names) replace `build_mpidebug` and `build_mpirelease` with the directory names you used. + +### 1. Search Paths + +#### HEADER_SEARCH_PATHS + +Obtain a list of header locations required by Grid by running the following from your Grid build directory: + + ./grid-config --cxxflags + +Output should look similar to: + + -I$GridPre/openmpi-3.1.3/include -I$GridPkg/include -I$GridPre/lime-1.3.2/include -I$GridPkg/include -I$GridPkg/include -I$GridPkg/include -O3 -g -std=c++11 + +The header locations follow the `-I` switches. You can ignore the other switches, and you can ignore duplicate entries, which just mean that your package manager has installed multiple packages in the same location. + +*Note: `grid-config` will output absolute paths. Make sure to replace absolute paths with environment variables (such as `$GridPre`) in your settings, so that the project will work unmodified for other collaborators downloading the same project from git.* + +Set the **Debug** HEADER_SEARCH_PATHS to: + + $Grid/build_$GridDebug/Grid + $Grid + +followed by (***the order is important***) the locations reported by `grid-config --cxxflags`, ignoring duplicates, e.g.: + + $GridPre/openmpi-3.1.3/include + $GridPkg/include + $GridPre/lime-1.3.2/include + +**Note: the easiest way to set this value is to put it all on one line, space separated, and edit the text to the right of `Debug`**, i.e.: + + $Grid/build_$GridDebug/Grid $Grid $GridPre/openmpi-3.1.3/include $GridPkg/include $GridPre/lime-1.3.2/include + +Similarly, set the **Release** HEADER_SEARCH_PATHS to exactly the same settings, replacing `debug` in the first entry with `release`, e.g.: + + $Grid/build_$GridRelease/Grid $Grid $GridPre/openmpi-3.1.3/include $GridPkg/include $GridPre/lime-1.3.2/include + +#### LIBRARY_SEARCH_PATHS + +Obtain a list of library locations required by Grid by running the following from your Grid build directory: + + ./grid-config --ldflags + +Output should look similar to: + + -L$GridPre/openmpi-3.1.3/lib -L$GridPkg/lib -L$GridPre/lime-1.3.2/lib -L$GridPkg/lib -L$GridPkg/lib -L$GridPkg/lib + +Set the **Debug** LIBRARY_SEARCH_PATHS to: + + $Grid/build_$GridDebug/Grid + $Grid/build_$GridDebug/Hadrons + +followed by the locations reported by `grid-config --ldflags`, ignoring duplicates (again, the order is important), e.g.: + + $GridPre/openmpi-3.1.3/lib + $GridPkg/lib + $GridPre/lime-1.3.2/lib + +Again, this can be done all on one line: + + $Grid/build_$GridDebug/Grid $Grid/build_$GridDebug/Hadrons $GridPre/openmpi-3.1.3/lib $GridPkg/lib $GridPre/lime-1.3.2/lib + +Similarly, set the **Release** LIBRARY_SEARCH_PATHS to exactly the same settings, replacing `debug` in the first two entries with `release`, e.g.: + + $Grid/build_$GridRelease/Grid $Grid/build_$GridRelease/Hadrons $GridPre/openmpi-3.1.3/lib $GridPkg/lib $GridPre/lime-1.3.2/lib + +### 2. Linking + +#### OTHER_LDFLAGS + +The easiest way to link to all required libraries is to obtain a list of all libraries required by Grid by running the following from your Grid build directory: + + ./grid-config --libs + +and pasting the output ***with `-lGrid -lHadrons ` prepended*** (including the `-l` switches) directly into `OTHER_LDFLAGS`, e.g.: + + -lGrid -lHadrons -lmpi -lhdf5_cpp -lz -lcrypto -llime -lfftw3f -lfftw3 -lmpfr -lgmp -lstdc++ -lm -lz -lhdf5 + +NB: The library names should be the same for your **debug** and **release** configurations, so you can set this once for both configurations. + +## Edit your source code + +A hello world for grid is: + +```c++ +#include +using namespace Grid; + +int main(int argc, char * argv[]) { + Grid_init(&argc,&argv); + std::cout << GridLogMessage << "Hello Grid" << std::endl; + Grid_finalize(); + return 0; +} +``` + +## Create a `.gitignore` file for Xcode + +You can create an up-to-date .gitignore file to ignore all the Xcode temporary build files using [gitignore.io][GIO]. + +[GIO]: https://www.gitignore.io/api/xcode + +NB: If you let Xcode add your project to git when you created it, you probably want to remove your personal scheme selection from git: + + git rm --cached HelloGrid.xcodeproj/xcuserdata/$USER.xcuserdatad/xcschemes/xcschememanagement.plist + +## Run your program under the Xcode debugger + +First, specify command-line arguments. From the menu, select `Product`, then `Scheme`, then `Edit Scheme`. Select `Run` on the left, then select the `Arguments` tab on the right. Add the following to `Arguments passed on Launch`: + + --grid 4.4.4.8 + +If your program will be manipulating files, it's a good idea to specify the working directory on the `Options` tab under `Use Custom Working Directory` (by default, Xcode launches the program inside the Xcode build folder). + +Then click `Close`. + +Let's set a breakpoint by clicking on: + + Grid_finalize(); + +then from the menu selecting `Debug`, then `Breakpoints`, then `Add Breakpoint at Current Line`. + +Now click on the `Play` button (the right pointing triangle just to the right of the maximise button) to run your program under the debugger. (You may see dialog boxes the first couple of times asking whether to allow MPI to receive network requests - say yes to these.) + +The debug output pane opens at the bottom of Xcode, with output on the right (ending with `Hello Grid`) and local variables on the left i.e.: + +![Running under the debugger](GridXcFig2.png) + +See the Xcode documentation to learn about the debugger. When you're done, press `ctl-cmd-Y` to let the program run to completion. + +## Running multiple MPI processes + +### Use Xcode to launch `mpirun` + +You can tell Xcode to use mpirun to launch multiple copies of a target executable, however if you do this the debugger will attach to mpirun - not your target process. This can be useful - so long as you do not need to debug. + +To do this, edit the Scheme again (cmd-<), click on the `info` tab, then under Executable select `Other...` and enter the full path to your `mpirun`, e.g.: + + $GridPre/openmpi-3.1.3/bin + +NB: if `mpirun` is a link, don't be surprised if Xcode saves the name of the linked-to executable. + +Click on the `Arguments` tab, then under `Arguments Passed on Launch` add: + + -np 2 $(TARGET_BUILD_DIR)/$(TARGETNAME) + +and make sure this is the first argument - i.e. drag it to the top of the list of arguments. NB: You probably want to specify more arguments for the MPI run, but this should get you started. + +Then click `Close`. + +### Use Xcode to debug multple instances of your target + +From the `Debug` menu, select `Attach to Process by PID or Name ...`. In the `PID or Process Name` field, enter the name of your target. Then click `Attach`. + +From a terminal session, locate and run your executable using mpirun (*the mangled name of the project build products will not be exactly the same as this example*): + + $GridPre/openmpi-3.1.3/bin/mpirun -np 2 ~/Library/Developer/Xcode/DerivedData/HelloGrid-fiyyuveptaqelbbvllomcgjyvghr/Build/Products/Debug/HelloGrid --grid 4.4.4.8 + +The Xcode debugger will attach to the first process. From the `Debug` menu in Xcode, select `Attach to Process`, and other running instances of your application will appear at the top of the list. Attach to as many instances as you wish to debug. + +You are now debugging multiple MPI instances, and the Xcode debugger should look similar to this: + +![Debugging multiple MPI instances under the Xcode debugger](GridXcFig3.png) From 9c8aa2047dbf7372d39c7c8358cd11fdb61134b7 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 19 Mar 2019 07:33:19 +0000 Subject: [PATCH 167/347] Put GridXcode doc in subdirectory --- documentation/{ => GridXcode}/GridXcFig1.png | Bin documentation/{ => GridXcode}/GridXcFig2.png | Bin documentation/{ => GridXcode}/GridXcFig3.png | Bin documentation/{GridXcode.md => GridXcode/readme.md} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename documentation/{ => GridXcode}/GridXcFig1.png (100%) rename documentation/{ => GridXcode}/GridXcFig2.png (100%) rename documentation/{ => GridXcode}/GridXcFig3.png (100%) rename documentation/{GridXcode.md => GridXcode/readme.md} (100%) diff --git a/documentation/GridXcFig1.png b/documentation/GridXcode/GridXcFig1.png similarity index 100% rename from documentation/GridXcFig1.png rename to documentation/GridXcode/GridXcFig1.png diff --git a/documentation/GridXcFig2.png b/documentation/GridXcode/GridXcFig2.png similarity index 100% rename from documentation/GridXcFig2.png rename to documentation/GridXcode/GridXcFig2.png diff --git a/documentation/GridXcFig3.png b/documentation/GridXcode/GridXcFig3.png similarity index 100% rename from documentation/GridXcFig3.png rename to documentation/GridXcode/GridXcFig3.png diff --git a/documentation/GridXcode.md b/documentation/GridXcode/readme.md similarity index 100% rename from documentation/GridXcode.md rename to documentation/GridXcode/readme.md From 24cf3b9df5d102bc176602fa882ebc407688bd27 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 19 Mar 2019 12:12:39 +0000 Subject: [PATCH 168/347] Ignore Version.h as it's created by automake/autoconf --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 45a3ea53..5338acb9 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,4 @@ gh-pages/ ##################### Grid/qcd/spin/gamma-gen/*.h Grid/qcd/spin/gamma-gen/*.cc +Grid/util/Version.h From 02b96b46025c664bfcb34bda8e436d1e0806327f Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 20 Mar 2019 11:20:40 +0000 Subject: [PATCH 169/347] Fixed module list (messed up when I merged from develop) --- Hadrons/Modules.hpp | 100 +++++----------------- Hadrons/modules.inc | 199 +++++++++++--------------------------------- 2 files changed, 70 insertions(+), 229 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index b2c283c7..0ca8d61b 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,8 +1,3 @@ -#include -#include -#include -#include -#include #include #include #include @@ -26,59 +21,8 @@ #include #include #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -94,6 +38,17 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -107,27 +62,16 @@ #include #include #include -#include #include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 40c65f83..9768c830 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,55 +1,4 @@ modules_cc =\ - Modules/MUtilities/TestSeqConserved.cc \ - Modules/MUtilities/RandomVectors.cc \ - Modules/MUtilities/TestSeqGamma.cc \ - Modules/MUtilities/PrecisionCast.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MDistil/g5_multiply.cc \ - Modules/MDistil/BContraction.cc \ - Modules/MDistil/BC2.cc \ - Modules/MDistil/LapEvec.cc \ - Modules/MDistil/PerambFromSolve.cc \ - Modules/MDistil/Baryon2pt.cc \ - Modules/MDistil/PerambLight.cc \ - Modules/MDistil/DistilSink.cc \ - Modules/MDistil/DistilVectors.cc \ - Modules/MDistil/PerambMultipleSolves.cc \ - Modules/MContraction/WeakHamiltonianEye.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/Meson.cc \ - Modules/MContraction/WeakNeutral4ptDisc.cc \ - Modules/MContraction/WardIdentity.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/WeakHamiltonianNonEye.cc \ - Modules/MContraction/Baryon.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MAction/ZMobiusDWF.cc \ - Modules/MAction/WilsonClover.cc \ - Modules/MAction/Wilson.cc \ - Modules/MAction/DWF.cc \ - Modules/MAction/MobiusDWF.cc \ - Modules/MAction/ScaledDWF.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadA2AVectors.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadCosmHol.cc \ Modules/MContraction/Baryon.cc \ Modules/MContraction/Meson.cc \ Modules/MContraction/WeakEye3pt.cc \ @@ -88,44 +37,43 @@ modules_cc =\ Modules/MUtilities/PrecisionCast.cc \ Modules/MScalar/FreeProp.cc \ Modules/MScalar/ChargedProp.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MDistil/g5_multiply.cc \ + Modules/MDistil/BC2.cc \ + Modules/MDistil/BContraction.cc \ + Modules/MDistil/LapEvec.cc \ + Modules/MDistil/PerambMultipleSolves.cc \ + Modules/MDistil/DistilSink.cc \ + Modules/MDistil/Baryon2pt.cc \ + Modules/MDistil/PerambFromSolve.cc \ + Modules/MDistil/PerambLight.cc \ Modules/MNPR/Amputate.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MLoop/NoiseLoop.cc \ - Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/Div.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ - Modules/MScalarSUN/ShiftProbe.cc \ + Modules/MAction/Wilson.cc \ + Modules/MAction/MobiusDWF.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MAction/WilsonClover.cc \ + Modules/MAction/DWF.cc \ + Modules/MAction/ScaledDWF.cc \ Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/Grad.cc \ Modules/MScalarSUN/TrMag.cc \ Modules/MScalarSUN/TrKinetic.cc \ Modules/MScalarSUN/EMT.cc \ Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/TrKinetic.cc \ Modules/MScalarSUN/StochFreeField.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MSink/Smear.cc \ - Modules/MSink/Point.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MFermion/FreeProp.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/ScalarVP.cc \ - Modules/MScalar/VPCounterTerms.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MSource/SeqConserved.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/Z2.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/Momentum.cc + Modules/MScalarSUN/TwoPoint.cc \ + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/Div.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadCosmHol.cc \ + Modules/MIO/LoadA2AVectors.cc modules_hpp =\ - Modules/MUtilities/RandomVectors.hpp \ - Modules/MUtilities/TestSeqGamma.hpp \ - Modules/MUtilities/PrecisionCast.hpp \ - Modules/MUtilities/TestSeqConserved.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ Modules/MContraction/WeakEye3pt.hpp \ Modules/MContraction/Baryon.hpp \ Modules/MContraction/A2AAslashField.hpp \ @@ -149,59 +97,8 @@ modules_hpp =\ Modules/MSolver/LocalCoherenceLanczos.hpp \ Modules/MSolver/A2AAslashVectors.hpp \ Modules/MSolver/Guesser.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ Modules/MSolver/RBPrecCG.hpp \ Modules/MSolver/A2AVectors.hpp \ - Modules/MDistil/LapEvec.hpp \ - Modules/MDistil/Distil.hpp \ - Modules/MDistil/g5_multiply.hpp \ - Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/Baryon2pt.hpp \ - Modules/MDistil/BContraction.hpp \ - Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/DistilSink.hpp \ - Modules/MDistil/PerambFromSolve.hpp \ - Modules/MDistil/BC2.hpp \ - Modules/MDistil/PerambMultipleSolves.hpp \ - Modules/MContraction/WeakHamiltonian.hpp \ - Modules/MContraction/WeakNeutral4ptDisc.hpp \ - Modules/MContraction/WeakHamiltonianEye.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/WeakHamiltonianNonEye.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MContraction/WardIdentity.hpp \ - Modules/MAction/Wilson.hpp \ - Modules/MAction/WilsonClover.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/ScaledDWF.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MIO/LoadBinary.hpp \ - Modules/MIO/LoadCosmHol.hpp \ - Modules/MIO/LoadNersc.hpp \ - Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadEigenPack.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MLoop/NoiseLoop.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ Modules/MGauge/UnitEm.hpp \ Modules/MGauge/StoutSmearing.hpp \ Modules/MGauge/Unit.hpp \ @@ -217,6 +114,17 @@ modules_hpp =\ Modules/MScalar/FreeProp.hpp \ Modules/MScalar/Scalar.hpp \ Modules/MScalar/ChargedProp.hpp \ + Modules/MDistil/Distil.hpp \ + Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/DistilSink.hpp \ + Modules/MDistil/BC2.hpp \ + Modules/MDistil/PerambLight.hpp \ + Modules/MDistil/PerambMultipleSolves.hpp \ + Modules/MDistil/PerambFromSolve.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/Baryon2pt.hpp \ + Modules/MDistil/g5_multiply.hpp \ Modules/MNPR/Bilinear.hpp \ Modules/MNPR/Amputate.hpp \ Modules/MNPR/FourQuark.hpp \ @@ -230,28 +138,17 @@ modules_hpp =\ Modules/MScalarSUN/TwoPointNPR.hpp \ Modules/MScalarSUN/Div.hpp \ Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/TrKinetic.hpp \ Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ - Modules/MScalarSUN/ShiftProbe.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ - Modules/MFermion/GaugeProp.hpp \ - Modules/MFermion/FreeProp.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/ScalarVP.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/ChargedProp.hpp \ - Modules/MScalar/VPCounterTerms.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Point.hpp \ - Modules/MSource/Z2.hpp \ - Modules/MSource/Wall.hpp \ - Modules/MSource/SeqConserved.hpp + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MIO/LoadEigenPack.hpp \ + Modules/MIO/LoadNersc.hpp \ + Modules/MIO/LoadA2AVectors.hpp \ + Modules/MIO/LoadCosmHol.hpp \ + Modules/MIO/LoadCoarseEigenPack.hpp \ + Modules/MIO/LoadBinary.hpp From 4ae35000a91e90203344795126daaab24011e4fd Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 20 Mar 2019 13:36:57 +0000 Subject: [PATCH 170/347] removed module which we do not need --- Hadrons/Modules.hpp | 1 - .../Modules/MDistil/PerambMultipleSolves.cc | 7 - .../Modules/MDistil/PerambMultipleSolves.hpp | 288 ------------------ Hadrons/modules.inc | 1 - 4 files changed, 297 deletions(-) delete mode 100644 Hadrons/Modules/MDistil/PerambMultipleSolves.cc delete mode 100644 Hadrons/Modules/MDistil/PerambMultipleSolves.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 0ca8d61b..b0e50bed 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include diff --git a/Hadrons/Modules/MDistil/PerambMultipleSolves.cc b/Hadrons/Modules/MDistil/PerambMultipleSolves.cc deleted file mode 100644 index b5b92315..00000000 --- a/Hadrons/Modules/MDistil/PerambMultipleSolves.cc +++ /dev/null @@ -1,7 +0,0 @@ -#include - -using namespace Grid; -using namespace Hadrons; -using namespace MDistil; - -template class Grid::Hadrons::MDistil::TPerambMultipleSolves; diff --git a/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp b/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp deleted file mode 100644 index 845dca46..00000000 --- a/Hadrons/Modules/MDistil/PerambMultipleSolves.hpp +++ /dev/null @@ -1,288 +0,0 @@ -#ifndef Hadrons_MDistil_PerambMultipleSolves_hpp_ -#define Hadrons_MDistil_PerambMultipleSolves_hpp_ - -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation -#include - -BEGIN_HADRONS_NAMESPACE - - -/****************************************************************************** - * PerambMultipleSolves * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) - -class PerambMultipleSolvesPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(PerambMultipleSolvesPar, - std::string, eigenPack, - std::string, UniqueIdentifier, - int, nsolves, - std::vector, nvecs, - int, nvec, - bool, multiFile, - DistilParameters, Distil, - std::string, solver); -}; - -template -class TPerambMultipleSolves: public Module -{ -public: - FERM_TYPE_ALIASES(FImpl,); - SOLVER_TYPE_ALIASES(FImpl,); - // constructor - TPerambMultipleSolves(const std::string name); - // destructor - virtual ~TPerambMultipleSolves(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); -protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) -protected: - virtual void Cleanup(void); -private: - unsigned int Ls_; -}; - -MODULE_REGISTER_TMP(PerambMultipleSolves, TPerambMultipleSolves, MDistil); - -// constructor ///////////////////////////////////////////////////////////////// -template -TPerambMultipleSolves::TPerambMultipleSolves(const std::string name) -: grid3d{nullptr}, grid4d{nullptr}, Module(name) -{} - -// destructor -template -TPerambMultipleSolves::~TPerambMultipleSolves(void) -{ - Cleanup(); -}; - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TPerambMultipleSolves::getInput(void) -{ - std::vector in; - - in.push_back(par().eigenPack); - in.push_back(par().solver); - - return in; -} - -template -std::vector TPerambMultipleSolves::getOutput(void) -{ - std::vector out; - const int nsolves{par().nsolves}; - std::vector nvecs{par().nvecs}; - - for(int i=0;i -void TPerambMultipleSolves::setup(void) -{ - Cleanup(); - - const int nvec{par().nvec}; - const int nsolves{par().nsolves}; - std::vector nvecs{par().nvecs}; - const DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - const int nnoise{Distil.nnoise}; - const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; - const int Ns{Distil.Ns}; - std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - - envCreate(std::vector, getName() + "_noise", 1, - nvec*Distil.Ns*Distil.Nt*Distil.nnoise); - for(int i=0;i, getName() + "_solve_"+std::to_string(nvecs[i]), 1, - nnoise*nvecs[i]*Ns*Nt_inv, envGetGrid(FermionField)); - } - grid4d = env().getGrid(); - grid3d = MakeLowerDimGrid(grid4d);//new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); - - envTmpLat(GaugeField, "Umu"); - envTmpLat(LatticeSpinColourVector, "dist_source"); - //envTmp(std::vector, "sources", 1, - // std::vector( nsolves, grid4d )); - envTmpLat(LatticeSpinColourVector, "tmp2"); - envTmpLat(LatticeSpinColourVector, "result"); - envTmpLat(LatticeColourVector, "result_nospin"); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); - - Ls_ = env().getObjectLs(par().solver); - envTmpLat(FermionField, "v4dtmp"); - envTmpLat(FermionField, "v5dtmp", Ls_); - envTmpLat(FermionField, "v5dtmp_sol", Ls_); -} - -// clean up any temporaries created by setup (that aren't stored in the environment) -template -void TPerambMultipleSolves::Cleanup(void) -{ - if( grid3d != nullptr ) { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; -} - -// execution /////////////////////////////////////////////////////////////////// -template -void TPerambMultipleSolves::execute(void) -{ - const int nsolves{par().nsolves}; - const int nvec{par().nvec}; - std::vector nvecs{par().nvecs}; - const DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - //const int SI{Distil.SI}; - const int TI{Distil.TI}; - const int nnoise{Distil.nnoise}; - const int Nt{Distil.Nt}; - const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; - const int tsrc{Distil.tsrc}; - const int Ns{Distil.Ns}; - - auto &solver=envGet(Solver, par().solver); - auto &mat = solver.getFMat(); - envGetTmp(FermionField, v4dtmp); - envGetTmp(FermionField, v5dtmp); - envGetTmp(FermionField, v5dtmp_sol); - - const bool full_tdil{TI==Nt}; - const bool exact_distillation{full_tdil && LI==nvec}; - - const std::string &UniqueIdentifier{par().UniqueIdentifier}; - - auto &noise = envGet(std::vector, getName() + "_noise"); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - std::vector> solves(nsolves); - for(int i=0;i, getName() +"_solve_"+std::to_string(nvecs[i])); - solves[i].resize(nnoise*nvecs[i]*Ns*Nt_inv, grid4d); - solves[i]=unsmeared_sink; - } - - GridSerialRNG sRNG; - sRNG.SeedUniqueString(UniqueIdentifier + std::to_string(vm().getTrajectory())); - Real rn; - - for (int inoise=0;inoise 0.5) ? -1 : 1; - } - } - } - } - } - - - envGetTmp(LatticeSpinColourVector, dist_source); - envGetTmp(LatticeSpinColourVector, tmp2); - envGetTmp(LatticeSpinColourVector, result); - envGetTmp(LatticeColourVector, result_nospin); - envGetTmp(LatticeSpinColourVector, tmp3d); - envGetTmp(LatticeColourVector, tmp3d_nospin); - envGetTmp(LatticeColourVector, result_3d); - envGetTmp(LatticeColourVector, evec3d); - //envGetTmp(std::vector, sources); - - const int Ntlocal{grid4d->LocalDimensions()[3]}; - const int Ntfirst{grid4d->LocalStarts()[3]}; - - { - - int t_inv; - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < Ns; ds++) { - std::cout << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; - dist_source = zero; - tmp3d_nospin = zero; - evec3d = zero; - for (int it = dt; it < Nt; it += TI){ - if (full_tdil) t_inv = tsrc; else t_inv = it; - if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += Ns){ // TODO: Also allow non-full spin dilution (re-define exact_distillation?) - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); - tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; - tmp3d=zero; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=zero; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); - dist_source += tmp2; - } - } - } - } - std::cout << "Inversion for noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; - result=zero; - v4dtmp = dist_source; - if (Ls_ == 1){ - solver(result, v4dtmp); - } else { - mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); - solver(v5dtmp_sol, v5dtmp); - mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); - result = v4dtmp; - } - for (int isource = 0; isource < nsolves; isource ++){ - if(dk < nvecs[isource]){ - solves[isource][inoise+nnoise*(dk+nvecs[isource]*(dt+Nt_inv*ds))] = result; - } - } - } - } - } - } - } -} - - - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MDistil_PerambMultipleSolves_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 9768c830..9a839f1d 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -42,7 +42,6 @@ modules_cc =\ Modules/MDistil/BC2.cc \ Modules/MDistil/BContraction.cc \ Modules/MDistil/LapEvec.cc \ - Modules/MDistil/PerambMultipleSolves.cc \ Modules/MDistil/DistilSink.cc \ Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambFromSolve.cc \ From a66bb8acba6f8398d8398dbcb393e412cf71d66a Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 20 Mar 2019 14:41:36 +0000 Subject: [PATCH 171/347] fixed possible memory leak --- Hadrons/Modules/MDistil/DistilSink.hpp | 36 +++++++++++++++++++---- Hadrons/Modules/MDistil/DistilVectors.hpp | 36 +++++++++++++++++++---- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilSink.hpp b/Hadrons/Modules/MDistil/DistilSink.hpp index ccd8a8aa..c83fd755 100644 --- a/Hadrons/Modules/MDistil/DistilSink.hpp +++ b/Hadrons/Modules/MDistil/DistilSink.hpp @@ -47,7 +47,7 @@ public: // constructor TDistilSink(const std::string name); // destructor - virtual ~TDistilSink(void) {}; + virtual ~TDistilSink(void); // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); @@ -55,6 +55,12 @@ public: virtual void setup(void); // execution virtual void execute(void); +protected: + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) +protected: + virtual void Cleanup(void); }; MODULE_REGISTER_TMP(DistilSink, TDistilSink, MDistil); @@ -65,8 +71,14 @@ MODULE_REGISTER_TMP(DistilSink, TDistilSink, MDistil); // constructor ///////////////////////////////////////////////////////////////// template TDistilSink::TDistilSink(const std::string name) -: Module(name) +: grid3d{nullptr}, grid4d{nullptr}, Module(name) {} +// destructor +template +TDistilSink::~TDistilSink(void) +{ + Cleanup(); +}; // dependencies/products /////////////////////////////////////////////////////// template @@ -92,6 +104,7 @@ std::vector TDistilSink::getOutput(void) template void TDistilSink::setup(void) { + Cleanup(); int nnoise=par().nnoise; int LI=par().LI; int Ns=par().Ns; @@ -100,7 +113,8 @@ void TDistilSink::setup(void) envCreate(std::vector, getName(), 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); - GridCartesian * grid4d = env().getGrid(); + //GridCartesian * grid4d = env().getGrid(); + grid4d = env().getGrid(); std::vector latt_size = GridDefaultLatt(); std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); std::vector mpi_layout = GridDefaultMpi(); @@ -108,7 +122,8 @@ void TDistilSink::setup(void) latt_size[Nd-1] = 1; simd_layout_3.push_back( 1 ); mpi_layout[Nd-1] = 1; - GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + //GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + grid3d = MakeLowerDimGrid(grid4d); envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); @@ -119,6 +134,17 @@ void TDistilSink::setup(void) envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); } +// clean up any temporaries created by setup (that aren't stored in the environment) +template +void TDistilSink::Cleanup(void) +{ + if( grid3d != nullptr ) { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; +} + // execution /////////////////////////////////////////////////////////////////// template void TDistilSink::execute(void) @@ -135,7 +161,7 @@ void TDistilSink::execute(void) envGetTmp(LatticeSpinColourVector, sink_tslice); envGetTmp(LatticeColourVector, evec3d); - GridCartesian * grid4d = env().getGrid(); + //GridCartesian * grid4d = env().getGrid(); int Ntlocal = grid4d->LocalDimensions()[3]; int Ntfirst = grid4d->LocalStarts()[3]; diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 2efd10fa..0cc3cf58 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -48,7 +48,7 @@ public: // constructor TDistilVectors(const std::string name); // destructor - virtual ~TDistilVectors(void) {}; + virtual ~TDistilVectors(void); // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); @@ -56,6 +56,12 @@ public: virtual void setup(void); // execution virtual void execute(void); +protected: + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) +protected: + virtual void Cleanup(void); }; MODULE_REGISTER_TMP(DistilVectors, TDistilVectors, MDistil); @@ -66,8 +72,14 @@ MODULE_REGISTER_TMP(DistilVectors, TDistilVectors, MDistil); // constructor ///////////////////////////////////////////////////////////////// template TDistilVectors::TDistilVectors(const std::string name) -: Module(name) +: grid3d{nullptr}, grid4d{nullptr}, Module(name) {} +// destructor +template +TDistilVectors::~TDistilVectors(void) +{ + Cleanup(); +}; // dependencies/products /////////////////////////////////////////////////////// template @@ -94,6 +106,7 @@ std::vector TDistilVectors::getOutput(void) template void TDistilVectors::setup(void) { + Cleanup(); //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); @@ -107,7 +120,8 @@ void TDistilVectors::setup(void) envCreate(std::vector, getName() + "_phi", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); - GridCartesian * grid4d = env().getGrid(); + //GridCartesian * grid4d = env().getGrid(); + grid4d = env().getGrid(); std::vector latt_size = GridDefaultLatt(); std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); std::vector mpi_layout = GridDefaultMpi(); @@ -115,7 +129,8 @@ void TDistilVectors::setup(void) latt_size[Nd-1] = 1; simd_layout_3.push_back( 1 ); mpi_layout[Nd-1] = 1; - GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + //GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); + grid3d = MakeLowerDimGrid(grid4d); envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); @@ -126,6 +141,17 @@ void TDistilVectors::setup(void) envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); } +// clean up any temporaries created by setup (that aren't stored in the environment) +template +void TDistilVectors::Cleanup(void) +{ + if( grid3d != nullptr ) { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; +} + // execution /////////////////////////////////////////////////////////////////// template void TDistilVectors::execute(void) @@ -145,7 +171,7 @@ void TDistilVectors::execute(void) envGetTmp(LatticeSpinColourVector, sink_tslice); envGetTmp(LatticeColourVector, evec3d); - GridCartesian * grid4d = env().getGrid(); + //GridCartesian * grid4d = env().getGrid(); int Ntlocal = grid4d->LocalDimensions()[3]; int Ntfirst = grid4d->LocalStarts()[3]; From 88cb00473151afad4e937f4d7703f548b2def287 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 20 Mar 2019 22:05:16 +0000 Subject: [PATCH 172/347] Fixed single-precision issues in Test_serialisation --- tests/IO/Test_serialisation.cc | 241 ++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 109 deletions(-) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 7a6b33cd..d97041ef 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -30,6 +30,7 @@ Author: Michael Marshall /* END LEGAL */ #include #include +#include using namespace Grid; using namespace Grid::QCD; @@ -109,126 +110,148 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam std::cout << " done." << std::endl; } -typedef ComplexD TestScalar; -typedef Eigen::TensorFixedSize> TensorRank5UShort; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> TensorRank5UShortAlt; -typedef Eigen::Tensor TensorRank3; -typedef Eigen::TensorFixedSize, Eigen::StorageOptions::RowMajor> Tensor_9_4_2; -typedef std::vector aTensor_9_4_2; -typedef Eigen::TensorFixedSize> LSCTensor; +// Perform I/O tests on a range of tensor types +// Test coverage: scalars, complex and GridVectors in single, double and default precision +class TensorIO : public Serializable { + using TestScalar = ComplexD; + using SR3 = Eigen::Sizes<9,4,2>; + using SR5 = Eigen::Sizes<5,4,3,2,1>; + using ESO = Eigen::StorageOptions; + using TensorRank3 = Eigen::Tensor; + using TensorR5 = Eigen::TensorFixedSize; + using TensorR5Alt = Eigen::TensorFixedSize; + using Tensor942 = Eigen::TensorFixedSize; + using aTensor942 = std::vector; + using Perambulator = Eigen::Tensor; + using LSCTensor = Eigen::TensorFixedSize>; + + static const Real FlagR; + static const Complex Flag; + static const ComplexF FlagF; + static const TestScalar FlagTS; + static const char * const pszFilePrefix; -class PerambIOTestClass: Serializable { - ComplexD Flag; + void Init(unsigned short Precision) + { + SequentialInit(Perambulator1, Flag, Precision); + SequentialInit(Perambulator2, Flag, Precision); + SequentialInit(tensorR5, FlagR, Precision); + SequentialInit(tensorRank3, FlagF, Precision); + SequentialInit(tensor_9_4_2, FlagTS, Precision); + for( auto &t : atensor_9_4_2 ) + SequentialInit(t, FlagTS, Precision); + SequentialInit(MyLSCTensor, Flag, Precision); + } + + // Perform an I/O test for a single Eigen tensor (of any type) + template + static void TestOne(const char * MyTypeName, unsigned short Precision, std::string &filename, + const char * pszExtension, unsigned int &TestNum, + typename EigenIO::Traits::scalar_type Flag, IndexTypes... otherDims) + { + using Traits = EigenIO::Traits; + using scalar_type = typename Traits::scalar_type; + std::unique_ptr pTensor{new T(otherDims...)}; + SequentialInit( * pTensor, Flag, Precision ); + filename = pszFilePrefix + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; + ioTest(filename, * pTensor, MyTypeName, MyTypeName); + } + public: - using PerambTensor = Eigen::Tensor; - GRID_SERIALIZABLE_CLASS_MEMBERS(PerambIOTestClass - , SpinColourVector, spinColourVector - , SpinColourMatrix, spinColourMatrix + GRID_SERIALIZABLE_CLASS_MEMBERS(TensorIO + , SpinColourVector, spinColourVector + , SpinColourMatrix, spinColourMatrix , std::vector, DistilParameterNames , std::vector, DistilParameterValues - , PerambTensor, Perambulator - , PerambTensor, Perambulator2 - , TensorRank5UShort, tensorRank5UShort + , Perambulator, Perambulator1 + , Perambulator, Perambulator2 + , TensorR5, tensorR5 , TensorRank3, tensorRank3 - , Tensor_9_4_2, tensor_9_4_2 - , aTensor_9_4_2, atensor_9_4_2 + , Tensor942, tensor_9_4_2 + , aTensor942, atensor_9_4_2 , LSCTensor, MyLSCTensor ); - PerambIOTestClass() + TensorIO() : DistilParameterNames {"do", "androids", "dream", "of", "electric", "sheep?"} , DistilParameterValues{2,3,1,4,5,1} - , Perambulator(2,3,1,4,5,1) + , Perambulator1(2,3,1,4,5,1) , Perambulator2(7,1,6,1,5,1) , tensorRank3(7,3,2) - , atensor_9_4_2(3) - //, Flag(1,-3.1415927) - , Flag(1,-1) + , atensor_9_4_2(3) {} + +#define TEST_PARAMS( T ) #T, Precision, filename, pszExtension, TestNum + + // Perform a series of I/O tests for Eigen tensors, including a serialisable object + template + static void Test(const char * pszExtension, unsigned short Precision = 0) { - SequentialInit(Perambulator, Flag); - SequentialInit(Perambulator2, Flag); - SequentialInit(tensorRank5UShort); - SequentialInit(tensorRank3, Flag); - SequentialInit(tensor_9_4_2, Flag); - for( auto &t : atensor_9_4_2 ) SequentialInit(t, Flag); - SequentialInit( MyLSCTensor, Flag ); + // Perform a series of tests on progressively more complex tensors + unsigned int TestNum = 0; + std::string filename; + // Rank 1 tensor containing a single integer + using TensorSingle = Eigen::TensorFixedSize>; + TestOne( TEST_PARAMS( TensorSingle ), 7 ); // lucky! + // Rather convoluted way of defining a single complex number + using TensorSimple = Eigen::Tensor, 6>; + using I = typename TensorSimple::Index; // NB: Never specified, so same for all my test tensors + // Try progressively more complicated tensors + TestOne( TEST_PARAMS( TensorSimple ), FlagTS, 1,1,1,1,1,1 ); + TestOne( TEST_PARAMS( TensorRank3 ), FlagF, 6, 3, 2 ); + TestOne(TEST_PARAMS( Tensor942 ), FlagTS); + TestOne(TEST_PARAMS( LSCTensor ), Flag ); + + // Now see whether we can write a tensor in one memory order and read back in the other + { + TestOne(TEST_PARAMS( TensorR5 ), FlagR); + std::cout << " Testing alternate memory order read ... "; + TensorR5Alt t2; + RDR_ reader(filename); + ::Grid::read(reader, "TensorR5", t2); + bool good = true; + TensorR5 cf; + SequentialInit( cf, FlagR, Precision ); + for_all( t2, [&](typename EigenIO::Traits::scalar_type c, I n, + const std::array &TensorIndex, + const std::array::Rank> &GridTensorIndex ){ + Real &r = cf(TensorIndex); + if( c != r ){ + good = false; + std::cout << "\nError: " << n << ": " << c << " != " << r; + } + } ); + if (!good) { + std::cout << std::endl; + dump_tensor(t2,"t2"); + exit(EXIT_FAILURE); + } + std::cout << " done." << std::endl; + } + // Now test a serialisable object containing a number of tensors + { + static const char MyTypeName[] = "TensorIO"; + filename = pszFilePrefix + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; + std::unique_ptr pObj{new TensorIO()}; + pObj->Init(Precision); + ioTest(filename, * pObj, MyTypeName, MyTypeName, Precision); + } + // Stress test. Too large for the XML or text readers and writers! +#ifdef STRESS_TEST + const std::type_info &tw = typeid( WTR_ ); + if( tw == typeid( Hdf5Writer ) || tw == typeid( BinaryWriter ) ) { + using LCMTensor=Eigen::TensorFixedSize,2>,7>,3>, + Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor>; + std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; + TestOne(TEST_PARAMS( LCMTensor ), Flag); + } +#endif } }; -#define TEST_PARAMS( T ) #T, Flag, Precision, filename, pszExtension, TestNum - -// Perform an I/O test for a single Eigen tensor (of any type) -template -void EigenTensorTestSingle(const char * MyTypeName, typename EigenIO::Traits::scalar_type Flag, - unsigned short Precision, std::string &filename, const char * pszExtension, - unsigned int &TestNum, IndexTypes... otherDims) -{ - using Traits = EigenIO::Traits; - using scalar_type = typename Traits::scalar_type; - std::unique_ptr pTensor{new T(otherDims...)}; - SequentialInit( * pTensor, Flag, Precision ); - filename = "iotest_" + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; - ioTest(filename, * pTensor, MyTypeName, MyTypeName); -} - -// Perform a series of I/O tests for Eigen tensors, including a serialisable object -template -void EigenTensorTest(const char * pszExtension, unsigned short Precision = 0) -{ - // Perform a series of tests on progressively more complex tensors - unsigned int TestNum = 0; - std::string filename; - { - int Flag = 7; - using TensorSingle = Eigen::TensorFixedSize>; - EigenTensorTestSingle(TEST_PARAMS( TensorSingle )); - } - TestScalar Flag{1,-3.1415927}; - using TensorSimple = Eigen::Tensor, 6>; - using I = typename TensorSimple::Index; - EigenTensorTestSingle( TEST_PARAMS( TensorSimple ), 1, 1, 1, 1, 1, 1 ); - EigenTensorTestSingle( TEST_PARAMS( TensorRank3 ), 6, 3, 2 ); - EigenTensorTestSingle(TEST_PARAMS( Tensor_9_4_2 )); - EigenTensorTestSingle(TEST_PARAMS( LSCTensor )); - // Now see whether we could write out a tensor in one memory order and read back in the other - { - unsigned short Flag = 1; - EigenTensorTestSingle(TEST_PARAMS( TensorRank5UShort )); - std::cout << " Testing alternate memory order read ... "; - TensorRank5UShortAlt t2; - RDR_ reader(filename); - read(reader, "TensorRank5UShort", t2); - bool good = true; - using Index = typename TensorRank5UShortAlt::Index; - // NB: I can't call - for_all( t2, [&](unsigned short c, Index n, - const std::array &TensorIndex, - const std::array::Rank> &GridTensorIndex ){ - good = good && ( c == n ); - } ); - if (!good) { - std::cout << " failure!" << std::endl; - dump_tensor(t2,"t2"); - exit(EXIT_FAILURE); - } - std::cout << " done." << std::endl; - } - // Now test a serialisable object containing a number of tensors - { - static const char MyTypeName[] = "PerambIOTestClass"; - std::unique_ptr pObj{new PerambIOTestClass()}; - filename = "iotest_" + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; - ioTest(filename, * pObj, MyTypeName, MyTypeName); - } - // Stress test. Too large for the XML or text readers and writers! -#ifdef STRESS_TEST - if( typeid( WTR_ ).name() == typeid( Hdf5Writer ).name() || typeid( WTR_ ).name() == typeid( BinaryWriter ).name() ) { - using LCMTensor=Eigen::TensorFixedSize,2>,7>,3>, - Eigen::Sizes<2,4,11,10,9>, Eigen::StorageOptions::RowMajor>; - std::cout << "sizeof( LCMTensor ) = " << sizeof( LCMTensor ) / 1024 / 1024 << " MB" << std::endl; - EigenTensorTestSingle(TEST_PARAMS( LCMTensor )); - } -#endif -} +const Real TensorIO::FlagR {-1.001}; +const Complex TensorIO::Flag {1,-3.1415927}; +const ComplexF TensorIO::FlagF {1,-3.1415927}; +const TensorIO::TestScalar TensorIO::FlagTS{1,-3.1415927}; +const char * const TensorIO::pszFilePrefix = "tensor_"; template void tensorConvTestFn(GridSerialRNG &rng, const std::string label) @@ -314,14 +337,14 @@ int main(int argc,char **argv) ioTest("iotest.h5", obj, "HDF5 (object) "); ioTest("iotest.h5", vec, "HDF5 (vector of objects)"); std::cout << "\n==== detailed Hdf5 tensor tests (Grid::EigenIO)" << std::endl; - EigenTensorTest(".h5"); + TensorIO::Test(".h5"); #endif std::cout << "\n==== detailed binary tensor tests (Grid::EigenIO)" << std::endl; - EigenTensorTest(".bin"); + TensorIO::Test(".bin"); std::cout << "\n==== detailed xml tensor tests (Grid::EigenIO)" << std::endl; - EigenTensorTest(".xml", 6); + TensorIO::Test(".xml", 6); std::cout << "\n==== detailed text tensor tests (Grid::EigenIO)" << std::endl; - EigenTensorTest(".dat", 5); + TensorIO::Test(".dat", 5); std::cout << "\n==== vector flattening/reconstruction" << std::endl; typedef std::vector>> vec3d; From d2d26b302db0f2d1ce1d758a1175e42508ec1e46 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 20 Mar 2019 22:59:20 +0000 Subject: [PATCH 173/347] Removed the module we don't need from modules.inc (so make now works) i.e. removed Modules/MDistil/PerambMultipleSolves.hpp from Hadrons/modules.inc --- Hadrons/modules.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 9a839f1d..d63333cd 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -119,7 +119,6 @@ modules_hpp =\ Modules/MDistil/DistilSink.hpp \ Modules/MDistil/BC2.hpp \ Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/PerambMultipleSolves.hpp \ Modules/MDistil/PerambFromSolve.hpp \ Modules/MDistil/BContraction.hpp \ Modules/MDistil/Baryon2pt.hpp \ From 8700dd4d0d3c76abf6542ea78fd5b923fd021982 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 22 Mar 2019 12:41:53 +0000 Subject: [PATCH 174/347] modules list --- Hadrons/modules.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 9a839f1d..d63333cd 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -119,7 +119,6 @@ modules_hpp =\ Modules/MDistil/DistilSink.hpp \ Modules/MDistil/BC2.hpp \ Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/PerambMultipleSolves.hpp \ Modules/MDistil/PerambFromSolve.hpp \ Modules/MDistil/BContraction.hpp \ Modules/MDistil/Baryon2pt.hpp \ From fbf286b0e39415e5d32bbf3574bdf1705a3510ed Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 22 Mar 2019 13:30:11 +0000 Subject: [PATCH 175/347] added Spin dilution --- Hadrons/Modules/MDistil/DistilSink.hpp | 16 ++++++---------- Hadrons/Modules/MDistil/DistilVectors.hpp | 17 +++++++++-------- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 3 ++- Hadrons/Modules/MDistil/PerambLight.hpp | 6 +++--- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilSink.hpp b/Hadrons/Modules/MDistil/DistilSink.hpp index c83fd755..d56ab5ac 100644 --- a/Hadrons/Modules/MDistil/DistilSink.hpp +++ b/Hadrons/Modules/MDistil/DistilSink.hpp @@ -108,13 +108,13 @@ void TDistilSink::setup(void) int nnoise=par().nnoise; int LI=par().LI; int Ns=par().Ns; + int SI=par().SI; int Nt_inv=par().Nt_inv; envCreate(std::vector, getName(), 1, - nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); + nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - //GridCartesian * grid4d = env().getGrid(); - grid4d = env().getGrid(); + grid4d = env().getGrid(); std::vector latt_size = GridDefaultLatt(); std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); std::vector mpi_layout = GridDefaultMpi(); @@ -122,7 +122,6 @@ void TDistilSink::setup(void) latt_size[Nd-1] = 1; simd_layout_3.push_back( 1 ); mpi_layout[Nd-1] = 1; - //GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); grid3d = MakeLowerDimGrid(grid4d); @@ -161,14 +160,13 @@ void TDistilSink::execute(void) envGetTmp(LatticeSpinColourVector, sink_tslice); envGetTmp(LatticeColourVector, evec3d); - //GridCartesian * grid4d = env().getGrid(); - int Ntlocal = grid4d->LocalDimensions()[3]; int Ntfirst = grid4d->LocalStarts()[3]; int tsrc=par().tsrc; int nnoise=par().nnoise; int LI=par().LI; + int SI=par().SI; int Ns=par().Ns; int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI int Nt=par().Nt; @@ -182,8 +180,8 @@ void TDistilSink::execute(void) for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < Ns; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + for (int ds = 0; ds < SI; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; phi[vecindex] = zero; for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { sink_tslice=zero; @@ -198,8 +196,6 @@ void TDistilSink::execute(void) } } - std::cout << "size phi" << phi.size() << std::endl; - } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 0cc3cf58..9493f0e6 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -113,12 +113,13 @@ void TDistilVectors::setup(void) int nnoise=par().nnoise; int LI=par().LI; int Ns=par().Ns; + int SI=par().SI; int Nt_inv=par().Nt_inv; envCreate(std::vector, getName() + "_rho", 1, - nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); + nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); envCreate(std::vector, getName() + "_phi", 1, - nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); + nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); //GridCartesian * grid4d = env().getGrid(); grid4d = env().getGrid(); @@ -184,6 +185,7 @@ void TDistilVectors::execute(void) int Nt=par().Nt; int TI=par().TI; int nvec=par().nvec; + int SI=par().SI; bool full_tdil=(TI==Nt); @@ -192,18 +194,17 @@ void TDistilVectors::execute(void) for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < Ns; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + for (int ds = 0; ds < SI; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; rho[vecindex] = zero; tmp3d_nospin = zero; for (int it = dt; it < Nt; it += TI){ if (full_tdil) t_inv = tsrc; else t_inv = it; if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += Ns){ //at the moment, full spin dilution is enforced + for (int is = ds; is < Ns; is += SI){ ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; - //tmp3d_nospin = evec3d * noise[inoise][it][ik]()(is)(); //noises do not have to be a spin vector tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; @@ -221,8 +222,8 @@ void TDistilVectors::execute(void) for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < Ns; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * Ns*dt; + for (int ds = 0; ds < SI; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; phi[vecindex] = zero; for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { sink_tslice=zero; diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index c9f62b6e..e1a45317 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -164,6 +164,7 @@ void TPerambFromSolve::execute(void) const DistilParameters & Distil{par().Distil}; const int LI{Distil.LI}; const int TI{Distil.TI}; + const int SI{Distil.SI}; const int nnoise{Distil.nnoise}; const int Nt{Distil.Nt}; const int Nt_inv{Distil.Nt_inv}; @@ -191,7 +192,7 @@ void TPerambFromSolve::execute(void) for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI_reduced; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < Ns; ds++) { + for (int ds = 0; ds < SI; ds++) { for (int is = 0; is < Ns; is++) { result_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 51f3b735..316d80a6 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -156,7 +156,7 @@ void TPerambLight::execute(void) const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; const int LI{Distil.LI}; - //const int SI{Distil.SI}; + const int SI{Distil.SI}; const int TI{Distil.TI}; const int nnoise{Distil.nnoise}; const int Nt{Distil.Nt}; @@ -239,7 +239,7 @@ void TPerambLight::execute(void) for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < Ns; ds++) { + for (int ds = 0; ds < SI; ds++) { std::cout << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; dist_source = zero; tmp3d_nospin = zero; @@ -248,7 +248,7 @@ void TPerambLight::execute(void) if (full_tdil) t_inv = tsrc; else t_inv = it; if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += Ns){ // TODO: Also allow non-full spin dilution (re-define exact_distillation?) + for (int is = ds; is < Ns; is += SI){ ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; tmp3d=zero; From 4fc045b563990f7ff21e5dc6ba03f9cd1adf626f Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 22 Mar 2019 13:50:47 +0000 Subject: [PATCH 176/347] added module to load perambulators from disk --- Hadrons/Modules.hpp | 127 ++++++------ Hadrons/Modules/MIO/LoadPerambulator.cc | 7 + Hadrons/Modules/MIO/LoadPerambulator.hpp | 116 +++++++++++ Hadrons/modules.inc | 248 ++++++++++++----------- 4 files changed, 312 insertions(+), 186 deletions(-) create mode 100644 Hadrons/Modules/MIO/LoadPerambulator.cc create mode 100644 Hadrons/Modules/MIO/LoadPerambulator.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index b0e50bed..761cbccc 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,76 +1,77 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include #include #include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/Modules/MIO/LoadPerambulator.cc b/Hadrons/Modules/MIO/LoadPerambulator.cc new file mode 100644 index 00000000..bc52b0ea --- /dev/null +++ b/Hadrons/Modules/MIO/LoadPerambulator.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MIO; + +template class Grid::Hadrons::MIO::TLoadPerambulator; diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp new file mode 100644 index 00000000..891a898a --- /dev/null +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -0,0 +1,116 @@ +#ifndef Hadrons_MIO_LoadPerambulator_hpp_ +#define Hadrons_MIO_LoadPerambulator_hpp_ + +#include +#include +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * LoadPerambulator * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MIO) + +class LoadPerambulatorPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(LoadPerambulatorPar, + std::string, PerambFileName, //stem!!! + std::string, UniqueIdentifier, + int, nvec, + MDistil::DistilParameters, Distil); +}; + +template +class TLoadPerambulator: public Module +{ +public: + // constructor + TLoadPerambulator(const std::string name); + // destructor + virtual ~TLoadPerambulator(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(LoadPerambulator, TLoadPerambulator, MIO); + +/****************************************************************************** + * TLoadPerambulator implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TLoadPerambulator::TLoadPerambulator(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TLoadPerambulator::getInput(void) +{ + std::vector in; + + return in; +} + +template +std::vector TLoadPerambulator::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TLoadPerambulator::setup(void) +{ + const int nvec{par().nvec}; + const MDistil::DistilParameters & Distil{par().Distil}; + const int LI{Distil.LI}; + const int nnoise{Distil.nnoise}; + const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; + const int Ns{Distil.Ns}; + std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + + envCreate(MDistil::Perambulator, getName() + "_perambulator_light", 1, + sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TLoadPerambulator::execute(void) +{ + auto &perambulator = envGet(MDistil::Perambulator, + getName() + "_perambulator_light"); + + + const std::string &PerambFileName{par().PerambFileName}; + if( PerambFileName.length() ){ + bool bExists = false; + { + std::ifstream f(PerambFileName, std::ios::binary); + if( f.is_open() ) + bExists = true; + } + if( bExists ) { + perambulator.ReadBinary(PerambFileName); + return; + } + } +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MIO_LoadPerambulator_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index d63333cd..26f9a396 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,152 +1,154 @@ modules_cc =\ - Modules/MContraction/Baryon.cc \ - Modules/MContraction/Meson.cc \ - Modules/MContraction/WeakEye3pt.cc \ - Modules/MContraction/A2ALoop.cc \ - Modules/MContraction/WeakNonEye3pt.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MFermion/FreeProp.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MSource/Momentum.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/SeqConserved.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/Z2.cc \ - Modules/MSink/Point.cc \ - Modules/MSink/Smear.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ Modules/MUtilities/RandomVectors.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MDistil/DistilVectors.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/A2AVectors.cc \ + Modules/MSolver/A2AAslashVectors.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSolver/RBPrecCG.cc \ Modules/MDistil/g5_multiply.cc \ - Modules/MDistil/BC2.cc \ Modules/MDistil/BContraction.cc \ + Modules/MDistil/BC2.cc \ Modules/MDistil/LapEvec.cc \ - Modules/MDistil/DistilSink.cc \ - Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambFromSolve.cc \ + Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambLight.cc \ + Modules/MDistil/DistilSink.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MContraction/WeakEye3pt.cc \ + Modules/MContraction/WeakNonEye3pt.cc \ + Modules/MContraction/Gamma3pt.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/Meson.cc \ + Modules/MContraction/A2ALoop.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MContraction/Baryon.cc \ + Modules/MContraction/A2AAslashField.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MAction/WilsonClover.cc \ + Modules/MAction/Wilson.cc \ + Modules/MAction/DWF.cc \ + Modules/MAction/MobiusDWF.cc \ + Modules/MAction/ScaledDWF.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/GaugeFix.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadA2AVectors.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadPerambulator.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadCosmHol.cc \ Modules/MNPR/Amputate.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MAction/Wilson.cc \ - Modules/MAction/MobiusDWF.cc \ - Modules/MAction/ZMobiusDWF.cc \ - Modules/MAction/WilsonClover.cc \ - Modules/MAction/DWF.cc \ - Modules/MAction/ScaledDWF.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/StochFreeField.cc \ Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ Modules/MScalarSUN/Div.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MIO/LoadA2AVectors.cc + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/Grad.cc \ + Modules/MSink/Smear.cc \ + Modules/MSink/Point.cc \ + Modules/MFermion/GaugeProp.cc \ + Modules/MFermion/FreeProp.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MSource/SeqConserved.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Wall.cc \ + Modules/MSource/Z2.cc \ + Modules/MSource/Point.cc \ + Modules/MSource/Momentum.cc modules_hpp =\ - Modules/MContraction/WeakEye3pt.hpp \ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/A2ALoop.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/WeakNonEye3pt.hpp \ - Modules/MFermion/FreeProp.hpp \ - Modules/MFermion/GaugeProp.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Point.hpp \ - Modules/MSource/Wall.hpp \ - Modules/MSource/Z2.hpp \ - Modules/MSource/SeqConserved.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ + Modules/MUtilities/RandomVectors.hpp \ + Modules/MUtilities/PrecisionCast.hpp \ Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ Modules/MSolver/A2AAslashVectors.hpp \ Modules/MSolver/Guesser.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ Modules/MSolver/RBPrecCG.hpp \ Modules/MSolver/A2AVectors.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MUtilities/PrecisionCast.hpp \ - Modules/MUtilities/RandomVectors.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/ChargedProp.hpp \ - Modules/MDistil/Distil.hpp \ Modules/MDistil/LapEvec.hpp \ - Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/DistilSink.hpp \ - Modules/MDistil/BC2.hpp \ - Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/PerambFromSolve.hpp \ - Modules/MDistil/BContraction.hpp \ - Modules/MDistil/Baryon2pt.hpp \ + Modules/MDistil/Distil.hpp \ Modules/MDistil/g5_multiply.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ + Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/Baryon2pt.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/PerambLight.hpp \ + Modules/MDistil/DistilSink.hpp \ + Modules/MDistil/PerambFromSolve.hpp \ + Modules/MDistil/BC2.hpp \ + Modules/MContraction/WeakNonEye3pt.hpp \ + Modules/MContraction/WeakEye3pt.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/A2ALoop.hpp \ + Modules/MContraction/A2AMesonField.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/Meson.hpp \ Modules/MAction/Wilson.hpp \ Modules/MAction/WilsonClover.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ + Modules/MAction/DWF.hpp \ Modules/MAction/ScaledDWF.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MIO/LoadEigenPack.hpp \ + Modules/MAction/ZMobiusDWF.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/Unit.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MGauge/StoutSmearing.hpp \ + Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ + Modules/MGauge/FundtoHirep.hpp \ + Modules/MGauge/GaugeFix.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ + Modules/MIO/LoadBinary.hpp \ + Modules/MIO/LoadCosmHol.hpp \ Modules/MIO/LoadNersc.hpp \ Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCosmHol.hpp \ Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadBinary.hpp + Modules/MIO/LoadEigenPack.hpp \ + Modules/MIO/LoadPerambulator.hpp \ + Modules/MNPR/Amputate.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MSink/Point.hpp \ + Modules/MFermion/GaugeProp.hpp \ + Modules/MFermion/FreeProp.hpp \ + Modules/MScalar/Scalar.hpp \ + Modules/MScalar/FreeProp.hpp \ + Modules/MScalar/ChargedProp.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Z2.hpp \ + Modules/MSource/Wall.hpp \ + Modules/MSource/SeqConserved.hpp From 4e87cbd40072ced03d213e4460e088d08cde8a5a Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 23 Mar 2019 09:28:41 +0000 Subject: [PATCH 177/347] Fix build with Intel '17 compiler, i.e. workaround incorrect auto types for c++ style definitions. E.g. assuming T::rank is an int, then objects defined like so: const auto rank{T::rank}; should also be int. Unfortunately, Intel '17 instead defines them to be std::initializer_list, then proceeds to complain where these variables are used that they cannot be converted to int. NB: This was fixed under Intel '18 --- Grid/serialisation/BaseIO.h | 4 ++-- Grid/serialisation/VectorUtils.h | 29 +++++++++++++++++++++++++++++ Grid/util/EigenUtil.h | 16 ++++++++-------- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index 2a780fb4..be556955 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -454,7 +454,7 @@ namespace Grid { } assert( NumContainers * ElementsPerContainer == buf.size() && "EigenIO: Number of elements != product of dimensions" ); // Now see whether the tensor is the right shape, or can be made to be - const auto & dims{output.dimensions()}; + const auto & dims = output.dimensions(); bool bShapeOK = (output.data() != nullptr); for( auto i = 0; bShapeOK && i < TensorRank ; i++ ) if( dims[i] != dimData[i] ) @@ -558,7 +558,7 @@ namespace Grid { template static inline typename std::enable_if::value, bool>::type CompareMember(const std::vector &lhs, const std::vector &rhs) { - const auto NumElements{lhs.size()}; + const auto NumElements = lhs.size(); bool bResult = ( NumElements == rhs.size() ); for( auto i = 0 ; i < NumElements && bResult ; i++ ) bResult = CompareMember(lhs[i], rhs[i]); diff --git a/Grid/serialisation/VectorUtils.h b/Grid/serialisation/VectorUtils.h index 658bc187..a5a73992 100644 --- a/Grid/serialisation/VectorUtils.h +++ b/Grid/serialisation/VectorUtils.h @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./Grid/serialisation/VectorUtils.h + + Copyright (C) 2015 + + Author: Antonin Portelli + Author: Peter Boyle + Author: paboyle + + 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 */ #ifndef GRID_SERIALISATION_VECTORUTILS_H #define GRID_SERIALISATION_VECTORUTILS_H diff --git a/Grid/util/EigenUtil.h b/Grid/util/EigenUtil.h index c6f10276..ae8a1e50 100644 --- a/Grid/util/EigenUtil.h +++ b/Grid/util/EigenUtil.h @@ -51,11 +51,11 @@ namespace Grid { std::array::Rank> &GridTensorIndex) { using Traits = EigenIO::Traits; - const auto InnerRank = Traits::Rank; + const int InnerRank = Traits::Rank; for( typename Traits::scalar_type &Source : container ) { lambda(Source, Seq++, MyIndex, GridTensorIndex ); // Now increment SubIndex - for( auto i = InnerRank - 1; i != -1 && ++GridTensorIndex[i] == DimGridTensor[i]; i-- ) + for( int i = InnerRank - 1; i != -1 && ++GridTensorIndex[i] == DimGridTensor[i]; i-- ) GridTensorIndex[i] = 0; } } @@ -74,7 +74,7 @@ namespace Grid { const Index NumScalars = ET.size(); assert( NumScalars > 0 && "EigenUtil: tensor has no elements" ); Index ScalarElementCount{1}; - const auto rank{ETensor::NumIndices}; + const int rank{ETensor::NumIndices}; std::array DimTensor, MyIndex; for(int i = 0; i < rank; i++ ) { DimTensor[i] = ET.dimension(i); @@ -83,7 +83,7 @@ namespace Grid { } assert( NumScalars == ScalarElementCount && "EigenUtil: tensor size not product of dimensions" ); // Save the GridTensor dimensions - const auto InnerRank{Traits::Rank}; + const int InnerRank{Traits::Rank}; std::array DimGridTensor, GridTensorIndex; for(int i = 0; i < InnerRank; i++ ) { DimGridTensor[i] = Traits::Dimension(i); @@ -96,13 +96,13 @@ namespace Grid { for_all_do_lambda( lambda, * pScalar, Seq, MyIndex, DimGridTensor, GridTensorIndex ); // Now increment the index to pass to the lambda (bearing in mind we're walking in memory order) if( ETensor::Options & Eigen::RowMajor ) { - for( auto i = rank - 1; i != -1 && ++MyIndex[i] == DimTensor[i]; i-- ) + for( int i = rank - 1; i != -1 && ++MyIndex[i] == DimTensor[i]; i-- ) MyIndex[i] = 0; } else { - for( auto i = 0; i < rank && ++MyIndex[i] == DimTensor[i]; i++ ) + for( int i = 0; i < rank && ++MyIndex[i] == DimTensor[i]; i++ ) MyIndex[i] = 0; Seq = 0; - for( auto i = 0; i < rank; i++ ) { + for( int i = 0; i < rank; i++ ) { Seq *= DimTensor[i]; Seq += MyIndex[i]; } @@ -166,7 +166,7 @@ namespace Grid { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; using Index = typename T::Index; - const auto rank{T::NumIndices}; + const int rank{T::NumIndices}; const auto &dims = t.dimensions(); std::cout << "Dumping rank " << rank + Traits::Rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; if( pName ) From b3b9e608e16a8799e82637613f4525bec250bc63 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 25 Mar 2019 14:13:03 +0000 Subject: [PATCH 178/347] added new module for noises --- Hadrons/Modules.hpp | 1 + Hadrons/Modules/MDistil/Noises.cc | 7 ++ Hadrons/Modules/MDistil/Noises.hpp | 131 +++++++++++++++++++++++++++++ Hadrons/modules.inc | 2 + 4 files changed, 141 insertions(+) create mode 100644 Hadrons/Modules/MDistil/Noises.cc create mode 100644 Hadrons/Modules/MDistil/Noises.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 761cbccc..3a90c371 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/Hadrons/Modules/MDistil/Noises.cc b/Hadrons/Modules/MDistil/Noises.cc new file mode 100644 index 00000000..c4757c26 --- /dev/null +++ b/Hadrons/Modules/MDistil/Noises.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TNoises; diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp new file mode 100644 index 00000000..e30b2fe1 --- /dev/null +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -0,0 +1,131 @@ +#ifndef Hadrons_MDistil_Noises_hpp_ +#define Hadrons_MDistil_Noises_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation + #include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * Noises * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class NoisesPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(NoisesPar, + std::string, UniqueIdentifier, + int, nvec, + DistilParameters, Distil, + unsigned int, i); +}; + +template +class TNoises: public Module +{ +public: + // constructor + TNoises(const std::string name); + // destructor + virtual ~TNoises(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(Noises, TNoises, MDistil); + +/****************************************************************************** + * TNoises implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TNoises::TNoises(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TNoises::getInput(void) +{ + std::vector in; + + return in; +} + +template +std::vector TNoises::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TNoises::setup(void) +{ + const DistilParameters & Distil{par().Distil}; + const int nvec{par().nvec}; + + envCreate(std::vector, getName(), 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TNoises::execute(void) +{ + const std::string &UniqueIdentifier{par().UniqueIdentifier}; + auto &noise = envGet(std::vector, getName()); + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; + const int nnoise{Distil.nnoise}; + const int Nt{Distil.Nt}; + const int Ns{Distil.Ns}; + const int TI{Distil.TI}; + const int LI{Distil.LI}; + const bool full_tdil{TI==Nt}; + const bool exact_distillation{full_tdil && LI==nvec}; + + GridSerialRNG sRNG; + sRNG.SeedUniqueString(UniqueIdentifier + std::to_string(vm().getTrajectory())); //maybe add more?? + Real rn; + + for (int inoise=0;inoise 0.5) ? -1 : 1; + } + } + } + } + } + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_Noises_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 26f9a396..c427e020 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -14,6 +14,7 @@ modules_cc =\ Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/PerambLight.cc \ Modules/MDistil/DistilSink.cc \ + Modules/MDistil/Noises.cc \ Modules/MDistil/DistilVectors.cc \ Modules/MContraction/WeakEye3pt.cc \ Modules/MContraction/WeakNonEye3pt.cc \ @@ -85,6 +86,7 @@ modules_hpp =\ Modules/MDistil/LapEvec.hpp \ Modules/MDistil/Distil.hpp \ Modules/MDistil/g5_multiply.hpp \ + Modules/MDistil/Noises.hpp \ Modules/MDistil/DistilVectors.hpp \ Modules/MDistil/Baryon2pt.hpp \ Modules/MDistil/BContraction.hpp \ From 48b03c4590d7014fc2c3b5d43e9b1b041fd8a0e7 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Mon, 25 Mar 2019 15:45:35 +0000 Subject: [PATCH 179/347] bugfix --- Hadrons/Modules/MDistil/Noises.hpp | 3 +-- Hadrons/Modules/MIO/LoadPerambulator.hpp | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index e30b2fe1..ef50f03a 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -25,8 +25,7 @@ public: GRID_SERIALIZABLE_CLASS_MEMBERS(NoisesPar, std::string, UniqueIdentifier, int, nvec, - DistilParameters, Distil, - unsigned int, i); + DistilParameters, Distil); }; template diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 891a898a..4d33706a 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -19,7 +19,6 @@ class LoadPerambulatorPar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(LoadPerambulatorPar, std::string, PerambFileName, //stem!!! - std::string, UniqueIdentifier, int, nvec, MDistil::DistilParameters, Distil); }; @@ -81,7 +80,7 @@ void TLoadPerambulator::setup(void) const int Ns{Distil.Ns}; std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - envCreate(MDistil::Perambulator, getName() + "_perambulator_light", 1, + envCreate(MDistil::Perambulator, getName(), 1, sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); } @@ -91,7 +90,7 @@ template void TLoadPerambulator::execute(void) { auto &perambulator = envGet(MDistil::Perambulator, - getName() + "_perambulator_light"); + getName()); const std::string &PerambFileName{par().PerambFileName}; From d1e02f50ff5822dab9dbbecd3d1aa7fc837e7e06 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 25 Mar 2019 15:50:29 +0000 Subject: [PATCH 180/347] Added iterator for Eigen tensors --- Grid/util/EigenUtil.h | 85 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/Grid/util/EigenUtil.h b/Grid/util/EigenUtil.h index ae8a1e50..57c6cbd6 100644 --- a/Grid/util/EigenUtil.h +++ b/Grid/util/EigenUtil.h @@ -30,6 +30,83 @@ #include #include +namespace Grid { + // Custom iterator for Eigen tensors + namespace EigenUtil { + template // Is the tensor constant + class TensorIterator_raw{ + public: + using Index = typename ETensor::Index; + using Scalar = typename ETensor::Scalar; + using FullIndex = std::array; + const ETensor * pET; + const Index end; // same as pET->size() + Index position; // position (memory order) + Index Seq; // sequence (what our position would be if we were column major) + FullIndex indexPos; + FullIndex indexSize; + + inline TensorIterator_raw( ETensor & eT, Index pos = 0 ) : pET{&eT}, position{pos}, Seq{pos}, end{pET->size()} { + for( int i = 0 ; i < ETensor::NumIndices ; i++ ) { + indexPos[i] = 0; + indexSize[i] = pET->dimension(i); + } + } + inline TensorIterator_raw & operator++() { + auto sz = pET->size(); + if( position < sz ) { + position++; + if( ETensor::Options & Eigen::RowMajor ) { + for( int i = ETensor::NumIndices - 1; i != -1 && ++indexPos[i] == indexSize[i]; i-- ) + indexPos[i] = 0; + Seq++; + } else { + for( int i = 0; i < ETensor::NumIndices && ++indexPos[i] == indexSize[i]; i++ ) + indexPos[i] = 0; + Seq = 0; + for( int i = 0; i < ETensor::NumIndices; i++ ) { + Seq *= indexSize[i]; + Seq += indexPos[i]; + } + } + } + return * this; + } + inline typename std::conditional::type operator*() const { + assert( position >= 0 && position < pET->size() && "Attempt to access Eigen tensor iterator out of range" ); + return ( ( typename std::conditional::type ) pET->data() )[position]; + } + inline bool operator!=(const TensorIterator_raw &r) + { return pET == nullptr || pET != r.pET || position != r.position; } + // These functions aren't rerquired for iterators, but they make using them easier + inline bool AtEnd() { return position == end; } + inline void DumpIndex(void) { + for( auto dim : indexPos ) + std::cout << "[" << dim << "]"; + } + }; + } +} + +// The only way I could get these iterators to work is to put the begin() and end() functions in the Eigen namespace +// So if Eigen ever defines these, we'll have a conflict and have to change this +namespace Eigen { + template using TensorIterator = Grid::EigenUtil::TensorIterator_raw< ETensor, false>; + template using TensorIteratorConst = Grid::EigenUtil::TensorIterator_raw; + template + inline typename std::enable_if::value, TensorIterator>::type + begin( ETensor & ET ) { return TensorIterator(ET); } + template + inline typename std::enable_if::value, TensorIterator>::type + end( ETensor & ET ) { return TensorIterator(ET, ET.size()); } + template + inline typename std::enable_if::value, TensorIteratorConst>::type + begin( const ETensor & ET ) { return TensorIteratorConst(ET); } + template + inline typename std::enable_if::value, TensorIteratorConst>::type + end( const ETensor & ET ) { return TensorIteratorConst(ET, ET.size()); } +} + namespace Grid { // for_all helper function to call the lambda for scalar template @@ -174,6 +251,7 @@ namespace Grid { for( int i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; for( int i = 0 ; i < Traits::Rank; i++ ) std::cout << "(" << Traits::Dimension(i) << ")"; std::cout << " in memory order:" << std::endl; +#ifdef OLD_DEFINITION for_all( t, [&](scalar_type &c, Index n, const std::array &TensorIndex, const std::array &ScalarIndex ){ std::cout << " "; @@ -183,6 +261,13 @@ namespace Grid { std::cout << "(" << dim << ")"; std::cout << " = " << c << std::endl; } ); +#else + for( auto it = begin(t); !it.AtEnd(); ++it ) { + std::cout << " "; + it.DumpIndex(); + std::cout << " = " << (const typename T::Scalar)(*it) << std::endl; + } +#endif std::cout << "========================================" << std::endl; } From ce501afec6bd520230907abbd6bf05132418e4ea Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Mon, 25 Mar 2019 16:38:25 +0000 Subject: [PATCH 181/347] bugfix --- Hadrons/Modules/MIO/LoadPerambulator.hpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 4d33706a..c6324a1a 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -92,20 +92,10 @@ void TLoadPerambulator::execute(void) auto &perambulator = envGet(MDistil::Perambulator, getName()); - - const std::string &PerambFileName{par().PerambFileName}; - if( PerambFileName.length() ){ - bool bExists = false; - { - std::ifstream f(PerambFileName, std::ios::binary); - if( f.is_open() ) - bExists = true; - } - if( bExists ) { + const std::string &PerambFileName{par().PerambFileName + "." + std::to_string(vm().getTrajectory())}; + std::cout << "reading perambulator from file " << PerambFileName << std::endl; perambulator.ReadBinary(PerambFileName); - return; - } - } + } END_MODULE_NAMESPACE From 625a97a466b0d25cd3d21e8a732f0c7754f1a42a Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 25 Mar 2019 18:16:04 +0000 Subject: [PATCH 182/347] cosmetic --- Grid/util/EigenUtil.h | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Grid/util/EigenUtil.h b/Grid/util/EigenUtil.h index 57c6cbd6..63a0a860 100644 --- a/Grid/util/EigenUtil.h +++ b/Grid/util/EigenUtil.h @@ -78,7 +78,7 @@ namespace Grid { } inline bool operator!=(const TensorIterator_raw &r) { return pET == nullptr || pET != r.pET || position != r.position; } - // These functions aren't rerquired for iterators, but they make using them easier + // These functions aren't required for iterators, but they make using them easier inline bool AtEnd() { return position == end; } inline void DumpIndex(void) { for( auto dim : indexPos ) @@ -251,23 +251,11 @@ namespace Grid { for( int i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; for( int i = 0 ; i < Traits::Rank; i++ ) std::cout << "(" << Traits::Dimension(i) << ")"; std::cout << " in memory order:" << std::endl; -#ifdef OLD_DEFINITION - for_all( t, [&](scalar_type &c, Index n, const std::array &TensorIndex, - const std::array &ScalarIndex ){ - std::cout << " "; - for( auto dim : TensorIndex ) - std::cout << "[" << dim << "]"; - for( auto dim : ScalarIndex ) - std::cout << "(" << dim << ")"; - std::cout << " = " << c << std::endl; - } ); -#else for( auto it = begin(t); !it.AtEnd(); ++it ) { std::cout << " "; it.DumpIndex(); std::cout << " = " << (const typename T::Scalar)(*it) << std::endl; } -#endif std::cout << "========================================" << std::endl; } From 850266002394350a24b6937163f8706814c17b9f Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 25 Mar 2019 20:40:05 +0000 Subject: [PATCH 183/347] Begin fixes for single precision --- Hadrons/Modules/MDistil/BContraction.hpp | 2 +- Hadrons/Modules/MDistil/Baryon2pt.hpp | 2 +- Hadrons/Modules/MDistil/Distil.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 62300c3a..c98c4008 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -215,7 +215,7 @@ void TBContraction::execute(void) tmp111 = 0.5*(double)parity*(tmp111 + tmp222); // P_\pm * ... diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); for (int is=0 ; is < 4 ; is++){ - BField3(imom,ig,t,is,i1,i2,i3)+=(double)epsilon_sgn[ie]*tmp111()(is)()*diquark2; + BField3(imom,ig,t,is,i1,i2,i3)+=static_cast(epsilon_sgn[ie])*tmp111()(is)()*diquark2; } } } diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index 3c26bbe4..ce2bb2c4 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -169,7 +169,7 @@ void TBaryon2pt::execute(void) Eigen::Tensor B3L = B4L.chip(is,0); Eigen::Tensor B3R = B4R.chip(is,0); Eigen::Tensor C2 = B3L.contract(B3R,product_dims); - corr(imom,t) += (double)epsilon_sgn[pairs[ipair]]*C2(0); + corr(imom,t) += static_cast(epsilon_sgn[pairs[ipair]])*C2(0); } } } diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 747d5f51..84e791ca 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -146,7 +146,7 @@ inline void SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Bu *************************************************************************************/ -template +template class LinOpPeardonNabla : public LinearOperatorBase, public LinearFunction { typedef typename GaugeField::vector_type vCoeff_t; protected: // I don't really mind if _gf is messed with ... so make this public? From ae565b006a3a80cddf73e3703cd9c98e73e0c45a Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 25 Mar 2019 22:56:01 +0000 Subject: [PATCH 184/347] Compiling in single-precision now works --- Hadrons/Modules/MDistil/BC2.hpp | 2 +- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp index f83962f6..34459b60 100644 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -173,7 +173,7 @@ void TBC2::execute(void) } envCache(std::vector, momphName_, 1, mom_.size(), envGetGrid(ComplexField)); - Eigen::Tensor m(Nmom,Nt,N_1,N_2,N_3,4); + Eigen::Tensor m(Nmom,Nt,N_1,N_2,N_3,4); A2Autils::NucleonFieldMom(m, &one[0], &two[0], &three[0], ph, parity, orthogDim); for (int is=0 ; is < 4 ; is++){ for (int t=0 ; t < Nt ; t++){ diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index e1a45317..9fff87f3 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -199,7 +199,7 @@ void TPerambFromSolve::execute(void) ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); for (int ivec = 0; ivec < nvec_reduced; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); - pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); + pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; } } diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 316d80a6..8dfff057 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -278,7 +278,7 @@ void TPerambLight::execute(void) ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); - pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); + pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); } } } From 9fce1263be9648045e14ef9593ce2c0c9be1eb44 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 26 Mar 2019 13:24:39 +0000 Subject: [PATCH 185/347] Fixed bug in LapEvec if machine running spread-out in time --- Hadrons/Modules/MDistil/Distil.hpp | 6 ++++++ Hadrons/Modules/MDistil/LapEvec.hpp | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 84e791ca..19e08171 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -250,6 +250,12 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) (Disable big-endian by setting Endian_Scalar_Size=1) IndexNames contains one name for each index, and IndexNames are validated on load. (NB: Indices of dimension 1 are not saved, and not validated on load) + WHAT TO SAVE / VALIDATE ON LOAD (Override to warn instead of assert on load) + Ensemble string + Configuration number + Noise unique string + Distillation parameters + ******************************************************************************/ template diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 1e0f459b..8b9841ae 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -328,7 +328,9 @@ void TLapEvec::execute(void) for (int i=0;i Date: Wed, 27 Mar 2019 11:59:06 +0000 Subject: [PATCH 186/347] : --- Grid/qcd/smearing/StoutSmearing.h | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index bfc37d0d..7d42edb1 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -13,7 +13,13 @@ template class Smear_Stout : public Smear { private: const Smear* SmearBase; - + inline std::vector rho3D(double rho, int orthogdim){ + std::vector rho3d(Nd*Nd); + for (int mu=0; mu { } /*! Default constructor */ - Smear_Stout(double rho = 1.0) : SmearBase(new Smear_APE(rho)) { +/* Smear_Stout(double rho = 1.0) : SmearBase(new Smear_APE(rho)) { assert(Nc == 3);// "Stout smearing currently implemented only for Nc==3"); - } + } */ + + /*! general constructor */ + Smear_Stout(std::vector& rho_) : SmearBase(new Smear_APE(rho_)) { + assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); + } + + /*! 3D constructor */ + Smear_Stout(double rho = 1.0, int orthogdim = -1) : SmearBase(new Smear_APE(rho3D(rho,orthogdim))) { + assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); + } ~Smear_Stout() {} // delete SmearBase... From f757b80e1cdec6b134be91b0a75c4c1289cc4684 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 27 Mar 2019 12:00:36 +0000 Subject: [PATCH 187/347] tried to fix mem leak --- Hadrons/Modules/MDistil/DistilSink.hpp | 4 +++- Hadrons/Modules/MDistil/DistilVectors.hpp | 9 +++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilSink.hpp b/Hadrons/Modules/MDistil/DistilSink.hpp index d56ab5ac..d9ef303e 100644 --- a/Hadrons/Modules/MDistil/DistilSink.hpp +++ b/Hadrons/Modules/MDistil/DistilSink.hpp @@ -195,7 +195,9 @@ void TDistilSink::execute(void) } } } - + // TEST TO SEE WHETHER THIS MIGHT BE THE MEMORY LEAK + Cleanup(); + } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 9493f0e6..069a2cc6 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -107,7 +107,6 @@ template void TDistilVectors::setup(void) { Cleanup(); - //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); int nnoise=par().nnoise; @@ -121,8 +120,7 @@ void TDistilVectors::setup(void) envCreate(std::vector, getName() + "_phi", 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - //GridCartesian * grid4d = env().getGrid(); - grid4d = env().getGrid(); + grid4d = env().getGrid(); std::vector latt_size = GridDefaultLatt(); std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); std::vector mpi_layout = GridDefaultMpi(); @@ -130,7 +128,6 @@ void TDistilVectors::setup(void) latt_size[Nd-1] = 1; simd_layout_3.push_back( 1 ); mpi_layout[Nd-1] = 1; - //GridCartesian * grid3d = new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); grid3d = MakeLowerDimGrid(grid4d); @@ -158,7 +155,6 @@ template void TDistilVectors::execute(void) { - //auto &noise = envGet(std::vector>>, par().noise); auto &noise = envGet(std::vector, par().noise); auto &perambulator = envGet(Perambulator, par().perambulator); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); @@ -172,7 +168,6 @@ void TDistilVectors::execute(void) envGetTmp(LatticeSpinColourVector, sink_tslice); envGetTmp(LatticeColourVector, evec3d); - //GridCartesian * grid4d = env().getGrid(); int Ntlocal = grid4d->LocalDimensions()[3]; int Ntfirst = grid4d->LocalStarts()[3]; @@ -238,6 +233,8 @@ void TDistilVectors::execute(void) } } + // TEST TO SEE WHETHER THIS MIGHT BE THE MEMORY LEAK + Cleanup(); std::cout << "size rho" << rho.size() << std::endl; std::cout << "size phi" << phi.size() << std::endl; From 4c02ed6d0c800bad4bea7fa470f7f22bc3daa9b2 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 27 Mar 2019 13:54:39 +0000 Subject: [PATCH 188/347] Updated GridXcode documentation --- documentation/GridXcode/GridXcFig4.png | Bin 0 -> 416812 bytes documentation/GridXcode/readme.md | 190 +++++++++++++------------ 2 files changed, 98 insertions(+), 92 deletions(-) create mode 100644 documentation/GridXcode/GridXcFig4.png diff --git a/documentation/GridXcode/GridXcFig4.png b/documentation/GridXcode/GridXcFig4.png new file mode 100644 index 0000000000000000000000000000000000000000..5abd150b8718617b6929ae84213b004e659b6f87 GIT binary patch literal 416812 zcmbq)1yEg0wl#7C7kAg-t_gl|cMb0D?(VL^-7UC7(BK4zKyY_=3*o={X5P%anpgE# z%}{kYhjUK%>E3(yT6?d)L@LTlAi?9qLqI?vNlA(-LqNbCLO=j`fH2@IA9Yd2ARrLw zEJZ{Vr9?zX6`kzOENx67AS5GG)L}JLMzM3XlM)g_&?5qpr?Y6pI_9UzfRb1?AruLU z!m>a*Hy5S8=6s{RsH!NM4s1be0&E6`s4f9iC8dSIAfyydDG1%yb?*({4fpfciwt)2 z-_GabKI{+{qRx~_45}CbHjXtPs6Hfod{3Jpb_@Z`hi0pTa?{C`MMotfg3L$kextpx z6Sk*WB*d)uvwN#89K;#%fFPYgExR`Dw24504)}USF{=XsT${Gz2dP7`qBQ_`lqFG( z5Z=kF8=}0ESK9)XDCi&(Y-2*qm+RDgAcE)!!l92(L*AKA6FQ~Yj~+nf6g>To;q-*+ zALb$I=7v0g&w;v9?1w}BO2VoVZsgQ{B%+ZLtvnwj$|i;FzW=5f_r|!SdELi4UZ5O0 zTZbz)8P#FBA41Q9wSBWaKSC4H{JxuMoBkE+F;bBjTVV4oklMIE6OsyoCS?i5eAgJ68mR`0=+j*gy15GfuOBe!2Qiul?A{_F&LA|d$$-jU(rG7#|vVz+aLd?OzbDhto331p0?#Sy7s(E20Z zi)ybzJPdYS0UvL}k7K+ybAvm(pD=P!wcz4`oIu+UUKO!-vn1(_y7A$Y}74YbHDh|?ASLGMu@pl6zv+C_%}?%BObcB zEEdp2N&D&}VAakSSk;GX`Hcs^>b^c+68$csZUOLyd^9YpL(qW!grM;gt_svFJ=n{UE&1_}USjD;{Gprs7ps~S-!RcOwgeFwaQ=iv4$_*2kPmd;L~nz#3-ETRaS6i7hwX&+1qLOc;(rso zMZ*|Cub0S1Lm9w<6DLc+rGS$XnnhzJlQ1JkNHi)2EJfHz&?Rmp7LCyELd{043N0rx z?S8z58&ssL48||`bVxG~jU?DV!{x%387D7tGb48B6P_+V@9 zvi*_GhrFAhwr^^4@zSjm_Dwn;4H51hzzh}65||*LE>A;|jP8aWhdTMKc}~hhf&x85 z)Vh#Y$6IOuahqYAZQE?yZ(GWUzCQS;3|Zo>)I9}>GG?UA7$sH`N5XxQd_qkUj}*!L z{14gr>>tAONk0ax-dUAcdC$jv(N*)VT##6x&`#d_zBUo=ZSRc}(wYOzJWOVLZyYg=b55LY8;O*Kg^e_HNa z_Bj(<=5122c4uJ;;|`wKWW{Eq!!^gt;SnXWCW?~}ldqrT&%w?i*0o)m>458CclquT zf7HUtT8j0#f840XfY@Z+#1G%>1A{@j!OvO4AN@7R-nE=MQ~C<|>cy@47h&tvzNvn7 zex<+ZUbS9fVcG(7`aFV9$Iyj*Ce)M~1XbI_66zPwQjL zbTd}R9+m0u<2_my7*6 z?6LiLcxAeiH^4PG9%mV4Iv^jrhVjD0#9}A5+U1eu;pt->B>t^9WGv(%QYex#(iTHp zE}eZ#IY}sqrh+LWIXj_~z1`jDa-bQ5m%3e^F5AVq;cgf2GqWsfdeXNB@<2E8e~2b&hNInJtHeOJ~w06{IUw^eimU1h*vTD8E))plRlXwNpC9* zKG^o3c5LRB5X@Sq;L;m?nb=*O_E>Gn*dzNhA` z+23>dIu*-~zNLzy2D7%aea!yHIeK-{ucWviJD(P-46A9iZrsl1n{lKi((H64G^zC$ zTlkH9*I=TN=sA7XX;wXo80u`^sQTq(IJ-8+H+)>9-%^jO#kHeb{g$bg-MqaI?=zjg zT+b2>5P9Y)E%7$pBkl*$S5DJ;sx7WddDosI zEUT-ZDR%h^IQw5gItco0K3&lEZ1Caw)m^HpZb+x^?uc5`8c1Nx&czZbu+ANDNWx!tY|tskz3Uo>po zbumBZzZpJA#K+6!p$mY{vVLdWdcNYUFyHC-_pDz`onNFml<4d!K?FyYmOx&5;XpJ| zK;WuxJ{cAs+m&L9y5^~pUFOvt|1QPkgs9w-aYQuF18hR5(wDoGN0EDE3kd4!gt-Ws zSTapkYN&b`zxO%3@HYlPR1gO+&;I`1J@NXgPe;1M3z3~&+JQkQ>Il2GUn96k$1w#f zFwz6*bm24?<2@3bD5cp^sh%;tU+WNa*CuP_D-gx zADKQdv5@h@laiA1IhmO8D2s~!y*cpYcGYcCt z8yh3|2}Wm6I~PL_MmuNnzjg9I{fL@68#`G#xLDfTk^bq|(8%7^1w=;n=SBbc`P(>6 zJuLtECOhZ9PYXOj=0A6sS(#Xv|Is(NDc_%~Jc^bcrZyU)mbRvL&fs_OvwdXe;QOn= zzuo%hEB~db=0BUVvT(8eYtw(Z_4lTH%zvivFH`!Pz5coiHW)uVAM-zq&kx_{OP&b! z2ce~?f-3k3^~X8j0}=Qa&EJmTbLgcBh#Zoi5DeGht>$e6HM zw_88&?kHG&qI?-^;ysY_wBH}^QaV|vzDaN(j+gj-aB!d^FON(F@=A^);uV*Yl2UD! zR;$!$XQND&D?Ti@0GJO0S)gJd$b0|eLv|7n(+i&VL<9S5qbKkFY(oMM4=*`AU2<UsCG0)TT$W|E!(GqIuJ@N%m^IVnk4E{mhYO05zN{RY#kA41O2 zO*&{S)C_2+?hY*|e`7^Dw#rGRbOw5riUib6j;sz;YZ#_GCCX5?tqfz(&YT96P1)>83%GIg5 z5d*{1JQ-B3FAI?gxi&|?e=qWVxmR*?y)* z9k)#;-S=c;XQ#BjxaBERk#&(h{CWEQKaQ$@3{D7*hK3=ZQM*-f{(S8#2o{C7csxe7 zQspahRx=Vjp7%c<{7-{;3oW31rFx}BdA>&XD_@;wR&u)N-uE;VlFuUlb>A_9l7QK{ zIa;fi`?K6??N;ULc`ZyF0vt9dZ?^w#)%~%)2jKephDJK!hA)f5wh-oyASAx;{eK%l zkm9|lP)gb@Hdi)3>jLp}mPQl-$OHeqAGgpF09|({iz+)|vHjuusQ;TeOA3*bsycf`QiR%dbFVN?&&U!yUDS^Fm{l|!MtC?YO zc0-wX+Ed5WVK#yPY(L@OfyTpeGc!dJ6~cq?s@=k#(z24tCe7(T zaaE48%L5Uv5onm19kv_io}e-laZjuq#pWfGPH4m#X}b= zg!>U#Z>s(ZQMl9DX#a-V*-pUAha1|5k@j9{G^ny?AF1N!TGQ{TGY9%UOevm%1+Wp2 zkLEb|fiW?Ncm_k?*@anBo+KhDZ<1Aqy z{S?@OvBOROoeg`5p?0Kr+_%aUS5`2CI-A%31gcZ@cRiSUMd$LkBw zOOAjODTn=q4GNhoAcvE?o!iB%pPdqK;)~}JMDUsrUR-N2W+RcA-(sz{!jO}pdtEq$zrad=yfHgeqHw^0 zj5OJq_*=8jlT!sZGZRydNth2c15{K#r|ol7ogud%E7VuFvm~64)(Z^Xqvr3?9Ex6@ z1qjfu>vDDC`-O`M0vzW;DR<>Km;L9%kO+!lKvajJ1Gyj~QHYlLhx0WVBm&N4zz3a1 zdU1%aCM+y(4;TKbMdC=`E35DhjAZX7am_)hkHmiqrT{fSHokh*{?yg@``!zw{mbp? ze_JxWK(0{m1b9O!%KCv^@eosKAw%;OzrWPgB~bCP2aYAv8hawKG!;|#Fk{?Sfp&Cx zqk&$uveWF*`6^%sXf}?dAx_=3QBaAWv)G9%&Z6w7zDOL)PR|XeUvxwWf_b~o%}Z3zvTMmYs%+ zS1L!9Z}LAk7kO_p9)D8LnhhnwY8M!h{?)DUm3+7y4DO9EAm^XX7FodJvNDxB{2zao zIEM;Pdl3o0OZxrkx(Jw9TU8Z@mT+xi8d2Tf5u)1H*Tt%(#>0_Ba+~!9+|`Z(uz?X|erACUjG*)r z!fi$1q0#U?GC{zJqeg@aG>ctFU3!OQ>nnLu1Zpd-raLZAY;K$RebGlUm1_k&l60SOW=qnU;4^n#LaljU zbfv=Cy~l_TC@Y&5p;xaeKK}7VdyG+;OB?;1D@rw!%bPDZf(&xy@D!ZS^hWn)*>_73 zaMv?qx3q^DI%K^qg4#MxjK~tP?Sx5bE=>JnR-=O~_x#x-~Dk`ody-Az4 z^f!n@h3~2G=HX`aLAG6#njfl!RX@q`L$KekBMGgqZj3~H9tlp)&X%ifj_>iq8dAEbk3QkZJ0r2j= z5>IdZVoIZ|Y%$bD0Y+JTLNgy%U}Bvsy}-O&@x0rnanamf*%ni~LVsk&qB{xoxo+dHwCo%7`pP!km0B$@IZ8FGAFF>R zSy89N`ADxzBJz#MtVC|_{pzr;Epna&NhGBtGYr4X$V10tP71u;M1WJ)D>}x4!yG4CY{Oo?Pw|gVw0?GoM{13&I9KX_3#<&+%QLEd`PJEFvT^>iHXVaTqkJOVb z+da52FH6{U!G$zOjkxuJTTlE{bE}nPeJ_3H-DWpa z_-+mC-bn~UQY_xr#kQ-Ud}>J@yAG)-1t)e-{pEN>zhc$#JMpA^_s2^^ue0Vwi{Ut; zhE*^hrQKEQb$Nx{nRAUaI_`*T)f;SLKZ3s7;1MC{*BR6A`COsxF}wxSQB%XsN^7Dsprap8b$ViACKxmVokm%C z5)!4Z%v%c|t?dfa=Xv5rQRxc4#Z)Kvn4;~SzvNV^#*`E{Tx~;Zk6^15M^-|R{Ct<} z$q;@|GFHZ>HdXd>&c5JqcVwiM+?n3D{RUdgag#IZOS<5dDF!W-e1b#NB|YXN*6yCl zWIe+eZ?YUCoxb3ivR%o1@yG~H1S3rDzBwmK=h@@kPP~8~*@RZJ)l4ksx}@uIviy<^ z5OaTMZ0}xnRUGPoQYJNoHWXxUA?UIqhHsv5^G8JTcH(nUqc=R)M7=NmOl1)&4vIog zWAUet+YVT%3nQRp+Jx25iw=U}e~kRPO=#X0$Sx|HO(Qg|{VTj&#x!Cqm62Y`tNYRC z8!-DDFrexA`a84C^|9-sF16ZJ-Zuw_86wd?sFRPItqS}Lb%X)m^?uez$6Ll6f#dbA zq|yJ)4_tsyoCd^3J1^J-Fk`?f7?}C2wAi{Fm83~mFN!C9GF6b4E~Hbd#EnGf7>6bC zA*D{@bvZ=kIK9FZM3D$pF(87Ye(Uyq+2uWsR1W`^av*xY=v`y>9ixia0PSR$#J^ZP z79XuUJ|5>lG$TH(p^k9!eSo9U_f$qUp%_mVoW0UuaYT!@aNdi~8$#_5T^HRi7JWhQ z?4pRTnmorNxOTI*;6enq;s#aQ?zy9wm=`!d)LcsPi*e$L&QzRzaDPhf$!kYuff~m7 zgMFUI6=l0RLjjP`zxKJt^Mf9e4)XkSS=28FFci`wt>~+Aej@-tt<`>@w{JHsW9A>O zH-&;_Qt~-nmsN`jwcyiobLHns<;YR{4V2r#LJuHFk0$&=+XfgqYG&bavi6lTg;}3p zH96v`kAazR{3^E`dCF)qo4Z)jYpugIx{V6NdnSPXdiqoZXcceUkVrlsLT%l3_(Du? zH#(;vpj$c7&&a*{ZKC?X=Kj5?G&1iQiRm@kZRSTyvzC~+kg3-rZJw4xxk|{{5zDef z#k1YLlEV_@Pw*cUh`5EnzH2WzWpr1{kYQRxIQrT3i%4H(a)^^wtm-Y9Hjg5LiL?hG zMXVP}JSI0)obwX7eX=H2U!e{IcRGHS=k6~T-O?@3s`gz``N_#|**|I@ld@f6FD!Cs z{I*E7jycV)!Wl(Zr(gF;wR@QyCKsFW9qp)T84q)p%_R2$VaI)@HEnygyLqr)`p(vR zlqtPUMq4akk{Viwd6vt#9+B7XeouU+xmsJ2a%-+yIbCM)j4kW)kihM1iHzmd_F&Q>)SQZ) zLd@gAq&hV{6c|rGV}0JK){*au$Bu|)0f$T6d=$Z?DBUxG?6l#H(Ajb`qez4Ai`(%= zk3f$9@23wYBdGd37}W9RpW(l4gw#yW6MFWq@;6lY}wir&=aS$tP z)K!Z+Ks${|*XQ%4_(v*{u)bhejBm!*>u?7jgcN?jftKVkHolVffRm7Mbh|)Hn0{6b zBDfe5kJz<>X$D@?F-%(X{GR*gJpbQ+BvMQJP`=>@^Gj1ciy&xys^DKU!k(+aUkj^kAD06;_dfTo#bOn``v;zQaC5Z^mw`< z)fxsXL&TK6&sFH^W2hItT|I_#qrk6AXfTj#S!wopV~v!h>AGboKu2p5lJ=M7`AJ(Z z)=+jH7`?~6$np>f9eNam-97;i4&USJz()pw5bt3|v?J9XrF4ZS0avUWwwFN||9Xvl z$6P)XEHm}eHkpH{kEs6Fxm4*#DmJryZ76cG)AP_Lc+^Qd7Y7(emi1L-P*Cy{X}E*Z z)jNUZ;ZHF1$k@@~jE<^EJW18C>@5l)GzFhZtxOo>?_h_1TbIh|yf0h9eF09B%3dyd z^d}+l``~UYAMb~}afS|ZLEgkT1XgQghq!$MqbwzIvyF3q@;?~#l=)^am_)Mk3ZIB6%Hs@+c)QUpY&$!UIV07wb4p%E*>PG&ZB0tgoCJ6sHi*7HZ1|Uy3NgPl ziebL39jGGjVwPX44R8)JAc)FzWp5G5p`ymY2g%5D}0Yz=&duWE3neKmkF{nVYZEpLAj!50|mQ~wHjz8;ZC*17sXr&0{BIfgAP z_|CAVmw9Iz&B}dQ&PA^J?H$$Gu`2}(F{YraULU+lsVrW5d4$;Pj_dZfzj73C9iYH9 zF)@*X!0Fupa_Oh6)={@dV%ww^coq7AQCMy?7!}e%G8wqLx2BKAMDfd5hdc{!^i>r8 zv$Ms!*kk_81z>@;JDyzp>d*P4M`8sIUywJM@1r~R+O#{WuCtj~#^h{hzvZg;8!Bb4 z#MA__tg9{=WG32j1zXnWoUDfI=6EhB3oL{S;^;U865@;x2q=B41Gs>3k|4#U-b%{7fseZz*`e-|v85!$Cu|CUeECB$73ZKYy8Pw0qj-4V@8rUCXztQbmVX1PngNJ=z zAHnCeE7`wXtpEtht;9gjClw$)0;efcXx}o4=WSJQPPSf5ZPx7p5=WRn$gw_yubnCD-R#N>^C7?$&|CGaipU zEME=}nQEQ|1rYv3VsmP-*3sPgNq`h6pUbDI3j-3C8L!f840>*qkOIKB`U{7i=ocT( z-AXwrN3Y|((sV2!QIcnM7GeLcKSFf{B2V>z*Yf)w&W6EklH<454L|zP-JeyshnL+f zK985ZD}IaeXcLi&$QkRL)=r>l4u3;lqluSQv5yRgN~Fw)&fzt z!pYK+q}0wmwX4O;7&9noSpI*OVC0%tiSYDy-D{$hr%OgU^^yh+6UJGxT?qyi8_>H9 zvx5+$46NOmnO9hvhkf4iaWPvpL5aAhuiYiZYc~}>&DWS}9}?*XRoGxK6h=x1ZX!az z+;$x$YZhS)E_AM6gD&w?YPBMbc7-!8(Hdy4RNH!slB*?$pz3LIyDvyx z%U2Vbs`jgV0-vmESueAFCzqDBYqGyg((7!e(5t7K*{^Y=pvxnJ{CPR5$U|>N1*fAw zOr|G&&Ut*HZfw2@*K}e6R%TO!JyYe$D55 zrZl$xxmw-S<>Gy9eD_s;{|S|9yO2-h0p!@EqfmlV`1i-$xJ#w#-H8#as~ze>PwXyA2OS9*#ES;@v~?sWF>_S3@~1 zwf%&R#lLz(%h;amV-}Zywc80Ukmqoa&WL@tI$TeR+b#BHYIJ$Qic4C@ZV}vHz`$Uk zJI6CGme66az~?}WxWQ?j>siXtW_(~aggDIR%2nXtt6Y8>@IsyEv(2uHSV%N90dvyUK8j z1kpiMRTzd!FJLi*3=~AXk6r+&Gu21<*z@+AzQ>>Ce#sTAo#P0_O#!*015|D(#Y>bg znB9g6%4S*VIKM3&^$aQCPNnLM2UKHlkduq7=WzID#?`)RlP-c@P8)^w6**ins8sti znvurQu2@@Jz-sk!hwGQyp%-phR*@aH^+v5G3&YG?ob>o5>pNsz`IE*a7a4k=e+UnO zqLi-Hxt~`6R4MUei$}j!D~C&1Q)#N2B*4ix0%vP(BR0la`hWQhC^RjxU(jeNTZo4kfNS-7cXu|mdI@vG2_QzT&1qVa zH0-!oZ+rWz5>2%wPTe^-Ne-caqw_)sK{qjGy4c<31Py=M2J;8QNsLeTub^yEqfMcO z${Tnx@yM0*rhyRgAC-DWs*w;S4*mbk0n`9pwnxK<3-rt-(pX&-l?dWFGO!DTg{$Q4 zsulI_kj{&>Un|T`@Eh*8I(5#RnqE(GN`YQKb8h-L1(eUOO_9)U?-}$3yM{aM3y0JT zht1^*oawRd4ycUqk>J^;-ayNd>r0}^UONU}_0eyIwKFc#ygKJw{+2qe)RrZv#ja^( zAuwIysiLzxl-0LhV( zq=(?Nuf8|t6MPp#ek_DB>NtRB3tTl*;)oeAjYi$r&(Rik@e?$3VPl?_X`M z0FcEC>iheaSLxg|inKV$l`BjCv;sM(tbRUv>Xk)%mfFjw zyk54hO(`~Rx``L~t&zzdBoHxD+j$MoEo7wlR6*XTT5sF3n=3!8V7Y*{08mZL8F`hhI-O&|SJ;TX@adsL~@t&TggV$`s0K zyPu^q6Ny+1Abce96RDbrtzV$#W5bEPB+;+(#iqs|D1bTwzaNb?z7@c=DMaRnYV+rRo0y~R z$X55(yS1#qRJU(Zlur=#%#u*rf@JP%xwPOHD+5W+lxcGcA<;ujhj>5jrOCuDz;DJ9 zaL$s)^O03U3(mza2+^;XD;1S{-t4m>Z|j5Wsty3imhX&P5}x!n2Ryz8Kg-vu+?$6+ zwOuY^@=fHw%7GO5>YW@bu*;@{EuGvJ&>XVzAHXO*8&2XUd3XGS+~)$%HJyH-gSP3y za20V+5tixeDO*qpC+~(_!8P#KIXWLBU4f!jCb-!A;FS{n)rM3_O83jbHbqjTBSNp%y|Hw6Y>@y^yr;z5)df8;L^leY4(~NT5ENBg3`aWPZvNw{g(UKc& zJuYfoa_iT3<{mDk`Fu-&)dmoMI!vP2@e`nH0ybVT+$~LIPdccIVRb1!TMSm6r0`Zw zqI`x3X}9ZbT}#5XR$$GYE`Jt`wH z>7gq(bBQW~w_pN~ckHX+2r1Aj=+}=`Q`;yzOzblguxJAK-0HsJ+p4o!mpP?ro+H-0 zj9hYPsfaPb{Rk6oEQr6hSwN|@ZJ z;1MS}^Bc2%w!*1<^n9m@SZNpO+StKP?AmS|wdE~Q-|$xOTjySx7zUtg6qIA@yv%c& zQu#jC+m_;^T*7)A_dk$hoBG`7b5g@TDc zWEh}vP7g57KJ91OTf9Es&F;EYYc&IX;eZ{1$i-RUU`?MCc=LhklWCXwOf?GE3l??b zW@l&TZ9M@(P*hb3zt_9GOIol`e+>o^ulqw2$zB!mkurv7QiMM>1wzBjCKgE)n_*ip z1qUyrO!2?SGOd2Jm=RJp(;%2D2glLPLHue5hM6G(w7APr$){F%ZT{7h*@k zCUit5%yH0Agv}UY;?^EDDwb!^X-oJcIJdXKRa1@yZiW#pkcz?Z$mR>GpnNE>IEnvL zCPhd)FEyBkyz{kK6w>7ebFrx}^eFnSlf{zKl=2{}Q^=Aw>aD>iMli&OJZ|{C#O6z( zZs@p%4zc?9_fcf!g6o2}-2o0De0nDg93=i|eyAch>RAEO;mwpl+TGEt40DQJ`mWt< zaU{IiW5hv#TL4eQvj&*5q)J7z=95TNgUpue*6tw->6Y1#0V6om-SaKB+EU4C*PFOi z#Q7+_WYEj*q?-LU?XhD2Ir9nsvIoTvM9%$R$6J z9`+WT7sgBYmk{P60hx}Dj;s!$FSw7%QY_=k2UagRkXHqt`lEL(eGt@mR18>70HSCcXd$(052yr9_q( z^=xLiM+Esf2mo2Wi(ZcWYjt1PWt3xmE^`X~;)2;`cC+4Fb1SP#!bU@J?0q94i+*8g z7K~Qu6Dmn(jR+){!#tsKs0{a^7`$#VttOGqT7zGncR$o$>jd9goc5%x!2XZyhC2G< zj%;z&`F#8Zx^cd1=rQnt!KOE0{1lN)F+t=>U|nHG3XlKH^NO)lMRgmG4N_rUXzb}m z{(`&d(3DE$w4@gPTCcMY@mFxcn+QqdgJ&m!rEZygPPt~I*>#tuH;HrGBXY-Hs2X5IaN9l=u z@^4shLF)*g^;uL17yJ2F-wY*g6aifobEQ0)HPVO|y4DL-?`;yMv+`87nk0PmH(wDp zN*qWZ0&(L#-}#8piLrXv5AqU5+&OsE4)8KJv3)&b!2ul1bDbx5RwD5x>NQ*a*xgs6 zuKEP{JS_hbFDl^OcPLEzY~SB?gL$l#A^}bt?TFO6w` z^BdFu!R)H9OYdJ658lf#xk9_0wDL-wYd9)ZZ!@Mh<>O7V+UjSOc7Zn{8$B6N%E8V9 z6c_w7ZL6LfoKcZn89d+Hl@M2UvC0YH+GhgcX@}nP)o0ZUVLcVl4fe+;$>n^pPT?C! ze3NF<@rH9qXIN>l>zOX(Of5`08*RP$`N`}4Vz|PpUrX<4;9FNRnv>u-+0!oCLsT}| zK?S)T!qotIM3Uou^^$yi_T7h)D{3$?HJK@;Y_xbBClN(XDW7U%bWq=xP zL#vvxl}CQu8&=3eDlPZJYOxGoNYYQ!$noPGxnu#>_sA=m`Bu5YZd1voa;?k=_e{Sv zQU0yf?ZN1h3^t$MK*EAn2offw$6!7OvD$Vc#}8Tzl>^G5(o#wJIC(S;&GlAdn8?3#kYT>3;^lBq8G2iV@YQgiLJ)mB1)~xNuuBJGI^}bFBqmMovdS6 zTw>k*DA~9=Twtzv=*^($2p}s{m}b|qSEJ*4{?YRjAYG9ou4a=t+00&JVY=h|@JFT_ z1>(u1kl_lU6q8%<_3aW$Vre7~7JekewimL1eiCjsD9$nffEd>MP>%YyK%DICD!IYM zut(?4Yl6mN%g=h*`X&-?=jo8Vh3}V|^^ds!t!rfh;43K!SwI1qv6 z1 zr};v`zkrHnD|I>dkjiksw=TOO3Dq)KGirZSVS-5tw5^SYaIH1LWA@!v9GiSfqL!&B z+w6rUl=(w}(08Jde3lEKil0L{uSa)aF{ectxZFt@0QjnL!6xm_R}3NuCFuVcc5tkp zhE5_T9I<0bD|Q8E7oP!It0ExS$WPp;UG)`!DZQzWwzJ7$@wn&e64e8c^=QkT)BJbY z#G1&EJi+q9-yUa$n`P zUxHrMDrFwXKY&6gnOB0lV*VJiB2iwj!C}IvyPI_m2-kc=k_m1TTnr2a`iMRUz!aEu znXBBOdZ+;bw;@!T)n6Q*Et6N4QzBI%2>fUpucn=F9!|6`tpq-iLeYAvId;OI*U!4W z<+B4&A=sSbi9ynx&;@Ij2i)W6x6@NaxV{7X24l2PGCFwtou+2Ni~ zeMFM~b0+#1)szr6)*bQzh(=kV9bP3)s5r6cu4__1!Esc7&6fwIAxd@SUmsfmA)M|R zJ$KM_{=3F>>eZd<4E0>+pOG4b`6vsMb#+8wxCnRsuRa1D+T302c}nu!$OjkN)nJ;R zZwBKYj#jFs%6-%rHW8FaNeCyScRx#9==SJ8K_WXwu>@1^_M1L;L%VoxySm6(dG~PH z>3~Iuz}3o|1{Ii7tpF+Jv*N6r+wr01xnA;6c8uF`4Eg$zz|e=cmW#r#_8sf4jbq@i zWtVXBKgZzzsI<(;SC_qA^E@LXrd|$AYQQ)76##g;eYEE|78=`s^ zC{WuD-dkT2J?^fXlCI{x&F^;CrHi!mA3s&V=te{go=!PrOvCZnul(X{{WJj9ESio^HF?sWV{kBfBh}|RpFlM2lF{KyP6R2G2Zz#L<=-vn z8bXdgqfn=;AQz^+=+R*RW)Dhjue)UNWiuw6cry#u*U_2pDZ~KX_adnZ6-5G`KxEhb z#I+pmuKE4Z>m@0W9?jh+N?vUu^fCi-vK+xQLFBT@mN+6ZK-_H@>~ZD`xFEeMjQ}(g zjsh&^rzX*;*yzLu^zTDTZt{LtxN%JbTzbr67VWZ8IMG4GLQWXFoqP#Y@S*1Kk-A@X z4uD={VzymE_hY6PFDcKU$LtMuup~6G!6y-1_DUB-rJRRjKD$TzTZ3i&`b$;>*l}VV-}vyw7n)&~( z?bzexXfI^HJMkk@L&Ha|=HlIHP5H$Y=LrC-w+%AW{ocGI@YA#J;h+#&r)n#vsw4GIs>?XL!;*MRP>(s(4Tf^EDBcyY1Sx zU^MMnXW~YmnynY5o$aJ=5>q&Antk*00z6G*7m79@A`$WH1?iwOpbltS$yQ-b=!4L1 z$CFp87>+eVy!gF*gHrfqx1ygzej~kVg8!2T?nW=q@3~@rNTanzU<>xb54`KxQ>D+j z;nT>c&IZd1rOz-gD9+VHV&NMs&bJ%g4&Z(${2ZVbMBHRua8}412<-`5tYlFs!Yj_b z$&w3U%|-91xB*^&^rLk&&=I)PEp2vdJdq@l4GuItpNh(6=idq3vXcfD^3%nZx5c+( zS$L@}!w8LqnW=`7V>cLZpm_%9z%>vqgR)ce&DF$FyAo=gJT@fq0^LH)E3bpzMun#v zXX6z8=#9gcx*;=-H#`rBbji>pt1ReAwT$(clG(FrrRi1kiofB4!pIK`CWB(4IRe~U z4D9Ytmy`(@fJJT$(wn;Q7lb6fw`jVadnx=#Lw(n34?;G?d>(c4k9d!M<=-xqU;~2~ z#@>W1rs&;c3XKLty%VGDxz%m-ZYTFVoZOWr^&jY&uOlvgeJ;Eaw>SU84Oo5(_XPUH8wBI*zld=A6dA6=i+Xlm zY`rK?_`fIa{&6`G9oznsrP%fHzR#PPBVdyko#MqHi@WKnD)+0{G#q98tlMF#IB#)$ ztj*)^SO7PQY4gAAOv~G0q^t7N>6hI&e9iATK8>GmB;A33A-YUX9IZiyv9rhJ&)aCN$i*1F8e1p^( zMk1a*ZPXv?Fk6m5&g}N^JfLsFhl&>wWi->aXtJ{FF-JN)l)iDs-6wB|z)6|zed`WE zCBq)2j(?Ay$XtqAE&0h4m7?o(+*lRV#RK|zSdUttbG6*)9zQR8AZyj9qLib4i8|z4 zXN(zb@d=wI_CT3!x1lFh>CWnhj2CVj25q(iOpxAR+EfpxZOO~>hRG8iK{*T?P)}Ol z`%Q%BiyhG-(2F;XDc^5L0Qg?n*^tbg<|FN_v|3R1=XBxNxk8J(Va7p^ z)eulf`%I1a@XnR}NLeZ$KbO%(EIBdeaE1)|>m=ETtEPob5y?y>UHg=JH3KR{tOW{) z8#nD_Q{kmvLs zI`dP0fn*{8k(v~0JNUKX{3axF1@}rCRb*6n$7Hbb|1tK~Ur|8q9|trtL?O-sCe^@Q z9eq2ODVt=X0lCVZ0YgA>9aE*Rbd7mhq`L=7n4?LeVb{P%MQU-N3F~@s4Y3i{ya6Zt zw|Z?W0`h@ur~XcdUyB&@&Ag9GIY0U7FAFO}r3ZtOqnC4Kf~RT4e=C7(Qbpxac-vpk z6MEWaHJk6Sdv5C)8-AFl;m5uof`52_wpL-M^%Q~sQtWVSv%yi zCQO7%McJoy%W-ZRToAJbH`OVp<}@m~2%!NUE~W3SGDt!<`K3s}%nzu1?D$M9NUl!% zbu@fzMoqFDpMRl!Dn#Z^I*>~G30*8I;2i|QIL{IVaA*}j8_~9z{`sn*qvI3@Y>6uP z0@Q=X@UBisE-B>DVkc38AAFeXCN&F74NFJp5IZ9hu>S=ObX9-^-4)1KQ)&HStO5=j z5*PC`irWkT%=;n+tDPX^gi%=^PkZO1Hp@`%CSBtD61ZH)|kbW92zK>7^ zy_C5Q&JUAh>Vv$dsGD=fCzxC+xcr$eKP$F{Pk2AC;h@@BNpmwFbHac2!bTp2Y%Mgo z{FO@%&We_=1I8L_U;}i64}=&0zAL~Qi>LVfe{BU+8rnzP(_$n7gTim9FWirUb(BY> ze0tslHdJgeL~e%fzBrnTBZ8Lv_VXC`v5)5KjtK`?0GK4FA27%<=%iYHhJFml2(3h} zpnqo+eGN4Q6?tfcmI1-~(=r5Rfl z(C|}}amf&;{YJu4ZH_fC2IWI93fSl%+9Of2X0|O#3@JGB8fjSy&5MFf@63|h`nrVSd3lR$!W@N(%t&H?nC;)St8DOR=`7wr98h^O;Gv@6%~3^0_Y2u~Z7iKGasx zYyeMglj96HF_J@@B+(2NgGh@CKdT;aXj4AmQvBu^BmY9>GGvL1w5IfJic^!fGtX0I zuw~|73SENjkX3i>M4?pCVy0Zf0|A2d<2SC|^MRJG@RIvU{8UU(NdB{p?{~!4!+VR}XYMHV24>x{jOgo_3cgtc zr|Ij&y>4wDX%2^pP}XV9{o(Nv_D(D2o^+qS*<;67pRpJiD+0cVtpQl_=KTk%w0#mZ znT<#}iL$?#G}~gFf*B5xi^$-)z2m{dJ1HkT2S$C}kakEoezB44 z=mPLhN$$>EVKUClpmurDwX9MBiz0ojgsf*WW_PEIq5yRWzdnL*?1!(P$4>dIH{Lx` zn35N%eLRDx%h7%B=X?7_p?z&?n>-O$>}f+6DrtA!tX(g;kIBw(Krc}7+TW>j{3vbx z2@rQ6_+f%C)SMC=PH;-s5%ec^cDE~A`;ig>`E-z;Dzi-U&0Yaa@3>|*;s5#~n^~F7 z*plTqO3p^U?lJwr{>O@)Edb_L+G9OTqwG0xJRo9X%7Gt^;rKTv<8+eIT8CEq;3=Pb zp#!R|b>+sQ3(#j;5$GSDQMu4D1Wz(M@zIWr5Wc(KIsm>~z>J~-u|d?tX>|GWj&W;y zJ{BL@YTY!XYMKg(I9|kI{3nRA*#JNrovd{Fv-uJx$Z_Fe>qfTmcpz9>I+yAoz3fO@ z#=MK`sngzG1Rv#!{P&0R&!^bs&NWClzGAzvq{4DLVu=-+VWSxgE(%^9x=uLAW21js z8qATlOdHJO4|g-Bz7E=i7;1Kl$#tdTw?99h@?D<6H0zoQI=;Q*zxR31j&q_i=RH*} zNwOSo9{OOSHDnikTg68Px)3mFo*FDZ{VLwTI8N!_qO5;cSx2bma-!~dY&TOu!`a<7 z=v5|s6WB)G4e48^QG4t8^QoI%U1#U-GXcLgKh;FHm8G-;4sre&Im|qyYYp)y<>3*mShyS=2Q)@a(A>U}6G0!1 zF*NL~l13nl(Df0ptzm4O4H*KlDheHxG{;&~8HBKvqSs7E{$R(-f+Z*TfK9Pe^^T45VaAaFsO8X>Xzh(O7;Ofa(M7T!-`39g*TW3U!@S$ zxC|>10hmNWn*{8}bkW3IL&sq{Ua$ik4!$}V+o+9$rL3yiAFeT@V2ojj7~CxN@lmfs zFb1fT2D&{t57vBdBz2%kB6xEIvlUVp>1x}x zT{yvPU%tYUu%@L!fDaALP1TH|SU|m*KEn0nN}Ui48wUpu*|%!?B!mcF_jvDX=ov>P zRkm^)h~Z3DYVV+^hXFNwTR=0ny(7H^TrbTZGwwfNVWjpe*Ibt*N*wVTU=(N3SXcW( zgW{5~{wt=1xE9pQjctCL^85dn>bC@E}Qda@pI27e5W3(?UHe--t1 zKb>{%3agsba3gF}UCoF%NDcT4=X$GtqoMt+BWEQGB=V@%$6t4n!`M-XQyKw0rTw5Z zxT_vAKdbP^4H2jP z&I02gP1@%pu*3i(NLF|@K*DK~sg2qVbtF=Px5P}6`59t9Qk;7`O0 z-|Na>mduX|XksBGxGR4?EvA~!Evi?s|~$= z$O10AGmoHo7y45b!D%5@&>H32tHSD8j}9A6jKq{&q}idzns2NzzoSiFVXL+bIOk9U zEDAX{nf0JU;+4%~9IGHbdi*&2;P}|jXOvyu6_p?SLS2ndszMciL%|c<6eiz5fm^QC z*)fQlEF9@QGykG6xME{lZ8D>xn`)CEVs4c~hw3G0!8x|)SY{gBQw{CP^7$U|dIIzS z@(V)F!gPRh({nn%y`IzN>qe;(X_UF@?9yles-Jb4#Ju5m&Ae2(F;VJOp&EK_oDN(b>$c;1#7i#il&E1Fvfw z2utg!3@@-%?sI{O$eU&MMKrkn{Z%GYFHPbDERoo+p^N47E_R=0uk(2`r)w@Ez4t0? zIf{f{`K%KqgF@u&f3moB`RTKc1gqt<6q+2*>pi&>6oy}r!x6&Mw9Cl&#lMs6#$4B) z1dNQcCK=y~QSC=x3QP8W$@kx284>wvE01QexV5j7AwLPs>>-HlS&uE%cEu|Ft3K%L zxZvthwJwXr9{zBUQBdr)OVwk>t_9l9!cLm+a1*n zrK+JKdZx)5DR)JccTFccMcqm4(rJCmp<;>YYd#bYp>Q&B*Om)M_ipB`hgV5G7lr2N z&P!6_yw1hl1M(i+5AFik#Dd%JRY)#Aa|2v=DwDOXXR^>I@JK&zDdXW-3wcjsQ1E4P zv~9p1aw-YJ!2=r7F#zWoG@!LZN1j^cx4~gf1xJ*C4B1l|+Vv1bsYMXoWHtl6hhgfaMlR6Mm_^Lp!;B9omN>gRN#Gi^gxZxP7;rH~M;af`bn zDhbN3JpW97ets}hiIzHga}*J*@C~QJBV(e;WmvWUSxoZKuZmqaC1!6D6?f?v{d*o9 zB3c?Dkt=w9gYHlhqGgQOw<5R>Y4T`pDy_w1a-YDCGd$_=Ono zSHo`#05WF;fOFoeIh`3bpkMU~=)VK&{rgKF{iWnQ?tWEC1SR?qm%Y*15`WApcKAD~ z4|1Xaac-x*xlcWR7OqE2-r-e7R4?$;4r80 z4NNWN;d>jwnVp`8+K&g@i97yF{afA4WM7@e0^Ju=r<+?WKmpizA$>O>)7JerWrs_|W>UZ4<&qQ~zd-?%p@kVM3$GPzVOeDmC1=d;@f`JZ0!x!Ih^jFrZ{GV7pmG;YJ| z=1r6VG8O^ZPm3EZF1+4jhNM-*u}&3lp!edeUrpkayq&_J-sz_C!V|L5O9lIL+ z{T7sLgn31P4I($@H8t=ou`KwHPgjJ!0Gd*1uRb!UzplxuNcI18Qpy$MsEfFK z{%6niEACc2>%C~-&9z+lJUUdo$Ph3bAc#S4eB&5W0}rFn@@kT?m$`bz7i8dT6c1oh zY*QB-5@ghq0$dAjphu^F)u4vp6J7FJm?;wGudd*O)?$3*!G?|?fn*U$fw*is1+5+& zW`X?M7m>*P;5Q35M!BS*uBAEm+ZXwlM=?hJe&iPa*~cXePrqY3-3E2%>LgS7!CUf| zKVskk`;bC=0S(q&XjhG|l*fX4Ao>RxR4zc-9-jP~?ZLy-6c&;9X=G%ySdVhN6f;af zn4O#3P(*^}YeeCHU448ldSFVK?+=HwbG~KE``l#zPA9plua7JYa@n>jvuqF+r;SSV z3MUX@2b}8zU^@lB!0)BX=mho3qMHi=z#qNN$&z!Z4x-5~xx7qT-T14sfF!4j?n&-V z?%@_`hBlmd#(pE3IjGZgmYlS_3Tzz&n%?RZyZh1lbgHX+J4haF#S&vd&vuB=6xyu7 zn^DZ$6*I{XEaxU7lcf1Ik|iyit4d1+;73nU%4ju{yQ?O_srx6&vUD1Ov6Y05x+)=B z78g#?#chP-rrLX?`>FZZS#OzxzZ2<>v*L-$9>Z#9_=js-YIooz%*y@7a31gd{k<7_ zh4qTF5(%t@S9tOCu4Dn5($^|TEyZb1p~-m0HZfF_xYO!{9)p?xwmH)BJ1r`nSfEG{ z2hPL`DzWo06;V8(+?BoWeh11|rrZH!#vyxZ%fuUgLN#TJ@Gc!U5kRCLahwSuuJc(7 z9CD@ptB@38*Yj>GI;KY9x1VQ}fv7H*pXCvTW$F8F+_b{Tt8KH!1-S0g=y`*^uB~P9 z6Q~Rp3&G9Lk}@#!(*w&H-qYri!grSi$2=nxA>DALdH1;Qj!YGX3>r^LiG|G96l6TJ z4Tn0xQmoqq0`D#z33P5on41umelM&CuwQ=hmiz;Qs3waE0sd@FDqhOW`sb2-7!p@Z zSoFg{1Zl~I$Y{H#1O%J5y5zBgD5m+0UA{L_Ik#y-9dixM`}fDVkjKW)_ZySeI$c1=nDvmHkbme$Wa^;|OTII1nev`Efb<2MG>f6+! zIv0aVtGo=Qj+W|b8oCSE;Kf-_{~Tpnmbpsxu9ethI{xt6iAugo7JD7ocIW+BoAv#9 zEc0VQ#_fu7PZVXf#&o&sju;sL^E|Hd+<}z0d=e?URC#}x5kcv!b`Q``eDm(%Wq4C# zB%Xvy6w%}3LR~tED0e=fgjqBcX@|TBL>YeD72`+WyiXl|Fyo|^#o%FU)&6#awR9yg z>T#MlTkOO1)ruo9o#~9PE~CgJY3?%4@{#O?Np+vtOCA}>wU5#Fv#nx?c-r9;Uod_` zZvj3|fDL>C=6`0P0ZB{HJ{_kTVl(4iV(^esUlG%<5z}8~4Jwnav4au>5>xYXBb%1~)8_tPx;=A?c+uRl zmZd(P|2@u*EkphPmvj5DN)@2NkN1i4MII$Xcuz}N4R1+*h0Bfg51Nw3qedffvJxr7 zUE(iKV2lcbi_dTEEd5Ex@4SH$-h)!b#vEB~tL9Su#;QE*seh4@iF~P2kGjT22-(C` zSNe8PR8t0};cVb@^WIZ1=Vle!=M~=dpQ}{)EKH;sv(C*eHJfGj|k3HXpk`;0Bj z${NX&NP%n94Ga9Zayo$p35)2D$k&57G`Qis33M}}pu2e~*1%xy40YzvPX`pWTmAyM$F$2y|Uih|XSM;|fL05z5rIf34Kj`Avb2>j}|0=T%xon%=j|&UKKP5C{!t zns!@E@KX2@8psEY#0Dg-N2`Nn9xm%(<4wTDCD52B5FB><``9%N9B`EZra>GR(C0r; z!fgsgR2ui-%BtGO`c80;7a$7u)l4W}=kh?>M3&ks75qqtq0h zf}Q$WnL$Mbpq&~QTs>L7bSXnOai*LUJLd29|7)8uwF)}WYj?M$r%}3|{BF0zaPWYG z$5=E1y%D9mRPL87Fd82A_iaL*Y|K-_CDhHr2dyVDUB7gFWm5~c2mI+VzKIs-)DeJz zhcaixC7;SofC7&P%h75;{&sMn(|mr!X?O39a;JVXNFCgG-|&Y=3=3 zMe@i2s^9$TFCbI5&-40fj@45UDz$0+Z7OixzhmhdON^z5ZLrmp&U%l{RU`idEl+pg zx4gqt&7)+D#P`nn;Km$ZSNJ0Qy^f*>uElF~f^}%j$Qj40RsB@NT@~f)*Ssfa!3a@2 zVHApl`WFt}r0Wj|D~MheaPE^sKIBwe3990DTW(YObD{3Lbl3|$xW6H{se!Mv>c_Fm zKXxAE)XOnnaImOt#>AHb%Rd815CELi5?zPJpTvF%*6@Wt{VBNxOe_6@j=GT}U8?<} zTJNP`y2rm##VH;7lf%xisUr%z$DTnhbJm0GmY{qf=y!sP`2L6Xn&-6MYKx|op|&$#8y&S&Zv!(Yp*2JIJnY|mtdSZY3$j!wB;DKq@j!kd{BM@ zgIKAbLm5||*!;&4z7+UwXfH3d=kbvhO1ERXIr*Eq^{wbE>3cBrDksiT@n%r^L(lh1)_)+jE z;t!C`>pNk2@JI6^G<@9k`ih18bJfv(w50MheBpX+U4m|kfm+CvSApgN;~uhZmz|5r z4Cic(KtzP@X^vBLro~4qOQN%#r_n>&fQi7)Fyml+tGdI4V2B~d!PJWO%g!Y zRhr;p#%tpGf&>H{S#Y(-ueI_9k&cJ<|CNEB66reOyIa?QD%rDVi{DD2o-aw$oQ7z z6ckj7LwP1~5~xD?W~DDY>88fM)U`pFW+_c)wOZ4xiI-yVYiIlBA--nt6i`lVEv?EE30BU^2&~4 z_L3}*OgEEnRtN|<)KWiwh6#-vG>0BeGe(F5>}MrqQs|R={$rU!PUdu+M*RJLkJ9s5 zP59Yj1in7(C*VNvf`I=VQ)O1Dx_pdV3$fh1@QNwCo1V=$_-Ad(g~}s1;7eOnk_iNg z-QHVEpi|_DdXEMxS)irB~S?Uc5+y4Dp#=(yPd8ji|xUDp%%HU>rE zdctGJA*CV0)JX{G5ge>;ctxm=t=+ZkH-|F{wzM;m`iNDhMl0bDUJf7oeBVvyx)X^u zdk}N|#Q?FBH9M4-l?{88wc`@khoKAu$*R#*=zXY++=!gF_7*)R41>3oDrG#_t7mrWy|asa?p%P(jgtJ;Tm-i? zIP{P=?R1HXBtq{=@@~cBWh_cshntU}q&HLiD0a6bI}&ld_dRkJCH9Y= z(5@6;z`$pxl{W5p3Vve~HVMpdsWa*-anACl%v8elhYoescXXx~?H4iSCzAEyb->jR znai9)SoF;Y9 zeAp`m*2!^#9qn8AjRj5FATl@l;agKTQ*A#1&a(hB;(LCLZcJcGx?abbisA9h(|Q*L z=RPbgY>I30``;w|?rGdqsuAw7i}2hl4Ev7PKmDL|FqJXa%^<7x=&DYoKy6 zE@T#$&)XBKC6Ai@zwg*znYgRSwa|fnv698^lEE_3dSO4vE-#s*T~jQ}3xADk)*r+g zPz+$|cMMw0zXp0jwpf|Yj&h{sFpdq{USz62Lva6vF*EKNRA-cgv2|3YbryuA-}?;+ zzs5F{z(1hhC7h{M^zXV99vBOb7U~BRoEyuo+Xk!e*682Hz&&hG*%76fb2Cq};uZrW zqH=o9-?gZ@v1be6N5TnNR#`NAcG*#zvJ6qFNK z%u7R)mfTI>4s4D*)zL@P-f|)!B**ldW^3^gKHV`a1NcKaj!vU>85q-d;uTa6v}mS{ z?t*NF*2V;-`=bUGK?lRx1>dt6JW00rQ-PC;ByJwobldy-y@E0FeV*=Ha(r&yltp3! zU)_0M%QshtMjneUX5_DivCE&AgqA!4-c4qUt2lfc{aSiLFkTn0$Hl);xXKH~L|e7n zh}5~u0p?lrqZC>cRpW}jPt?jsU%I7nHXrUHS(!ugo?XV*?jB9>?G2g%EBmo;rjOp{ z79(SSW@A3yuSCY~J&>8VMgheiIuvQOGUEa>za|nXs0`|j`YTL>b4Z@%^qf~^$`Qh! z4tIM351vA40xG`EhOCuYUdvG?(Hy-Dq}))r!RGxiuY>@apFY?yD(SQPn;RUhdlJXX zboAk!hdszJu}kQ;)@M1(1l^n`Sn_QJ)AP&z)UDsp(f%YYHu&Dm8JSJ*vL(;0ObB1X z0~e-ZEmcYcH2fvto{}2Q(hdO*;99DyCs2T_rIjutb7TijPQxR@#;ts6yn>?g>Czpd;|3qXFtVg9jC^t^D18AQWc+mK3{nwRsN`HA_b zkP6%La78W!6Skyqg^IDz-1BVZuYp7u6e`XU_&T^hyBSP=kRW=Nq@`8y_IS*B10S<5FT@85?I=>EZs6i)kC-yV@I;LH*YZM03M8q;qe zP8ad4wjUMf1?};X@;OkAhWA{>PWBecL}F6McrrEc6xiy?v*lOqgPP2IPQ7$X?9d;f z+rCWLIh`D3TEW{(vRUJm7gMEJG^70|q+`-)k-#*y$xvq&ht#!4Jk-o&wSM5i2y~n`P zH}WL=ofQ1`lJP;GFpyZ(8p(Hl0-C9#j8%Jm%_NOAX-HJotvOUM+;;einX!}iKqMRL=|>FWumSpcEd&~C%_OHYR}Z{Z#H~}izpvUIWQ^r z%CZ1#|6=!Ai+_e{v0Z$&cj{Fvru*88pNAuqz@y#CxfcN|82KpI=mz7?@QB$uEl897 zpY?lNT+!_jqe5}_XRGp~o&dBB*XgDEAd**La$^`z?>$7<>DxgcY0}nylN-8Od8PAO z9aQynWc(l+*gW$}tulMHJ4&kzW?gAdZ(@uuGdASwLuomX>Q*P4bG%eaE)ek*#XT3P z5+PKueDCt~l|7B?Gm%`ytX2p+xpHO6QBN9+s{tpcIzf!~st9!?e7&QwL5UJBOe(`k0hOdln zDOF>a@Q4X9|0}yRg4+P*UPrA6+ZU_LW3tRLCSa-lOcSo_AWsf_H_}Ee!^UYEudXSZ zMCU0@{CApzz}q=NzP2}+9y!m}k#;>u;-q@M^n#gJI5glC(z||v4atY1$QvRM|K#?Z z_0tq5f(yZcV(4uw;|)xag0JcpFF!)AM#{CGa!hnU2c(#;X7ZA3>kMrG=Gn7qk6m&6 zL-6_a;V-3v=utcp9&51{yrmv7P~qKD;o0=R#1S}WX)d6fE9c*KwzvpOEEF`sfjo<1 zrERNeVPK&*q{4R}r7Hf28k9s9*Zwaem8alxDt#So;NELG~ zj10;iSyye(8H_<*JKM8Ke&E2pu?A)QtjZ@o>pvVup_wlp5}nD(!73JxFL<6U1nta+v^j%#@prowsa7Ba`cGAWYz$70rg2m7NcZnnn!Pl_U(UOy z%wcVnWt=j*Bj)jvZz4>6B2b_fKpo){`^XZuYg zpdnQ_u`-swRHY_{al_tZ0jHXFNWi-$l0U9s*l^?um(`Akq0` z;qUR~z@d~^0lVo2bi=T2QUYJ}i_26pa0pg`kOP2$sav@6Zy(vUf3QLafKsg|;UD0qUQ{o@@;2KMwWmR{&f z9x9SpkjIaty6dth+YAne4R zI|bM~4c9{(dvmuT0aATZt1u66r)17>>()OJ8S)wp3+WFIZ}5Nm-(PGtsfScn;S+fJY%aP&zcmJq6@ZFc&Lpsn<7#{i!Rv`7#IeY6$am_EC_h|Q7!X6BpFWk8@ zl2Nak!p&}2mYU~q9Zs^6OGIG7!|nW%XJK&x_9uj0nUfI@W-fD za`Lmm8=* zL{nw>t@zn)HVARk|NXUiW<{SrA(;T)FC5sl%|>d1^_+iij<7NaIBc0=pF1xJ_cmgy z!E2Is<}SsB)dD8^0}%fBsS9@of^^*BRsOr{eK`XPrCpg!BpfN}_)3`l1a*T-Mhu2G zbtPP8t^eqgSVvPZ{0}@({L^QR|Ia-#3Kx>(cvi!yvs*{Xq>g5ja?ik!+V$V7%VKJw zUaZo?4KLWw>Wipmg+zLLuYs1Ke`Fv3OqKB%i*jWe+ivpKrowQ!xPcLXj@yXSY^uEr z4~h!ob#Mjn#<<)UqBtMlFO@qsbGY!Q*BJil+~C|v=AqGxB9@hqo0j2f_+$>d`41fe zFt^?@2Bnn-hgS5bkbzNMe-K>1)K#}uS0Bp_wr ztuQ(^$!936!n8_~Maw}glgkZf)pNC_9t(mJE>PEQRb=CaJ+(hVkvOyJOrjG2z;HId zK^K2pIi~B@hM#gO&TwdXTxE2aU6!87{mj0MSt#))T9C3`%oKR;1l)hnYWBqqc#byd z?GlS(2=0_wX;8rwpDUP9tJAb<#j!&0_>arN3g5lbKk9G*hYPhVN7!A;6e!xVoYGlk zB7I)vu77J3m&vs`DXAK}r+%&6qvdpjnlmGLDL1W-Mqhl+Ue^ydds25i>euTD(c8ND z=@}U`rP-n!>6N0RzAIpHKdI1^w^e#3a}oP+@)In5oVj~dLmg5tq?g<6aVZ4z`38B( zUefY9jk1dmCYnE_TeQ(i1Obvl=0(_|zO;Rqv$QOoKuFa36aYII{cS*$!{362i(m+8 z%l!Ixm6aO;fr}Wlo=#zFzpI_UNR62K!?5-;;h2I24^GsI!|z;60U5}&ymwwP4L+27 zj4^uHdb2+l+bCsfl6g$ZM)H?Rf(4BsI9*ia+~A=x9M? z;c1JRsG?|;K!ue^EL5FzFfhm|e{K7pkLNzIi+79j=GlAi83Zim*VhB}Zt5_2S9^ob zIe-uO6z*oCu1kR$`12EfOy`w_`svX>;%s^=9Z0*yb~qt7mU^88tJmxMRsJt*Sz01P zB;ShitQ`!hDs?mK7DFhAyji^}O~NtGCW?kR#W^Pck-l)~#KJL;0_cA1I^sOvJ>6df ztSN1CaX%mXr{Gu%V;@{PEP423PRfvF>mQqikILg%$l)Bgkq6z6+&;i? z73>S`;QPB)>Q^MF*boK<-(!U?+a8El(85Oyo|4_gO=v^ zk2UsvPdJe`Gbma2djkH#_SD(0rydeNjQ9qn5eV2XBqoaB>*!wVC!Bdu36$Nojs;OE z-eZt4g;xXp^%E=^hY(S}OFZo0LZ#WH6WreL$sy{*n=Jc$H#+cl5f|1#t*+v6jb;x} zx`!cKk36jS8D*@&A}Ka%LJLS}&ZmUlWB(;A{F6K;O*Zl+32MCIL6t^8&~{*YctP&H zIXtQth3^K8A6gSrTB>>Bkl!X)v)n_NKknP?079`#hHcMKo|O_WFoeRM z2el^923u@-XMI>K@);}M`TBAkR_fL4Cvz4&P>h0g2s>lnWyFLTDSrO7HWjN5`=y4bhpjpI2X5j$h2 z<+YzkleX4NFmiWrSZg&3b4@dBtP5Ly7DpYX3z;inYTS=qT)Pev;_RG}wd7Ou8+{+u zid_;5DJASC*~cLd){Xnrq_EpT6eO8(d?>S@n)J?svQFasGYB9)r4i~CW7L!>5`_#WKy=TOsUq3`u9z%tAwI4zvsb}c5hPhPft zWzt_NGkCd($uYRDwJmayA->~lbu6Oc(m*lYF0v-_*kaC#J?&X( zt3e|bllyc`Y23-@aqY{L@<7(@A2Wn5ty~t{mkGSG+W8o?H|fid?z0w-C&gg6M$Nj5 z-j>dF*~zs)@2bc_SYnKOW@bCx=Vt!qZ37DUmgSN_yF|(7{w(pwMx+6yEy}Ga9RF2E zaA8P*Z{HRY!M#Jqv(d|bWW;j;rT!JBnP^p7>~B{3J3+2yfzHNCDSm3ys7-Dq}TeVa;C5a!L2pTnq>~-H# zbu-BIt|S4Pgc#}&7#CdY$OCQ@zF4fM)+T%zlsch~8+0{3SI|F|Wtp@>{xM0z-QCEn zO`bl9d1y5DlLm`m%#E^JeT5+w04Zh{T=rTH%{K|OzQm9;yo=$ru=ln200Yl^EBcKp z57HtAi+1cC}Wp?bt`VxNjOTuXVR%LR8w=xeld(Q z!b$R2)|;ZL(>NWAp^>)J+rhJEA>qiwA;;#4_2Jrl(!UtN2G-xmETCVY-ylwj$*VWR z=-zcV@ms2_bbKH+wLW=EULq=i_xB7PiLb)BmbEsm82)cN-uH%FGaC8UgQ3S4aLl|z zG?X4)7AAR!6d7|8SFZ!lw8$A&-wOz&4_gh3GtHMWA0+?>FtBC<~}t!51jAYiJbm3k+1x0t9$rSFt=5M#y z3)hJEJaPCS90Z|2d~2Wmlg07A-vNVu6S7~rG6ZRDHP5Jjhb%`}IPBQtJ-g%jkKEDt z(SxUAAor~o@mt};hLcKNL%$f+hR3@ELC2ELmy#MofKM*z5UHkhaNws7#nO~ z0;}r7tIb-@^gGSnuATB}<4syBe$>3E?ACa-te%#oZWlb^U=>A6!**l5B176X`v$M8 zrtK+p=B&dy7a%CPgFdp-qu`i5-r(64T?4_zSQLl12{X?F&?r;Ma#qLHgnn#mpxm5K zkIG!QV1d+dKkQ%eIR$5fmQ~z;BP1rL+5c@(#Xpf)*73$)c^D zAI%L{|3=MA!=TGk@3yWqQEsbfY>mA0Fy+WVXF=rbwxJmiiW#sed{~-__MhiXw5OoXfv6M;BvNLJ@wul%I;K&e7ZrHP*<`~N56X4 zFC>y^gA1?W6oZl`L2##UeAs7STiqy7Y3?nt-u4kYyVy7|0`>dE%qg%XUkBwK8&;7| z4yMYI7xGrCbjggDgi|SV zO$}59LaZ>5?{CD1Ed{k!5n**C@u^)rzOvmYj=#wyAc$Mi*$skxHsd z^!jhSBC^RPHceyPW$8d87InJ3dipVt0iyeGRp#kVYCk!kdP1<|#P?N&F!SGL5KX11 z;@!~Z$3L0;yKG5kdG59L zYOZ7H>~?ziaxySFM=Ft&GgCO8g)>u5Z_p%gOPMw(+`V3Li@MoTTN-ixC>nJmXj5zR zLt>BaW^Pc#V4fX;YrgW_oodMu&8#z(36hj@2Y!uzEj`*Ec~Lh_^XVGN3sc?Ypn9j3 zVetGsGvEFOTDA7~Y)I))xXpU8rzW+{q#xqzib6MQfkq&ysg=WejjAMv*ou$WRSX|S z5N#(^>%Kydh=Vj)l_=gkkNLQNc^V{!6+Z^)(1ZP5w-EVM!*Dk*#LdfFK6)Hp21`Jx z(ELAtNKGogVVwU}@*hN%J^4x9>F!mXV&=}v14{m|2D9YFg(nd=%{s%)AI2)4TIKL& z_xM^7PiHO@V@hi%g%GY+Www8-TOxg+Qk5rAm%!>CN#mOQg2U7VCzqzxo{ff0+TMJ_ zuK)Yg%c8m4{@Xl<+jwn~UI@w+YNc}B(vNU0<4!w??->Xf{VgBwIF44@d2htNS7bTa zj)*-#6p8co&7&TyNDqxo%gcw+Y<1XX!W+-0BHt{VihTxyoF%Eb6Q2Qi}!QF>_0fOqgxKY3t~AaTi;{o{TXirK{(mmL=>NY<@Ba1a ze0vL^5yFI%4?!tcD}GF8)sk;cbuEJC{eak@cW4L)~Q_WA2! z?r1_!30mIMy8Nv)+lZlMcQaB%xrz-5)y`xLQltWvwuSmV4faLpb zMMqg%VFuaXiRZ8wRFQpCrTKL4mL-D;l=%D#4c3Usc|jkj0Xh}LKaCq9L)QGx&4BKU zlD3L!oQPRDIVy<~uS914<5U*V4?985)qlT<+jFv4hN26y3^~3_q|h*P%Dnqg(U5qv zL)ZA}FHbhFeF9pD50qD&gJNMXYxOW$b5Mv>)>^WI&R4?2;@TI6Of%ptGqTq#8&vdBT2~^=qx5=7lK3#964l zzHJV1Fi7)Svpg(Dnu+K>>S^c7a^RO`-$MSVq z4C+<$M7)Y?>N`es9UMAOiztxVoO5XV;K@G^eUiuD94Qw>Fj-@xU>SJZ z74&&;JAo1;nX?_HO|?M?(|ev#x$jFhkyw(jsPw#|6Aq($d$Rb=cxd<`R{TJ3OyIoo zr_;K2(Q`;>(Z|Mh3P{|d`~Sh(TR>I0c5B0mC@3mON;gQ0AiZEA-QCgx0#ee877%HX z?w0OuBt*J91%XA^qU*ca@&51GXP$FL>sqal5 z6LcT;GaOqVaZ=6hkkfWFk*-d%iDq=?Xq?E0z0S3Nsr#GtRo<$dO;S5Asr?9#*4Z1J z9<9n7J6zr@bZPI+a}Hsb9Zi}lJYtC~8XI+}iUvk}I}jUQ;543UCNG#UF(i}}0j3(D8QWIKMAau&-K^!AbDQFxF8@xB`56@L4u$u^+mwlrnC>Qx&-WELN@uOx6G3y-PW~*AJLx; z3tYZA0r?+Cu&ibvpbUKo7%X)<4-jA0 zZnOI(C)T}jvqOhb@xhJQGx*))QoA@jenrP3y`j2$`)cp}JE0Y7R=u;`(WHsg_<;k8 zgZQU|mA*tNLQYGnE5Sa_k2BKrZ!E#KjU2E8P*EQK)vc_Ot8H_ub#5HnN*uk3=ZkDU z37E$C)h^c+z*Mq0qI)D+AeUN%$d;ock=^>^<1gvi5PKlhbF;FtwiYEJJ5i(wdJCD4 z(sRB)Db-gWqgbSQ=bV~SEF|LC7_1$V&DE+Z+~RNA?_yBII1KeT!Lx7cJVir-Xekk^ z(uDzhp#;p@7>Z#ve0zO$xz9ZVN(s`vaWBy}%G`Zhr(_d(RQOr)H7n>J;lJ?7RVylF zSy``DLn3yg4cE?C{dID@#J3SGFCnmVW?yvnbJRV*oZ2jhG*ovZ*R8@XPy06quiCMt zCIIXy-`8}I0_8jh~J<=CP8J<-$i+%cem69T_-Yu(|Lm_NsPd8AHlSw=;=o18y zRoS!0=POott*?f$r7P6#ca6acJNBC@l8+ps4`2yV(ZW*MfqK!)xNb$8z3Wa5E;pNh zujyg^Xsc&h76nb{GA}_PGNiLmte3v(D^{+3hG^vCu)wIC^H1){^Gg|*p~WhjNv7v6 zeP!pkA?r=a-J~AYaKV0*J_&Nb#$^k1Jn23I8-*m%a2YW|1Op+n1w%g2B9s24kz5sq z)8(XAUP`Z9sGkuD9VCeLksP6MZAa~zTK+ckD+eZ*!0oH!Ao&BF_`zQEIXSm!RL)j$ zedL7KO|1|0+u4xZ-AE7L-Et&&H}mH#faeeYzf=DGxFw*snv%oyFj;*vC>D+{zkp)P z5ZFqZqgtSOvu{Z}Oh;8!Rl2BlM$W;fxf>L4FkMyv7o!02V`JP0fJhQvIudp`O=t1% zwy5Hup{M({P3Zh5&_dMvZz8mDj&~E#n{|pM&`>OwKEmgEo~w&| zmfZ|qkEh47-r%lrb6;I@^`B8I5B1l$&*r&1Rgf}R_-n3rERENi=`ziljWby4^p=g< zoR6Z@b_j&hk+r=PJW3hQF{I5i6m|wm>B+)J%xhn+GA>E&VA^Z7;IKC23l<}*Ft@5Q z=~m7RY6-bpSH7V2*BrCz^Btmu>ra)Lpl@N3*EQ@`W~!HwMc#ED6-M17cxi@;o{2+8 zNm=pA=ozj$p#I&OFTYuvlL0-t*)vhNS~0yh83QeT9{PA0?|#R3UuBK@UcWMbR$AI| z9K*mL_VECr0By~jo8atz_X&Ue34QUT8D!FNGC|b%H<9!kK-gn<*i>JHQ_=6)Hi(fp z3l|VD16mUEha%rsobSCPBk2dh;L+D=iW|-b7jYmHrV^J1ybKa*(_kvA4G4PnGio++TF3FDXOME zzxtYd2MB-q15{29dm{;Jcd4b-+X;+MUJGC-Ofd}M{(ivfJm`t4C@K5;%~|_oV9B{D z8%e^XAxWUtNjLYkngCS;s(&ZrDGHOv;OaGFpJ6CWob2;D@(nnA`cV@MH}IeBrS$UD zLQc?8n^{NM6sGmukr+JHWB9LvyoKr{12ngR=HWS+ULurlJ9=}zDjAnWLmKS)iA?1` zJ@v=;A`w^x1D9zk-VT1KO&kl(Rn0SVYvwTLg4|%ASoL2u(;gs{@ecO)-?j(fw8?&% zFdqd=&X#FHPE?C@Ad3J?1!+oZtO~I}lxvXfzEnzw#ool}sR_;#R#yBk!%Op_2k%PJ zzJkO9K!A=uG(bF=i-;2wEiX`*Zt+fvqUU#!)dR~1Mk?L3oc00)Yl~Apt6r53RW@TJ z7~W!@Bx32ipbWo0h*mtDe;ry7X@Cw}7r55*^SPQW^-Oz6(9aM`h`?p;fBECf0`ct&_oEOdMsH7Tq-tG*kc8ob@eM53R|deEi5;oRsuq`w1l1GX%)6v6%F7G-Ob_ z7qj<$(WGkO(!z#UE6*&wIaL$ra8cr&tCDA&*R=5NF(KD)@zigwZUvRAp)kh$Ze{{z z^D$KxvH~eGaC86uNw|@%$%Q;2ay^;nIrU0555z+z=|${Vk#6WGu@!5=s1MM745=Pz zR67HT0FCCgJ)jZNZLDZ|0wMlCif>ip8Ax>7mBh9-WJmQ0Ew1l+C-v70{tU$&D z?bTv;G^5YiDC*T<=!i~Cv@lQj|K$k_Ue|o#kKe;LGB%Dg$H?r$@PrmU#8kq^fXB38 zS9~5`0%2UXlZ-{1`@089v9A_X@A)r31iHZa>@JL(o7+w@ONr6(77n!e@$JVOr=TJs zpEp*JEjdc>AWh|#c;_5lzAugl_1UIZI zguvnEh7p@(-}&ne>qA{QE@N}geuSih)qMHohtDjZ2h+I?z8io%h60nc#fZaP;x4oz zpgYTIIU4&z-I z2pv6M$q$owU7{Qf7i!mKU;1z50H<{nM5VzL!h3xXkes8srKwyYsiG!|(r3i_!B&Ij z$L-%lS}fNbxDjwUm+}<{=_KimFK*X|zqlJuHl&lw%^*@UZ%!u7wQl>`wGVX5`TVE{-6EW!TH+X8}mog^6|9 zU}@49?|9<7Ce{wN9v_%)R6*Px$z8iiDX~mg z>&H?TJRVG!mUO+bY<@$-!$#FwI-=6IpAg7h2vL9wIH&XZ3sNGPg8T(TxMXQiB@PZw z`{8NBpmPdhf|Kj+4~u)JM&A>t@abwu>z+H(bu!%!y>157d?pAy5w@ShEX^S83Dlsy zy}jIaRTh7r<64{_2NzdSK|jo1Xt`JvCdy;<=8e3nanwUotvUyb)4MZ)sh(_HE+x9( zXl|4!gW|S8(R|JiP7*`eL8AfzUkwxxApEzF`unv?O zbw6{oVBVT*h~GaD>X)o=QGKqis+Z9p{wlq+z1;ZA_Q9lEzich5K9m{rIYvR`gW2+4 zcvog-CU`$cvP9P=+k zA9y@53e%yd!d)$SFlQC&wXoSg7ipGonHr#a^O$5;$cy%uA=f{Y(ow42e|yfqoX8v& zu!KAK>31=91EIOIEdJxCQZ{2~c0B<*Jy8edO@h2+7@SD25)7YxA9VbYLWG7`JiQ?4 zXmbBj{1^%M{!^mykGK!JoZW!lGif5D!#+h@HM{Yb@XIK+NM}p+RM`rPVdPLA>Ot0p8=Ytz3 z@dEp|pz8nqpu8rKGoa`s2m|j|_y6Dj_3zdiknkP1Ujw0#TNdaEY5a%3=94VYmI?2< z`xdgFbTGC4VrTvrjsG``B#njI6U!zB8^}{Bi&~R>hWb~p_pj6ZU%p@@LiIpq`ptf0 zY_MV+Jp?=ywU?*N<8P>kn^S>$%zcxVK{S|j>8Uk?NnzSDN$4_fHEoyE=d~+e7#2Rn z5QUOS+5VhmYMV`6Y+olS7NINC426$ zIk5nSxrsW?yEJ_>ezSJ}?Op6Mi@`BUeQ%!p# zL){%GyMk(7K%d_BqM}ZLI4J$B{s3i>aD4L+?VmJQF#$l)bd7iPPX3V8%_F!RZR*L& zKGwDw9laMHmPwm}BM+|<(c;9^nyA1jNIbE$H53-jpa|{?KBAap?mSNQe+ntC=OzJC zF^@v=c|i6V2^rC0K~JQF0ncm>2`2xmYyY>a?+JZ?=?P`IOG_;DhkAQGMMf$!?tQSb zvhpeg?@q*2KAuAMg}M}FiLbU+$yC6+1E=U4Vfvv@GjCbGY<+Phjr%lsr#pUW(1@YT z*co~=1{@0YnEBemde=+m!REwMihr_78Ds!!SF|2tee;J-e-iv%Mkp{+TraD0t5r3DML=25C&Al z7blbN1OKE7WWM*IDbX$Ap{d@#iv)^{Lc z0KvD@5VhX@Bgn-9b|Mr4*M4dJ*?;kwfpBvWS(muA{?{Z0mu1TS;6Z$a;gGptj`E|2 z{Qvr&-{)(*XMd^;&Q4qt2?I{OHDaLmPnYiV5Hi_-)Y@9d!XLN4`7=~+r6+dCn* zx(KqBnWqIwX@nex3wyucnaLgI4U_iwehRli97BDh3}1#r1piQmf4?RF`VJk!0;jf? z_4E$%AE(xhjHK(jCt?j&ru$%2MNQ(XexeGwc`bVLw_J2D5<}GG26Gyo%0oCv)9t6@ zFSje*Ro?;m0r~&O^1E^Rwzj6tqvLUfJ|DvF3uB6bL!*cce%DZMWnZYggOJ+@#fr!MpfmUYDM9}tl zdVeDWn8+tUI285u^=;b-QzXMk2?~0ct6oAenynzj%#D8kpKK+?&3(bd|MllxUB~>* z#dGe)Z74>62&4=>`k{J#!l|i&R~Mo8H6cV(YA~nZ>07=(2GrXa-+B0 zTlZPvi;D(eJ1WDa#aPIT%b(_N*D7Ql9Dh)zlLzoU?(k`ES+>mZcjYo;8A&-g79~9V zf4W=E;A*wH|0|+Np}pmml=P|fP<=6b$geRiQQe5xfm$$+m$)JnInm+eW3~mg=!!B7 zKm9Zbj9Xq*WVgI@Fr^K+iI7~n4U3#_CLd%)qpX$i%cO-EjMWO5&h`nFyAM0i_whpq6&kEkJa$wLz|5@uwWD~q4UGn zSvCG;{&XEze)`u{9CNlimzJ@Tw|M)GaDsISS#iPfEf{Pob-eANIz zyfVYOBJw5*rD^;7v#YvH#TaX|RpybxOaH>k zaPjZ&$1WrpqOQdOx)?E=t9|}-ePEBuY_wK}!XC@*_U);I)#UP%4PV5x72(>CFv>!V z&Yqr(r4aL1S&ln`qasdnoLI;(%OM}8GGz4>HFnH^5_zB9#*;EPd$Bd){HMZ0$q}8A`uRtW-w>8V1y0L*C;3jBXKk{d_B4PSQA2CYo1eT=Dn0;XGwA#_y7CK?N z75jK`hs${SmHqsyuX(O~9Q2(#i%Svl)a7qF$3F^jZayYlU%mBySls6dAF;*vg^NM5{RuDCNL)PADZ2ck=uH1881Af(y#)v4x^jw4VS>Rys;mxnq$ z;fyDMM3 z8iVLyXv*qC>UpRO=;!e-5=gMDS;pnr$S+I4w^u(0tLf9h;%{*V9D3yx&#C2p!hi?$GJ)C#Hin|7uu0>OX-DsGir zWr??hX59z6V3zdqLC|wgbpTlHMnV@tva(PduaeNbk{)ehz$ZP8(f=-lDVs#P9HM3>mI8#tc%rc7$Z{N4hsua@{CHUOvn(^L!r3_;`Co72_XH})} zwMM`p$Z?iTIJ?T*1x|1o)yaf@?0*-=UNgN5E3z~f_1Mvjy*k)R{ajW*G*OwFy7y95 z=T0UqWKRRN41IG#nC120M>CbK2wzZGkh!>1<$ihgz8=+)(7>uw|XT7#-9iU>^~-X*pSdo(E#>02drv! zoW5YifS-QA5B9uOLq1>_w5*a*rQdoFkRu+0YFy{Yob_f&#{)6ah8(~G_}Z0@0nA_B z?}2N>*)P56s6&wHI#&)yS4~^zJ8Vw9)RJQv&^p`V9LZLo+-oY)z|vo?d^af#y1L16 zd2AGquzJ0d%C3Z?A{r0|KU&o&?jyMJw-9SeMklpWXS$#?MM}tF5~f=DZcbQ<#)DA= zhIdpqF=|~Ec3*ToxXhJ$pqtk<=6eIT=mVBQ0_#VkSO&pgjhC(QcSD_evxXl`k|v^t?mV93ZJr8IPT~? zA#jz*5fAlE+?0@WPN?8CJ2nI7m0rb>g(8I52_=~k@l*q)Qi__>D5nJB=bnz`@cHr zlB;lC3FcbQI47|#pX*$tm8oVN|DX)TKvS-l1s6}|>5(9rFch4`h48a8ZAShw54wyF z+GPcW5*VgVqcDtUZ40cK%>v$_E7cevvY<^)M7UaYNQz|sVAT%F^;2)>2H$Tx9^~HS z%<4 z>$RKv3_1G#&2p2DG@oprL$y_K7RNL^D7w2bWl}nRZq7No5N%DCiGAeS0?`0*7(8!N zB2*}KEL*|5utBz-_}9=~<`>`KmnC&7ZcG~GdihXoQ}0qJJS zCd#I=^=;x;7Vs6s;H>7b3T9-9>)~4=>7c^P4fk94fm?1_;U}DhE)6^cOaeBO<}5|V zA+35RRsh^~;bKW-WZ}RYN;A1Hmk^Td`x~voUIU{c*HNwy)7uY`7Li=7k2&Zu1oXT` z#Iko$S`# z%ENe>q9X0ODCYXzSL@SNEPxo2TRwMrJTJ#)+>_E7PCLBUL8=?KgSa#QFoxf))~=$- zY?Qv!U&J#j2}ZB&FlqF)-G0h_*WCg5fZ;~BbNHl5a^%vWWhf$c_R=<<&%MwO`R~nk zr!*PKF>dB@J@>dGnR(W33aw?DsbVpi__vEG<&xvNBMlHxe1I1bnW6w?v#p)4*mfO(gKw0iip|w-D08 z+I9L|^}fU%VM4Y_FKF5m)S11J@`cAw7RTh!VGiVQ$PRTJ3WQ|*SE#_3SG-p}26gIl z2=#=HG=5pe%?*!9oPD-y30W_aP{s=>u2pMEb}t>vZGyPHN%kkvmrrX~rZ$8U%TJCQ z9Op1?<{XDk@et`z1)5s<7O!Vwl58|M-)rPsi&x(w<=h5gJl;lLQB9N?DHe%nSjWj# zot0AD38pn*Ce@G_3CXHU-tz1e`{jmb8NnmhtFw4Msv&-h$w?&7ejOe!%kOHXH z$k!DRl19F5YY|$^^RKQg&El)UZvNG-+I9FQVVVplFdzC1+Y;)t@Qe{tzYon9TJ$M| zh#z`-?x569vu3xP6Z}c&(Lty|oo zd@>Tmoj26iqn^dqfSj^vxq}mls|0U7j8d$N^Z3n2UjfHCXIduR#@DWD__XsF>`FLD z5Vi>Udw-WPkl~}YZOj>b`JCi|=$#9pHifhdjX^pr?IccRIo342W&Fn@P5@E$#_^zsY@X_W0PrTg#*6bMIVJ$%mucSbw+ z$&5`Bzgrp>f^Nt8wd|zhN{Q@z+NHfCA3W!%yQ>G75Kh~bhiCSO^gIVWIwK{+ow-X< zQQgs((1X?fbdHW>=xTyXV`gpGgJ^2JoDAMmMmaq>&&?;rDjOVjbEkL&1Xr%#?F>o? zTnIKN^Q!b?g?q~x`jnQu#O1=o6Ed~+;|5VRE8kIv&fA6SV+Fr+O3eD6hc}SpnztoZ zn%-Y!rl!TOg9Nc4El<|ieG{SFs7VmH$76L<>PqNHTH^wNjsJwLPFlbLcIfZEpXJ>< z!B9lDxEvEU2|}5E@K=tCPROQGSS9JhA=^L^?#xL;*5nJ8HrOJHjW@jr!o2TZ?Q(?V z$Wq(a_aXVPK`CReIZ%m`shV|&f9U243W~#{YPvGOONR-Zo!(m`w5lg>I4s#kfcCyr zELko=-n(D<4}$MAY@dL0^2PD}+fcoW+skQI>lZ4vpNX!Jm!!+H&-xc{7}THD!-;bR zan9?-yb#iI5(kiMFv=$GF)=2~Mu0L{8BV}smu+$rGw~(LK1K0TM3S`5dJ(ZJTd)zG zTVMIw8FS4IFwb7(GX)-ee2D*wk*hL%0>2I9pc&$!t+PBf$a-0BBe%VjsWC9&7EeeJ zAJHp&p*xd(mE?gqScBdq9_=H3`H=U&P%oePXiisIg;VCM74auhA?p(D%MfC!G4w5c z=wuoQf7X_quG@tLw84Gm=~~XG*5R>y6)F3Pq5Xa^b0ua5%;g(?QU2*_dPZuC$tztg_1y1vz;I74A}0 zxJpU~$v$)AiGhKYTm8-)Fm!w~FV+ArgZA;P%c4K+kRR#CdZ}o-Uk3M(VazQ)bpI{s z1%Mn@R&3F7dl1DbN&)y?t)ZHU-$P6(gkVSJ*px;!*p*I7d6 z*-rgAKQB$!$l$i25eMuutI1I`0z^rJdmUAoW&Mz%^1GDZ`toCS2Xa-K{lUH$gLWTrs%S)Z^p=V&6skD!(q;Y$RJS z8qt)H>(9A?$yD=lPJC&<#l0RG)`gYij&HhY+%1y3L#69+C)h*0q&df^J4&|aOlMp? z!x4CYrAYd#>tYq8hS5B=VOw*p!6g%;!D8oAa+(VxMzZS&r!Meb+NE$k!OyBY^k0?M zvxcjUPVK6pul+S5XqDb}I*{&Yhb6@~B$B`*#AM>w!^YhibPzL`@V!9ta2lC3uw2t0 z0CqGGrWb%4(G9Drz*?+?x>lWn$v5qe-pNMQQ%fPdAyZXWa?#oj_(pGhEUTlM!vet? z&g74e2x(f6-FLsCOv&gcgLOJ0U=?$^fbW9G_aOX{d=0jm#Cc11Kqt-tl*OU%<)*`L z_ZHi4ur}C!C#@0wT;DQac{jeWdRPj)K(u7uW_V*SJF`@9dFtT%RA3PAAW5r^}{Z9KYX0O7=vG1%{IMfCaV_SUQv1Hb8Uq^afCKc!O_l!d>zgj;R%S3Y0XH}jy zykRBwV-#2lV#}6G=GX7_09Lpy>|Gjlh-K^XeuXJm+F1_j%{L5ce-RIHkgv^Q?h2=@ zMw;+8WbqeGu?D`r4+kj^h`Gdu`4cHf?bP5m{Gemy!FENIIM$X?%gV_ z35xk@r8X(Y?v>P-Y*{pKyK_fDIu~W}b(>ZWpfOQFI2ta;;@|TZ*1229Jns}QF4L`U zbu+(E*GPh(tKxn5DlEhJb*Z?X=y7N(y6uS$=NHwCj-Q^hr5mF2ik%+^uS^{;{CvC5 z<+5a>bmTS2M&+D_^5#>#v~80{Y;EgBi4ixkvuH->3M=VRLdPP9>@tX(MB6BGtQt>0 zg3T^QfbNM4%uui6Z^kS+L6tpU4i~~-C!CVSqgDFL0*3+l8C&Ap{dJ_=FE}ah_D<82 z1BCDIxn(G4;N?Egm*aqNy9iu1%q4Tq8@g#JAW5Fr6gc|I&NsEtij5*zBJFAJ9uj1 zFy7y0*nnyB^czyw`N{{yu1yh^^vQyV$^G>qJ{AGE2<+lLe;{I_J3{L|667~Jo-l0+ zvSY3?qhMlVeQt+TVctARcT#0zbn>au#kh`(%pB(`-stG1?dS@CM~xy#aDVPyT2&LeX$|}*X19kh&(=EH~OkbrbJ4Rx|5Mi zSf!T1jx&#F9jq%%_}o_piuEaLFS-V_^a?p{8M59No)R!r4U$hOPLW6D$$ElT#P))I zpk_5uu2+jH1dpw%Bd=q@*Nr*`K##Fps?OEJ$J|4;^hHr&U26vgQRCxPrF}^a6KpZ6 z3CE!x7eWDz`4bZ5r69E{edF&c=yi^ZexsM1@9m{v`v3=h7li?j4A?^2=bgNNI0SFB z)QHf<&M?7U8TM~+nM-zVAIZ|V3v-!#GIe4iUr*D_^5|0&MGsRJZ9l*T`uL>LtF+`m zutkpBa!NO_RPyNQb7*aG)A5{(>5tFk5wWp!4RFZGg0~`Brxb2a&Kr@Gj-#UL#Rq50 z(Yo+B^Fne6!1U9*T0$}WmY^HipzT9w@T=a2wKi`4$lgmBEOXGY{ z+Nl-?bv%^khe!?Sx@@&yBBLws1~&kB)e^nejlpeSLZ$ z#lp{0wh237(9)HYpQk7~Xtj>)iBK~tU@_@S5Ns;c5ixn-Wz-={tHnuX#;AMbU9H|{ ztCGL6l^y!nwhDFYbenf6)30X3s$Rm0I~Qh`eu6DgY`f6x4hFxg(=#)F1#uyisEhl_ z@U@PcMxz8-Xg?+kWXe5v&-5UOpU_vn7te}tcdysrgO*iOcXr&4bIJGg?@w}G=Wm%o z|MdBzSEx2!_0G2NES&C4_*XIXCzgXNHQy|l!@lS37gT1ge*O3C@`7z1)!-*KEw1w; zVx9;v+mKi%JE5>7CeT;YHF%+ooC?gX`Z@i!9H}8otuigEuQx4Kw-}{?%*Y7&SrgP1 zTi=qDhaq0Hr!%Xr)?}zVFhssc)l`|9E%;~!W$napXzh^tH7!|T7}a+Qet(|U{!ZM= zeIF=`nv)Fww@Ofio-b);(Oe`fa7}dIu33eDKzn6HXsfP%WH}DjqYe3eF)CT*UQM9v z*LJm)Z-AHp-A*;SKB{W!JcV5zj(6MbfOeAzBx(2Q?HSwt$m!E_=D;I*m0V%4o763q z$M{7AvwUTorH7}RS2S7%#`>e0x_O1#y_m+j~CH@zMgmF&nc{kMPb z=jHqEx1G-|hP|{>_-VEDtRMhYBvm4|L*SL^OITOJVnZPOM=mY9Ry(8;^ds8L1cQ1* zwlzz>Ckli4SbkU%qdr$LO5=<~~jH?}ru0!JDRZ?eyl#^R9ma95>~# z+7(R~0n|B{RVJ`^n>NuyP{3@mi`d4uDxJT}uOlhE>?6;Eoluh_z76=tFLHy(ya!Ej zQr*7MKU58RR{cw84FLDVRW)K)ve*1XHBEh5#re-9#ms|jfMFJWCPu9aRL~H<7SGU~ z&DNr{c^%3*2i5Zr$D6KH0tz=LB+Gd`F7{BEv}=`}64A8#PkTq$`nX!FEY0r1t*??=iIt;nXR+Uf#8W9spgmTIWHE(>iDmq$_gdT9M{`6XUG~v% zwtJ-f>HIO?u#b^%i)vK)A>5za?qaV)YRBGz56rSz#%IQSircssEK~s9Dd`{y*VzOh zgJ84{%rrK4bI%g66xv!|Zj;h7AVG`-6tkxQQ{xNJP7(t4x1U|7W43OJ2(^wo%(IJm z@m~^(hQNR^3X%6_+Dwz@q|-k$kYRY*c26!>1+EoDTB1ae8vIDzV{Z2Q!N6F!Y{#8B z{bgrgk)R6>a%7m%!2jUgiLgGcHni(~yZU@wzkZ4>j^+^;Irz)0>#cP-l~%O{AC9FH zZP0u9nFxltc%1z-wbV_tIDKDjO^rrZZpis&S=(GDmte{&rh3fn+oboanX+xCleC_x z>vg=UYr0rw8QSyGsM};h1hO=KK3<<>Z3Zod-O96djx3kQP1iVmu-qT33PulZ9V6qR-)*W#UpP2UYT!{ z$gr?u;iC`^*Ar`eo(QQDJjmnz&ZNOETLT*sKOB|0gNbf980Jc%6tuDu6A85%%~fH4 z=WKp9?yCVNJMOO)HN030On9~K@#?N5Ui5}O+re7;2L0ghL0hk|%})p!->SCVt9zqA z9uL6CCwQ|&?!xqK_(yYyQlct`;gil{Z-3pLY*8hFjOhm>0K9Cpg^HL^n)z@SE$wl< zZw8U1D#JkC)!D`xh%h71@0X3zy4QuY)-@uAq(F~cYVn~eNWu88V^%rW5dIB=HsTt% zzZ&+w6K2Nq+OJbV#`Yh{%|%!EADcfG%)>-Ga}xM@p=H&eV$9%`q#q6wGe>7!IKEOcz0sU`lH=R(jq?;-!fsn7(9K#YMwC3wLZ!kT( ziy{T{#Pgs4Frv|03pQwO_sA&ec}5RM72Amp&b*e=+=1Cxz68-(2`C*DvS;e30w6t$ z;Ybe|=t+#{^aq}*E&Sst*mk%aKV5Rct}{IZG9#1|_7a!G1LRue@0UUS5flvDj(aqG zvgAZWMD}Bq;|5+7{~)}<_jy5i`?w?O_1U}<=d@+!JlfAMsiw!KBFdG$Q8{omUL%5# z&#B0EFs~%~M*T~kxT7^}=P!H>F6ev3l?5~9W}}j4ut6kp9r`qPJndHib5~Q56oE;P zOtii>9jx}So)TC(`+2T7DQI?Fpl;MXRvAIrQ-p;cpCy+}zq=h5~Bl} zxQk9I=hXV*`Sy`|5h~z?7+M0$?YB%;~zBwI~Qo$W#*M`RY&Ms zVaAmf%vn=aTJw@xgI(XJ1ky)Gt$1DG7;f|-{(zyc03eEX=Q@E=$2GCqMy9$A%aHMG zbe#!DsT7mZr9=>wiY~qED$|_%@i7w2FR1J!6(;%~frS>&M+9S! z%Hpc($i;y}8uMRXuBI`bO^>(|jBtAs+((CPKHBjnePFskhd+|iCX`f(&ifcgsUn|! zilz$(wF5Ncp1bL?Hk@lK

-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�mEN&}52}sl8t*s(e)nb6Jmi<(J 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 F{s-zs&J_Rv literal 0 HcmV?d00001 diff --git a/documentation/GridXcode/readme.md b/documentation/GridXcode/readme.md index aba91dea..b75d9323 100644 --- a/documentation/GridXcode/readme.md +++ b/documentation/GridXcode/readme.md @@ -10,7 +10,7 @@ For first time setup of the Xcode and Grid build environment on Mac OS, you will 2. Set Grid environment variables 3. Install and build Open MPI ***optional*** 4. Install and build Grid pre-requisites -5. Install and Build Grid +5. Install, Configure and Build Grid Apple's [Xcode website][Xcode] is the go-to reference for 1, and the definitive reference for 4 and 5 is the [Grid Documentation][GridDoc]. @@ -42,10 +42,10 @@ Variable | Typical Value | Use `Grid` | `/Users/user_id/src/Grid` | Path to grid source `GridPre` | `/Users/user_id/bin` | Path to install directory containing grid pre-requisites built from source `GridPkg` | **MacPorts**=`/opt/local`, **Homebrew**=`/usr/local` | Path to package manager install directory -`GridDebug` | `mpidebug` | Grid environment for debug configurations -`GridRelease` | `mpirelease` | Grid environment for release configurations -Choose either of the following ways to do this: +Choose either of the following ways to do this, and when you're done, log out and in again. To check these have been set: + + printenv|grep -i grid ### Method 1 -- Apple Script @@ -54,8 +54,6 @@ Choose either of the following ways to do this: ```apple script do shell script "launchctl setenv Grid $HOME/src/Grid -launchctl setenv GridDebug mpidebug -launchctl setenv GridRelease mpirelease launchctl setenv GridPre $HOME/bin launchctl setenv GridPkg /opt/local" ``` @@ -84,8 +82,6 @@ Make the file `environment.plist` in `~/Library/LaunchAgents` with the following sh -c launchctl setenv Grid $HOME/src/Grid -launchctl setenv GridDebug mpidebug -launchctl setenv GridRelease mpirelease launchctl setenv GridPre $HOME/bin launchctl setenv GridPkg /opt/local @@ -96,16 +92,6 @@ launchctl setenv GridPkg /opt/local ``` -These environment variables will be set each time your machine boots, so either reboot, or load them manually with: - - launchctl load ~/Library/LaunchAgents/environment.plist - launchctl start ~/Library/LaunchAgents/environment.plist - -NB: if they are already loaded, you will need to unload them first, with: - - launchctl stop ~/Library/LaunchAgents/environment.plist - launchctl unload ~/Library/LaunchAgents/environment.plist - ## 3. Install and build Open MPI -- ***optional*** Download the latest version of [Open MPI][OMPI] version 3.1 (I used 3.1.3). @@ -163,13 +149,13 @@ There isn't currently a port for [C-LIME][C-LIME], so download the source and th [C-LIME]: https://usqcd-software.github.io/c-lime/ "C-language API for Lattice QCD Interchange Message Encapsulation / Large Internet Message Encapsulation" -../configure --prefix=$GridPre/lime-1.3.2 CC=clang -make -j 4 -make install + ../configure --prefix=$GridPre/lime-1.3.2 CC=clang + make -j 4 + make install -## 5. Install and Build Grid +## 5. Install, Configure and Build Grid -### Install Grid +### 5.1 Install Grid [Grid]: https://github.com/paboyle/Grid @@ -187,45 +173,49 @@ or depending on whether you are using https or ssh. -### Build Grid +### 5.2 Configure Grid -The Xcode build system supports ***debug*** and ***release*** configurations for each project (more configurations can be defined). We will create separate Grid build directories for each configuration, using the Grid **Autoconf** build system to make each configuration. NB: it is **not** necessary to run `make install` on them once they are built (IDE features such as *jump to definition* will work better of you don't). +The Xcode build system supports multiple configurations for each project, by default: `Debug` and `Release`, but more configurations can be defined. We will create separate Grid build directories for each configuration, using the Grid **Autoconf** build system to make each configuration. NB: it is **not** necessary to run `make install` on them once they are built (IDE features such as *jump to definition* will work better of you don't). -Below are shown the ``configure`` script invocations below for debug and release configurations, with and without MPI. You are unlikely to need all four combinations, so as a minimum, just build ``build_mpidebug`` (or ``build_debug`` if you don't want MPI). You probably want to build the corresponding release configuration (``build_mpirelease`` or ``build_release``), but this is optional. +Below are shown the `configure` script invocations for three recommended configurations. You are free to define more, less or different configurations, but as a minimum, be sure to build a `Debug` configuration. -For each configuration, run the `configure` script (shown below) ***first***, then make the configuration with: +#### 1. `Debug` - make -j 4 +This is the build for every day developing and debugging with Xcode. It uses the Xcode clang c++ compiler, without MPI, and defaults to double-precision. Xcode builds the `Debug` configuration with debug symbols for full debugging: -NB: you **do not** need to run ``make install`` for these to work with Xcode. + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridDebug --enable-comms=none --enable-doxygen-doc -### 1. build_mpidebug +#### 2. `Release` -This is the build for every day developing with Xcode. It uses the Xcode clang c++ compiler, with MPI and debug symbols. This is the version of Grid Xcode links to for **debug** configurations. +Since Grid itself doesn't really have debug configurations, the release build is recommended to be the same as `Debug`, except using single-precision (handy for validation): - ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridMPIDebug --enable-comms=mpi-auto MPICXX=$GridPre/openmpi-3.1.3/bin/mpicxx CXXFLAGS=-g --enable-doxygen-doc + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=single CXX=clang++ --prefix=$GridPre/GridRelease --enable-comms=none --enable-doxygen-doc -### 2. build_mpirelease +#### 3. `MPIDebug` -Much the same as `build_mpidebug`, except without debug symbols for **release** configurations (it can be handy to use `#ifdef DEBUG` while developing, and it's useful to be able to compile and test the alternate case). +Debug configuration with MPI: - ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridMPIRelease --enable-comms=mpi-auto MPICXX=$GridPre/openmpi-3.1.3/bin/mpicxx CXXFLAGS=-g --enable-doxygen-doc + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridMPIDebug --enable-comms=mpi-auto MPICXX=$GridPre/openmpi-3.1.3/bin/mpicxx --enable-doxygen-doc -### 3. build_debug +### 5.3 Build Grid -Debug configuration without MPI: +Each configuration must be built before they can be used. You can either: - ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridDebug --enable-comms=none CXXFLAGS=-g --enable-doxygen-doc +1. Use automake and the Grid Makefile with `make -j 4` (NB: you **do not** need to run `make install` for these to work with Xcode) +2. Build `Grid` and `Hadrons` under Xcode (see below) -### 4. build_release +# Make a new application which links to Grid / Hadrons -Release configuration without MPI: +Making an Xcode project which links to Grid / Hadrons is straightforward: - ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridRelease --enable-comms=none +* Make a new application (in the usual way) +* Configure your application to use Grid (via three project settings:) + 1. `HEADER_SEARCH_PATHS` + 2. `LIBRARY_SEARCH_PATHS` + 3. `OTHER_LDFLAGS` +* Make additional configurations, e.g. `MPIDebug` (NB Xcode will make `Debug` and `Release` by default) -# Make a new application using Grid - -NB: Instead of following the instructions in this section, you can clone `HelloGrid` from the [University of Edinburgh GitLab site][HelloGrid]. +Detailed instructions follow, but instead of following the instructions in this section, you can clone `HelloGrid` from the [University of Edinburgh GitLab site][HelloGrid]. [HelloGrid]: https://git.ecdf.ed.ac.uk/s1786208/HelloGrid @@ -257,7 +247,7 @@ We now need to make changes to two sections (these are listed in alphabetical or #### HEADER_SEARCH_PATHS -Obtain a list of header locations required by Grid by running the following from your Grid build directory: +Obtain a list of header locations required by Grid by running the following from your Grid build directory (choose an MPI configuration if you built one, e.g. `MPIDebug`): ./grid-config --cxxflags @@ -269,10 +259,11 @@ The header locations follow the `-I` switches. You can ignore the other switches *Note: `grid-config` will output absolute paths. Make sure to replace absolute paths with environment variables (such as `$GridPre`) in your settings, so that the project will work unmodified for other collaborators downloading the same project from git.* -Set the **Debug** HEADER_SEARCH_PATHS to: +Set HEADER_SEARCH_PATHS to: - $Grid/build_$GridDebug/Grid + $Grid/build$(CONFIGURATION)/Grid $Grid + $Grid/Grid followed by (***the order is important***) the locations reported by `grid-config --cxxflags`, ignoring duplicates, e.g.: @@ -280,17 +271,13 @@ followed by (***the order is important***) the locations reported by `grid-confi $GridPkg/include $GridPre/lime-1.3.2/include -**Note: the easiest way to set this value is to put it all on one line, space separated, and edit the text to the right of `Debug`**, i.e.: +**Note: the easiest way to set this value is to put it all on one line, space separated, and edit the text to the right of `HEADER_SEARCH_PATHS`**, i.e.: - $Grid/build_$GridDebug/Grid $Grid $GridPre/openmpi-3.1.3/include $GridPkg/include $GridPre/lime-1.3.2/include - -Similarly, set the **Release** HEADER_SEARCH_PATHS to exactly the same settings, replacing `debug` in the first entry with `release`, e.g.: - - $Grid/build_$GridRelease/Grid $Grid $GridPre/openmpi-3.1.3/include $GridPkg/include $GridPre/lime-1.3.2/include + $Grid/build$(CONFIGURATION)/Grid $Grid $Grid/Grid $GridPre/openmpi-3.1.3/include $GridPkg/include $GridPre/lime-1.3.2/include #### LIBRARY_SEARCH_PATHS -Obtain a list of library locations required by Grid by running the following from your Grid build directory: +Obtain a list of library locations required by Grid by running the following from your Grid build directory (again, choose an MPI configuration if you built one, e.g. `MPIDebug`): ./grid-config --ldflags @@ -298,24 +285,9 @@ Output should look similar to: -L$GridPre/openmpi-3.1.3/lib -L$GridPkg/lib -L$GridPre/lime-1.3.2/lib -L$GridPkg/lib -L$GridPkg/lib -L$GridPkg/lib -Set the **Debug** LIBRARY_SEARCH_PATHS to: +Paste the output ***with `$Grid/build$(CONFIGURATION)/Grid $Grid/build$(CONFIGURATION)/Hadrons ` prepended*** into `LIBRARY_SEARCH_PATHS`: - $Grid/build_$GridDebug/Grid - $Grid/build_$GridDebug/Hadrons - -followed by the locations reported by `grid-config --ldflags`, ignoring duplicates (again, the order is important), e.g.: - - $GridPre/openmpi-3.1.3/lib - $GridPkg/lib - $GridPre/lime-1.3.2/lib - -Again, this can be done all on one line: - - $Grid/build_$GridDebug/Grid $Grid/build_$GridDebug/Hadrons $GridPre/openmpi-3.1.3/lib $GridPkg/lib $GridPre/lime-1.3.2/lib - -Similarly, set the **Release** LIBRARY_SEARCH_PATHS to exactly the same settings, replacing `debug` in the first two entries with `release`, e.g.: - - $Grid/build_$GridRelease/Grid $Grid/build_$GridRelease/Hadrons $GridPre/openmpi-3.1.3/lib $GridPkg/lib $GridPre/lime-1.3.2/lib + $Grid/build$(CONFIGURATION)/Grid $Grid/build$(CONFIGURATION)/Hadrons $GridPre/openmpi-3.1.3/lib $GridPkg/lib $GridPre/lime-1.3.2/lib ### 2. Linking @@ -329,7 +301,13 @@ and pasting the output ***with `-lGrid -lHadrons ` prepended*** (including the ` -lGrid -lHadrons -lmpi -lhdf5_cpp -lz -lcrypto -llime -lfftw3f -lfftw3 -lmpfr -lgmp -lstdc++ -lm -lz -lhdf5 -NB: The library names should be the same for your **debug** and **release** configurations, so you can set this once for both configurations. +## Make additional configurations + +On the project settings, `Info` tab, click the plus sign underneath configurations: + +![Add configurations](GridXcFig4.png) + +Choose `Duplicate "Debug" Configuration` (you can choose `Release` if you prefer) and give the new configuration a name, e.g. `MPIDebug`. ## Edit your source code @@ -381,36 +359,64 @@ The debug output pane opens at the bottom of Xcode, with output on the right (en See the Xcode documentation to learn about the debugger. When you're done, press `ctl-cmd-Y` to let the program run to completion. -## Running multiple MPI processes +# Debugging multiple MPI processes under Xcode -### Use Xcode to launch `mpirun` +You could tell Xcode to use mpirun to launch multiple copies of a target executable, however if you do this the debugger will attach to mpirun - not your target process. -You can tell Xcode to use mpirun to launch multiple copies of a target executable, however if you do this the debugger will attach to mpirun - not your target process. This can be useful - so long as you do not need to debug. +Instead: -To do this, edit the Scheme again (cmd-<), click on the `info` tab, then under Executable select `Other...` and enter the full path to your `mpirun`, e.g.: +1. Set a breakpoint just inside `main()` (otherwise your programs may complete before you attach to them all) - $GridPre/openmpi-3.1.3/bin +2. From the `Debug` menu, select `Attach to Process by PID or Name ...`. In the `PID or Process Name` field, enter the name of your target. Then click `Attach`. -NB: if `mpirun` is a link, don't be surprised if Xcode saves the name of the linked-to executable. +3. From a terminal session, locate and run your executable using `mpirun` (*the mangled name of the project build products will not be exactly the same as this example*): -Click on the `Arguments` tab, then under `Arguments Passed on Launch` add: + `$GridPre/openmpi-3.1.3/bin/mpirun -np 2 ~/Library/Developer/Xcode/DerivedData/HelloGrid-fiyyuveptaqelbbvllomcgjyvghr/Build/Products/Debug/HelloGrid --grid 4.4.4.8 --mpi 1.1.1.2` - -np 2 $(TARGET_BUILD_DIR)/$(TARGETNAME) + The Xcode debugger will attach to the first process. -and make sure this is the first argument - i.e. drag it to the top of the list of arguments. NB: You probably want to specify more arguments for the MPI run, but this should get you started. +4. From the `Debug` menu in Xcode, select `Attach to Process`, and other running instances of your application will appear at the top of the list. Attach to as many instances as you wish to debug. -Then click `Close`. - -### Use Xcode to debug multple instances of your target - -From the `Debug` menu, select `Attach to Process by PID or Name ...`. In the `PID or Process Name` field, enter the name of your target. Then click `Attach`. - -From a terminal session, locate and run your executable using mpirun (*the mangled name of the project build products will not be exactly the same as this example*): - - $GridPre/openmpi-3.1.3/bin/mpirun -np 2 ~/Library/Developer/Xcode/DerivedData/HelloGrid-fiyyuveptaqelbbvllomcgjyvghr/Build/Products/Debug/HelloGrid --grid 4.4.4.8 - -The Xcode debugger will attach to the first process. From the `Debug` menu in Xcode, select `Attach to Process`, and other running instances of your application will appear at the top of the list. Attach to as many instances as you wish to debug. +5. Click on the first process (which should have stopped at the breakpoint) and restart it with ctl-cmd-y You are now debugging multiple MPI instances, and the Xcode debugger should look similar to this: ![Debugging multiple MPI instances under the Xcode debugger](GridXcFig3.png) + +# Build `Grid` and `Hadrons` libraries under Xcode + +If you want to build `Grid` and `Hadrons` libraries using Xcode, you will need to: + +1. Make new library targets for `Grid` and `Hadrons` + +2. Add Grid source folders to your project: + a. Right click project then `Add files to "project" ...` + b. Choose `$Grid/Grid` folder + c. Select `Create groups` (`folder references` doesn't work) + d. Select `Grid` (containing just the Grid sources, not the entire Git repository) as your target + e. Click `Add` + +3. Add Hadrons source folders to your project + a. As per `Grid`, but change the target to `Hadrons` + b. For each source file in `Archive`, remove them from the target (option-command-1, then untick source files) + +4. Set the following values for each target in `Build Settings` + + Group | Variable | Value + --- | --- | --- + `Deployment` | `DSTROOT` | `$Grid/build$(CONFIGURATION)` *(do this for the entire project)* + `Deployment` | `DEPLOYMENT_LOCATION` | `Yes` + `Deployment` | `INSTALL_PATH` | `$(PRODUCT_NAME)/` + `Deployment` | `SKIP_INSTALL` | `No` + `Linking` | `OTHER_LDFLAGS` | remove `-lGrid -lHadrons` from the list + + This ensures that the libraries are copied back into the build folders when they are made (removing the need to run `make -j 4`) + +5. For `Grid`, in `Build Settings` in the `Build Options` group, set: + + Variable | Configuration | Value + --- | --- | --- + `EXCLUDED_SOURCE_FILE_NAMES` | Non-MPI configurations (`Debug` and `Release`) | `$(Grid)/Grid/communicator/Communicator_mpi3.cc $(Grid)/Grid/communicator/SharedMemoryMPI.cc` + `EXCLUDED_SOURCE_FILE_NAMES` | MPI configurations (`MPIDebug`) | `$(Grid)/Grid/communicator/Communicator_none.cc $(Grid)/Grid/communicator/SharedMemoryNone.cc` + +You should now be able to build and debug any configuration. From faa8bb9bc6d0685db5c18751285b5aada9427016 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 27 Mar 2019 17:55:52 +0000 Subject: [PATCH 189/347] Fixed funny memory leak --- Grid/qcd/smearing/StoutSmearing.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 7d42edb1..23a6b290 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -38,11 +38,11 @@ class Smear_Stout : public Smear { } /*! 3D constructor */ - Smear_Stout(double rho = 1.0, int orthogdim = -1) : SmearBase(new Smear_APE(rho3D(rho,orthogdim))) { + Smear_Stout(double rho = 1.0, int orthogdim = -1) : SmearBase{new Smear_APE(rho3D(rho,orthogdim))} { assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } - ~Smear_Stout() {} // delete SmearBase... + ~Smear_Stout() {delete SmearBase;} void smear(GaugeField& u_smr, const GaugeField& U) const { GaugeField C(U._grid); From 4161429dccf616effeca72dffd924680701fc3eb Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 3 Apr 2019 22:30:07 +0100 Subject: [PATCH 190/347] Serialisation fixes after Antonin's review --- Grid/serialisation/BaseIO.h | 31 ++-- Grid/serialisation/Hdf5IO.h | 35 ++--- Grid/util/EigenUtil.h | 271 --------------------------------- tests/IO/Test_serialisation.cc | 70 ++++----- 4 files changed, 56 insertions(+), 351 deletions(-) delete mode 100644 Grid/util/EigenUtil.h diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index be556955..f11dcde5 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -61,15 +61,25 @@ namespace Grid { template struct is_tensor_of_container : public std::false_type {}; template struct is_tensor_of_container::value && isGridTensor::value>::type> : public std::true_type {}; - // Traits are the default for scalars, or come from GridTypeMapper for GridTensors + // These traits describe the scalars inside Eigen tensors + // I wish I could define these in reference to the scalar type (so there would be fewer traits defined) + // but I'm unable to find a syntax to make this work template struct Traits {}; - template struct Traits::value>::type> : public GridTypeMapper_Base { - using scalar_type = typename T::Scalar; + // Traits are the default for scalars, or come from GridTypeMapper for GridTensors + template struct Traits::value>::type> + : public GridTypeMapper_Base { + using scalar_type = typename T::Scalar; // ultimate base scalar static constexpr bool is_complex = ::Grid::EigenIO::is_complex::value; }; - template struct Traits::value>::type> : public GridTypeMapper { - using scalar_type = typename GridTypeMapper::scalar_type; - static constexpr bool is_complex = ::Grid::EigenIO::is_complex::value; + // Traits are the default for scalars, or come from GridTypeMapper for GridTensors + template struct Traits::value>::type> { + using BaseTraits = GridTypeMapper; + using scalar_type = typename BaseTraits::scalar_type; // ultimate base scalar + static constexpr bool is_complex = ::Grid::EigenIO::is_complex::value; + static constexpr int TensorLevel = BaseTraits::TensorLevel; + static constexpr int Rank = BaseTraits::Rank; + static constexpr std::size_t count = BaseTraits::count; + static constexpr int Dimension(int dim) { return BaseTraits::Dimension(dim); } }; // Is this a fixed-size Eigen tensor @@ -310,15 +320,15 @@ namespace Grid { // If the Tensor isn't in Row-Major order, then we'll need to copy it's data const bool CopyData{NumElements > 1 && ETensor::Layout != Eigen::StorageOptions::RowMajor}; const Scalar * pWriteBuffer; - Scalar * pCopyBuffer = nullptr; + std::vector CopyBuffer; const Index TotalNumElements = NumElements * Traits::count; if( !CopyData ) { pWriteBuffer = getFirstScalar( output ); } else { // Regardless of the Eigen::Tensor storage order, the copy will be Row Major - pCopyBuffer = new Scalar[TotalNumElements]; - pWriteBuffer = pCopyBuffer; - Scalar * pCopy = pCopyBuffer; + CopyBuffer.resize( TotalNumElements ); + Scalar * pCopy = &CopyBuffer[0]; + pWriteBuffer = pCopy; std::array MyIndex; for( auto &idx : MyIndex ) idx = 0; for( auto n = 0; n < NumElements; n++ ) { @@ -330,7 +340,6 @@ namespace Grid { } } upcast->template writeMultiDim(s, TotalDims, pWriteBuffer, TotalNumElements); - if( pCopyBuffer ) delete [] pCopyBuffer; } template diff --git a/Grid/serialisation/Hdf5IO.h b/Grid/serialisation/Hdf5IO.h index 0876de02..19537599 100644 --- a/Grid/serialisation/Hdf5IO.h +++ b/Grid/serialisation/Hdf5IO.h @@ -51,7 +51,7 @@ namespace Grid std::vector path_; H5NS::H5File file_; H5NS::Group group_; - unsigned int dataSetThres_{HDF5_DEF_DATASET_THRES}; + const unsigned int dataSetThres_{HDF5_DEF_DATASET_THRES}; }; class Hdf5Reader: public Reader @@ -117,15 +117,8 @@ namespace Grid // write the entire dataset to file H5NS::DataSpace dataSpace(rank, dim.data()); - size_t DataSize = NumElements * sizeof(U); - if (DataSize > dataSetThres_) + if (NumElements > dataSetThres_) { - // First few prime numbers from https://oeis.org/A000040 - static const unsigned short Primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, - 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, - 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, - 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271 }; - constexpr int NumPrimes = sizeof( Primes ) / sizeof( Primes[0] ); // Make sure 1) each dimension; and 2) chunk size is < 4GB const hsize_t MaxElements = ( sizeof( U ) == 1 ) ? 0xffffffff : 0x100000000 / sizeof( U ); hsize_t ElementsPerChunk = 1; @@ -136,13 +129,9 @@ namespace Grid d = 1; // Chunk size is already as big as can be - remaining dimensions = 1 else { // If individual dimension too big, reduce by prime factors if possible - for( int PrimeIdx = 0; d > MaxElements && PrimeIdx < NumPrimes; ) { - if( d % Primes[PrimeIdx] ) - PrimeIdx++; - else - d /= Primes[PrimeIdx]; - } - const char ErrorMsg[] = " dimension > 4GB without small prime factors. " + while( d > MaxElements && ( d & 1 ) == 0 ) + d >>= 1; + const char ErrorMsg[] = " dimension > 4GB and not divisible by 2^n. " "Hdf5IO chunk size will be inefficient. NB Serialisation is not intended for large datasets - please consider alternatives."; if( d > MaxElements ) { std::cout << GridLogWarning << "Individual" << ErrorMsg << std::endl; @@ -156,17 +145,13 @@ namespace Grid ElementsPerChunk *= d; assert( OverflowCheck == ElementsPerChunk / d && "Product of dimensions overflowed hsize_t" ); // If product of dimensions too big, reduce by prime factors - for( int PrimeIdx = 0; ElementsPerChunk > MaxElements && PrimeIdx < NumPrimes; ) { + while( ElementsPerChunk > MaxElements && ( ElementsPerChunk & 1 ) == 0 ) { bTooBig = true; - if( d % Primes[PrimeIdx] ) - PrimeIdx++; - else { - d /= Primes[PrimeIdx]; - ElementsPerChunk /= Primes[PrimeIdx]; - } + d >>= 1; + ElementsPerChunk >>= 1; } if( ElementsPerChunk > MaxElements ) { - std::cout << GridLogMessage << "Product of" << ErrorMsg << std::endl; + std::cout << GridLogWarning << "Product of" << ErrorMsg << std::endl; hsize_t quotient = ElementsPerChunk / MaxElements; if( ElementsPerChunk % MaxElements ) quotient++; @@ -270,7 +255,7 @@ namespace Grid // read the flat vector buf.resize(size); - if (size * sizeof(Element) > dataSetThres_) + if (size > dataSetThres_) { H5NS::DataSet dataSet; diff --git a/Grid/util/EigenUtil.h b/Grid/util/EigenUtil.h deleted file mode 100644 index 63a0a860..00000000 --- a/Grid/util/EigenUtil.h +++ /dev/null @@ -1,271 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Grid/util/EigenUtil.h - - Copyright (C) 2019 - - Author: Michael Marshall - - 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 */ -#ifndef GRID_UTIL_EIGENUTIL_H -#define GRID_UTIL_EIGENUTIL_H -#include -#include - -namespace Grid { - // Custom iterator for Eigen tensors - namespace EigenUtil { - template // Is the tensor constant - class TensorIterator_raw{ - public: - using Index = typename ETensor::Index; - using Scalar = typename ETensor::Scalar; - using FullIndex = std::array; - const ETensor * pET; - const Index end; // same as pET->size() - Index position; // position (memory order) - Index Seq; // sequence (what our position would be if we were column major) - FullIndex indexPos; - FullIndex indexSize; - - inline TensorIterator_raw( ETensor & eT, Index pos = 0 ) : pET{&eT}, position{pos}, Seq{pos}, end{pET->size()} { - for( int i = 0 ; i < ETensor::NumIndices ; i++ ) { - indexPos[i] = 0; - indexSize[i] = pET->dimension(i); - } - } - inline TensorIterator_raw & operator++() { - auto sz = pET->size(); - if( position < sz ) { - position++; - if( ETensor::Options & Eigen::RowMajor ) { - for( int i = ETensor::NumIndices - 1; i != -1 && ++indexPos[i] == indexSize[i]; i-- ) - indexPos[i] = 0; - Seq++; - } else { - for( int i = 0; i < ETensor::NumIndices && ++indexPos[i] == indexSize[i]; i++ ) - indexPos[i] = 0; - Seq = 0; - for( int i = 0; i < ETensor::NumIndices; i++ ) { - Seq *= indexSize[i]; - Seq += indexPos[i]; - } - } - } - return * this; - } - inline typename std::conditional::type operator*() const { - assert( position >= 0 && position < pET->size() && "Attempt to access Eigen tensor iterator out of range" ); - return ( ( typename std::conditional::type ) pET->data() )[position]; - } - inline bool operator!=(const TensorIterator_raw &r) - { return pET == nullptr || pET != r.pET || position != r.position; } - // These functions aren't required for iterators, but they make using them easier - inline bool AtEnd() { return position == end; } - inline void DumpIndex(void) { - for( auto dim : indexPos ) - std::cout << "[" << dim << "]"; - } - }; - } -} - -// The only way I could get these iterators to work is to put the begin() and end() functions in the Eigen namespace -// So if Eigen ever defines these, we'll have a conflict and have to change this -namespace Eigen { - template using TensorIterator = Grid::EigenUtil::TensorIterator_raw< ETensor, false>; - template using TensorIteratorConst = Grid::EigenUtil::TensorIterator_raw; - template - inline typename std::enable_if::value, TensorIterator>::type - begin( ETensor & ET ) { return TensorIterator(ET); } - template - inline typename std::enable_if::value, TensorIterator>::type - end( ETensor & ET ) { return TensorIterator(ET, ET.size()); } - template - inline typename std::enable_if::value, TensorIteratorConst>::type - begin( const ETensor & ET ) { return TensorIteratorConst(ET); } - template - inline typename std::enable_if::value, TensorIteratorConst>::type - end( const ETensor & ET ) { return TensorIteratorConst(ET, ET.size()); } -} - -namespace Grid { - // for_all helper function to call the lambda for scalar - template - typename std::enable_if::value, void>::type - for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &scalar, typename ETensor::Index &Seq, - const std::array &MyIndex, - const std::array::Rank> &DimGridTensor, - std::array::Rank> &GridTensorIndex) - { - lambda( scalar, Seq++, MyIndex, GridTensorIndex ); - } - - // for_all helper function to call the lambda for container - template - typename std::enable_if::value, void>::type - for_all_do_lambda( Lambda lambda, typename ETensor::Scalar &container, typename ETensor::Index &Seq, - const std::array &MyIndex, - const std::array::Rank> &DimGridTensor, - std::array::Rank> &GridTensorIndex) - { - using Traits = EigenIO::Traits; - const int InnerRank = Traits::Rank; - for( typename Traits::scalar_type &Source : container ) { - lambda(Source, Seq++, MyIndex, GridTensorIndex ); - // Now increment SubIndex - for( int i = InnerRank - 1; i != -1 && ++GridTensorIndex[i] == DimGridTensor[i]; i-- ) - GridTensorIndex[i] = 0; - } - } - - // Calls a lamda (passing index and sequence number) for every member of an Eigen::Tensor - // For efficiency, iteration proceeds in memory order, - // ... but parameters guaranteed to be the same regardless of memory order - template - typename std::enable_if::value, void>::type - for_all( ETensor &ET, Lambda lambda ) - { - using Scalar = typename ETensor::Scalar; // This could be a Container - we'll check later - using Index = typename ETensor::Index; - using Traits = EigenIO::Traits; - // Check that the number of elements in the container is the product of tensor dimensions - const Index NumScalars = ET.size(); - assert( NumScalars > 0 && "EigenUtil: tensor has no elements" ); - Index ScalarElementCount{1}; - const int rank{ETensor::NumIndices}; - std::array DimTensor, MyIndex; - for(int i = 0; i < rank; i++ ) { - DimTensor[i] = ET.dimension(i); - ScalarElementCount *= DimTensor[i]; - MyIndex[i] = 0; - } - assert( NumScalars == ScalarElementCount && "EigenUtil: tensor size not product of dimensions" ); - // Save the GridTensor dimensions - const int InnerRank{Traits::Rank}; - std::array DimGridTensor, GridTensorIndex; - for(int i = 0; i < InnerRank; i++ ) { - DimGridTensor[i] = Traits::Dimension(i); - GridTensorIndex[i] = 0; - } - // Now walk the tensor in memory order - Index Seq = 0; - Scalar * pScalar = ET.data(); - for( Index j = 0; j < NumScalars; j++ ) { - for_all_do_lambda( lambda, * pScalar, Seq, MyIndex, DimGridTensor, GridTensorIndex ); - // Now increment the index to pass to the lambda (bearing in mind we're walking in memory order) - if( ETensor::Options & Eigen::RowMajor ) { - for( int i = rank - 1; i != -1 && ++MyIndex[i] == DimTensor[i]; i-- ) - MyIndex[i] = 0; - } else { - for( int i = 0; i < rank && ++MyIndex[i] == DimTensor[i]; i++ ) - MyIndex[i] = 0; - Seq = 0; - for( int i = 0; i < rank; i++ ) { - Seq *= DimTensor[i]; - Seq += MyIndex[i]; - } - Seq *= Traits::count; - } - pScalar++; - } - } - - // Sequential initialisation of tensors up to specified precision - // Would have preferred to define template variables for this, but that's c++ 17 - template - typename std::enable_if::value && !EigenIO::Traits::is_complex>::type - SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc = 1, unsigned short Precision = 0 ) - { - using Traits = EigenIO::Traits; - using scalar_type = typename Traits::scalar_type; - using Index = typename ETensor::Index; - for_all( ET, [&](scalar_type &c, Index n, const std::array &TensorIndex, - const std::array &ScalarIndex ) { - scalar_type x = Inc * static_cast(n); - if( Precision ) { - std::stringstream s; - s << std::setprecision(Precision) << x; - s >> x; - } - c = x; - } ); - } - - template - typename std::enable_if::value && EigenIO::Traits::is_complex>::type - SequentialInit( ETensor &ET, typename EigenIO::Traits::scalar_type Inc={1,-1}, unsigned short Precision = 0 ) - { - using Traits = EigenIO::Traits; - using scalar_type = typename Traits::scalar_type; - using Index = typename ETensor::Index; - for_all( ET, [&](scalar_type &c, Index n, const std::array &TensorIndex, - const std::array &ScalarIndex ) { - auto re = Inc.real(); - auto im = Inc.imag(); - re *= n; - im *= n; - if( Precision ) { - std::stringstream s; - s << std::setprecision(Precision) << re; - s >> re; - s.clear(); - s << im; - s >> im; - } - c = scalar_type(re,im); - } ); - } - - // Helper to dump a tensor - template - typename std::enable_if::value, void>::type - dump_tensor(T &t, const char * pName = nullptr) - { - using Traits = EigenIO::Traits; - using scalar_type = typename Traits::scalar_type; - using Index = typename T::Index; - const int rank{T::NumIndices}; - const auto &dims = t.dimensions(); - std::cout << "Dumping rank " << rank + Traits::Rank << ((T::Options & Eigen::RowMajor) ? ", row" : ", column") << "-major tensor "; - if( pName ) - std::cout << pName; - for( int i = 0 ; i < rank; i++ ) std::cout << "[" << dims[i] << "]"; - for( int i = 0 ; i < Traits::Rank; i++ ) std::cout << "(" << Traits::Dimension(i) << ")"; - std::cout << " in memory order:" << std::endl; - for( auto it = begin(t); !it.AtEnd(); ++it ) { - std::cout << " "; - it.DumpIndex(); - std::cout << " = " << (const typename T::Scalar)(*it) << std::endl; - } - std::cout << "========================================" << std::endl; - } - - template - typename std::enable_if::value, void>::type - dump_tensor(T &t, const char * pName = nullptr) - { - std::cout << "Dumping non-tensor object "; - if( pName ) std::cout << pName; - std::cout << "=" << t; - } -} -#endif diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index d97041ef..d1ec1309 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -29,7 +29,6 @@ Author: Michael Marshall *************************************************************************************/ /* END LEGAL */ #include -#include #include using namespace Grid; @@ -103,13 +102,22 @@ void ioTest(const std::string &filename, const O &object, const std::string &nam bool good = Serializable::CompareMember(object, *buf); if (!good) { std::cout << " failure!" << std::endl; - if (EigenIO::is_tensor::value) - dump_tensor(*buf); exit(EXIT_FAILURE); } std::cout << " done." << std::endl; } +// The only way I could get these iterators to work is to put the begin() and end() functions in the Eigen namespace +// So if Eigen ever defines these, we'll have a conflict and have to change this +namespace Eigen { + template + inline typename std::enable_if::value, typename EigenIO::Traits::scalar_type *>::type + begin( ET & et ) { return reinterpret_cast::scalar_type *>(et.data()); } + template + inline typename std::enable_if::value, typename EigenIO::Traits::scalar_type *>::type + end( ET & et ) { return begin(et) + et.size() * EigenIO::Traits::count; } +} + // Perform I/O tests on a range of tensor types // Test coverage: scalars, complex and GridVectors in single, double and default precision class TensorIO : public Serializable { @@ -133,14 +141,14 @@ class TensorIO : public Serializable { void Init(unsigned short Precision) { - SequentialInit(Perambulator1, Flag, Precision); - SequentialInit(Perambulator2, Flag, Precision); - SequentialInit(tensorR5, FlagR, Precision); - SequentialInit(tensorRank3, FlagF, Precision); - SequentialInit(tensor_9_4_2, FlagTS, Precision); + for( auto &s : Perambulator1 ) s = Flag; + for( auto &s : Perambulator2 ) s = Flag; + for( auto &s : tensorR5 ) s = FlagR; + for( auto &s : tensorRank3 ) s = FlagF; + for( auto &s : tensor_9_4_2 ) s = FlagTS; for( auto &t : atensor_9_4_2 ) - SequentialInit(t, FlagTS, Precision); - SequentialInit(MyLSCTensor, Flag, Precision); + for( auto &s : t ) s = FlagTS; + for( auto &s : MyLSCTensor ) s = Flag; } // Perform an I/O test for a single Eigen tensor (of any type) @@ -152,7 +160,7 @@ class TensorIO : public Serializable { using Traits = EigenIO::Traits; using scalar_type = typename Traits::scalar_type; std::unique_ptr pTensor{new T(otherDims...)}; - SequentialInit( * pTensor, Flag, Precision ); + for( auto &s : * pTensor ) s = Flag; filename = pszFilePrefix + std::to_string(++TestNum) + "_" + MyTypeName + pszExtension; ioTest(filename, * pTensor, MyTypeName, MyTypeName); } @@ -191,41 +199,15 @@ public: // Rank 1 tensor containing a single integer using TensorSingle = Eigen::TensorFixedSize>; TestOne( TEST_PARAMS( TensorSingle ), 7 ); // lucky! - // Rather convoluted way of defining a single complex number - using TensorSimple = Eigen::Tensor, 6>; + // Rather convoluted way of defining four complex numbers + using TensorSimple = Eigen::Tensor, 6>; using I = typename TensorSimple::Index; // NB: Never specified, so same for all my test tensors // Try progressively more complicated tensors TestOne( TEST_PARAMS( TensorSimple ), FlagTS, 1,1,1,1,1,1 ); TestOne( TEST_PARAMS( TensorRank3 ), FlagF, 6, 3, 2 ); TestOne(TEST_PARAMS( Tensor942 ), FlagTS); TestOne(TEST_PARAMS( LSCTensor ), Flag ); - - // Now see whether we can write a tensor in one memory order and read back in the other - { - TestOne(TEST_PARAMS( TensorR5 ), FlagR); - std::cout << " Testing alternate memory order read ... "; - TensorR5Alt t2; - RDR_ reader(filename); - ::Grid::read(reader, "TensorR5", t2); - bool good = true; - TensorR5 cf; - SequentialInit( cf, FlagR, Precision ); - for_all( t2, [&](typename EigenIO::Traits::scalar_type c, I n, - const std::array &TensorIndex, - const std::array::Rank> &GridTensorIndex ){ - Real &r = cf(TensorIndex); - if( c != r ){ - good = false; - std::cout << "\nError: " << n << ": " << c << " != " << r; - } - } ); - if (!good) { - std::cout << std::endl; - dump_tensor(t2,"t2"); - exit(EXIT_FAILURE); - } - std::cout << " done." << std::endl; - } + TestOne(TEST_PARAMS( TensorR5 ), FlagR); // Now test a serialisable object containing a number of tensors { static const char MyTypeName[] = "TensorIO"; @@ -247,10 +229,10 @@ public: } }; -const Real TensorIO::FlagR {-1.001}; -const Complex TensorIO::Flag {1,-3.1415927}; -const ComplexF TensorIO::FlagF {1,-3.1415927}; -const TensorIO::TestScalar TensorIO::FlagTS{1,-3.1415927}; +const Real TensorIO::FlagR {1}; +const Complex TensorIO::Flag {1,-1}; +const ComplexF TensorIO::FlagF {1,-1}; +const TensorIO::TestScalar TensorIO::FlagTS{1,-1}; const char * const TensorIO::pszFilePrefix = "tensor_"; template From 25e4ee3a494247ac4cbb0ecf571b27f5a484cef5 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 4 Apr 2019 12:13:16 +0100 Subject: [PATCH 191/347] 3D Stout smearing added --- Hadrons/Modules.hpp | 141 +++++------ Hadrons/Modules/MGauge/StoutSmearing3D.cc | 7 + Hadrons/Modules/MGauge/StoutSmearing3D.hpp | 137 +++++++++++ Hadrons/modules.inc | 262 +++++++++++---------- 4 files changed, 347 insertions(+), 200 deletions(-) create mode 100644 Hadrons/Modules/MGauge/StoutSmearing3D.cc create mode 100644 Hadrons/Modules/MGauge/StoutSmearing3D.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 3a90c371..94cfd316 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,78 +1,79 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include #include -#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/Modules/MGauge/StoutSmearing3D.cc b/Hadrons/Modules/MGauge/StoutSmearing3D.cc new file mode 100644 index 00000000..8eebed98 --- /dev/null +++ b/Hadrons/Modules/MGauge/StoutSmearing3D.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MGauge; + +template class Grid::Hadrons::MGauge::TStoutSmearing3D; diff --git a/Hadrons/Modules/MGauge/StoutSmearing3D.hpp b/Hadrons/Modules/MGauge/StoutSmearing3D.hpp new file mode 100644 index 00000000..ef9db9e9 --- /dev/null +++ b/Hadrons/Modules/MGauge/StoutSmearing3D.hpp @@ -0,0 +1,137 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MGauge/StoutSmearing3D.hpp + +Copyright (C) 2015-2019 + +Author: Antonin Portelli + +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 */ +#ifndef Hadrons_MGauge_StoutSmearing3D_hpp_ +#define Hadrons_MGauge_StoutSmearing3D_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * Stout smearing * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MGauge) + +class StoutSmearing3DPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(StoutSmearing3DPar, + std::string, gauge, + unsigned int, steps, + double, rho, + unsigned int, orthogdim); +}; + +template +class TStoutSmearing3D: public Module +{ +public: + GAUGE_TYPE_ALIASES(GImpl,); +public: + // constructor + TStoutSmearing3D(const std::string name); + // destructor + virtual ~TStoutSmearing3D(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(StoutSmearing3D, TStoutSmearing3D, MGauge); + +/****************************************************************************** + * TStoutSmearing3D implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TStoutSmearing3D::TStoutSmearing3D(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TStoutSmearing3D::getInput(void) +{ + std::vector in = {par().gauge}; + + return in; +} + +template +std::vector TStoutSmearing3D::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TStoutSmearing3D::setup(void) +{ + envCreateLat(GaugeField, getName()); + envTmpLat(GaugeField, "buf"); +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TStoutSmearing3D::execute(void) +{ + LOG(Message) << "Smearing '" << par().gauge << "' with " << par().steps + << " step" << ((par().steps > 1) ? "s" : "") + << " of 3D-stout smearing and rho= " << par().rho + << "orthogonal to dimension " << par().orthogdim << std::endl; + + Smear_Stout smearer(par().rho, par().orthogdim); + auto &U = envGet(GaugeField, par().gauge); + auto &Usmr = envGet(GaugeField, getName()); + + envGetTmp(GaugeField, buf); + buf = U; + LOG(Message) << "plaquette= " << WilsonLoops::avgPlaquette(U) + << std::endl; + for (unsigned int n = 0; n < par().steps; ++n) + { + smearer.smear(Usmr, buf); + buf = Usmr; + LOG(Message) << "plaquette= " << WilsonLoops::avgPlaquette(Usmr) + << std::endl; + } +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MGauge_StoutSmearing3D_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index c427e020..65e876c9 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,156 +1,158 @@ modules_cc =\ + Modules/MScalarSUN/Grad.cc \ + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/Div.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/TwoPoint.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadCosmHol.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadA2AVectors.cc \ + Modules/MIO/LoadPerambulator.cc \ + Modules/MSink/Smear.cc \ + Modules/MSink/Point.cc \ + Modules/MFermion/FreeProp.cc \ + Modules/MFermion/GaugeProp.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/StoutSmearing3D.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/GaugeFix.cc \ Modules/MUtilities/RandomVectors.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MDistil/g5_multiply.cc \ - Modules/MDistil/BContraction.cc \ - Modules/MDistil/BC2.cc \ - Modules/MDistil/LapEvec.cc \ Modules/MDistil/PerambFromSolve.cc \ - Modules/MDistil/Baryon2pt.cc \ - Modules/MDistil/PerambLight.cc \ - Modules/MDistil/DistilSink.cc \ + Modules/MDistil/g5_multiply.cc \ + Modules/MDistil/LapEvec.cc \ Modules/MDistil/Noises.cc \ Modules/MDistil/DistilVectors.cc \ + Modules/MDistil/BContraction.cc \ + Modules/MDistil/Baryon2pt.cc \ + Modules/MDistil/BC2.cc \ + Modules/MDistil/PerambLight.cc \ + Modules/MDistil/DistilSink.cc \ + Modules/MSource/Momentum.cc \ + Modules/MSource/Z2.cc \ + Modules/MSource/Point.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Wall.cc \ + Modules/MSource/SeqConserved.cc \ Modules/MContraction/WeakEye3pt.cc \ - Modules/MContraction/WeakNonEye3pt.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MContraction/DiscLoop.cc \ Modules/MContraction/Meson.cc \ - Modules/MContraction/A2ALoop.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/Baryon.cc \ Modules/MContraction/A2AAslashField.cc \ - Modules/MAction/ZMobiusDWF.cc \ + Modules/MContraction/Baryon.cc \ + Modules/MContraction/WeakNonEye3pt.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MContraction/A2ALoop.cc \ + Modules/MContraction/Gamma3pt.cc \ + Modules/MAction/MobiusDWF.cc \ Modules/MAction/WilsonClover.cc \ Modules/MAction/Wilson.cc \ Modules/MAction/DWF.cc \ - Modules/MAction/MobiusDWF.cc \ Modules/MAction/ScaledDWF.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadA2AVectors.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadPerambulator.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MNPR/Amputate.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MSolver/A2AVectors.cc \ + Modules/MSolver/RBPrecCG.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/A2AAslashVectors.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/Div.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/StochFreeField.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MSink/Smear.cc \ - Modules/MSink/Point.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MFermion/FreeProp.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MSource/SeqConserved.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/Z2.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/Momentum.cc + Modules/MNPR/Amputate.cc modules_hpp =\ - Modules/MUtilities/RandomVectors.hpp \ - Modules/MUtilities/PrecisionCast.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ - Modules/MSolver/Guesser.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MDistil/LapEvec.hpp \ - Modules/MDistil/Distil.hpp \ - Modules/MDistil/g5_multiply.hpp \ - Modules/MDistil/Noises.hpp \ - Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/Baryon2pt.hpp \ - Modules/MDistil/BContraction.hpp \ - Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/DistilSink.hpp \ - Modules/MDistil/PerambFromSolve.hpp \ - Modules/MDistil/BC2.hpp \ - Modules/MContraction/WeakNonEye3pt.hpp \ - Modules/MContraction/WeakEye3pt.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/A2ALoop.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MAction/Wilson.hpp \ - Modules/MAction/WilsonClover.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/ScaledDWF.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MIO/LoadBinary.hpp \ - Modules/MIO/LoadCosmHol.hpp \ - Modules/MIO/LoadNersc.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MScalar/FreeProp.hpp \ + Modules/MScalar/Scalar.hpp \ + Modules/MScalar/ChargedProp.hpp \ + Modules/MIO/LoadPerambulator.hpp \ + Modules/MIO/LoadEigenPack.hpp \ Modules/MIO/LoadA2AVectors.hpp \ Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadEigenPack.hpp \ - Modules/MIO/LoadPerambulator.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MIO/LoadCosmHol.hpp \ + Modules/MIO/LoadBinary.hpp \ + Modules/MIO/LoadNersc.hpp \ Modules/MSink/Smear.hpp \ Modules/MSink/Point.hpp \ - Modules/MFermion/GaugeProp.hpp \ Modules/MFermion/FreeProp.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/ChargedProp.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Point.hpp \ + Modules/MFermion/GaugeProp.hpp \ + Modules/MGauge/FundtoHirep.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/StoutSmearing.hpp \ + Modules/MGauge/Unit.hpp \ + Modules/MGauge/GaugeFix.hpp \ + Modules/MGauge/StoutSmearing3D.hpp \ + Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MUtilities/RandomVectors.hpp \ + Modules/MUtilities/PrecisionCast.hpp \ + Modules/MDistil/Noises.hpp \ + Modules/MDistil/PerambLight.hpp \ + Modules/MDistil/Distil.hpp \ + Modules/MDistil/BC2.hpp \ + Modules/MDistil/g5_multiply.hpp \ + Modules/MDistil/PerambFromSolve.hpp \ + Modules/MDistil/Baryon2pt.hpp \ + Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/DistilSink.hpp \ + Modules/MSource/SeqConserved.hpp \ Modules/MSource/Z2.hpp \ Modules/MSource/Wall.hpp \ - Modules/MSource/SeqConserved.hpp + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/WeakEye3pt.hpp \ + Modules/MContraction/WeakNonEye3pt.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MContraction/Meson.hpp \ + Modules/MContraction/A2ALoop.hpp \ + Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/A2AMesonField.hpp \ + Modules/MAction/WilsonClover.hpp \ + Modules/MAction/ScaledDWF.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MAction/Wilson.hpp \ + Modules/MAction/DWF.hpp \ + Modules/MAction/ZMobiusDWF.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/A2AVectors.hpp \ + Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/Guesser.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MNPR/Amputate.hpp From 63dc0fa7e98ce8e7ca369c6f3fff59da295f50cb Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Thu, 4 Apr 2019 16:00:17 +0100 Subject: [PATCH 192/347] Fixed memory leak ... without breaking semantics of prior code. Possibly should change the semantics? For Peter / Antonin to comment --- Grid/qcd/smearing/StoutSmearing.h | 78 ++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 23a6b290..e4e11a63 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -1,5 +1,34 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./lib/qcd/smearing/StoutSmearing.h + + Copyright (C) 2019 + + Author: unknown + Author: Felix Erben + Author: Michael Marshall + + 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 + *************************************************************************************/ /* - @file stoutSmear.hpp + @file StoutSmearing.h @brief Declares Stout smearing class */ #ifndef STOUT_SMEAR_ @@ -12,37 +41,42 @@ namespace QCD { template class Smear_Stout : public Smear { private: - const Smear* SmearBase; - inline std::vector rho3D(double rho, int orthogdim){ + // Smear* ownership semantics: + // Smear* passed in to constructor are owned by caller, so we don't delete them here + // Smear* created within constructor need to be deleted as part of the destructor + const Smear* SmearBase; // Not owned by this object, so not deleted at destruction + const std::unique_ptr> OwnedBase; // deleted at destruction + +public: + INHERIT_GIMPL_TYPES(Gimpl) + + // only anticipated to be used from default constructor, but might as well be visible + inline static std::vector rho3D(double rho = 1.0, int orthogdim = -1){ std::vector rho3d(Nd*Nd); for (int mu=0; mu* base) : SmearBase(base) { - assert(Nc == 3);// "Stout smearing currently implemented only for Nc==3"); + /*! Stout smearing with base explicitly specified */ + Smear_Stout(Smear* base) : SmearBase{base} { + assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } - /*! Default constructor */ -/* Smear_Stout(double rho = 1.0) : SmearBase(new Smear_APE(rho)) { - assert(Nc == 3);// "Stout smearing currently implemented only for Nc==3"); - } */ + /*! Construct stout smearing object from explicitly specified rho matrix */ + Smear_Stout(const std::vector& rho_) + : OwnedBase{new Smear_APE(rho_)}, SmearBase{OwnedBase.get()} { + assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); + } - /*! general constructor */ - Smear_Stout(std::vector& rho_) : SmearBase(new Smear_APE(rho_)) { - assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); - } + /*! Default constructor. rho is constant in all directions, optionally except for orthogonal dimension */ + Smear_Stout(double rho = 1.0, int orthogdim = -1) + : OwnedBase{new Smear_APE(rho3D(rho,orthogdim))}, SmearBase{OwnedBase.get()} { + assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); + } - /*! 3D constructor */ - Smear_Stout(double rho = 1.0, int orthogdim = -1) : SmearBase{new Smear_APE(rho3D(rho,orthogdim))} { - assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); - } - - ~Smear_Stout() {delete SmearBase;} + ~Smear_Stout() {} // delete SmearBase... void smear(GaugeField& u_smr, const GaugeField& U) const { GaugeField C(U._grid); From ea2f34de7bf268d9a5c5471b4c921ef991b0bf0d Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 6 Apr 2019 13:37:47 +0100 Subject: [PATCH 193/347] Updated documentation after Peter's review. 1) Removed version numbers from Grid dependencies 2) Explained in a little more detail how to use Xcode to build Grid and Hadrons libraries --- documentation/GridXcode/readme.md | 68 +++++++++++++++++++------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/documentation/GridXcode/readme.md b/documentation/GridXcode/readme.md index b75d9323..031ec72a 100644 --- a/documentation/GridXcode/readme.md +++ b/documentation/GridXcode/readme.md @@ -1,6 +1,8 @@ -# Using Xcode with Grid on Mac OS +# Using Xcode for Grid on Mac OS -This guide explains how to use Xcode as an IDE on Mac OS. +This guide explains how to use Xcode as an IDE for Grid on Mac OS. + +*NB: this guide, and the screenshots, were generated using Xcode 10.1.* # Initial setup @@ -27,11 +29,9 @@ Once Xcode is installed, install the Xcode command-line utilities using: xcode-select --install -*NB: the screenshots from this guide were generated from Xcode 10.1.* - ## 2. Set Grid environment variables -To make sure we can share Xcode projects via git and have them work without requiring modification, we will define Grid environment variables. To make sure these environment variables will be available to the Xcode build system, issue the following command: +To make sure we can share Xcode projects via git and have them work without requiring modification, we will define Grid environment variables. To make sure these environment variables will be available to the Xcode build system, issue the following shell command: defaults write com.apple.dt.Xcode UseSanitizedBuildSystemEnvironment -bool NO @@ -100,7 +100,7 @@ NB: Grid does not have any dependencies on fortran, however many standard scient [OMPI]: https://www.open-mpi.org/software/ompi/v3.1/ - ../configure CC=clang CXX=clang++ F77=gfortran FC=gfortran CXXFLAGS=-g --prefix=$GridPre/openmpi-3.1.3 + ../configure CC=clang CXX=clang++ F77=gfortran FC=gfortran CXXFLAGS=-g --prefix=$GridPre/openmpi make -j 4 all install (If you don't want to bother with fortran bindings, just don't include the F77 and FC flags) @@ -149,9 +149,8 @@ There isn't currently a port for [C-LIME][C-LIME], so download the source and th [C-LIME]: https://usqcd-software.github.io/c-lime/ "C-language API for Lattice QCD Interchange Message Encapsulation / Large Internet Message Encapsulation" - ../configure --prefix=$GridPre/lime-1.3.2 CC=clang - make -j 4 - make install + ../configure --prefix=$GridPre/lime CC=clang + make -j 4 all install ## 5. Install, Configure and Build Grid @@ -171,31 +170,31 @@ or git clone https://github.com/paboyle/Grid.git -depending on whether you are using https or ssh. +depending on how many times you like to enter your password. ### 5.2 Configure Grid The Xcode build system supports multiple configurations for each project, by default: `Debug` and `Release`, but more configurations can be defined. We will create separate Grid build directories for each configuration, using the Grid **Autoconf** build system to make each configuration. NB: it is **not** necessary to run `make install` on them once they are built (IDE features such as *jump to definition* will work better of you don't). -Below are shown the `configure` script invocations for three recommended configurations. You are free to define more, less or different configurations, but as a minimum, be sure to build a `Debug` configuration. +Below are shown the `configure` script invocations for three recommended configurations. You are free to define more, fewer or different configurations, but as a minimum, be sure to build a `Debug` configuration. #### 1. `Debug` This is the build for every day developing and debugging with Xcode. It uses the Xcode clang c++ compiler, without MPI, and defaults to double-precision. Xcode builds the `Debug` configuration with debug symbols for full debugging: - ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridDebug --enable-comms=none --enable-doxygen-doc + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridDebug --enable-comms=none --enable-doxygen-doc #### 2. `Release` Since Grid itself doesn't really have debug configurations, the release build is recommended to be the same as `Debug`, except using single-precision (handy for validation): - ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=single CXX=clang++ --prefix=$GridPre/GridRelease --enable-comms=none --enable-doxygen-doc + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime --enable-simd=GEN --enable-precision=single CXX=clang++ --prefix=$GridPre/GridRelease --enable-comms=none --enable-doxygen-doc #### 3. `MPIDebug` Debug configuration with MPI: - ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime-1.3.2 --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridMPIDebug --enable-comms=mpi-auto MPICXX=$GridPre/openmpi-3.1.3/bin/mpicxx --enable-doxygen-doc + ../configure --with-hdf5=$GridPkg --with-gmp=$GridPkg --with-mpfr=$GridPkg --with-fftw=$GridPkg --with-lime=$GridPre/lime --enable-simd=GEN --enable-precision=double CXX=clang++ --prefix=$GridPre/GridMPIDebug --enable-comms=mpi-auto MPICXX=$GridPre/openmpi/bin/mpicxx --enable-doxygen-doc ### 5.3 Build Grid @@ -253,7 +252,7 @@ Obtain a list of header locations required by Grid by running the following from Output should look similar to: - -I$GridPre/openmpi-3.1.3/include -I$GridPkg/include -I$GridPre/lime-1.3.2/include -I$GridPkg/include -I$GridPkg/include -I$GridPkg/include -O3 -g -std=c++11 + -I$GridPre/openmpi/include -I$GridPkg/include -I$GridPre/lime/include -I$GridPkg/include -I$GridPkg/include -I$GridPkg/include -O3 -g -std=c++11 The header locations follow the `-I` switches. You can ignore the other switches, and you can ignore duplicate entries, which just mean that your package manager has installed multiple packages in the same location. @@ -267,13 +266,13 @@ Set HEADER_SEARCH_PATHS to: followed by (***the order is important***) the locations reported by `grid-config --cxxflags`, ignoring duplicates, e.g.: - $GridPre/openmpi-3.1.3/include + $GridPre/openmpi/include $GridPkg/include - $GridPre/lime-1.3.2/include + $GridPre/lime/include **Note: the easiest way to set this value is to put it all on one line, space separated, and edit the text to the right of `HEADER_SEARCH_PATHS`**, i.e.: - $Grid/build$(CONFIGURATION)/Grid $Grid $Grid/Grid $GridPre/openmpi-3.1.3/include $GridPkg/include $GridPre/lime-1.3.2/include + $Grid/build$(CONFIGURATION)/Grid $Grid $Grid/Grid $GridPre/openmpi/include $GridPkg/include $GridPre/lime/include #### LIBRARY_SEARCH_PATHS @@ -283,11 +282,11 @@ Obtain a list of library locations required by Grid by running the following fro Output should look similar to: - -L$GridPre/openmpi-3.1.3/lib -L$GridPkg/lib -L$GridPre/lime-1.3.2/lib -L$GridPkg/lib -L$GridPkg/lib -L$GridPkg/lib + -L$GridPre/openmpi/lib -L$GridPkg/lib -L$GridPre/lime/lib -L$GridPkg/lib -L$GridPkg/lib -L$GridPkg/lib Paste the output ***with `$Grid/build$(CONFIGURATION)/Grid $Grid/build$(CONFIGURATION)/Hadrons ` prepended*** into `LIBRARY_SEARCH_PATHS`: - $Grid/build$(CONFIGURATION)/Grid $Grid/build$(CONFIGURATION)/Hadrons $GridPre/openmpi-3.1.3/lib $GridPkg/lib $GridPre/lime-1.3.2/lib + $Grid/build$(CONFIGURATION)/Grid $Grid/build$(CONFIGURATION)/Hadrons $GridPre/openmpi/lib $GridPkg/lib $GridPre/lime/lib ### 2. Linking @@ -390,21 +389,31 @@ If you want to build `Grid` and `Hadrons` libraries using Xcode, you will need t 1. Make new library targets for `Grid` and `Hadrons` 2. Add Grid source folders to your project: + a. Right click project then `Add files to "project" ...` b. Choose `$Grid/Grid` folder c. Select `Create groups` (`folder references` doesn't work) - d. Select `Grid` (containing just the Grid sources, not the entire Git repository) as your target + d. Make sure none of the targets are selected e. Click `Add` + f. Add each source file (not header) in `Grid` and its subdirectories to the `Grid` target (option-command-1, then tick source files) 3. Add Hadrons source folders to your project - a. As per `Grid`, but change the target to `Hadrons` - b. For each source file in `Archive`, remove them from the target (option-command-1, then untick source files) - -4. Set the following values for each target in `Build Settings` + + a. As per `Grid`, but add each source file in `Hadrons` (except those in `Archive` and `Utilities`) to the `Hadrons` target + +4. Set the following values *for the entire project* in `Build Settings` Group | Variable | Value --- | --- | --- `Deployment` | `DSTROOT` | `$Grid/build$(CONFIGURATION)` *(do this for the entire project)* + `Search Paths` | `LIBRARY_SEARCH_PATHS` | remove `$Grid/build$(CONFIGURATION)/Grid $Grid/build$(CONFIGURATION)/Hadrons` from the start of the path + + This sets the deployment location to the makefile build folders (but by default, targets will have `SKIP_INSTALL` set to `Yes`). The change to the paths is to make sure any executable targets link to the versions of the `Grid` and `Hadrons` libraries just built. + +5. Set the following values for each of the `Grid` and `Hadrons` targets in `Build Settings` + + Group | Variable | Value + --- | --- | --- `Deployment` | `DEPLOYMENT_LOCATION` | `Yes` `Deployment` | `INSTALL_PATH` | `$(PRODUCT_NAME)/` `Deployment` | `SKIP_INSTALL` | `No` @@ -412,11 +421,18 @@ If you want to build `Grid` and `Hadrons` libraries using Xcode, you will need t This ensures that the libraries are copied back into the build folders when they are made (removing the need to run `make -j 4`) -5. For `Grid`, in `Build Settings` in the `Build Options` group, set: +6. For `Grid`, in `Build Settings` in the `Build Options` group, set: Variable | Configuration | Value --- | --- | --- `EXCLUDED_SOURCE_FILE_NAMES` | Non-MPI configurations (`Debug` and `Release`) | `$(Grid)/Grid/communicator/Communicator_mpi3.cc $(Grid)/Grid/communicator/SharedMemoryMPI.cc` `EXCLUDED_SOURCE_FILE_NAMES` | MPI configurations (`MPIDebug`) | `$(Grid)/Grid/communicator/Communicator_none.cc $(Grid)/Grid/communicator/SharedMemoryNone.cc` +7. Make a new scheme called `Libraries` containing both `Grid` and `Hadrons` targets + + a. Edit the new scheme + b. On the Build tab, add both `Grid` and `Hadrons` targets + You should now be able to build and debug any configuration. + +Note that with this setup, the Xcode build system is not aware of dependencies of your targets on the grid libraries. So you can modify Grid and/or Hadrons headers if you need to, and build your target without rebuilding the entire Grid and Hadrons Libraries (you can manually force the Libraries to rebuild by making the `Libraries` scheme). You can instead configure target dependencies to `Grid` and `Hadrons` libraries in the Xcode build system, just remember to also remove `-lGrid -lHadrons` from the list under `OTHER_LDFLAGS` for the entire project. From ed2427d5f7d39ea2330a8da1693d069f9a0eb81f Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 6 Apr 2019 15:37:53 +0100 Subject: [PATCH 194/347] Make sure Grid::Serializable can write Eigen Tensors to output streams. NB: 1) The Eigen package defines operator<< for Eigen tensors, but this format is different, hence Grid::Serializable::WriteMember 2) For simplification, the contents are written in memory order. I.e. Different results will be obtained depending on whether the tensor is row- or column-major --- Grid/serialisation/BaseIO.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Grid/serialisation/BaseIO.h b/Grid/serialisation/BaseIO.h index f11dcde5..e9d5bedf 100644 --- a/Grid/serialisation/BaseIO.h +++ b/Grid/serialisation/BaseIO.h @@ -583,7 +583,27 @@ namespace Grid { template static inline typename std::enable_if::value, void>::type WriteMember(std::ostream &os, const T &object) { - os << "Eigen::Tensor"; + using Index = typename T::Index; + const Index NumElements{object.size()}; + assert( NumElements > 0 ); + Index count = 1; + os << "T<"; + for( int i = 0; i < T::NumIndices; i++ ) { + Index dim = object.dimension(i); + count *= dim; + if( i ) + os << ","; + os << dim; + } + assert( count == NumElements && "Number of elements doesn't match tensor dimensions" ); + os << ">{"; + const typename T::Scalar * p = object.data(); + for( Index i = 0; i < count; i++ ) { + if( i ) + os << ","; + os << *p++; + } + os << "}"; } }; From 2b598294c99dc1197d4f51556d925f181fec7ade Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 18 Apr 2019 17:47:09 +0100 Subject: [PATCH 195/347] added distil source module --- Hadrons/Modules.hpp | 1 + Hadrons/Modules/MDistil/DistilSource.cc | 7 + Hadrons/Modules/MDistil/DistilSource.hpp | 221 +++++++++++++++++++++++ Hadrons/Modules/MDistil/PerambLight.hpp | 43 +---- Hadrons/modules.inc | 2 + 5 files changed, 236 insertions(+), 38 deletions(-) create mode 100644 Hadrons/Modules/MDistil/DistilSource.cc create mode 100644 Hadrons/Modules/MDistil/DistilSource.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 94cfd316..dda60c3b 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include diff --git a/Hadrons/Modules/MDistil/DistilSource.cc b/Hadrons/Modules/MDistil/DistilSource.cc new file mode 100644 index 00000000..9073185a --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilSource.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TDistilSource; diff --git a/Hadrons/Modules/MDistil/DistilSource.hpp b/Hadrons/Modules/MDistil/DistilSource.hpp new file mode 100644 index 00000000..c9ce63df --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilSource.hpp @@ -0,0 +1,221 @@ +#ifndef Hadrons_MDistil_DistilSource_hpp_ +#define Hadrons_MDistil_DistilSource_hpp_ + +#include +#include +#include +#include +#include +#include +#include + +// These are members of Distillation +#include + +BEGIN_HADRONS_NAMESPACE + + +/****************************************************************************** + * DistilSource * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +class DistilSourcePar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilSourcePar, + std::string, noise, + std::string, eigenPack, + bool, multiFile, + int, tsrc, + int, nnoise, + int, LI, + int, SI, + int, TI, + int, nvec, + int, Ns, + int, Nt, + int, Nt_inv); +}; + +template +class TDistilSource: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl,); +public: + // constructor + TDistilSource(const std::string name); + // destructor + virtual ~TDistilSource(void); + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +protected: + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) +protected: + virtual void Cleanup(void); +}; + +MODULE_REGISTER_TMP(DistilSource, TDistilSource, MDistil); + +/****************************************************************************** + * TDistilSource implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TDistilSource::TDistilSource(const std::string name) +: grid3d{nullptr}, grid4d{nullptr}, Module(name) +{} +// destructor +template +TDistilSource::~TDistilSource(void) +{ + Cleanup(); +}; + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TDistilSource::getInput(void) +{ + std::vector in; + + in.push_back(par().noise); + in.push_back(par().eigenPack); + + return in; +} + +template +std::vector TDistilSource::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TDistilSource::setup(void) +{ + Cleanup(); + auto &noise = envGet(std::vector, par().noise); + + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int SI=par().SI; + int Nt_inv=par().Nt_inv; + + envCreate(std::vector, getName(), 1, + nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); + + grid4d = env().getGrid(); + std::vector latt_size = GridDefaultLatt(); + std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); + std::vector mpi_layout = GridDefaultMpi(); + std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); + latt_size[Nd-1] = 1; + simd_layout_3.push_back( 1 ); + mpi_layout[Nd-1] = 1; + grid3d = MakeLowerDimGrid(grid4d); + + + envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); +} + +// clean up any temporaries created by setup (that aren't stored in the environment) +template +void TDistilSource::Cleanup(void) +{ + if( grid3d != nullptr ) { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TDistilSource::execute(void) +{ + + auto &noise = envGet(std::vector, par().noise); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + auto &rho = envGet(std::vector, getName()); + + envGetTmp(LatticeSpinColourVector, tmp2); + envGetTmp(LatticeColourVector, tmp_nospin); + envGetTmp(LatticeSpinColourVector, tmp3d); + envGetTmp(LatticeColourVector, tmp3d_nospin); + envGetTmp(LatticeSpinColourVector, sink_tslice); + envGetTmp(LatticeColourVector, evec3d); + + + int Ntlocal = grid4d->LocalDimensions()[3]; + int Ntfirst = grid4d->LocalStarts()[3]; + + int tsrc=par().tsrc; + int nnoise=par().nnoise; + int LI=par().LI; + int Ns=par().Ns; + int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI + int Nt=par().Nt; + int TI=par().TI; + int nvec=par().nvec; + int SI=par().SI; + + bool full_tdil=(TI==Nt); + + int vecindex; + int t_inv; + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; + rho[vecindex] = zero; + tmp3d_nospin = zero; + for (int it = dt; it < Nt; it += TI){ + if (full_tdil) t_inv = tsrc; else t_inv = it; + if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += SI){ + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); + tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; + tmp3d=zero; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=zero; + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); + rho[vecindex] += tmp2; + } + } + } + } + } + } + } + } + + + // TEST TO SEE WHETHER THIS MIGHT BE THE MEMORY LEAK + Cleanup(); + +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_DistilSource_hpp_ diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 8dfff057..2b18e00c 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -25,6 +25,7 @@ class PerambLightPar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, std::string, eigenPack, + std::string, noise, std::string, PerambFileName, //stem!!! std::string, UniqueIdentifier, bool, multiFile, @@ -86,6 +87,7 @@ std::vector TPerambLight::getInput(void) in.push_back(par().eigenPack); in.push_back(par().solver); + in.push_back(par().noise); return in; } @@ -93,7 +95,7 @@ std::vector TPerambLight::getInput(void) template std::vector TPerambLight::getOutput(void) { - std::vector out = {getName() + "_perambulator_light",getName() + "_noise",getName() + "_unsmeared_sink"}; + std::vector out = {getName(),getName() + "_unsmeared_sink"}; return out; } @@ -112,10 +114,8 @@ void TPerambLight::setup(void) const int Ns{Distil.Ns}; std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - envCreate(Perambulator, getName() + "_perambulator_light", 1, + envCreate(Perambulator, getName(), 1, sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); - envCreate(std::vector, getName() + "_noise", 1, - nvec*Distil.Ns*Distil.Nt*Distil.nnoise); envCreate(std::vector, getName() + "_unsmeared_sink", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); @@ -176,50 +176,17 @@ void TPerambLight::execute(void) const std::string &UniqueIdentifier{par().UniqueIdentifier}; - auto &noise = envGet(std::vector, getName() + "_noise"); + auto &noise = envGet(std::vector, par().noise); auto &perambulator = envGet(Perambulator, getName() + "_perambulator_light"); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); - //Create Noises - GridSerialRNG sRNG; - sRNG.SeedUniqueString(UniqueIdentifier + std::to_string(vm().getTrajectory())); //maybe add more?? - Real rn; - - for (int inoise=0;inoise 0.5) ? -1 : 1; - } - } - } - } - } // Load perambulator if it exists on disk instead of creating it // Not sure this is how we want it - rather specify an input flag 'read' // and assert that the file is there. const std::string &PerambFileName{par().PerambFileName}; -/* if( PerambFileName.length() ){ - bool bExists = false; - { - std::ifstream f(PerambFileName, std::ios::binary); - if( f.is_open() ) - bExists = true; - } - if( bExists ) { - perambulator.ReadBinary(PerambFileName); - return; - } - }*/ envGetTmp(LatticeSpinColourVector, dist_source); envGetTmp(LatticeSpinColourVector, tmp2); diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 65e876c9..4cfc5e7e 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -39,6 +39,7 @@ modules_cc =\ Modules/MDistil/g5_multiply.cc \ Modules/MDistil/LapEvec.cc \ Modules/MDistil/Noises.cc \ + Modules/MDistil/DistilSource.cc \ Modules/MDistil/DistilVectors.cc \ Modules/MDistil/BContraction.cc \ Modules/MDistil/Baryon2pt.cc \ @@ -122,6 +123,7 @@ modules_hpp =\ Modules/MDistil/PerambFromSolve.hpp \ Modules/MDistil/Baryon2pt.hpp \ Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/DistilSource.hpp \ Modules/MDistil/BContraction.hpp \ Modules/MDistil/DistilVectors.hpp \ Modules/MDistil/DistilSink.hpp \ From 4a4203c610143f0f77ab6c65a427b18de8593925 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 18 Apr 2019 19:10:49 +0100 Subject: [PATCH 196/347] fixed stout smearing for now --- Grid/qcd/smearing/StoutSmearing.h | 6 +++++- Hadrons/Modules/MGauge/StoutSmearing3D.hpp | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index e4e11a63..65501c7e 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -71,9 +71,13 @@ public: } /*! Default constructor. rho is constant in all directions, optionally except for orthogonal dimension */ - Smear_Stout(double rho = 1.0, int orthogdim = -1) + /*Smear_Stout(double rho = 1.0, int orthogdim = -1) : OwnedBase{new Smear_APE(rho3D(rho,orthogdim))}, SmearBase{OwnedBase.get()} { assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); + }*/ + + Smear_Stout(double rho = 1.0) : SmearBase(new Smear_APE(rho)) { + assert(Nc == 3);// "Stout smearing currently implemented only for Nc==3"); } ~Smear_Stout() {} // delete SmearBase... diff --git a/Hadrons/Modules/MGauge/StoutSmearing3D.hpp b/Hadrons/Modules/MGauge/StoutSmearing3D.hpp index ef9db9e9..c5724943 100644 --- a/Hadrons/Modules/MGauge/StoutSmearing3D.hpp +++ b/Hadrons/Modules/MGauge/StoutSmearing3D.hpp @@ -113,7 +113,8 @@ void TStoutSmearing3D::execute(void) << " of 3D-stout smearing and rho= " << par().rho << "orthogonal to dimension " << par().orthogdim << std::endl; - Smear_Stout smearer(par().rho, par().orthogdim); + //Smear_Stout smearer(par().rho, par().orthogdim); + Smear_Stout smearer(par().rho); auto &U = envGet(GaugeField, par().gauge); auto &Usmr = envGet(GaugeField, getName()); From 143b75956c735ad349516b221822e19202fc0b41 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 19 Apr 2019 11:54:02 +0100 Subject: [PATCH 197/347] Stout smearing 3D fixes. Changed LapEvec to perform spatial smearing only --- Grid/qcd/smearing/StoutSmearing.h | 28 ++++++++++++---------- Hadrons/Modules/MDistil/LapEvec.hpp | 8 +++---- Hadrons/Modules/MGauge/StoutSmearing3D.hpp | 11 ++++----- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 65501c7e..27283d73 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -44,20 +44,20 @@ class Smear_Stout : public Smear { // Smear* ownership semantics: // Smear* passed in to constructor are owned by caller, so we don't delete them here // Smear* created within constructor need to be deleted as part of the destructor - const Smear* SmearBase; // Not owned by this object, so not deleted at destruction const std::unique_ptr> OwnedBase; // deleted at destruction + const Smear* SmearBase; // Not owned by this object, so not deleted at destruction -public: - INHERIT_GIMPL_TYPES(Gimpl) - - // only anticipated to be used from default constructor, but might as well be visible - inline static std::vector rho3D(double rho = 1.0, int orthogdim = -1){ + // only anticipated to be used from default constructor + inline static std::vector rho3D(double rho, int orthogdim){ std::vector rho3d(Nd*Nd); for (int mu=0; mu* base) : SmearBase{base} { @@ -71,15 +71,17 @@ public: } /*! Default constructor. rho is constant in all directions, optionally except for orthogonal dimension */ - /*Smear_Stout(double rho = 1.0, int orthogdim = -1) - : OwnedBase{new Smear_APE(rho3D(rho,orthogdim))}, SmearBase{OwnedBase.get()} { + Smear_Stout(double rho, int orthogdim = -1) + : OwnedBase{(orthogdim<0 || orthogdim>=Nd) ? new Smear_APE(rho) : new Smear_APE(rho3D(rho,orthogdim))}, + SmearBase{OwnedBase.get()} { + assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); + } + + /* + Smear_Stout(double rho = 1.0) : SmearBase(new Smear_APE(rho)) { assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); }*/ - Smear_Stout(double rho = 1.0) : SmearBase(new Smear_APE(rho)) { - assert(Nc == 3);// "Stout smearing currently implemented only for Nc==3"); - } - ~Smear_Stout() {} // delete SmearBase... void smear(GaugeField& u_smr, const GaugeField& U) const { diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 8b9841ae..bccd873e 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -51,7 +51,7 @@ BEGIN_MODULE_NAMESPACE(MDistil) struct StoutParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(StoutParameters, int, steps, - double, parm) + double, parm) // TODO: change name of this to rho StoutParameters() = default; template StoutParameters(Reader& Reader){read(Reader,"StoutSmearing",*this);} }; @@ -139,8 +139,8 @@ MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); template TLapEvec::TLapEvec(const std::string name) : gridLD{nullptr}, Module(name) { - LOG(Message) << "TLapEvec constructor : TLapEvec::TLapEvec(const std::string name)" << std::endl; - LOG(Message) << "this=" << this << ", gridLD=" << gridLD << std::endl; + //LOG(Message) << "TLapEvec constructor : TLapEvec::TLapEvec(const std::string name)" << std::endl; + //LOG(Message) << "this=" << this << ", gridLD=" << gridLD << std::endl; } // destructor ///////////////////////////////////////////////////////////////// @@ -235,7 +235,7 @@ void TLapEvec::execute(void) { const StoutParameters &Stout{par().Stout}; envGetTmp(GaugeField, Umu_stout); - Smear_Stout LS(Stout.parm); + Smear_Stout LS(Stout.parm, Tdir); for (int i = 0; i < Stout.steps; i++) { LS.smear(Umu_stout, Umu_smear); Umu_smear = Umu_stout; diff --git a/Hadrons/Modules/MGauge/StoutSmearing3D.hpp b/Hadrons/Modules/MGauge/StoutSmearing3D.hpp index c5724943..3036c46a 100644 --- a/Hadrons/Modules/MGauge/StoutSmearing3D.hpp +++ b/Hadrons/Modules/MGauge/StoutSmearing3D.hpp @@ -108,13 +108,12 @@ void TStoutSmearing3D::setup(void) template void TStoutSmearing3D::execute(void) { - LOG(Message) << "Smearing '" << par().gauge << "' with " << par().steps - << " step" << ((par().steps > 1) ? "s" : "") - << " of 3D-stout smearing and rho= " << par().rho - << "orthogonal to dimension " << par().orthogdim << std::endl; + LOG(Message) << "Smearing '" << par().gauge + << "' with " << par().steps << " step" << ((par().steps > 1) ? "s" : "") + << " of 3D-stout smearing and rho=" << par().rho + << " orthogonal to dimension " << par().orthogdim << std::endl; - //Smear_Stout smearer(par().rho, par().orthogdim); - Smear_Stout smearer(par().rho); + Smear_Stout smearer(par().rho, par().orthogdim); auto &U = envGet(GaugeField, par().gauge); auto &Usmr = envGet(GaugeField, getName()); From 7214681e1188d7f64e0a54d9fffbcdfe43b35b03 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 19 Apr 2019 13:54:25 +0100 Subject: [PATCH 198/347] Spatial smearing doesn't work yet. Fixed inconsistency in naming of perambulator in PerambLight.hpp --- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index bccd873e..1c1ba281 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -235,7 +235,7 @@ void TLapEvec::execute(void) { const StoutParameters &Stout{par().Stout}; envGetTmp(GaugeField, Umu_stout); - Smear_Stout LS(Stout.parm, Tdir); + Smear_Stout LS(Stout.parm );//, Tdir); // should be spatial - doesn't work yet for (int i = 0; i < Stout.steps; i++) { LS.smear(Umu_stout, Umu_smear); Umu_smear = Umu_stout; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index 2b18e00c..ef634e5a 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -176,11 +176,10 @@ void TPerambLight::execute(void) const std::string &UniqueIdentifier{par().UniqueIdentifier}; - auto &noise = envGet(std::vector, par().noise); - auto &perambulator = envGet(Perambulator, - getName() + "_perambulator_light"); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); + auto &noise = envGet(std::vector, par().noise); + auto &perambulator = envGet(Perambulator, getName()); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); // Load perambulator if it exists on disk instead of creating it From a97b814f0c8372d298299c9d7bcb762d2b7072d9 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 19 Apr 2019 14:09:36 +0100 Subject: [PATCH 199/347] Remove redundancy in LapEvec filename --- Hadrons/Modules/MDistil/LapEvec.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 1c1ba281..c166c233 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -257,9 +257,6 @@ void TLapEvec::execute(void) // Invert Peardon Nabla operator separately on each time-slice //////////////////////////////////////////////////////////////////////// - std::string sEigenPackName(getName()); - sEigenPackName.append("_"); - sEigenPackName.append(std::to_string(vm().getTrajectory())); bool bReturnValue = true; auto & eig4d = envGet(DistilEP, getName() ); envGetTmp(std::vector, eig); // Eigenpack for each timeslice @@ -337,7 +334,10 @@ void TLapEvec::execute(void) // Now write out the 4d eigenvectors eig4d.record.operatorXml = DefaultOperatorXml; eig4d.record.solverXml = DefaultsolverXml; - eig4d.write(sEigenPackName + "." + std::to_string(vm().getTrajectory()),false); + std::string sEigenPackName(getName()); + sEigenPackName.append("."); + sEigenPackName.append(std::to_string(vm().getTrajectory())); + eig4d.write(sEigenPackName,false); // Close the local debugging log file if( ll ) { From 606698511c1c57fb91769248499bc413f7439e7e Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 22 Apr 2019 19:03:24 +0100 Subject: [PATCH 200/347] Seems we've not been keeping the test up-to-date --- tests/hadrons/Test_hadrons_distil.cc | 69 ++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_hadrons_distil.cc index 062c805e..694972a1 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_hadrons_distil.cc @@ -30,11 +30,22 @@ #include #include #include -#include using namespace Grid; using namespace Hadrons; +// Very simple iterators for Eigen tensors +// The only way I could get these iterators to work is to put the begin() and end() functions in the Eigen namespace +// So if Eigen ever defines these, we'll have a conflict and have to change this +namespace Eigen { + template + inline typename std::enable_if::value, typename EigenIO::Traits::scalar_type *>::type + begin( ET & et ) { return reinterpret_cast::scalar_type *>(et.data()); } + template + inline typename std::enable_if::value, typename EigenIO::Traits::scalar_type *>::type + end( ET & et ) { return begin(et) + et.size() * EigenIO::Traits::count; } +} + ///////////////////////////////////////////////////////////// // Test creation of laplacian eigenvectors ///////////////////////////////////////////////////////////// @@ -115,6 +126,7 @@ void test_Perambulators(Application &application) // PerambLight parameters MDistil::PerambLight::Par PerambPar; PerambPar.eigenPack="LapEvec"; + PerambPar.noise="Peramb_noise"; PerambPar.PerambFileName="peramb.bin"; PerambPar.UniqueIdentifier="full_dilution"; PerambPar.solver="CG_s"; @@ -227,16 +239,32 @@ void test_MultiPerambulators(Application &application) A2AMesonFieldPar.output="MesonSinksPhi5"; application.createModule("DistilMesonFieldPhi5",A2AMesonFieldPar); } + +void test_Noises(Application &application) { + // DistilVectors parameters + MDistil::NoisesPar NoisePar; + NoisePar.UniqueIdentifier = "full_dilution"; + NoisePar.nvec = 5; + NoisePar.Distil.LI = 5; + NoisePar.Distil.nnoise = 1; + NoisePar.Distil.Ns = 4; + NoisePar.Distil.Nt = 8; + NoisePar.Distil.Nt = 1; + application.createModule("Peramb_noise",NoisePar); +} + ///////////////////////////////////////////////////////////// // DistilVectors ///////////////////////////////////////////////////////////// void test_DistilVectors(Application &application) { + test_Noises(application); // DistilVectors parameters MDistil::DistilVectors::Par DistilVecPar; DistilVecPar.noise="Peramb_noise"; - DistilVecPar.perambulator="Peramb_perambulator_light"; + //DistilVecPar.perambulator="Peramb_perambulator_light"; + DistilVecPar.perambulator="Peramb"; DistilVecPar.eigenPack="LapEvec"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; @@ -390,8 +418,10 @@ void test_MesonFieldRhoAll(Application &application) { // DistilVectors parameters MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_rho_all_tsrc"; - A2AMesonFieldPar.right="DistilVecs_rho_all_tsrc"; + //A2AMesonFieldPar.left="DistilVecs_rho_all_tsrc"; + //A2AMesonFieldPar.right="DistilVecs_rho_all_tsrc"; + A2AMesonFieldPar.left="DistilVecs_rho"; + A2AMesonFieldPar.right="DistilVecs_rho"; A2AMesonFieldPar.output="MesonSinksRhoAll"; A2AMesonFieldPar.gammas="all"; A2AMesonFieldPar.mom={"0 0 0"}; @@ -797,6 +827,15 @@ public: inline value_type * end(void) { return m_p + N; } }; +template typename std::enable_if::value>::type +dump_tensor(const ET & et, const char * psz = nullptr) { + if( psz ) + std::cout << psz << ": "; + else + std::cout << "Unnamed tensor: "; + Serializable::WriteMember( std::cout, et ); +} + template void EigenSliceExample() { @@ -824,17 +863,21 @@ void EigenSliceExample2() T3 a(2,3,4); std::cout << "Initialising a:"; - SequentialInit( a ); + TestScalar f{ 0 }; + const TestScalar Inc{ 1, -1 }; + for( auto &c : a ) { + c = f; + f += Inc; + } std::cout << std::endl; - //std::cout << "Validating a:"; - float z = 0; + std::cout << "Validating a (Eigen::" << ( ( Options & Eigen::RowMajor ) ? "Row" : "Col" ) << "Major):" << std::endl; + f = 0; for( int i = 0 ; i < a.dimension(0) ; i++ ) for( int j = 0 ; j < a.dimension(1) ; j++ ) for( int k = 0 ; k < a.dimension(2) ; k++ ) { - TestScalar w{z, -z}; - //std::cout << " a(" << i << "," << j << "," << k << ")=" << w; - assert( a(i,j,k) == w ); - z++; + std::cout << " a(" << i << "," << j << "," << k << ")=" << a(i,j,k) << std::endl; + assert( ( Options & Eigen::RowMajor ) == 0 || a(i,j,k) == f ); + f += Inc; } //std::cout << std::endl; //std::cout << "a initialised to:\n" << a << std::endl; @@ -933,8 +976,8 @@ int main(int argc, char *argv[]) << ", sizeof(hsize_t) = " << sizeof(hsize_t) << ", sizeof(unsigned long long) = " << sizeof(unsigned long long) << std::endl; - if( DebugEigenTest() ) return 0; - if(DebugGridTensorTest()) return 0; + //if( DebugEigenTest() ) return 0; + //if(DebugGridTensorTest()) return 0; #endif // Decode command-line parameters. 1st one is which test to run From ecdc3ddebf849b6b6307a08e1ca25643a0e26861 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 07:24:56 +0100 Subject: [PATCH 201/347] Moved Distil.hpp and added GNU license to all files --- Hadrons/{Modules/MDistil => }/Distil.hpp | 0 Hadrons/Modules/MDistil/BC2.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/BC2.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/BContraction.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/BContraction.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/Baryon2pt.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/Baryon2pt.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/DistilSink.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/DistilSink.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/DistilSource.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/DistilSource.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/DistilVectors.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/DistilVectors.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/LapEvec.cc | 1 - Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/Noises.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/Noises.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/PerambFromSolve.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/PerambFromSolve.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/PerambLight.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/PerambLight.hpp | 31 ++++++++++++++++++++- Hadrons/Modules/MDistil/g5_multiply.cc | 29 +++++++++++++++++++ Hadrons/Modules/MDistil/g5_multiply.hpp | 31 ++++++++++++++++++++- 23 files changed, 591 insertions(+), 12 deletions(-) rename Hadrons/{Modules/MDistil => }/Distil.hpp (100%) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Distil.hpp similarity index 100% rename from Hadrons/Modules/MDistil/Distil.hpp rename to Hadrons/Distil.hpp diff --git a/Hadrons/Modules/MDistil/BC2.cc b/Hadrons/Modules/MDistil/BC2.cc index c44d8745..62b7589d 100644 --- a/Hadrons/Modules/MDistil/BC2.cc +++ b/Hadrons/Modules/MDistil/BC2.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/BC2.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp index 34459b60..40602322 100644 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/BC2.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_BC2_hpp_ #define Hadrons_MDistil_BC2_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/BContraction.cc b/Hadrons/Modules/MDistil/BContraction.cc index 1ac09aac..8c5183c8 100644 --- a/Hadrons/Modules/MDistil/BContraction.cc +++ b/Hadrons/Modules/MDistil/BContraction.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/BContraction.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index c98c4008..8b766f0f 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/BContraction.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_BContraction_hpp_ #define Hadrons_MDistil_BContraction_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/Baryon2pt.cc b/Hadrons/Modules/MDistil/Baryon2pt.cc index 58de7d33..a0ce63a6 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.cc +++ b/Hadrons/Modules/MDistil/Baryon2pt.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/Baryon2pt.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index ce2bb2c4..6c94b606 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/Baryon2pt.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_Baryon2pt_hpp_ #define Hadrons_MDistil_Baryon2pt_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/DistilSink.cc b/Hadrons/Modules/MDistil/DistilSink.cc index eb151e32..ab350bc2 100644 --- a/Hadrons/Modules/MDistil/DistilSink.cc +++ b/Hadrons/Modules/MDistil/DistilSink.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/DistilSink.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/DistilSink.hpp b/Hadrons/Modules/MDistil/DistilSink.hpp index d9ef303e..8ccba9a3 100644 --- a/Hadrons/Modules/MDistil/DistilSink.hpp +++ b/Hadrons/Modules/MDistil/DistilSink.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/DistilSink.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_DistilSink_hpp_ #define Hadrons_MDistil_DistilSink_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilSource.cc b/Hadrons/Modules/MDistil/DistilSource.cc index 9073185a..c09ad9d9 100644 --- a/Hadrons/Modules/MDistil/DistilSource.cc +++ b/Hadrons/Modules/MDistil/DistilSource.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/DistilSource.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/DistilSource.hpp b/Hadrons/Modules/MDistil/DistilSource.hpp index c9ce63df..148916c7 100644 --- a/Hadrons/Modules/MDistil/DistilSource.hpp +++ b/Hadrons/Modules/MDistil/DistilSource.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/DistilSource.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_DistilSource_hpp_ #define Hadrons_MDistil_DistilSource_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.cc b/Hadrons/Modules/MDistil/DistilVectors.cc index 3f04a18b..6074a115 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.cc +++ b/Hadrons/Modules/MDistil/DistilVectors.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/DistilVectors.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 069a2cc6..587340b0 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/DistilVectors.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_DistilVectors_hpp_ #define Hadrons_MDistil_DistilVectors_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/LapEvec.cc b/Hadrons/Modules/MDistil/LapEvec.cc index 019af23c..6cee3f00 100644 --- a/Hadrons/Modules/MDistil/LapEvec.cc +++ b/Hadrons/Modules/MDistil/LapEvec.cc @@ -6,7 +6,6 @@ Copyright (C) 2015-2019 - *** MM making a change on Tesseract ** Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index c166c233..c1eba4d4 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -36,7 +36,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Noises.cc b/Hadrons/Modules/MDistil/Noises.cc index c4757c26..ec2f9a47 100644 --- a/Hadrons/Modules/MDistil/Noises.cc +++ b/Hadrons/Modules/MDistil/Noises.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/Noises.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index ef50f03a..971bac63 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/Noises.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_Noises_hpp_ #define Hadrons_MDistil_Noises_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation - #include + #include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.cc b/Hadrons/Modules/MDistil/PerambFromSolve.cc index 80d89aa2..fcc1be8d 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.cc +++ b/Hadrons/Modules/MDistil/PerambFromSolve.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/PerambFromSolve.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 9fff87f3..64af5495 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/PerambFromSolve.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_PerambFromSolve_hpp_ #define Hadrons_MDistil_PerambFromSolve_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/PerambLight.cc b/Hadrons/Modules/MDistil/PerambLight.cc index 36dc97af..9aa090a2 100644 --- a/Hadrons/Modules/MDistil/PerambLight.cc +++ b/Hadrons/Modules/MDistil/PerambLight.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/PerambLight.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index ef634e5a..f14c594f 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/PerambLight.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_PerambLight_hpp_ #define Hadrons_MDistil_PerambLight_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/g5_multiply.cc b/Hadrons/Modules/MDistil/g5_multiply.cc index 13d62eb0..54f38f1c 100644 --- a/Hadrons/Modules/MDistil/g5_multiply.cc +++ b/Hadrons/Modules/MDistil/g5_multiply.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/g5_multiply.cc + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MDistil/g5_multiply.hpp b/Hadrons/Modules/MDistil/g5_multiply.hpp index 7c2ba42e..32b19895 100644 --- a/Hadrons/Modules/MDistil/g5_multiply.hpp +++ b/Hadrons/Modules/MDistil/g5_multiply.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/g5_multiply.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MDistil_g5_multiply_hpp_ #define Hadrons_MDistil_g5_multiply_hpp_ @@ -10,7 +39,7 @@ #include // These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE From 23a9b93cdafcc571ce2e552d70a44a715444412d Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 07:39:05 +0100 Subject: [PATCH 202/347] More dependencies for Distil.hpp move and (C) 2019 only --- Hadrons/Modules.hpp | 2 +- Hadrons/Modules/MDistil/BC2.cc | 2 +- Hadrons/Modules/MDistil/BC2.hpp | 2 +- Hadrons/Modules/MDistil/BContraction.cc | 2 +- Hadrons/Modules/MDistil/BContraction.hpp | 2 +- Hadrons/Modules/MDistil/Baryon2pt.cc | 2 +- Hadrons/Modules/MDistil/Baryon2pt.hpp | 2 +- Hadrons/Modules/MDistil/DistilSink.cc | 2 +- Hadrons/Modules/MDistil/DistilSink.hpp | 2 +- Hadrons/Modules/MDistil/DistilSource.cc | 2 +- Hadrons/Modules/MDistil/DistilSource.hpp | 2 +- Hadrons/Modules/MDistil/DistilVectors.cc | 2 +- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- Hadrons/Modules/MDistil/LapEvec.cc | 2 +- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/Noises.cc | 2 +- Hadrons/Modules/MDistil/Noises.hpp | 2 +- Hadrons/Modules/MDistil/PerambFromSolve.cc | 2 +- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 2 +- Hadrons/Modules/MDistil/PerambLight.cc | 2 +- Hadrons/Modules/MDistil/PerambLight.hpp | 2 +- Hadrons/Modules/MDistil/g5_multiply.cc | 2 +- Hadrons/Modules/MDistil/g5_multiply.hpp | 2 +- Hadrons/Modules/MIO/LoadPerambulator.cc | 29 +++++++++++++++++++ Hadrons/Modules/MIO/LoadPerambulator.hpp | 31 ++++++++++++++++++++- 25 files changed, 82 insertions(+), 24 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index dda60c3b..5c881e1c 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Hadrons/Modules/MDistil/BC2.cc b/Hadrons/Modules/MDistil/BC2.cc index 62b7589d..187a2340 100644 --- a/Hadrons/Modules/MDistil/BC2.cc +++ b/Hadrons/Modules/MDistil/BC2.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/BC2.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp index 40602322..14ade917 100644 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/BC2.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/BContraction.cc b/Hadrons/Modules/MDistil/BContraction.cc index 8c5183c8..7fbd1880 100644 --- a/Hadrons/Modules/MDistil/BContraction.cc +++ b/Hadrons/Modules/MDistil/BContraction.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/BContraction.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 8b766f0f..21d28e3a 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/BContraction.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/Baryon2pt.cc b/Hadrons/Modules/MDistil/Baryon2pt.cc index a0ce63a6..21e79995 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.cc +++ b/Hadrons/Modules/MDistil/Baryon2pt.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/Baryon2pt.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index 6c94b606..4964c6db 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/Baryon2pt.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/DistilSink.cc b/Hadrons/Modules/MDistil/DistilSink.cc index ab350bc2..c438dd8b 100644 --- a/Hadrons/Modules/MDistil/DistilSink.cc +++ b/Hadrons/Modules/MDistil/DistilSink.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/DistilSink.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/DistilSink.hpp b/Hadrons/Modules/MDistil/DistilSink.hpp index 8ccba9a3..9435bcc4 100644 --- a/Hadrons/Modules/MDistil/DistilSink.hpp +++ b/Hadrons/Modules/MDistil/DistilSink.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/DistilSink.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/DistilSource.cc b/Hadrons/Modules/MDistil/DistilSource.cc index c09ad9d9..3438b8ea 100644 --- a/Hadrons/Modules/MDistil/DistilSource.cc +++ b/Hadrons/Modules/MDistil/DistilSource.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/DistilSource.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/DistilSource.hpp b/Hadrons/Modules/MDistil/DistilSource.hpp index 148916c7..da0c5028 100644 --- a/Hadrons/Modules/MDistil/DistilSource.hpp +++ b/Hadrons/Modules/MDistil/DistilSource.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/DistilSource.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/DistilVectors.cc b/Hadrons/Modules/MDistil/DistilVectors.cc index 6074a115..69b20853 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.cc +++ b/Hadrons/Modules/MDistil/DistilVectors.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/DistilVectors.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 587340b0..5f96a476 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/DistilVectors.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/LapEvec.cc b/Hadrons/Modules/MDistil/LapEvec.cc index 6cee3f00..be6838fa 100644 --- a/Hadrons/Modules/MDistil/LapEvec.cc +++ b/Hadrons/Modules/MDistil/LapEvec.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/LapEvec.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index c1eba4d4..603deda3 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/LapEvec.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/Noises.cc b/Hadrons/Modules/MDistil/Noises.cc index ec2f9a47..16576b0d 100644 --- a/Hadrons/Modules/MDistil/Noises.cc +++ b/Hadrons/Modules/MDistil/Noises.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/Noises.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 971bac63..c7b3ae17 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/Noises.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.cc b/Hadrons/Modules/MDistil/PerambFromSolve.cc index fcc1be8d..69cd0d35 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.cc +++ b/Hadrons/Modules/MDistil/PerambFromSolve.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/PerambFromSolve.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 64af5495..55186d86 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/PerambFromSolve.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/PerambLight.cc b/Hadrons/Modules/MDistil/PerambLight.cc index 9aa090a2..c827b4fe 100644 --- a/Hadrons/Modules/MDistil/PerambLight.cc +++ b/Hadrons/Modules/MDistil/PerambLight.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/PerambLight.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/PerambLight.hpp index f14c594f..75165874 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/PerambLight.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/PerambLight.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/g5_multiply.cc b/Hadrons/Modules/MDistil/g5_multiply.cc index 54f38f1c..420b6768 100644 --- a/Hadrons/Modules/MDistil/g5_multiply.cc +++ b/Hadrons/Modules/MDistil/g5_multiply.cc @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/g5_multiply.cc - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MDistil/g5_multiply.hpp b/Hadrons/Modules/MDistil/g5_multiply.hpp index 32b19895..498d50da 100644 --- a/Hadrons/Modules/MDistil/g5_multiply.hpp +++ b/Hadrons/Modules/MDistil/g5_multiply.hpp @@ -4,7 +4,7 @@ Source file: Hadrons/Modules/MDistil/g5_multiply.hpp - Copyright (C) 2015-2019 + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall diff --git a/Hadrons/Modules/MIO/LoadPerambulator.cc b/Hadrons/Modules/MIO/LoadPerambulator.cc index bc52b0ea..870d8da4 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.cc +++ b/Hadrons/Modules/MIO/LoadPerambulator.cc @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/LoadPerambulator.cc + + Copyright (C) 2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 using namespace Grid; diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index c6324a1a..9611774f 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -1,3 +1,32 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/LoadPerambulator.hpp + + Copyright (C) 2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + #ifndef Hadrons_MIO_LoadPerambulator_hpp_ #define Hadrons_MIO_LoadPerambulator_hpp_ @@ -5,7 +34,7 @@ #include #include #include -#include +#include BEGIN_HADRONS_NAMESPACE From 8419fbb33535a796a7fa3294b08929f5fd92f5d8 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 08:23:15 +0100 Subject: [PATCH 203/347] Renamed PerambLight module. Check with Felix whether Test_24 and Test_tesseract still need to be maintained --- Hadrons/Modules.hpp | 131 +++++---- .../{PerambLight.cc => Perambulator.cc} | 6 +- .../{PerambLight.hpp => Perambulator.hpp} | 41 +-- Hadrons/modules.inc | 261 +++++++++--------- tests/hadrons/Test_24.cc | 16 +- ...{Test_hadrons_distil.cc => Test_distil.cc} | 28 +- 6 files changed, 243 insertions(+), 240 deletions(-) rename Hadrons/Modules/MDistil/{PerambLight.cc => Perambulator.cc} (87%) rename Hadrons/Modules/MDistil/{PerambLight.hpp => Perambulator.hpp} (89%) rename tests/hadrons/{Test_hadrons_distil.cc => Test_distil.cc} (98%) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 5c881e1c..0ffe6b6d 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,80 +1,79 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include +#include #include #include #include -#include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include -#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/Modules/MDistil/PerambLight.cc b/Hadrons/Modules/MDistil/Perambulator.cc similarity index 87% rename from Hadrons/Modules/MDistil/PerambLight.cc rename to Hadrons/Modules/MDistil/Perambulator.cc index c827b4fe..055b8814 100644 --- a/Hadrons/Modules/MDistil/PerambLight.cc +++ b/Hadrons/Modules/MDistil/Perambulator.cc @@ -2,7 +2,7 @@ Grid physics library, www.github.com/paboyle/Grid - Source file: Hadrons/Modules/MDistil/PerambLight.cc + Source file: Hadrons/Modules/MDistil/Perambulator.cc Copyright (C) 2019 @@ -27,10 +27,10 @@ *************************************************************************************/ /* END LEGAL */ -#include +#include using namespace Grid; using namespace Hadrons; using namespace MDistil; -template class Grid::Hadrons::MDistil::TPerambLight; +template class Grid::Hadrons::MDistil::TPerambulator; diff --git a/Hadrons/Modules/MDistil/PerambLight.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp similarity index 89% rename from Hadrons/Modules/MDistil/PerambLight.hpp rename to Hadrons/Modules/MDistil/Perambulator.hpp index 75165874..547ef649 100644 --- a/Hadrons/Modules/MDistil/PerambLight.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -2,7 +2,7 @@ Grid physics library, www.github.com/paboyle/Grid - Source file: Hadrons/Modules/MDistil/PerambLight.hpp + Source file: Hadrons/Modules/MDistil/Perambulator.hpp Copyright (C) 2019 @@ -27,8 +27,8 @@ *************************************************************************************/ /* END LEGAL */ -#ifndef Hadrons_MDistil_PerambLight_hpp_ -#define Hadrons_MDistil_PerambLight_hpp_ +#ifndef Hadrons_MDistil_Perambulator_hpp_ +#define Hadrons_MDistil_Perambulator_hpp_ #include #include @@ -45,14 +45,14 @@ BEGIN_HADRONS_NAMESPACE /****************************************************************************** - * PerambLight * + * Perambulator * ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) -class PerambLightPar: Serializable +class PerambulatorPar: Serializable { public: - GRID_SERIALIZABLE_CLASS_MEMBERS(PerambLightPar, + GRID_SERIALIZABLE_CLASS_MEMBERS(PerambulatorPar, std::string, eigenPack, std::string, noise, std::string, PerambFileName, //stem!!! @@ -64,15 +64,15 @@ public: }; template -class TPerambLight: public Module +class TPerambulator: public Module { public: FERM_TYPE_ALIASES(FImpl,); SOLVER_TYPE_ALIASES(FImpl,); // constructor - TPerambLight(const std::string name); + TPerambulator(const std::string name); // destructor - virtual ~TPerambLight(void); + virtual ~TPerambulator(void); // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); @@ -90,27 +90,28 @@ private: unsigned int Ls_; }; -MODULE_REGISTER_TMP(PerambLight, TPerambLight, MDistil); +// Can't name the module Perambulator, because that's what we've called the object +MODULE_REGISTER_TMP(Peramb, TPerambulator, MDistil); /****************************************************************************** - * TPerambLight implementation * + * TPerambulator implementation * ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TPerambLight::TPerambLight(const std::string name) -: grid3d{nullptr}, grid4d{nullptr}, Module(name) +TPerambulator::TPerambulator(const std::string name) +: grid3d{nullptr}, grid4d{nullptr}, Module(name) {} // destructor template -TPerambLight::~TPerambLight(void) +TPerambulator::~TPerambulator(void) { Cleanup(); }; // dependencies/products /////////////////////////////////////////////////////// template -std::vector TPerambLight::getInput(void) +std::vector TPerambulator::getInput(void) { std::vector in; @@ -122,7 +123,7 @@ std::vector TPerambLight::getInput(void) } template -std::vector TPerambLight::getOutput(void) +std::vector TPerambulator::getOutput(void) { std::vector out = {getName(),getName() + "_unsmeared_sink"}; @@ -131,7 +132,7 @@ std::vector TPerambLight::getOutput(void) // setup /////////////////////////////////////////////////////////////////////// template -void TPerambLight::setup(void) +void TPerambulator::setup(void) { Cleanup(); @@ -169,7 +170,7 @@ void TPerambLight::setup(void) // clean up any temporaries created by setup (that aren't stored in the environment) template -void TPerambLight::Cleanup(void) +void TPerambulator::Cleanup(void) { if( grid3d != nullptr ) { delete grid3d; @@ -180,7 +181,7 @@ void TPerambLight::Cleanup(void) // execution /////////////////////////////////////////////////////////////////// template -void TPerambLight::execute(void) +void TPerambulator::execute(void) { const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; @@ -293,4 +294,4 @@ END_MODULE_NAMESPACE END_HADRONS_NAMESPACE -#endif // Hadrons_MDistil_PerambLight_hpp_ +#endif // Hadrons_MDistil_Perambulator_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 4cfc5e7e..d87aad20 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,160 +1,159 @@ modules_cc =\ - Modules/MScalarSUN/Grad.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ - Modules/MScalarSUN/Div.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/StochFreeField.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadA2AVectors.cc \ - Modules/MIO/LoadPerambulator.cc \ - Modules/MSink/Smear.cc \ - Modules/MSink/Point.cc \ + Modules/MContraction/Baryon.cc \ + Modules/MContraction/Meson.cc \ + Modules/MContraction/WeakEye3pt.cc \ + Modules/MContraction/A2ALoop.cc \ + Modules/MContraction/WeakNonEye3pt.cc \ + Modules/MContraction/A2AAslashField.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/Gamma3pt.cc \ Modules/MFermion/FreeProp.cc \ Modules/MFermion/GaugeProp.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/StoutSmearing3D.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MUtilities/RandomVectors.cc \ - Modules/MUtilities/PrecisionCast.cc \ - Modules/MDistil/PerambFromSolve.cc \ - Modules/MDistil/g5_multiply.cc \ - Modules/MDistil/LapEvec.cc \ - Modules/MDistil/Noises.cc \ - Modules/MDistil/DistilSource.cc \ - Modules/MDistil/DistilVectors.cc \ - Modules/MDistil/BContraction.cc \ - Modules/MDistil/Baryon2pt.cc \ - Modules/MDistil/BC2.cc \ - Modules/MDistil/PerambLight.cc \ - Modules/MDistil/DistilSink.cc \ Modules/MSource/Momentum.cc \ - Modules/MSource/Z2.cc \ Modules/MSource/Point.cc \ - Modules/MSource/SeqGamma.cc \ Modules/MSource/Wall.cc \ Modules/MSource/SeqConserved.cc \ - Modules/MContraction/WeakEye3pt.cc \ - Modules/MContraction/Meson.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MContraction/Baryon.cc \ - Modules/MContraction/WeakNonEye3pt.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/A2ALoop.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MAction/MobiusDWF.cc \ - Modules/MAction/WilsonClover.cc \ - Modules/MAction/Wilson.cc \ - Modules/MAction/DWF.cc \ - Modules/MAction/ScaledDWF.cc \ - Modules/MAction/ZMobiusDWF.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Z2.cc \ + Modules/MSink/Point.cc \ + Modules/MSink/Smear.cc \ Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ Modules/MSolver/A2AAslashVectors.cc \ + Modules/MSolver/RBPrecCG.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MGauge/StoutSmearing3D.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/GaugeFix.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MUtilities/RandomVectors.cc \ + Modules/MUtilities/PrecisionCast.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MDistil/g5_multiply.cc \ + Modules/MDistil/BC2.cc \ + Modules/MDistil/Noises.cc \ + Modules/MDistil/BContraction.cc \ + Modules/MDistil/DistilSource.cc \ + Modules/MDistil/LapEvec.cc \ + Modules/MDistil/DistilSink.cc \ + Modules/MDistil/Baryon2pt.cc \ + Modules/MDistil/Perambulator.cc \ + Modules/MDistil/PerambFromSolve.cc \ + Modules/MNPR/Amputate.cc \ Modules/MNPR/Bilinear.cc \ Modules/MNPR/FourQuark.cc \ - Modules/MNPR/Amputate.cc + Modules/MAction/Wilson.cc \ + Modules/MAction/MobiusDWF.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MAction/WilsonClover.cc \ + Modules/MAction/DWF.cc \ + Modules/MAction/ScaledDWF.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/Grad.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MScalarSUN/TwoPoint.cc \ + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/Div.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadPerambulator.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadCosmHol.cc \ + Modules/MIO/LoadA2AVectors.cc modules_hpp =\ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/ChargedProp.hpp \ - Modules/MIO/LoadPerambulator.hpp \ - Modules/MIO/LoadEigenPack.hpp \ - Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadCosmHol.hpp \ - Modules/MIO/LoadBinary.hpp \ - Modules/MIO/LoadNersc.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ + Modules/MContraction/WeakEye3pt.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/A2ALoop.hpp \ + Modules/MContraction/A2AMesonField.hpp \ + Modules/MContraction/Meson.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/WeakNonEye3pt.hpp \ Modules/MFermion/FreeProp.hpp \ Modules/MFermion/GaugeProp.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/Random.hpp \ + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Wall.hpp \ + Modules/MSource/Z2.hpp \ + Modules/MSource/SeqConserved.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MSink/Point.hpp \ + Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MSolver/Guesser.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSolver/A2AVectors.hpp \ + Modules/MGauge/UnitEm.hpp \ Modules/MGauge/StoutSmearing.hpp \ Modules/MGauge/Unit.hpp \ + Modules/MGauge/Random.hpp \ Modules/MGauge/GaugeFix.hpp \ + Modules/MGauge/FundtoHirep.hpp \ Modules/MGauge/StoutSmearing3D.hpp \ Modules/MGauge/StochEm.hpp \ Modules/MGauge/Electrify.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MUtilities/RandomVectors.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ Modules/MUtilities/PrecisionCast.hpp \ - Modules/MDistil/Noises.hpp \ - Modules/MDistil/PerambLight.hpp \ - Modules/MDistil/Distil.hpp \ - Modules/MDistil/BC2.hpp \ - Modules/MDistil/g5_multiply.hpp \ - Modules/MDistil/PerambFromSolve.hpp \ - Modules/MDistil/Baryon2pt.hpp \ - Modules/MDistil/LapEvec.hpp \ + Modules/MUtilities/RandomVectors.hpp \ + Modules/MScalar/FreeProp.hpp \ + Modules/MScalar/Scalar.hpp \ + Modules/MScalar/ChargedProp.hpp \ Modules/MDistil/DistilSource.hpp \ - Modules/MDistil/BContraction.hpp \ + Modules/MDistil/LapEvec.hpp \ Modules/MDistil/DistilVectors.hpp \ Modules/MDistil/DistilSink.hpp \ - Modules/MSource/SeqConserved.hpp \ - Modules/MSource/Z2.hpp \ - Modules/MSource/Wall.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Point.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/WeakEye3pt.hpp \ - Modules/MContraction/WeakNonEye3pt.hpp \ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MContraction/A2ALoop.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MAction/WilsonClover.hpp \ - Modules/MAction/ScaledDWF.hpp \ + Modules/MDistil/Noises.hpp \ + Modules/MDistil/BC2.hpp \ + Modules/MDistil/Perambulator.hpp \ + Modules/MDistil/PerambFromSolve.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/Baryon2pt.hpp \ + Modules/MDistil/g5_multiply.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MNPR/Amputate.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MAction/DWF.hpp \ Modules/MAction/MobiusDWF.hpp \ Modules/MAction/Wilson.hpp \ - Modules/MAction/DWF.hpp \ + Modules/MAction/WilsonClover.hpp \ Modules/MAction/ZMobiusDWF.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/Guesser.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MNPR/Amputate.hpp + Modules/MAction/ScaledDWF.hpp \ + Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MIO/LoadEigenPack.hpp \ + Modules/MIO/LoadNersc.hpp \ + Modules/MIO/LoadPerambulator.hpp \ + Modules/MIO/LoadA2AVectors.hpp \ + Modules/MIO/LoadCosmHol.hpp \ + Modules/MIO/LoadCoarseEigenPack.hpp \ + Modules/MIO/LoadBinary.hpp diff --git a/tests/hadrons/Test_24.cc b/tests/hadrons/Test_24.cc index 63b3986d..d7e5db4c 100644 --- a/tests/hadrons/Test_24.cc +++ b/tests/hadrons/Test_24.cc @@ -91,7 +91,7 @@ void test_LapEvec(Application &application) void test_Perambulators(Application &application) { // PerambLight parameters - MDistil::PerambLight::Par PerambPar; + MDistil::Peramb::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="peramb_" + std::to_string(Nconf) + ".bin"; PerambPar.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; @@ -111,7 +111,7 @@ void test_Perambulators(Application &application) PerambPar.Ls=16; PerambPar.Solver.CGPrecision=1e-7; PerambPar.Solver.MaxIterations=10000; - application.createModule("Peramb",PerambPar); + application.createModule("Peramb",PerambPar); } ///////////////////////////////////////////////////////////// // DistilVectors @@ -122,7 +122,8 @@ void test_DistilVectors(Application &application) // DistilVectors parameters MDistil::DistilVectors::Par DistilVecPar; DistilVecPar.noise="Peramb_noise"; - DistilVecPar.perambulator="Peramb_perambulator_light"; + //DistilVecPar.perambulator="Peramb_perambulator_light"; + DistilVecPar.perambulator="Peramb"; DistilVecPar.eigenPack="LapEvec"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; @@ -137,8 +138,8 @@ void test_DistilVectors(Application &application) } void test_PerambulatorsS(Application &application) { - // PerambLight parameters - MDistil::PerambLight::Par PerambPar; + // Peramb parameters + MDistil::Peramb::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="perambS.bin"; PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; @@ -158,7 +159,7 @@ void test_PerambulatorsS(Application &application) PerambPar.Ls=16; PerambPar.Solver.CGPrecision=1e-8; PerambPar.Solver.MaxIterations=10000; - application.createModule("PerambS",PerambPar); + application.createModule("PerambS",PerambPar); } ///////////////////////////////////////////////////////////// // DistilVectors @@ -169,7 +170,8 @@ void test_DistilVectorsS(Application &application) // DistilVectors parameters MDistil::DistilVectors::Par DistilVecPar; DistilVecPar.noise="PerambS_noise"; - DistilVecPar.perambulator="PerambS_perambulator_light"; + //DistilVecPar.perambulator="PerambS_perambulator_light"; + DistilVecPar.perambulator="PerambS"; DistilVecPar.eigenPack="LapEvec"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; diff --git a/tests/hadrons/Test_hadrons_distil.cc b/tests/hadrons/Test_distil.cc similarity index 98% rename from tests/hadrons/Test_hadrons_distil.cc rename to tests/hadrons/Test_distil.cc index 694972a1..173a4d9e 100644 --- a/tests/hadrons/Test_hadrons_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -123,8 +123,8 @@ void test_LapEvec(Application &application) void test_Perambulators(Application &application) { - // PerambLight parameters - MDistil::PerambLight::Par PerambPar; + // Perambulator parameters + MDistil::Peramb::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.noise="Peramb_noise"; PerambPar.PerambFileName="peramb.bin"; @@ -144,7 +144,7 @@ void test_Perambulators(Application &application) //PerambPar.Ls=16; //PerambPar.Solver.CGPrecision=1e-8; //PerambPar.Solver.MaxIterations=10000; - application.createModule("Peramb",PerambPar); + application.createModule("Peramb",PerambPar); } ///////////////////////////////////////////////////////////// // Multiple Perambulators @@ -152,8 +152,8 @@ void test_Perambulators(Application &application) void test_MultiPerambulators(Application &application) { - // PerambLight parameters - MDistil::PerambLight::Par PerambPar; + // Perambulator parameters + MDistil::Peramb::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.UniqueIdentifier="full_dilution"; PerambPar.PerambFileName="Peramb5"; @@ -167,7 +167,7 @@ void test_MultiPerambulators(Application &application) PerambPar.Distil.Ns=4; PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; - application.createModule("Peramb5",PerambPar); + application.createModule("Peramb5",PerambPar); MDistil::PerambFromSolve::Par SolvePar; SolvePar.eigenPack="LapEvec"; SolvePar.PerambFileName="Peramb2"; @@ -205,7 +205,8 @@ void test_MultiPerambulators(Application &application) DistilVecPar.LI=3; DistilVecPar.nvec=3; application.createModule("DistilVecs3",DistilVecPar); - DistilVecPar.perambulator="Peramb5_perambulator_light"; + //DistilVecPar.perambulator="Peramb5_perambulator_light"; + DistilVecPar.perambulator="Peramb5"; DistilVecPar.LI=5; DistilVecPar.nvec=5; application.createModule("DistilVecs5",DistilVecPar); @@ -279,8 +280,8 @@ void test_DistilVectors(Application &application) } void test_PerambulatorsS(Application &application) { - // PerambLight parameters - MDistil::PerambLight::Par PerambPar; + // Perambulator parameters + MDistil::Peramb::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="perambS.bin"; PerambPar.UniqueIdentifier="full_dilution"; @@ -299,7 +300,7 @@ void test_PerambulatorsS(Application &application) //PerambPar.Ls=16; //PerambPar.Solver.CGPrecision=1e-8; //PerambPar.Solver.MaxIterations=10000; - application.createModule("PerambS",PerambPar); + application.createModule("PerambS",PerambPar); } ///////////////////////////////////////////////////////////// // DistilVectors @@ -310,7 +311,8 @@ void test_DistilVectorsS(Application &application) // DistilVectors parameters MDistil::DistilVectors::Par DistilVecPar; DistilVecPar.noise="PerambS_noise"; - DistilVecPar.perambulator="PerambS_perambulator_light"; + //DistilVecPar.perambulator="PerambS_perambulator_light"; + DistilVecPar.perambulator="PerambS"; DistilVecPar.eigenPack="LapEvec"; DistilVecPar.tsrc = 0; DistilVecPar.nnoise = 1; @@ -556,7 +558,7 @@ void test_AslashSeq(Application &application) ///////////////////////////////////////////////////////////// void test_PerambulatorsSolve(Application &application) { - // PerambLight parameters + // Perambulator parameters MDistil::PerambFromSolve::Par PerambFromSolvePar; PerambFromSolvePar.eigenPack="LapEvec"; PerambFromSolvePar.solve="Aslash_seq"; @@ -759,7 +761,7 @@ bool DebugEigenTest() // Test initialisation of an array of strings for( auto a : as ) std::cout << a << std::endl; - Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; + Grid::Hadrons::MDistil::Peramb p{as,2,7,2}; DebugShowTensor(p, "p"); std::cout << "p.IndexNames follow" << std::endl; for( auto a : p.IndexNames ) From 50a74eaea3db05cec14209dc051fbdfb23ffcbdd Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 08:33:10 +0100 Subject: [PATCH 204/347] Doesn't compile. Does it still need to be maintained? --- tests/hadrons/Test_tesseract.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/hadrons/Test_tesseract.cc b/tests/hadrons/Test_tesseract.cc index a683e26f..77a5175c 100644 --- a/tests/hadrons/Test_tesseract.cc +++ b/tests/hadrons/Test_tesseract.cc @@ -89,8 +89,8 @@ void test_LapEvec(Application &application) void test_Perambulators(Application &application) { - // PerambLight parameters - MDistil::PerambLight::Par PerambPar; + // Perambulator parameters + MDistil::Peramb::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="peramb.bin"; PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; @@ -110,7 +110,7 @@ void test_Perambulators(Application &application) PerambPar.Ls=16; PerambPar.Solver.CGPrecision=1e-2; PerambPar.Solver.MaxIterations=10000; - application.createModule("Peramb",PerambPar); + application.createModule("Peramb",PerambPar); } ///////////////////////////////////////////////////////////// // DistilVectors @@ -136,8 +136,8 @@ void test_DistilVectors(Application &application) } void test_PerambulatorsS(Application &application) { - // PerambLight parameters - MDistil::PerambLight::Par PerambPar; + // Perambulator parameters + MDistil::Peramb::Par PerambPar; PerambPar.eigenPack="LapEvec"; PerambPar.PerambFileName="perambS.bin"; PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; @@ -157,7 +157,7 @@ void test_PerambulatorsS(Application &application) PerambPar.Ls=16; PerambPar.Solver.CGPrecision=1e-8; PerambPar.Solver.MaxIterations=10000; - application.createModule("PerambS",PerambPar); + application.createModule("PerambS",PerambPar); } ///////////////////////////////////////////////////////////// // DistilVectors From 3ac5a69a578f018ab8d73b59643a455ece65c356 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 08:54:30 +0100 Subject: [PATCH 205/347] Ready to test spatial smearing (again) --- Grid/qcd/smearing/StoutSmearing.h | 5 +++-- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 27283d73..7b388924 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -41,6 +41,7 @@ namespace QCD { template class Smear_Stout : public Smear { private: + const std::vector SmearRho{}; // Smear* ownership semantics: // Smear* passed in to constructor are owned by caller, so we don't delete them here // Smear* created within constructor need to be deleted as part of the destructor @@ -72,8 +73,8 @@ public: /*! Default constructor. rho is constant in all directions, optionally except for orthogonal dimension */ Smear_Stout(double rho, int orthogdim = -1) - : OwnedBase{(orthogdim<0 || orthogdim>=Nd) ? new Smear_APE(rho) : new Smear_APE(rho3D(rho,orthogdim))}, - SmearBase{OwnedBase.get()} { + //: OwnedBase{(orthogdim<0 || orthogdim>=Nd) ? new Smear_APE(rho) : new Smear_APE(rho3D(rho,orthogdim))}, + : SmearRho{ rho3D(rho,orthogdim) }, OwnedBase{ new Smear_APE(SmearRho) }, SmearBase{OwnedBase.get()} { assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 603deda3..9d1c6853 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -235,7 +235,7 @@ void TLapEvec::execute(void) { const StoutParameters &Stout{par().Stout}; envGetTmp(GaugeField, Umu_stout); - Smear_Stout LS(Stout.parm );//, Tdir); // should be spatial - doesn't work yet + Smear_Stout LS(Stout.parm, Tdir); // spatial smearing only for (int i = 0; i < Stout.steps; i++) { LS.smear(Umu_stout, Umu_smear); Umu_smear = Umu_stout; From b1768ba8206d2ef258b6164b30a5b31d7219e2f5 Mon Sep 17 00:00:00 2001 From: Author Name <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 09:58:34 +0100 Subject: [PATCH 206/347] Urgh! --- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 9d1c6853..e3d35584 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -235,7 +235,7 @@ void TLapEvec::execute(void) { const StoutParameters &Stout{par().Stout}; envGetTmp(GaugeField, Umu_stout); - Smear_Stout LS(Stout.parm, Tdir); // spatial smearing only + Smear_Stout LS(Stout.parm);//, Tdir); // spatial smearing only for (int i = 0; i < Stout.steps; i++) { LS.smear(Umu_stout, Umu_smear); Umu_smear = Umu_stout; From 4f3d1ea6e8c5eb99424b771aae946582119fd375 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 12:18:11 +0100 Subject: [PATCH 207/347] Two heads are better than one. Combined effort and hopefully spatial smearing now fixed! --- Grid/qcd/smearing/StoutSmearing.h | 9 ++++++--- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 7b388924..0a5a4185 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -53,7 +53,7 @@ class Smear_Stout : public Smear { std::vector rho3d(Nd*Nd); for (int mu=0; mu* base) : SmearBase{base} { + std::cout << GridLogDebug << "Stout smearing constructor : Smear_Stout(Smear* base)" << std::endl assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } /*! Construct stout smearing object from explicitly specified rho matrix */ Smear_Stout(const std::vector& rho_) : OwnedBase{new Smear_APE(rho_)}, SmearBase{OwnedBase.get()} { + std::cout << GridLogDebug << "Stout smearing constructor : Smear_Stout(const std::vector& rho_)" << std::endl assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } @@ -75,6 +77,7 @@ public: Smear_Stout(double rho, int orthogdim = -1) //: OwnedBase{(orthogdim<0 || orthogdim>=Nd) ? new Smear_APE(rho) : new Smear_APE(rho3D(rho,orthogdim))}, : SmearRho{ rho3D(rho,orthogdim) }, OwnedBase{ new Smear_APE(SmearRho) }, SmearBase{OwnedBase.get()} { + std::cout << GridLogDebug << "Stout smearing constructor : Smear_StoutSmear_Stout(double rho, int orthogdim = -1)\nrho3d=" << rho3D << std::endl; assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } @@ -89,7 +92,7 @@ public: GaugeField C(U._grid); GaugeLinkField tmp(U._grid), iq_mu(U._grid), Umu(U._grid); - std::cout << GridLogDebug << "Stout smearing started\n"; + std::cout << GridLogDebug << "Stout smearing started" << std::endl; // Smear the configurations SmearBase->smear(C, U); @@ -103,7 +106,7 @@ public: exponentiate_iQ(tmp, iq_mu); pokeLorentz(u_smr, tmp * Umu, mu); // u_smr = exp(iQ_mu)*U_mu } - std::cout << GridLogDebug << "Stout smearing completed\n"; + std::cout << GridLogDebug << "Stout smearing completed" << std::endl; }; void derivative(GaugeField& SigmaTerm, const GaugeField& iLambda, diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index e3d35584..9d1c6853 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -235,7 +235,7 @@ void TLapEvec::execute(void) { const StoutParameters &Stout{par().Stout}; envGetTmp(GaugeField, Umu_stout); - Smear_Stout LS(Stout.parm);//, Tdir); // spatial smearing only + Smear_Stout LS(Stout.parm, Tdir); // spatial smearing only for (int i = 0; i < Stout.steps; i++) { LS.smear(Umu_stout, Umu_smear); Umu_smear = Umu_stout; From ff5e2e0f471dc844ec003418c6dff4af8f46066d Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 12:30:41 +0100 Subject: [PATCH 208/347] Debug output fix. Meant to print the rho matrix for stout smearing ... not the address of the function that creates it --- Grid/qcd/smearing/StoutSmearing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 0a5a4185..af88e5d3 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -77,7 +77,7 @@ public: Smear_Stout(double rho, int orthogdim = -1) //: OwnedBase{(orthogdim<0 || orthogdim>=Nd) ? new Smear_APE(rho) : new Smear_APE(rho3D(rho,orthogdim))}, : SmearRho{ rho3D(rho,orthogdim) }, OwnedBase{ new Smear_APE(SmearRho) }, SmearBase{OwnedBase.get()} { - std::cout << GridLogDebug << "Stout smearing constructor : Smear_StoutSmear_Stout(double rho, int orthogdim = -1)\nrho3d=" << rho3D << std::endl; + std::cout << GridLogDebug << "Stout smearing constructor : Smear_StoutSmear_Stout(double rho, int orthogdim = -1)\nrho3d=" << SmearRho << std::endl; assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } From e70e03f560943d717c23cc7a4c8439870ce0bc0e Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 26 Apr 2019 14:27:40 +0100 Subject: [PATCH 209/347] started stout smearing for small w --- Grid/qcd/smearing/StoutSmearing.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 7b388924..119184b1 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -139,6 +139,7 @@ public: iQ2 = iQ * iQ; iQ3 = iQ * iQ2; + //We should check sgn(c0) here already and then apply eq (34) from 0311018 set_uw(u, w, iQ2, iQ3); set_fj(f0, f1, f2, u, w); @@ -198,9 +199,8 @@ public: } LatticeComplex func_xi0(const LatticeComplex& w) const { - // Define a function to do the check - // if( w < 1e-4 ) std::cout << GridLogWarning<< "[Smear_stout] w too small: - // "<< w <<"\n"; + // Definition from arxiv 0311018 + //if (abs(w) < 0.05) {w2 = w*w; return 1.0 - w2/6.0 * (1.0-w2/20.0 * (1.0-w2/42.0));} return sin(w) / w; } From 4333d97958e00587eaa542c79e06cf23c0c8fc33 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 26 Apr 2019 14:29:21 +0100 Subject: [PATCH 210/347] fixed parameter --- Grid/qcd/smearing/StoutSmearing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 0a5905ca..83e7c649 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -53,7 +53,7 @@ class Smear_Stout : public Smear { std::vector rho3d(Nd*Nd); for (int mu=0; mu Date: Fri, 26 Apr 2019 15:54:05 +0100 Subject: [PATCH 211/347] First attempt at minimising smearing --- Grid/qcd/smearing/StoutSmearing.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 83e7c649..fb86483b 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -142,11 +142,15 @@ public: iQ2 = iQ * iQ; iQ3 = iQ * iQ2; - //We should check sgn(c0) here already and then apply eq (34) from 0311018 - set_uw(u, w, iQ2, iQ3); - set_fj(f0, f1, f2, u, w); - - e_iQ = f0 * unity + timesMinusI(f1) * iQ - f2 * iQ2; + if(abs(real(trace(iQ2))) < 0.000000001) // Felix, please check this + e_iQ = unity; + else { + //We should check sgn(c0) here already and then apply eq (34) from 0311018 + set_uw(u, w, iQ2, iQ3); + set_fj(f0, f1, f2, u, w); + + e_iQ = f0 * unity + timesMinusI(f1) * iQ - f2 * iQ2; + } }; void set_uw(LatticeComplex& u, LatticeComplex& w, GaugeLinkField& iQ2, From e223d0b99f4059db0b5f7370141a9474065f8ca1 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 16:00:35 +0100 Subject: [PATCH 212/347] Need to validate range about which exp^iQ is considered unity --- Grid/qcd/smearing/StoutSmearing.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index fb86483b..590e7821 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -142,7 +142,8 @@ public: iQ2 = iQ * iQ; iQ3 = iQ * iQ2; - if(abs(real(trace(iQ2))) < 0.000000001) // Felix, please check this + Real tr = real(trace(iQ2)); + if(tr > -0.0000001 && tr < 0.00000001 ) // Felix, please check this e_iQ = unity; else { //We should check sgn(c0) here already and then apply eq (34) from 0311018 From 5aca4e86708f356a1434e53653cbb6241a52339a Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Apr 2019 17:23:18 +0100 Subject: [PATCH 213/347] Just realised that the trace is at every lattice site, so moved the check for no smearing further up --- Grid/qcd/smearing/StoutSmearing.h | 36 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index 590e7821..d867fe52 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -41,6 +41,7 @@ namespace QCD { template class Smear_Stout : public Smear { private: + int OrthogDim = -1; const std::vector SmearRho{}; // Smear* ownership semantics: // Smear* passed in to constructor are owned by caller, so we don't delete them here @@ -76,7 +77,7 @@ public: /*! Default constructor. rho is constant in all directions, optionally except for orthogonal dimension */ Smear_Stout(double rho, int orthogdim = -1) //: OwnedBase{(orthogdim<0 || orthogdim>=Nd) ? new Smear_APE(rho) : new Smear_APE(rho3D(rho,orthogdim))}, - : SmearRho{ rho3D(rho,orthogdim) }, OwnedBase{ new Smear_APE(SmearRho) }, SmearBase{OwnedBase.get()} { + : OrthogDim{orthogdim}, SmearRho{ rho3D(rho,orthogdim) }, OwnedBase{ new Smear_APE(SmearRho) }, SmearBase{OwnedBase.get()} { std::cout << GridLogDebug << "Stout smearing constructor : Smear_StoutSmear_Stout(double rho, int orthogdim = -1)\nrho3d=" << SmearRho << std::endl; assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } @@ -98,13 +99,17 @@ public: SmearBase->smear(C, U); for (int mu = 0; mu < Nd; mu++) { - tmp = peekLorentz(C, mu); - Umu = peekLorentz(U, mu); - iq_mu = Ta( - tmp * - adj(Umu)); // iq_mu = Ta(Omega_mu) to match the signs with the paper - exponentiate_iQ(tmp, iq_mu); - pokeLorentz(u_smr, tmp * Umu, mu); // u_smr = exp(iQ_mu)*U_mu + if( mu == OrthogDim ) + tmp = 1.0; + else { + tmp = peekLorentz(C, mu); + Umu = peekLorentz(U, mu); + iq_mu = Ta( + tmp * + adj(Umu)); // iq_mu = Ta(Omega_mu) to match the signs with the paper + exponentiate_iQ(tmp, iq_mu); + pokeLorentz(u_smr, tmp * Umu, mu); // u_smr = exp(iQ_mu)*U_mu + } } std::cout << GridLogDebug << "Stout smearing completed" << std::endl; }; @@ -142,16 +147,11 @@ public: iQ2 = iQ * iQ; iQ3 = iQ * iQ2; - Real tr = real(trace(iQ2)); - if(tr > -0.0000001 && tr < 0.00000001 ) // Felix, please check this - e_iQ = unity; - else { - //We should check sgn(c0) here already and then apply eq (34) from 0311018 - set_uw(u, w, iQ2, iQ3); - set_fj(f0, f1, f2, u, w); - - e_iQ = f0 * unity + timesMinusI(f1) * iQ - f2 * iQ2; - } + //We should check sgn(c0) here already and then apply eq (34) from 0311018 + set_uw(u, w, iQ2, iQ3); + set_fj(f0, f1, f2, u, w); + + e_iQ = f0 * unity + timesMinusI(f1) * iQ - f2 * iQ2; }; void set_uw(LatticeComplex& u, LatticeComplex& w, GaugeLinkField& iQ2, From adc1eaee685b25820ca9e5268ecf350aca292541 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sun, 28 Apr 2019 17:53:42 +0100 Subject: [PATCH 214/347] Switched to Hdf5 format for perambulators. Ready for first test on Tesseract. --- Hadrons/Distil.hpp | 154 ++++++++++++++------ Hadrons/Modules/MDistil/PerambFromSolve.hpp | 8 +- Hadrons/Modules/MDistil/Perambulator.hpp | 7 +- Hadrons/Modules/MIO/LoadPerambulator.hpp | 12 +- tests/hadrons/Test_distil.cc | 61 +++++++- 5 files changed, 179 insertions(+), 63 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 19e08171..63e75ab4 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -258,34 +258,83 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) ******************************************************************************/ -template -class NamedTensor : public Eigen::Tensor +template +class NamedTensor : Serializable { public: - typedef Eigen::Tensor ET; - std::array IndexNames; + using Scalar = Scalar_; + static constexpr int NumIndices = NumIndices_; + static constexpr uint16_t Endian_Scalar_Size = Endian_Scalar_Size_; + using ET = Eigen::Tensor; + using Index = typename ET::Index; + GRID_SERIALIZABLE_CLASS_MEMBERS(NamedTensor + , ET, tensor + , std::vector, IndexNames + ); public: + // Named tensors are intended to be a superset of Eigen tensor + inline operator ET&() const { return tensor; } template - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) - : IndexNames{IndexNames_}, ET(firstDimension, otherDimensions...) + inline const Scalar_& operator()(const std::array &Indices) const + { return tensor.operator()(Indices); } + inline Scalar_& operator()(const std::array &Indices) + { return tensor.operator()(Indices); } + template + inline const Scalar_& operator()(Eigen::Index firstDimension, IndexTypes... otherDimensions) const { - // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. - assert(sizeof...(otherDimensions) + 1 == NumIndices_ - && "NamedTensor error: dimensions in constructor != tensor rank"); + // The number of indices used to access a tensor coefficient must be equal to the rank of the tensor. + assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); + return tensor.operator()(std::array{{firstDimension, otherDimensions...}}); + } + template + inline Scalar_& operator()(Eigen::Index firstDimension, IndexTypes... otherDimensions) + { + // The number of indices used to access a tensor coefficient must be equal to the rank of the tensor. + assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); + return tensor.operator()(std::array{{firstDimension, otherDimensions...}}); } + // Construct a named tensor explicitly specifying size of each dimension + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) + : tensor(firstDimension, otherDimensions...), IndexNames{NumIndices} + { + // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. + assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); + for( int i = 0; i < NumIndices_; i++ ) + IndexNames[i] = IndexNames_[i]; + } + + // Default constructor (assumes tensor will be loaded from file) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor() : IndexNames{NumIndices_} {} + + // Construct a named tensor without specifying size of each dimension (because it will be loaded from file) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(std::array &IndexNames_) + : IndexNames{NumIndices_} + { + for( int i = 0; i < NumIndices_; i++ ) + IndexNames[i] = IndexNames_[i]; + } + // Share data for timeslices we calculated with other nodes inline void SliceShare( GridCartesian * gridLowDim, GridCartesian * gridHighDim ) { - Grid::SliceShare( gridLowDim, gridHighDim, this->data(), (int) (this->size() * sizeof(Scalar_))); + Grid::SliceShare( gridLowDim, gridHighDim, tensor.data(), (int) (tensor.size() * sizeof(Scalar_))); } // load and save - not virtual - probably all changes - inline void load(const std::string filename); - inline void save(const std::string filename) const; - inline void ReadBinary(const std::string filename); - inline void WriteBinary(const std::string filename); + template inline void read (Reader &r, const char * pszTag = nullptr); + template inline void write(Writer &w, const char * pszTag = nullptr) const; + template inline void read (const char * filename, const char * pszTag = nullptr); + template inline void write(const char * filename, const char * pszTag = nullptr) const; + EIGEN_DEPRECATED inline void ReadBinary (const std::string filename); // To be removed + EIGEN_DEPRECATED inline void WriteBinary(const std::string filename); // To be removed }; +// Is this a named tensor +template struct is_named_tensor : public std::false_type {}; +template struct is_named_tensor> : public std::true_type {}; +template struct is_named_tensor, T>::value>::type> : public std::true_type {}; + /****************************************************************************** Save NamedTensor binary format (NB: On-disk format is Big Endian) Assumes the Scalar_ objects are contiguous (no padding) @@ -301,7 +350,7 @@ void NamedTensor::WriteBinary(const st assert((sizeof(Scalar_) % Endian_Scalar_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Endian_Scalar_Size" ); // Size of the data (in bytes) const uint32_t Scalar_Size{sizeof(Scalar_)}; - const auto NumElements{this->size()}; + const auto NumElements{tensor.size()}; const std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; uint64_t u64 = htobe64(static_cast(TotalDataSize)); w.write(reinterpret_cast(&u64), sizeof(u64)); @@ -313,14 +362,14 @@ void NamedTensor::WriteBinary(const st w.write(reinterpret_cast(&u16), sizeof(u16)); // number of dimensions which aren't 1 u16 = static_cast(this->NumIndices); - for( auto dim : this->dimensions() ) + for( auto dim : tensor.dimensions() ) if( dim == 1 ) u16--; u16 = htobe16( u16 ); w.write(reinterpret_cast(&u16), sizeof(u16)); // dimensions together with names int d = 0; - for( auto dim : this->dimensions() ) { + for( auto dim : tensor.dimensions() ) { if( dim != 1 ) { // size of this dimension u16 = htobe16( static_cast( dim ) ); @@ -334,7 +383,7 @@ void NamedTensor::WriteBinary(const st d++; } // Actual data - char * const pStart{reinterpret_cast(this->data())}; + char * const pStart{reinterpret_cast(tensor.data())}; // Swap to network byte order in place (alternative is to copy memory - still slow) void * const pEnd{pStart + TotalDataSize}; if(Endian_Scalar_Size == 8) @@ -359,9 +408,9 @@ void NamedTensor::WriteBinary(const st * p = be16toh( * p ); // checksum #ifdef USE_IPP - u32 = htobe32(GridChecksum::crc32c(this->data(), TotalDataSize)); + u32 = htobe32(GridChecksum::crc32c(tensor.data(), TotalDataSize)); #else - u32 = htobe32(GridChecksum::crc32(this->data(), TotalDataSize)); + u32 = htobe32(GridChecksum::crc32(tensor.data(), TotalDataSize)); #endif w.write(reinterpret_cast(&u32), sizeof(u32)); } @@ -381,7 +430,7 @@ void NamedTensor::ReadBinary(const std assert((sizeof(Scalar_) % Endian_Scalar_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Endian_Scalar_Size" ); // Size of the data in bytes const uint32_t Scalar_Size{sizeof(Scalar_)}; - const auto NumElements{this->size()}; + const auto NumElements{tensor.size()}; const std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; uint64_t u64; r.read(reinterpret_cast(&u64), sizeof(u64)); @@ -397,13 +446,13 @@ void NamedTensor::ReadBinary(const std // number of dimensions which aren't 1 r.read(reinterpret_cast(&u16), sizeof(u16)); u16 = be16toh( u16 ); - for( auto dim : this->dimensions() ) + for( auto dim : tensor.dimensions() ) if( dim == 1 ) u16++; assert( this->NumIndices == u16 && "NamedTensor error: number of dimensions which aren't 1" ); // dimensions together with names int d = 0; - for( auto dim : this->dimensions() ) { + for( auto dim : tensor.dimensions() ) { if( dim != 1 ) { // size of dimension r.read(reinterpret_cast(&u16), sizeof(u16)); @@ -420,7 +469,7 @@ void NamedTensor::ReadBinary(const std d++; } // Actual data - char * const pStart{reinterpret_cast(this->data())}; + char * const pStart{reinterpret_cast(tensor.data())}; void * const pEnd{pStart + TotalDataSize}; r.read(pStart,TotalDataSize); // Swap back from network byte order @@ -437,44 +486,59 @@ void NamedTensor::ReadBinary(const std r.read(reinterpret_cast(&u32), sizeof(u32)); u32 = be32toh( u32 ); #ifdef USE_IPP - u32 -= GridChecksum::crc32c(this->data(), TotalDataSize); + u32 -= GridChecksum::crc32c(tensor.data(), TotalDataSize); #else - u32 -= GridChecksum::crc32(this->data(), TotalDataSize); + u32 -= GridChecksum::crc32(tensor.data(), TotalDataSize); #endif assert( u32 == 0 && "NamedTensor error: Perambulator checksum invalid"); } /****************************************************************************** - Save NamedTensor Hdf5 format + Write NamedTensor ******************************************************************************/ template -void NamedTensor::save(const std::string filename) const { +template +void NamedTensor::write(Writer &w, const char * pszTag)const{ + if( pszTag == nullptr ) + pszTag = "tensor"; + write(w, pszTag, *this); +} + +template +template +void NamedTensor::write(const char * filename, const char * pszTag)const{ LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; -#ifndef HAVE_HDF5 - LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; -#else - Hdf5Writer w(filename); - //w << this->NumIndices << this->dimensions() << this->IndexNames; -#endif + Writer w(filename); + write(w, pszTag); } /****************************************************************************** - Load NamedTensor Hdf5 format + Read NamedTensor ******************************************************************************/ template -void NamedTensor::load(const std::string filename) { +template +void NamedTensor::read(Reader &r, const char * pszTag) { + // Grab index names and dimensions + if( pszTag == nullptr ) + pszTag = "tensor"; + std::vector OldIndexNames{std::move(IndexNames)}; + typename ET::Dimensions OldDimensions{tensor.dimensions()}; + read(r, pszTag, *this); + const typename ET::Dimensions & NewDimensions{tensor.dimensions()}; + for( int i=0; i < NumIndices_; i++ ) { + assert(OldDimensions[i] == 0 || OldDimensions[i] == NewDimensions[i] && "NamedTensor::load dimension size"); + assert(OldIndexNames[i].size() == 0 || OldIndexNames[i] == IndexNames[i] && "NamedTensor::load dimension name"); + } +} + +template +template +void NamedTensor::read(const char * filename, const char * pszTag) { LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; -#ifndef HAVE_HDF5 - LOG(Message) << "Error: I/O for NamedTensor requires HDF5" << std::endl; -#else - Hdf5Reader r(filename); - typename ET::Dimensions d; - std::array n; - //r >> this->NumIndices >> d >> n; - //this->IndexNames = n; -#endif + Reader r(filename); + read(r, pszTag); } /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 55186d86..3ae9b4fb 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -238,9 +238,11 @@ void TPerambFromSolve::execute(void) } } - if(PerambFileName.length()) - perambulator.WriteBinary(PerambFileName + "." + std::to_string(vm().getTrajectory())); - + if(PerambFileName.length()) { + std::string sPerambName{PerambFileName + "." + std::to_string(vm().getTrajectory())}; + //perambulator.WriteBinary(sPerambName); + perambulator.template write((sPerambName + ".h5").c_str(), sPerambName.c_str()); + } } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 547ef649..e33ab5b1 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -286,8 +286,11 @@ void TPerambulator::execute(void) std::cout << "perambulator done" << std::endl; perambulator.SliceShare( grid3d, grid4d ); - if(PerambFileName.length()) - perambulator.WriteBinary(PerambFileName + "." + std::to_string(vm().getTrajectory())); + if(PerambFileName.length()) { + std::string sPerambName{PerambFileName + "." + std::to_string(vm().getTrajectory())}; + //perambulator.WriteBinary(sPerambName); + perambulator.template write((sPerambName + ".h5").c_str(), sPerambName.c_str()); + } } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 9611774f..1a15c793 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -118,13 +118,11 @@ void TLoadPerambulator::setup(void) template void TLoadPerambulator::execute(void) { - auto &perambulator = envGet(MDistil::Perambulator, - getName()); - - const std::string &PerambFileName{par().PerambFileName + "." + std::to_string(vm().getTrajectory())}; - std::cout << "reading perambulator from file " << PerambFileName << std::endl; - perambulator.ReadBinary(PerambFileName); - + auto &perambulator = envGet(MDistil::Perambulator, getName()); + const std::string sPerambName{par().PerambFileName + "." + std::to_string(vm().getTrajectory())}; + const std::string PerambFileName{sPerambName + ".h5"}; + std::cout << "reading perambulator from file " << PerambFileName << std::endl; + perambulator.template read(PerambFileName.c_str(), sPerambName.c_str()); } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index 173a4d9e..48261b49 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -647,7 +647,8 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; template -void DebugShowTensor(T &x, const char * n) +typename std::enable_if::value && !Grid::Hadrons::MDistil::is_named_tensor::value>::type +DebugShowTensor(T &x, const char * n, std::string * pIndexNames=nullptr) { const MyTensor::Index s{x.size()}; std::cout << n << ".size() = " << s << std::endl; @@ -662,7 +663,10 @@ void DebugShowTensor(T &x, const char * n) MyTensor::Index SizeCalculated{1}; std::cout << "Dimensions again"; for(int i=0 ; i < x.NumDimensions ; i++ ) { - std::cout << " : [" << i << /*", " << x.IndexNames[i] << */"]=" << x.dimension(i); + std::cout << " : [" << i; + if( pIndexNames ) + std::cout << ", " << pIndexNames[i]; + std::cout << "]=" << x.dimension(i); SizeCalculated *= d[i]; } std::cout << std::endl; @@ -686,6 +690,13 @@ void DebugShowTensor(T &x, const char * n) std::cout << std::endl; } +template +typename std::enable_if::value>::type +DebugShowTensor(T &x, const char * n) +{ + DebugShowTensor( x.tensor, n, &x.IndexNames[0] ); +} + // Test whether typedef and underlying types are the same void DebugTestTypeEqualities(void) @@ -757,21 +768,59 @@ bool DebugEigenTest() MyTensor x(as, 2,1,4); DebugShowTensor(x, "x"); x.WriteBinary(pszTestFileName); - DebugShowTensor(x, "x"); // Test initialisation of an array of strings for( auto a : as ) std::cout << a << std::endl; - Grid::Hadrons::MDistil::Peramb p{as,2,7,2}; + Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; DebugShowTensor(p, "p"); std::cout << "p.IndexNames follow" << std::endl; for( auto a : p.IndexNames ) std::cout << a << std::endl; + // Now see whether we can read a tensor back std::array Names2={"Alpha", "Gamma", "Delta"}; MyTensor y(Names2, 2,4,1); y.ReadBinary(pszTestFileName); DebugShowTensor(y, "y"); + // Now see whether we can read a tensor back from an hdf5 file + const char * pszH5Name = "test.h5"; + y.write(pszH5Name); + { + MyTensor z; + const char * pszName = "z1"; + DebugShowTensor(z, pszName); + z.read(pszH5Name); + DebugShowTensor(z, pszName); + } + { + MyTensor z(Names2,2,0,0); + const char * pszName = "z2"; + DebugShowTensor(z, pszName); + z.read(pszH5Name); + DebugShowTensor(z, pszName); + } + if((0)) // The following tests would fail + { + MyTensor z(Names2,2,0,78); + //std::array NamesBad={"Alpha", "Gamma", "Kilo"}; + //MyTensor z(NamesBad); + const char * pszName = "zFail"; + DebugShowTensor(z, pszName); + z.read(pszH5Name); + DebugShowTensor(z, pszName); + } + // Now see whether we can read a tensor back from an xml file + const char * pszXmlName = "test.xml"; + y.write(pszXmlName); + { + MyTensor z; + const char * pszName = "xml1"; + DebugShowTensor(z, pszName); + z.read(pszXmlName); + DebugShowTensor(z, pszName); + } + // Testing whether typedef produces the same type - yes it does DebugTestTypeEqualities(); @@ -954,8 +1003,8 @@ bool DebugGridTensorTest( void ) for( auto x : toc7 ) std::cout << " [" << i++ << "]=" << x; std::cout << std::endl; - t2 o2; - auto a2 = TensorRemove(o2); + //t2 o2; + //auto a2 = TensorRemove(o2); //t3 o3; //t4 o4; //auto a3 = TensorRemove(o3); From fb74de0798972bb076b47fadf4922163fd2bda0b Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sun, 28 Apr 2019 20:23:44 +0100 Subject: [PATCH 215/347] Making sure Hdf5 is an optional dependency (default to binary writer if not present) --- Hadrons/Distil.hpp | 42 ++++++++++++++------- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 2 +- Hadrons/Modules/MDistil/Perambulator.hpp | 2 +- Hadrons/Modules/MIO/LoadPerambulator.hpp | 4 +- tests/hadrons/Test_distil.cc | 36 ++++++++++-------- 5 files changed, 51 insertions(+), 35 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 63e75ab4..9e8b873d 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -242,14 +242,24 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) return gridLD; } +#ifdef HAVE_HDF5 +using Default_Reader = Grid::Hdf5Reader; +using Default_Writer = Grid::Hdf5Writer; +static const char * FileExtension = ".h5"; +#else +using Default_Reader = Grid::BinaryReader; +using Default_Writer = Grid::BinaryWriter; +static const char * FileExtension = ".dat"; +#endif + /****************************************************************************** - Perambulator object + NamedTensor object This is an Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) - They can be persisted to disk, with the on-disk format being big endian. + They can be persisted to disk Scalar_ objects are assumed to be composite objects of size Endian_Scalar_Size. - (Disable big-endian by setting Endian_Scalar_Size=1) + (Disable big-endian by setting Endian_Scalar_Size=1). + NB: Endian_Scalar_Size will disappear when ReadBinary & WriteBinary retired IndexNames contains one name for each index, and IndexNames are validated on load. - (NB: Indices of dimension 1 are not saved, and not validated on load) WHAT TO SAVE / VALIDATE ON LOAD (Override to warn instead of assert on load) Ensemble string Configuration number @@ -324,8 +334,8 @@ public: // load and save - not virtual - probably all changes template inline void read (Reader &r, const char * pszTag = nullptr); template inline void write(Writer &w, const char * pszTag = nullptr) const; - template inline void read (const char * filename, const char * pszTag = nullptr); - template inline void write(const char * filename, const char * pszTag = nullptr) const; + inline void read (const char * filename, const char * pszTag = nullptr); + inline void write(const char * filename, const char * pszTag = nullptr) const; EIGEN_DEPRECATED inline void ReadBinary (const std::string filename); // To be removed EIGEN_DEPRECATED inline void WriteBinary(const std::string filename); // To be removed }; @@ -506,11 +516,13 @@ void NamedTensor::write(Writer &w, con } template -template void NamedTensor::write(const char * filename, const char * pszTag)const{ - LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; - Writer w(filename); - write(w, pszTag); + const std::string sTag{pszTag == nullptr ? filename : pszTag}; + std::string sFileName{filename}; + sFileName.append( MDistil::FileExtension ); + LOG(Message) << "Writing NamedTensor to " << sFileName << ", tag " << sTag << std::endl; + MDistil::Default_Writer w(sFileName); + write(w, sTag.c_str()); } /****************************************************************************** @@ -534,11 +546,13 @@ void NamedTensor::read(Reader &r, cons } template -template void NamedTensor::read(const char * filename, const char * pszTag) { - LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; - Reader r(filename); - read(r, pszTag); + const std::string sTag{pszTag == nullptr ? filename : pszTag}; + std::string sFileName{filename}; + sFileName.append( MDistil::FileExtension ); + LOG(Message) << "Reading NamedTensor from " << sFileName << ", tag " << sTag << std::endl; + MDistil::Default_Reader r(sFileName); + read(r, sTag.c_str()); } /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 3ae9b4fb..2f9b4ebc 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -241,7 +241,7 @@ void TPerambFromSolve::execute(void) if(PerambFileName.length()) { std::string sPerambName{PerambFileName + "." + std::to_string(vm().getTrajectory())}; //perambulator.WriteBinary(sPerambName); - perambulator.template write((sPerambName + ".h5").c_str(), sPerambName.c_str()); + perambulator.write(sPerambName.c_str()); } } diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index e33ab5b1..3e16c805 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -289,7 +289,7 @@ void TPerambulator::execute(void) if(PerambFileName.length()) { std::string sPerambName{PerambFileName + "." + std::to_string(vm().getTrajectory())}; //perambulator.WriteBinary(sPerambName); - perambulator.template write((sPerambName + ".h5").c_str(), sPerambName.c_str()); + perambulator.write(sPerambName.c_str()); } } diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 1a15c793..b7f4e696 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -120,9 +120,7 @@ void TLoadPerambulator::execute(void) { auto &perambulator = envGet(MDistil::Perambulator, getName()); const std::string sPerambName{par().PerambFileName + "." + std::to_string(vm().getTrajectory())}; - const std::string PerambFileName{sPerambName + ".h5"}; - std::cout << "reading perambulator from file " << PerambFileName << std::endl; - perambulator.template read(PerambFileName.c_str(), sPerambName.c_str()); + perambulator.read(sPerambName.c_str()); } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index 48261b49..aada8d4f 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -784,20 +784,34 @@ bool DebugEigenTest() DebugShowTensor(y, "y"); // Now see whether we can read a tensor back from an hdf5 file - const char * pszH5Name = "test.h5"; - y.write(pszH5Name); + const char * pszFileName = "test"; + y.write(pszFileName); { MyTensor z; const char * pszName = "z1"; DebugShowTensor(z, pszName); - z.read(pszH5Name); + z.read(pszFileName); DebugShowTensor(z, pszName); } { MyTensor z(Names2,2,0,0); const char * pszName = "z2"; DebugShowTensor(z, pszName); - z.read(pszH5Name); + z.read(pszFileName); + DebugShowTensor(z, pszName); + } + { + // Now see whether we can read a tensor back from an xml file + const char * pszXmlName = "test.xml"; + { + XmlWriter w(pszXmlName); + y.write(w); + } + MyTensor z; + const char * pszName = "xml1"; + DebugShowTensor(z, pszName); + XmlReader r(pszXmlName); + z.read(r); DebugShowTensor(z, pszName); } if((0)) // The following tests would fail @@ -807,17 +821,7 @@ bool DebugEigenTest() //MyTensor z(NamesBad); const char * pszName = "zFail"; DebugShowTensor(z, pszName); - z.read(pszH5Name); - DebugShowTensor(z, pszName); - } - // Now see whether we can read a tensor back from an xml file - const char * pszXmlName = "test.xml"; - y.write(pszXmlName); - { - MyTensor z; - const char * pszName = "xml1"; - DebugShowTensor(z, pszName); - z.read(pszXmlName); + z.read(pszFileName); DebugShowTensor(z, pszName); } @@ -1027,7 +1031,7 @@ int main(int argc, char *argv[]) << ", sizeof(hsize_t) = " << sizeof(hsize_t) << ", sizeof(unsigned long long) = " << sizeof(unsigned long long) << std::endl; - //if( DebugEigenTest() ) return 0; + if( DebugEigenTest() ) return 0; //if(DebugGridTensorTest()) return 0; #endif From c48ae4f3adf50701380af544d62367d7ec77377d Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sun, 28 Apr 2019 23:24:57 +0100 Subject: [PATCH 216/347] 1) Only the boss should write the perambulator - possibly was a source of intermittent corruption? 2) Implemented and test a perambulator conversion utility in Test_distil (commented out near the start of main) --- Hadrons/Distil.hpp | 55 +++++++++++++++------ Hadrons/Modules/MDistil/PerambFromSolve.hpp | 37 +++++++------- Hadrons/Modules/MDistil/Perambulator.hpp | 17 ++++--- tests/hadrons/Test_distil.cc | 11 ++++- 4 files changed, 80 insertions(+), 40 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 9e8b873d..3daead34 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -440,11 +440,11 @@ void NamedTensor::ReadBinary(const std assert((sizeof(Scalar_) % Endian_Scalar_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Endian_Scalar_Size" ); // Size of the data in bytes const uint32_t Scalar_Size{sizeof(Scalar_)}; - const auto NumElements{tensor.size()}; - const std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; + Index NumElements{tensor.size()}; + std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; uint64_t u64; r.read(reinterpret_cast(&u64), sizeof(u64)); - assert( TotalDataSize == be64toh( u64 ) && "NamedTensor error: Size of the data in bytes" ); + assert( TotalDataSize == 0 || TotalDataSize == be64toh( u64 ) && "NamedTensor error: Size of the data in bytes" ); // Size of a Scalar_ uint32_t u32; r.read(reinterpret_cast(&u32), sizeof(u32)); @@ -454,19 +454,47 @@ void NamedTensor::ReadBinary(const std r.read(reinterpret_cast(&u16), sizeof(u16)); assert( Endian_Scalar_Size == be16toh( u16 ) && "NamedTensor error: Scalar_Unit_size"); // number of dimensions which aren't 1 - r.read(reinterpret_cast(&u16), sizeof(u16)); - u16 = be16toh( u16 ); - for( auto dim : tensor.dimensions() ) + uint16_t NumFileDimensions; + r.read(reinterpret_cast(&NumFileDimensions), sizeof(NumFileDimensions)); + NumFileDimensions = be16toh( NumFileDimensions ); + /*for( auto dim : tensor.dimensions() ) if( dim == 1 ) - u16++; - assert( this->NumIndices == u16 && "NamedTensor error: number of dimensions which aren't 1" ); - // dimensions together with names - int d = 0; - for( auto dim : tensor.dimensions() ) { - if( dim != 1 ) { + u16++;*/ + assert( ( TotalDataSize == 0 && this->NumIndices >= NumFileDimensions || this->NumIndices == NumFileDimensions ) + && "NamedTensor error: number of dimensions which aren't 1" ); + if( TotalDataSize == 0 ) { + // Read each dimension, using names to skip past dimensions == 1 + std::array NewDimensions; + for( Index &i : NewDimensions ) i = 1; + int d = 0; + for( int FileDimension = 0; FileDimension < NumFileDimensions; FileDimension++ ) { + // read dimension + uint16_t thisDim; + r.read(reinterpret_cast(&thisDim), sizeof(thisDim)); + // read dimension name + r.read(reinterpret_cast(&u16), sizeof(u16)); + size_t l = be16toh( u16 ); + std::string s( l, '?' ); + r.read(&s[0], l); + // skip forward to matching name + while( IndexNames[d].size() > 0 && s != IndexNames[d] ) + assert(++d < NumIndices && "NamedTensor error: dimension name" ); + if( IndexNames[d].size() == 0 ) + IndexNames[d] = s; + NewDimensions[d++] = be16toh( thisDim ); + } + tensor.resize(NewDimensions); + NumElements = 1; + for( Index i : NewDimensions ) NumElements *= i; + TotalDataSize = NumElements * Scalar_Size; + } else { + // dimensions together with names + const auto & TensorDims{tensor.dimensions()}; + for( int d = 0; d < NumIndices_; d++ ) { // size of dimension r.read(reinterpret_cast(&u16), sizeof(u16)); - assert( dim == be16toh( u16 ) && "size of dimension" ); + u16 = be16toh( u16 ); + assert( TensorDims[d] == u16 && "size of dimension" ); // length of dimension name r.read(reinterpret_cast(&u16), sizeof(u16)); size_t l = be16toh( u16 ); @@ -476,7 +504,6 @@ void NamedTensor::ReadBinary(const std r.read(&s[0], l); assert( s == IndexNames[d] && "NamedTensor error: dimension name" ); } - d++; } // Actual data char * const pStart{reinterpret_cast(tensor.data())}; diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 2f9b4ebc..14774a2d 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -215,31 +215,32 @@ void TPerambFromSolve::execute(void) int Ntlocal = grid4d->LocalDimensions()[3]; int Ntfirst = grid4d->LocalStarts()[3]; - const std::string &PerambFileName{par().PerambFileName}; - - - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI_reduced; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - for (int is = 0; is < Ns; is++) { - result_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); - for (int ivec = 0; ivec < nvec_reduced; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); - pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); - std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; - } + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI_reduced; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + for (int is = 0; is < Ns; is++) { + result_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); + for (int ivec = 0; ivec < nvec_reduced; ivec++) { + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); + std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; } } } } } } + } - if(PerambFileName.length()) { - std::string sPerambName{PerambFileName + "." + std::to_string(vm().getTrajectory())}; + if(grid4d->IsBoss()) { + std::string sPerambName{par().PerambFileName}; + if( sPerambName.length() == 0 ) + sPerambName = getName(); + sPerambName.append( "." ); + sPerambName.append( std::to_string(vm().getTrajectory())); //perambulator.WriteBinary(sPerambName); perambulator.write(sPerambName.c_str()); } diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 3e16c805..06a5f36d 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -215,7 +215,6 @@ void TPerambulator::execute(void) // Load perambulator if it exists on disk instead of creating it // Not sure this is how we want it - rather specify an input flag 'read' // and assert that the file is there. - const std::string &PerambFileName{par().PerambFileName}; envGetTmp(LatticeSpinColourVector, dist_source); envGetTmp(LatticeSpinColourVector, tmp2); @@ -276,18 +275,22 @@ void TPerambulator::execute(void) ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); } + } + } } } } } - } -} } - std::cout << "perambulator done" << std::endl; - perambulator.SliceShare( grid3d, grid4d ); + std::cout << "perambulator done" << std::endl; + perambulator.SliceShare( grid3d, grid4d ); - if(PerambFileName.length()) { - std::string sPerambName{PerambFileName + "." + std::to_string(vm().getTrajectory())}; + if(grid4d->IsBoss()) { + std::string sPerambName{par().PerambFileName}; + if( sPerambName.length() == 0 ) + sPerambName = getName(); + sPerambName.append( "." ); + sPerambName.append( std::to_string(vm().getTrajectory())); //perambulator.WriteBinary(sPerambName); perambulator.write(sPerambName.c_str()); } diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index aada8d4f..d5ac269c 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -1016,6 +1016,14 @@ bool DebugGridTensorTest( void ) return true; } + +bool ConvertPeramb(const char * pszSource, const char * pszDest) { + std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + Grid::Hadrons::MDistil::Perambulator p(sIndexNames); + p.ReadBinary( pszSource ); + p.write(pszDest); + return true; +} #endif int main(int argc, char *argv[]) @@ -1031,8 +1039,9 @@ int main(int argc, char *argv[]) << ", sizeof(hsize_t) = " << sizeof(hsize_t) << ", sizeof(unsigned long long) = " << sizeof(unsigned long long) << std::endl; - if( DebugEigenTest() ) return 0; + //if( DebugEigenTest() ) return 0; //if(DebugGridTensorTest()) return 0; + //if(ConvertPeramb("PerambL_100_tsrc0.3000","PerambL_100_tsrc0.3000")) return 0; #endif // Decode command-line parameters. 1st one is which test to run From ac19c0e04f83b0499beedf400b26c8276749ccf7 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 29 Apr 2019 09:20:08 +0100 Subject: [PATCH 217/347] This will need to be removed eventually, but should save us fiddling about with each release --- tests/hadrons/Test_distil.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index d5ac269c..e4aa0b23 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -347,6 +347,7 @@ void test_MesonSink(Application &application) // g5*unsmeared ///////////////////////////////////////////////////////////// +#ifdef DISTIL_PRE_RELEASE void test_g5_sinks(Application &application) { // DistilVectors parameters @@ -358,6 +359,8 @@ void test_g5_sinks(Application &application) g5_multiplyPar.Nt_inv=1; application.createModule("g5phi",g5_multiplyPar); } +#endif + ///////////////////////////////////////////////////////////// // MesonFields ///////////////////////////////////////////////////////////// @@ -435,6 +438,7 @@ void test_MesonFieldRhoAll(Application &application) // BaryonFields - phiphiphi - efficient ///////////////////////////////////////////////////////////// +#ifdef DISTIL_PRE_RELEASE void test_BaryonFieldPhi2(Application &application) { // DistilVectors parameters @@ -510,6 +514,8 @@ void test_Baryon2pt(Application &application) Baryon2ptPar.output="C2_baryon"; application.createModule("C2_b",Baryon2ptPar); } +#endif + ///////////////////////////////////////////////////////////// // emField ///////////////////////////////////////////////////////////// @@ -1124,6 +1130,7 @@ int main(int argc, char *argv[]) test_DistilVectorsS( application ); test_MesonFieldSL( application ); break; +#ifdef DISTIL_PRE_RELEASE case 6: // 3 test_Global( application ); test_SolverS( application ); @@ -1141,6 +1148,7 @@ int main(int argc, char *argv[]) test_BaryonFieldPhi( application ); test_BaryonFieldRho( application ); break; +#endif case 8: // 3 test_Global( application ); test_SolverS( application ); @@ -1149,6 +1157,7 @@ int main(int argc, char *argv[]) test_DistilVectors( application ); test_MesonField( application ); break; +#ifdef DISTIL_PRE_RELEASE case 9: // 3 test_Global( application ); test_SolverS( application ); @@ -1172,6 +1181,7 @@ int main(int argc, char *argv[]) test_BaryonFieldPhi2( application ); test_BaryonFieldRho2( application ); break; +#endif case 12: // 3 test_Global( application ); test_SolverS( application ); From 420310510460224be87b12f94f572c3de51b1573 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 29 Apr 2019 18:40:38 +0100 Subject: [PATCH 218/347] Part-way through release tidy-up --- Hadrons/Distil.hpp | 32 +-- Hadrons/Modules/MDistil/DistilVectors.hpp | 255 +++++++++++++--------- Hadrons/Modules/MDistil/LapEvec.hpp | 95 +++----- 3 files changed, 189 insertions(+), 193 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 3daead34..22f4f6fb 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -47,6 +47,9 @@ /****************************************************************************** A consistent set of cross-platform methods for big endian <-> host byte ordering I imagine this exists already? + + RELEASE NOTE: + ******************************************************************************/ #if defined(__linux__) @@ -589,6 +592,20 @@ void NamedTensor::read(const char * fi template using Perambulator = NamedTensor; +struct DistilParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, TI, + int, LI, + int, nnoise, + int, tsrc, + int, SI, + int, Ns, + int, Nt, + int, Nt_inv) + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} +}; + /************************************************************************************* Rotate eigenvectors into our phase convention @@ -624,21 +641,6 @@ inline void RotateEigen(std::vector & evec) } } -struct DistilParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, - int, TI, - int, LI, - int, nnoise, - int, tsrc, - int, SI, - int, Ns, - int, Nt, - int, Nt_inv) - DistilParameters() = default; - template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} -}; - - END_MODULE_NAMESPACE END_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 5f96a476..92347c50 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -43,54 +43,66 @@ BEGIN_HADRONS_NAMESPACE - /****************************************************************************** - * DistilVectors * + * DistilVectors * + * (Create rho and/or phi vectors) * ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) class DistilVectorsPar: Serializable { public: - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilVectorsPar, - std::string, noise, - std::string, perambulator, - std::string, eigenPack, - bool, multiFile, - int, tsrc, - int, nnoise, - int, LI, - int, SI, - int, TI, - int, nvec, - int, Ns, - int, Nt, - int, Nt_inv); + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilVectorsPar, + std::string, noise, + std::string, perambulator, + std::string, lapevec, + std::string, source, + std::string, sink, + bool, multiFile, + int, tsrc, + //int, nnoise, + int, LI, + int, SI, + int, TI, + int, nvec, + int, Ns, + int, Nt, + int, Nt_inv); }; template class TDistilVectors: public Module { public: - FERM_TYPE_ALIASES(FImpl,); + FERM_TYPE_ALIASES(FImpl,); + // This is the type of perambulator I expect + using DistilPeramb = Perambulator; + // constructor + TDistilVectors(const std::string name); + // destructor + virtual ~TDistilVectors(void); + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +protected: + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) + virtual void Cleanup(void); public: - // constructor - TDistilVectors(const std::string name); - // destructor - virtual ~TDistilVectors(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); -protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) -protected: - virtual void Cleanup(void); + // These variables contain parameters + std::string PerambulatorName; + std::string NoiseVectorName; + std::string LapEvecName; + bool bMakeSource; + bool bMakeSink; + std::string SourceName; + std::string SinkName; + int nnoise; }; MODULE_REGISTER_TMP(DistilVectors, TDistilVectors, MDistil); @@ -114,40 +126,70 @@ TDistilVectors::~TDistilVectors(void) template std::vector TDistilVectors::getInput(void) { - std::vector in; - - in.push_back(par().noise); - in.push_back(par().perambulator); - in.push_back(par().eigenPack); - - return in; + PerambulatorName = par().perambulator; + if( PerambulatorName.size() == 0 ) { + PerambulatorName = getName(); + PerambulatorName.append( "_peramb" ); + } + NoiseVectorName = par().noise; + if( NoiseVectorName.size() == 0 ) { + NoiseVectorName = PerambulatorName; + NoiseVectorName.append( "_noise" ); + } + LapEvecName = par().lapevec; + if( LapEvecName.size() == 0 ) { + LapEvecName = PerambulatorName; + LapEvecName.append( "_lapevec" ); + } + return { PerambulatorName, NoiseVectorName, LapEvecName }; } template std::vector TDistilVectors::getOutput(void) { - std::vector out = {getName() + "_rho", getName() + "_phi"}; - - return out; + SourceName = par().source; + SinkName = par().sink; + bMakeSource = ( SourceName.size() > 0 ); + bMakeSink = ( SinkName.size() > 0 ); + if( !bMakeSource && !bMakeSink ) { + SourceName = getName(); + SinkName = SourceName; + SourceName.append( "_rho" ); + SinkName.append( "_phi" ); + bMakeSource = true; + bMakeSink = true; + } + std::vector out; + if( bMakeSource ) + out.push_back( SourceName ); + if( bMakeSink ) + out.push_back( SinkName ); + return out; } // setup /////////////////////////////////////////////////////////////////////// template void TDistilVectors::setup(void) { - Cleanup(); - auto &noise = envGet(std::vector, par().noise); + Cleanup(); + auto &noise = envGet(std::vector, NoiseVectorName); + auto &perambulator = envGet(DistilPeramb, PerambulatorName); - int nnoise=par().nnoise; - int LI=par().LI; - int Ns=par().Ns; - int SI=par().SI; - int Nt_inv=par().Nt_inv; + // We expect the perambulator to have been created with these indices + std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + for(int i = 0; i < DistilPeramb::NumIndices; i++ ) + assert( sIndexNames[i] == perambulator.IndexNames[i] && "Perambulator indices bad" ); - envCreate(std::vector, getName() + "_rho", 1, - nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - envCreate(std::vector, getName() + "_phi", 1, - nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); + nnoise = static_cast( noise.size() ); + int LI=par().LI; + int Ns=par().Ns; + int SI=par().SI; + int Nt_inv=par().Nt_inv; + + if( bMakeSource ) + envCreate(std::vector, SourceName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); + if( bMakeSink ) + envCreate(std::vector, SinkName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); grid4d = env().getGrid(); std::vector latt_size = GridDefaultLatt(); @@ -158,10 +200,10 @@ void TDistilVectors::setup(void) simd_layout_3.push_back( 1 ); mpi_layout[Nd-1] = 1; grid3d = MakeLowerDimGrid(grid4d); - - + + envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); + //envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); @@ -183,26 +225,22 @@ void TDistilVectors::Cleanup(void) template void TDistilVectors::execute(void) { - - auto &noise = envGet(std::vector, par().noise); - auto &perambulator = envGet(Perambulator, par().perambulator); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - auto &rho = envGet(std::vector, getName() + "_rho"); - auto &phi = envGet(std::vector, getName() + "_phi"); - + auto &noise = envGet(std::vector, NoiseVectorName); + auto &perambulator = envGet(DistilPeramb, PerambulatorName); + auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); + envGetTmp(LatticeSpinColourVector, tmp2); - envGetTmp(LatticeColourVector, tmp_nospin); + //envGetTmp(LatticeColourVector, tmp_nospin); envGetTmp(LatticeSpinColourVector, tmp3d); envGetTmp(LatticeColourVector, tmp3d_nospin); envGetTmp(LatticeSpinColourVector, sink_tslice); envGetTmp(LatticeColourVector, evec3d); - - + + int Ntlocal = grid4d->LocalDimensions()[3]; int Ntfirst = grid4d->LocalStarts()[3]; - + int tsrc=par().tsrc; - int nnoise=par().nnoise; int LI=par().LI; int Ns=par().Ns; int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI @@ -212,28 +250,31 @@ void TDistilVectors::execute(void) int SI=par().SI; bool full_tdil=(TI==Nt); - + int vecindex; int t_inv; - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; - rho[vecindex] = zero; - tmp3d_nospin = zero; - for (int it = dt; it < Nt; it += TI){ - if (full_tdil) t_inv = tsrc; else t_inv = it; - if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += SI){ - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); - tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; - tmp3d=zero; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=zero; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); - rho[vecindex] += tmp2; + if( bMakeSource ) { + auto &rho = envGet(std::vector, SourceName); + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; + rho[vecindex] = zero; + tmp3d_nospin = zero; + for (int it = dt; it < Nt; it += TI){ + if (full_tdil) t_inv = tsrc; else t_inv = it; + if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += SI){ + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); + tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; + tmp3d=zero; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=zero; + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); + rho[vecindex] += tmp2; + } } } } @@ -242,31 +283,27 @@ void TDistilVectors::execute(void) } } } - - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; - phi[vecindex] = zero; - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - sink_tslice=zero; - for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); - sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); + if( bMakeSink ) { + auto &phi = envGet(std::vector, SinkName); + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; + phi[vecindex] = zero; + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + sink_tslice=zero; + for (int ivec = 0; ivec < nvec; ivec++) { + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); + } + InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); } - InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); } } } } } - - // TEST TO SEE WHETHER THIS MIGHT BE THE MEMORY LEAK - Cleanup(); - std::cout << "size rho" << rho.size() << std::endl; - std::cout << "size phi" << phi.size() << std::endl; - } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 9d1c6853..58a1ab51 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -74,7 +74,8 @@ struct LanczosParameters: Serializable { int, Np, int, MaxIt, //int, MinRes, - double, resid) + double, resid, + std::string, Log) // Any non-empty string will enable logging LanczosParameters() = default; template LanczosParameters(Reader& Reader){read(Reader,"Lanczos",*this);} }; @@ -133,14 +134,10 @@ MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); TLapEvec implementation ******************************************************************************/ -//constexpr char szEigenPackSuffix[] = "_eigenPack"; - // constructor ///////////////////////////////////////////////////////////////// template TLapEvec::TLapEvec(const std::string name) : gridLD{nullptr}, Module(name) { - //LOG(Message) << "TLapEvec constructor : TLapEvec::TLapEvec(const std::string name)" << std::endl; - //LOG(Message) << "this=" << this << ", gridLD=" << gridLD << std::endl; } // destructor ///////////////////////////////////////////////////////////////// @@ -155,7 +152,6 @@ template std::vector TLapEvec::getInput(void) { std::vector in = {par().gauge}; - return in; } @@ -163,7 +159,6 @@ template std::vector TLapEvec::getOutput(void) { std::vector out = {getName()}; // This is the higher dimensional eigenpack - return out; } @@ -209,11 +204,14 @@ void TLapEvec::Cleanup(void) template void TLapEvec::execute(void) { - LOG(Message) << "execute() : start for " << getName() << std::endl; - const ChebyshevParameters &ChebPar{par().Cheby}; const LanczosParameters &LPar{par().Lanczos}; const int &nvec{LPar.Nvec}; + + // Enable IRL logging if requested + if( LPar.Log.size() > 0 ) + GridLogIRL.Active(1); + //const bool exact_distillation{TI==Nt && LI==nvec}; //const bool full_tdil{TI==Nt}; //const int &Nt_inv{full_tdil ? 1 : TI}; @@ -243,21 +241,10 @@ void TLapEvec::execute(void) } LOG(Message) << "Smeared plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; - // For debugging only, write logging output to a local file - std::ofstream * ll = nullptr; - const int rank{gridHD->ThisRank()}; - if((0)) { // debug to a local log file - std::string filename{"Local_"}; - filename.append(std::to_string(rank)); - filename.append(".log"); - ll = new std::ofstream(filename); - } - //////////////////////////////////////////////////////////////////////// // Invert Peardon Nabla operator separately on each time-slice //////////////////////////////////////////////////////////////////////// - bool bReturnValue = true; auto & eig4d = envGet(DistilEP, getName() ); envGetTmp(std::vector, eig); // Eigenpack for each timeslice envGetTmp(LatticeGaugeField, UmuNoTime); // Gauge field without time dimension @@ -266,68 +253,45 @@ void TLapEvec::execute(void) const int Ntfirst{gridHD->LocalStarts()[Tdir]}; const char DefaultOperatorXml[] = "Michael"; const char DefaultsolverXml[] = "Felix"; - for(int t=Ntfirst;bReturnValue && t PeardonNabla(UmuNoTime); - std::cout << "Chebyshev preconditioning to order " << ChebPar.PolyOrder - << " with parameters (alpha,beta) = (" << ChebPar.alpha << "," << ChebPar.beta << ")" << std::endl; + LOG(Debug) << "Chebyshev preconditioning to order " << ChebPar.PolyOrder + << " with parameters (alpha,beta) = (" << ChebPar.alpha << "," << ChebPar.beta << ")" << std::endl; Chebyshev Cheb(ChebPar.alpha,ChebPar.beta,ChebPar.PolyOrder); //from Test_Cheby.cc - if ( ((0)) && Ntfirst == 0 && t==0) { - std::ofstream of("cheby_" + std::to_string(ChebPar.alpha) + "_" + std::to_string(ChebPar.beta) + "_" + std::to_string(ChebPar.PolyOrder)); - Cheb.csv(of); - } + //if( Ntfirst == 0 && t==0) { + //std::ofstream of("cheby_" + std::to_string(ChebPar.alpha) + "_" + std::to_string(ChebPar.beta) + "_" + std::to_string(ChebPar.PolyOrder)); + //Cheb.csv(of); + //} // Construct source vector according to Test_dwf_compressed_lanczos.cc - src=11.0; + src = 11.0; RealD nn = norm2(src); nn = Grid::sqrt(nn); src = src * (1.0/nn); - GridLogIRL.Active(1); LinOpPeardonNablaHerm PeardonNablaCheby(Cheb,PeardonNabla); - ImplicitlyRestartedLanczos IRL(PeardonNablaCheby,PeardonNabla,LPar.Nvec,LPar.Nk,LPar.Nk+LPar.Np,LPar.resid,LPar.MaxIt); + ImplicitlyRestartedLanczos + IRL(PeardonNablaCheby,PeardonNabla,LPar.Nvec,LPar.Nk,LPar.Nk+LPar.Np,LPar.resid,LPar.MaxIt); int Nconv = 0; - - if(ll) *ll << t << " : Before IRL.calc()" << std::endl; IRL.calc(eig[t].eval,eig[t].evec,src,Nconv); - if(ll) *ll << t << " : After IRL.calc()" << std::endl; - if( Nconv < LPar.Nvec ) { - bReturnValue = false; - if(ll) *ll << t << " : Convergence error : Only " << Nconv << " converged!" << std::endl; - } else { - if( Nconv > LPar.Nvec ) - eig[t].resize( LPar.Nvec, gridLD ); - std::cout << GridLogMessage << "Timeslice " << t << " has " << eig[t].eval.size() << " eigenvalues and " << eig[t].evec.size() << " eigenvectors." << std::endl; - - // Now rotate the eigenvectors into our phase convention - RotateEigen( eig[t].evec ); - - if((0)) { // Debugging only - // Write the eigenvectors and eigenvalues to disk - //std::cout << GridLogMessage << "Writing eigenvalues/vectors to " << pszEigenPack << std::endl; - eig[t].record.operatorXml = DefaultOperatorXml; - eig[t].record.solverXml = DefaultsolverXml; - eig[t].write("DistilEigen",false,t); - //std::cout << GridLogMessage << "Written eigenvectors" << std::endl; - } - } - std::cout << "T: " << t << " / " << Ntfirst + Ntlocal << std::endl; + assert( Nconv >= LPar.Nvec && "MDistil::LapEvec : Error - not enough eigenvectors converged" ); + if( Nconv > LPar.Nvec ) + eig[t].resize( LPar.Nvec, gridLD ); + RotateEigen( eig[t].evec ); // Rotate the eigenvectors into our phase convention + for (int i=0;i::execute(void) sEigenPackName.append("."); sEigenPackName.append(std::to_string(vm().getTrajectory())); eig4d.write(sEigenPackName,false); - - // Close the local debugging log file - if( ll ) { - *ll << " Returning " << bReturnValue << std::endl; - delete ll; - } - LOG(Message) << "execute() : end" << std::endl; } END_MODULE_NAMESPACE From d74d443d1b585ac21893bb522087580c2ec0e977 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 29 Apr 2019 22:18:29 +0100 Subject: [PATCH 219/347] Pre-release cleanup in progress --- Hadrons/Modules/MDistil/LapEvec.hpp | 13 ++++++++----- tests/hadrons/Test_distil.cc | 15 ++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 58a1ab51..c2139b0e 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -75,7 +75,7 @@ struct LanczosParameters: Serializable { int, MaxIt, //int, MinRes, double, resid, - std::string, Log) // Any non-empty string will enable logging + int, IRLLog) LanczosParameters() = default; template LanczosParameters(Reader& Reader){read(Reader,"Lanczos",*this);} }; @@ -208,10 +208,6 @@ void TLapEvec::execute(void) const LanczosParameters &LPar{par().Lanczos}; const int &nvec{LPar.Nvec}; - // Enable IRL logging if requested - if( LPar.Log.size() > 0 ) - GridLogIRL.Active(1); - //const bool exact_distillation{TI==Nt && LI==nvec}; //const bool full_tdil{TI==Nt}; //const int &Nt_inv{full_tdil ? 1 : TI}; @@ -224,6 +220,11 @@ void TLapEvec::execute(void) //else //assert(nnoise>1); + // Disable IRL logging if requested + LOG(Message) << "IRLLog=" << LPar.IRLLog << std::endl; + const int PreviousIRLLogState{GridLogIRL.isActive()}; + GridLogIRL.Active( LPar.IRLLog == 0 ? 0 : 1 ); + auto &Umu = envGet(GaugeField, par().gauge); envGetTmp(GaugeField, Umu_smear); @@ -302,6 +303,8 @@ void TLapEvec::execute(void) sEigenPackName.append("."); sEigenPackName.append(std::to_string(vm().getTrajectory())); eig4d.write(sEigenPackName,false); + + GridLogIRL.Active( PreviousIRLLogState ); } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index e4aa0b23..46e14985 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -114,6 +114,7 @@ void test_LapEvec(Application &application) p.Lanczos.Np = 2; p.Lanczos.MaxIt = 1000; p.Lanczos.resid = 1e-2; + p.Lanczos.IRLLog = 0; application.createModule("LapEvec",p); } @@ -190,9 +191,9 @@ void test_MultiPerambulators(Application &application) MDistil::DistilVectors::Par DistilVecPar; DistilVecPar.noise="Peramb5_noise"; DistilVecPar.perambulator="Peramb2"; - DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.lapevec ="LapEvec"; DistilVecPar.tsrc = 0; - DistilVecPar.nnoise = 1; + //DistilVecPar.nnoise = 1; DistilVecPar.LI=2; DistilVecPar.SI=4; DistilVecPar.TI=8; @@ -266,9 +267,9 @@ void test_DistilVectors(Application &application) DistilVecPar.noise="Peramb_noise"; //DistilVecPar.perambulator="Peramb_perambulator_light"; DistilVecPar.perambulator="Peramb"; - DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.lapevec="LapEvec"; DistilVecPar.tsrc = 0; - DistilVecPar.nnoise = 1; + //DistilVecPar.nnoise = 1; DistilVecPar.LI=5; DistilVecPar.SI=4; DistilVecPar.TI=8; @@ -313,9 +314,9 @@ void test_DistilVectorsS(Application &application) DistilVecPar.noise="PerambS_noise"; //DistilVecPar.perambulator="PerambS_perambulator_light"; DistilVecPar.perambulator="PerambS"; - DistilVecPar.eigenPack="LapEvec"; + DistilVecPar.lapevec="LapEvec"; DistilVecPar.tsrc = 0; - DistilVecPar.nnoise = 1; + //DistilVecPar.nnoise = 1; DistilVecPar.LI=5; DistilVecPar.SI=4; DistilVecPar.TI=32; @@ -1203,7 +1204,7 @@ int main(int argc, char *argv[]) LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; // execution - application.saveParameterFile("test_hadrons_distil.xml"); + application.saveParameterFile("test_distil.xml"); application.run(); // epilogue From e56ead55ef33f26e0903cc516a1d40177db11381 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 30 Apr 2019 14:41:48 +0100 Subject: [PATCH 220/347] WIP --- Hadrons/Distil.hpp | 110 +++++------ Hadrons/Modules/MDistil/DistilVectors.hpp | 32 ++- Hadrons/Modules/MDistil/Noises.hpp | 79 ++++---- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 49 +++-- Hadrons/Modules/MDistil/Perambulator.hpp | 78 ++++---- Hadrons/Modules/MIO/LoadPerambulator.hpp | 10 +- tests/hadrons/Test_distil.cc | 206 ++++++-------------- 7 files changed, 230 insertions(+), 334 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 22f4f6fb..3728f0d5 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -47,9 +47,7 @@ /****************************************************************************** A consistent set of cross-platform methods for big endian <-> host byte ordering I imagine this exists already? - - RELEASE NOTE: - + This can be removed once the (deprecated) NamedTensor::ReadBinary & WriteBinary methods are deleted ******************************************************************************/ #if defined(__linux__) @@ -81,7 +79,6 @@ /****************************************************************************** This potentially belongs in CartesianCommunicator - Turns out I don't actually need this when running inside hadrons ******************************************************************************/ BEGIN_MODULE_NAMESPACE(Grid) @@ -138,15 +135,13 @@ inline void SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Bu } /************************************************************************************* + + Not sure where the right home for this is? But presumably in Grid - -Grad^2 (Peardon, 2009, pg 2, equation 3) + -Grad^2 (Peardon, 2009, pg 2, equation 3, https://arxiv.org/abs/0905.2160) Field Type of field the operator will be applied to GaugeField Gauge field the operator will smear using - TODO CANDIDATE for integration into laplacian operator - should just require adding number of dimensions to act on to constructor, - where the default=all dimensions, but we could specify 3 spatial dimensions - *************************************************************************************/ template @@ -202,15 +197,6 @@ public: } }; - -class BFieldIO: Serializable{ -public: - using BaryonTensorSet = Eigen::Tensor; - GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, - BaryonTensorSet, BField - ); -}; - END_MODULE_NAMESPACE // Grid /****************************************************************************** @@ -221,29 +207,33 @@ BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) -typedef Grid::Hadrons::EigenPack DistilEP; -typedef std::vector > > DistilNoises; +using DistilEP = Grid::Hadrons::EigenPack; + +// Noise vector index order: nnoise, nt, nvec, ns +using NoiseTensor = Eigen::Tensor; + +struct DistilParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, TI, + int, LI, + int, nnoise, + int, tsrc, + int, SI, + int, Ns, + int, Nt_inv) + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} +}; + +class BFieldIO: Serializable{ +public: + using BaryonTensorSet = Eigen::Tensor; + GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, BaryonTensorSet, BField ); +}; /****************************************************************************** - Make a lower dimensional grid - ******************************************************************************/ - -inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) -{ - //LOG(Message) << "MakeLowerDimGrid() begin" << std::endl; - int nd{static_cast(gridHD->_ndimension)}; - std::vector latt_size = gridHD->_gdimensions; - latt_size[nd-1] = 1; - - std::vector simd_layout = GridDefaultSimd(nd-1, vComplex::Nsimd()); - simd_layout.push_back( 1 ); - - std::vector mpi_layout = gridHD->_processors; - mpi_layout[nd-1] = 1; - GridCartesian * gridLD = new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); - //LOG(Message) << "MakeLowerDimGrid() end" << std::endl; - return gridLD; -} + Default for distillation file operations. For now only used by NamedTensor +******************************************************************************/ #ifdef HAVE_HDF5 using Default_Reader = Grid::Hdf5Reader; @@ -348,6 +338,14 @@ template struct is_named_tensor : public std::fal template struct is_named_tensor> : public std::true_type {}; template struct is_named_tensor, T>::value>::type> : public std::true_type {}; +/****************************************************************************** + Perambulator object + Endian_Scalar_Size can be removed once (deprecated) NamedTensor::ReadBinary & WriteBinary methods deleted + ******************************************************************************/ + +template +using Perambulator = NamedTensor; + /****************************************************************************** Save NamedTensor binary format (NB: On-disk format is Big Endian) Assumes the Scalar_ objects are contiguous (no padding) @@ -586,31 +584,25 @@ void NamedTensor::read(const char * fi } /****************************************************************************** - Perambulator object + Make a lower dimensional grid in preparation for local slice operations ******************************************************************************/ -template -using Perambulator = NamedTensor; - -struct DistilParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, - int, TI, - int, LI, - int, nnoise, - int, tsrc, - int, SI, - int, Ns, - int, Nt, - int, Nt_inv) - DistilParameters() = default; - template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} -}; +inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) +{ + int nd{static_cast(gridHD->_ndimension)}; + std::vector latt_size = gridHD->_gdimensions; + latt_size[nd-1] = 1; + std::vector simd_layout = GridDefaultSimd(nd-1, vComplex::Nsimd()); + simd_layout.push_back( 1 ); + std::vector mpi_layout = gridHD->_processors; + mpi_layout[nd-1] = 1; + GridCartesian * gridLD = new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); + return gridLD; +} /************************************************************************************* - Rotate eigenvectors into our phase convention First component of first eigenvector is real and positive - *************************************************************************************/ inline void RotateEigen(std::vector & evec) @@ -642,7 +634,5 @@ inline void RotateEigen(std::vector & evec) } END_MODULE_NAMESPACE - END_HADRONS_NAMESPACE - #endif // Hadrons_MDistil_Distil_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 92347c50..4a76c68c 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -60,13 +60,11 @@ public: std::string, sink, bool, multiFile, int, tsrc, - //int, nnoise, int, LI, int, SI, int, TI, int, nvec, int, Ns, - int, Nt, int, Nt_inv); }; @@ -172,7 +170,7 @@ template void TDistilVectors::setup(void) { Cleanup(); - auto &noise = envGet(std::vector, NoiseVectorName); + auto &noise = envGet(NoiseTensor, NoiseVectorName); auto &perambulator = envGet(DistilPeramb, PerambulatorName); // We expect the perambulator to have been created with these indices @@ -180,7 +178,8 @@ void TDistilVectors::setup(void) for(int i = 0; i < DistilPeramb::NumIndices; i++ ) assert( sIndexNames[i] == perambulator.IndexNames[i] && "Perambulator indices bad" ); - nnoise = static_cast( noise.size() ); + nnoise = static_cast( noise.dimension(0) ); + LOG(Message) << "NoiseTensor has " << nnoise << " noise vectors" << std::endl; int LI=par().LI; int Ns=par().Ns; int SI=par().SI; @@ -225,7 +224,7 @@ void TDistilVectors::Cleanup(void) template void TDistilVectors::execute(void) { - auto &noise = envGet(std::vector, NoiseVectorName); + auto &noise = envGet(NoiseTensor, NoiseVectorName); auto &perambulator = envGet(DistilPeramb, PerambulatorName); auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); @@ -244,7 +243,7 @@ void TDistilVectors::execute(void) int LI=par().LI; int Ns=par().Ns; int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI - int Nt=par().Nt; + const int Nt{grid4d->GlobalDimensions()[Tdir]}; int TI=par().TI; int nvec=par().nvec; int SI=par().SI; @@ -255,10 +254,10 @@ void TDistilVectors::execute(void) int t_inv; if( bMakeSource ) { auto &rho = envGet(std::vector, SourceName); - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { + for( int inoise = 0; inoise < nnoise; inoise++ ) { + for( int dk = 0; dk < LI; dk++ ) { + for( int dt = 0; dt < Nt_inv; dt++ ) { + for( int ds = 0; ds < SI; ds++ ) { vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; rho[vecindex] = zero; tmp3d_nospin = zero; @@ -268,7 +267,8 @@ void TDistilVectors::execute(void) for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += SI){ ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); - tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; + //tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; + tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; @@ -285,10 +285,10 @@ void TDistilVectors::execute(void) } if( bMakeSink ) { auto &phi = envGet(std::vector, SinkName); - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { + for( int inoise = 0; inoise < nnoise; inoise++ ) { + for( int dk = 0; dk < LI; dk++ ) { + for( int dt = 0; dt < Nt_inv; dt++ ) { + for( int ds = 0; ds < SI; ds++ ) { vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; phi[vecindex] = zero; for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { @@ -307,7 +307,5 @@ void TDistilVectors::execute(void) } END_MODULE_NAMESPACE - END_HADRONS_NAMESPACE - #endif // Hadrons_MDistil_DistilVectors_hpp_ diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index c7b3ae17..fcb102f5 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -106,54 +106,53 @@ std::vector TNoises::getOutput(void) template void TNoises::setup(void) { - const DistilParameters & Distil{par().Distil}; - const int nvec{par().nvec}; - - envCreate(std::vector, getName(), 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); - + const int Nt{env().getGrid()->GlobalDimensions()[Tdir]}; + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; + //envCreate(std::vector, getName(), 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); + envCreate(NoiseTensor, getName(), 1, Distil.nnoise, Nt, nvec, Distil.Ns); } // execution /////////////////////////////////////////////////////////////////// template void TNoises::execute(void) { - const std::string &UniqueIdentifier{par().UniqueIdentifier}; - auto &noise = envGet(std::vector, getName()); - const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - const int nnoise{Distil.nnoise}; - const int Nt{Distil.Nt}; - const int Ns{Distil.Ns}; - const int TI{Distil.TI}; - const int LI{Distil.LI}; - const bool full_tdil{TI==Nt}; - const bool exact_distillation{full_tdil && LI==nvec}; - - GridSerialRNG sRNG; - sRNG.SeedUniqueString(UniqueIdentifier + std::to_string(vm().getTrajectory())); //maybe add more?? - Real rn; - - for (int inoise=0;inoise 0.5) ? -1 : 1; - } - } - } - } - } - + const std::string &UniqueIdentifier{par().UniqueIdentifier}; + auto &noise = envGet(NoiseTensor, getName()); + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; + const int nnoise{Distil.nnoise}; + const int Nt{env().getGrid()->GlobalDimensions()[Tdir]}; + const int Ns{Distil.Ns}; + const int TI{Distil.TI}; + const int LI{Distil.LI}; + const bool full_tdil{TI==Nt}; + const bool exact_distillation{full_tdil && LI==nvec}; + + GridSerialRNG sRNG; + sRNG.SeedUniqueString(UniqueIdentifier + std::to_string(vm().getTrajectory())); //maybe add more?? + Real rn; + + for( int inoise = 0; inoise < nnoise; inoise++ ) { + for( int t = 0; t < Nt; t++ ) { + for( int ivec = 0; ivec < nvec; ivec++ ) { + for( int is = 0; is < Ns; is++ ) { + if( exact_distillation ) + //noise[inoise + nnoise*(t + Nt*(ivec+nvec*is))] = 1.; + noise(inoise, t, ivec, is) = 1.; + else{ + random(sRNG,rn); + // We could use a greater number of complex roots of unity + // ... but this seems to work well + //noise[inoise + nnoise*(t + Nt*(ivec+nvec*is))] = (rn > 0.5) ? -1 : 1; + noise(inoise, t, ivec, is) = (rn > 0.5) ? -1 : 1; + } + } + } + } + } } END_MODULE_NAMESPACE - END_HADRONS_NAMESPACE - #endif // Hadrons_MDistil_Noises_hpp_ diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 14774a2d..e3160ee5 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -154,6 +154,7 @@ void TPerambFromSolve::setup(void) grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); + const int Nt{grid4d->GlobalDimensions()[Tdir]}; const int nvec_reduced{par().nvec_reduced}; @@ -161,9 +162,9 @@ void TPerambFromSolve::setup(void) //envCreate(Perambulator, getName(), 1, // sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(Perambulator, getName(), 1, - sIndexNames,Distil.Nt,nvec_reduced,LI_reduced,Distil.nnoise,Distil.Nt_inv,Distil.SI); + sIndexNames,Nt,nvec_reduced,LI_reduced,Distil.nnoise,Distil.Nt_inv,Distil.SI); envCreate(std::vector, getName() + "_noise", 1, - nvec*Distil.Ns*Distil.Nt*Distil.nnoise); + nvec*Distil.Ns*Nt*Distil.nnoise); envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); @@ -187,34 +188,32 @@ void TPerambFromSolve::Cleanup(void) template void TPerambFromSolve::execute(void) { - const int nvec_reduced{par().nvec_reduced}; - const int LI_reduced{par().LI_reduced}; - const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - const int TI{Distil.TI}; - const int SI{Distil.SI}; - const int nnoise{Distil.nnoise}; - const int Nt{Distil.Nt}; - const int Nt_inv{Distil.Nt_inv}; - const int tsrc{Distil.tsrc}; - const int Ns{Distil.Ns}; - const bool full_tdil{TI==Nt}; - const bool exact_distillation{full_tdil && LI==nvec}; - auto &perambulator = envGet(Perambulator, - getName()); - auto &solve = envGet(std::vector, par().solve); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + GridCartesian * grid4d = env().getGrid(); + const int Nt{grid4d->GlobalDimensions()[Tdir]}; + const int Ntlocal{grid4d->LocalDimensions()[3]}; + const int Ntfirst{grid4d->LocalStarts()[3]}; + + const int nvec_reduced{par().nvec_reduced}; + const int LI_reduced{par().LI_reduced}; + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; + const int LI{Distil.LI}; + const int TI{Distil.TI}; + const int SI{Distil.SI}; + const int nnoise{Distil.nnoise}; + const int Nt_inv{Distil.Nt_inv}; + const int tsrc{Distil.tsrc}; + const int Ns{Distil.Ns}; + const bool full_tdil{TI==Nt}; + const bool exact_distillation{full_tdil && LI==nvec}; + auto &perambulator = envGet(Perambulator, getName()); + auto &solve = envGet(std::vector, par().solve); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); envGetTmp(LatticeColourVector, result_nospin); envGetTmp(LatticeColourVector, result_3d); envGetTmp(LatticeColourVector, evec3d); - GridCartesian * grid4d = env().getGrid(); - - int Ntlocal = grid4d->LocalDimensions()[3]; - int Ntfirst = grid4d->LocalStarts()[3]; - for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI_reduced; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 06a5f36d..202efae0 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -134,38 +134,36 @@ std::vector TPerambulator::getOutput(void) template void TPerambulator::setup(void) { - Cleanup(); - - const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - const int nnoise{Distil.nnoise}; - const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; - const int Ns{Distil.Ns}; - std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - - envCreate(Perambulator, getName(), 1, - sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); - envCreate(std::vector, getName() + "_unsmeared_sink", 1, + Cleanup(); + grid4d = env().getGrid(); + grid3d = MakeLowerDimGrid(grid4d); + const int Nt{grid4d->GlobalDimensions()[Tdir]}; + const int nvec{par().nvec}; + const DistilParameters & Distil{par().Distil}; + const int LI{Distil.LI}; + const int nnoise{Distil.nnoise}; + const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; + const int Ns{Distil.Ns}; + std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + + envCreate(Perambulator, getName(), 1, + sIndexNames,Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); + envCreate(std::vector, getName() + "_unsmeared_sink", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); - - grid4d = env().getGrid(); - grid3d = MakeLowerDimGrid(grid4d);//new GridCartesian(latt_size,simd_layout_3,mpi_layout,*grid4d); - - envTmpLat(LatticeSpinColourVector, "dist_source"); - envTmpLat(LatticeSpinColourVector, "tmp2"); - envTmpLat(LatticeSpinColourVector, "result"); - envTmpLat(LatticeColourVector, "result_nospin"); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); - - Ls_ = env().getObjectLs(par().solver); - envTmpLat(FermionField, "v4dtmp"); - envTmpLat(FermionField, "v5dtmp", Ls_); - envTmpLat(FermionField, "v5dtmp_sol", Ls_); - + + envTmpLat(LatticeSpinColourVector, "dist_source"); + envTmpLat(LatticeSpinColourVector, "tmp2"); + envTmpLat(LatticeSpinColourVector, "result"); + envTmpLat(LatticeColourVector, "result_nospin"); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + + Ls_ = env().getObjectLs(par().solver); + envTmpLat(FermionField, "v4dtmp"); + envTmpLat(FermionField, "v5dtmp", Ls_); + envTmpLat(FermionField, "v5dtmp_sol", Ls_); } // clean up any temporaries created by setup (that aren't stored in the environment) @@ -183,30 +181,29 @@ void TPerambulator::Cleanup(void) template void TPerambulator::execute(void) { + const int Nt{grid4d->GlobalDimensions()[Tdir]}; const int nvec{par().nvec}; const DistilParameters & Distil{par().Distil}; const int LI{Distil.LI}; const int SI{Distil.SI}; const int TI{Distil.TI}; const int nnoise{Distil.nnoise}; - const int Nt{Distil.Nt}; - const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; const int tsrc{Distil.tsrc}; const int Ns{Distil.Ns}; - + const bool full_tdil{TI==Nt}; + const bool exact_distillation{full_tdil && LI==nvec}; + const int Nt_inv{full_tdil ? 1 : TI}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; + auto &solver=envGet(Solver, par().solver); auto &mat = solver.getFMat(); envGetTmp(FermionField, v4dtmp); envGetTmp(FermionField, v5dtmp); envGetTmp(FermionField, v5dtmp_sol); - - const bool full_tdil{TI==Nt}; - const bool exact_distillation{full_tdil && LI==nvec}; - const std::string &UniqueIdentifier{par().UniqueIdentifier}; - auto &noise = envGet(std::vector, par().noise); + //auto &noise = envGet(std::vector, par().noise); + auto &noise = envGet(NoiseTensor, par().noise); auto &perambulator = envGet(Perambulator, getName()); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); @@ -245,7 +242,8 @@ void TPerambulator::execute(void) for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += SI){ ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); - tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; + //tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; + tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); tmp3d=zero; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=zero; diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index b7f4e696..6f7c5283 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -101,17 +101,13 @@ std::vector TLoadPerambulator::getOutput(void) template void TLoadPerambulator::setup(void) { + GridCartesian * grid4d = env().getGrid(); + const int Nt{grid4d->GlobalDimensions()[Tdir]}; const int nvec{par().nvec}; const MDistil::DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - const int nnoise{Distil.nnoise}; - const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; - const int Ns{Distil.Ns}; std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - envCreate(MDistil::Perambulator, getName(), 1, - sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); - + sIndexNames,Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); } // execution /////////////////////////////////////////////////////////////////// diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index 46e14985..a791858e 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -68,25 +68,22 @@ void test_Global(Application &application) void test_SolverS(Application &application) { - std::string boundary = "1 1 1 -1"; - - - MAction::DWF::Par actionPar; - actionPar.gauge = "gauge"; - actionPar.Ls = 16; - actionPar.M5 = 1.8; - actionPar.mass = 0.005; - actionPar.boundary = boundary; - actionPar.twist = "0. 0. 0. 0."; - application.createModule("DWF_s", actionPar); - - - MSolver::RBPrecCG::Par solverPar; - solverPar.action = "DWF_s"; - solverPar.residual = 1.0e-7; - solverPar.maxIteration = 10000; - application.createModule("CG_s", solverPar); + std::string boundary = "1 1 1 -1"; + MAction::DWF::Par actionPar; + actionPar.gauge = "gauge"; + actionPar.Ls = 16; + actionPar.M5 = 1.8; + actionPar.mass = 0.005; + actionPar.boundary = boundary; + actionPar.twist = "0. 0. 0. 0."; + application.createModule("DWF_s", actionPar); + MSolver::RBPrecCG::Par solverPar; + solverPar.action = "DWF_s"; + solverPar.residual = 1.0e-2; + solverPar.maxIteration = 10000; + application.createModule("CG_s", solverPar); } + ///////////////////////////////////////////////////////////// // Test creation of laplacian eigenvectors ///////////////////////////////////////////////////////////// @@ -138,7 +135,6 @@ void test_Perambulators(Application &application) PerambPar.Distil.TI=8; PerambPar.nvec=5; PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; //PerambPar.Solver.mass=0.005; //PerambPar.Solver.M5=1.8; @@ -166,7 +162,6 @@ void test_MultiPerambulators(Application &application) PerambPar.Distil.TI=8; PerambPar.nvec=5; PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; application.createModule("Peramb5",PerambPar); MDistil::PerambFromSolve::Par SolvePar; @@ -181,7 +176,6 @@ void test_MultiPerambulators(Application &application) SolvePar.nvec_reduced=2; SolvePar.LI_reduced=2; SolvePar.Distil.Ns=4; - SolvePar.Distil.Nt=8; SolvePar.Distil.Nt_inv=1; application.createModule("Peramb2",SolvePar); SolvePar.PerambFileName="Peramb3"; @@ -199,7 +193,6 @@ void test_MultiPerambulators(Application &application) DistilVecPar.TI=8; DistilVecPar.nvec=2; DistilVecPar.Ns=4; - DistilVecPar.Nt=8; DistilVecPar.Nt_inv=1; application.createModule("DistilVecs2",DistilVecPar); DistilVecPar.perambulator="Peramb3"; @@ -215,7 +208,7 @@ void test_MultiPerambulators(Application &application) A2AMesonFieldPar.left="DistilVecs2_rho"; A2AMesonFieldPar.right="DistilVecs2_rho"; A2AMesonFieldPar.output="MesonSinksRho2"; - A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.gammas="Identity"; A2AMesonFieldPar.mom={"0 0 0"}; A2AMesonFieldPar.cacheBlock=2; A2AMesonFieldPar.block=4; @@ -247,11 +240,13 @@ void test_Noises(Application &application) { MDistil::NoisesPar NoisePar; NoisePar.UniqueIdentifier = "full_dilution"; NoisePar.nvec = 5; + NoisePar.Distil.TI = 8; + NoisePar.Distil.SI = 4; NoisePar.Distil.LI = 5; NoisePar.Distil.nnoise = 1; NoisePar.Distil.Ns = 4; - NoisePar.Distil.Nt = 8; - NoisePar.Distil.Nt = 1; + NoisePar.Distil.Nt_inv = 1; + NoisePar.Distil.tsrc = 0; application.createModule("Peramb_noise",NoisePar); } @@ -269,13 +264,11 @@ void test_DistilVectors(Application &application) DistilVecPar.perambulator="Peramb"; DistilVecPar.lapevec="LapEvec"; DistilVecPar.tsrc = 0; - //DistilVecPar.nnoise = 1; DistilVecPar.LI=5; DistilVecPar.SI=4; DistilVecPar.TI=8; DistilVecPar.nvec=5; DistilVecPar.Ns=4; - DistilVecPar.Nt=8; DistilVecPar.Nt_inv=1; application.createModule("DistilVecs",DistilVecPar); } @@ -294,7 +287,6 @@ void test_PerambulatorsS(Application &application) PerambPar.Distil.TI=8; PerambPar.nvec=5; PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt=8; PerambPar.Distil.Nt_inv=1; //PerambPar.Solver.mass=0.005; //strange mass??? //PerambPar.Solver.M5=1.8; @@ -322,7 +314,6 @@ void test_DistilVectorsS(Application &application) DistilVecPar.TI=32; DistilVecPar.nvec=5; DistilVecPar.Ns=4; - DistilVecPar.Nt=8; DistilVecPar.Nt_inv=1; application.createModule("DistilVecsS",DistilVecPar); } @@ -338,12 +329,40 @@ void test_MesonSink(Application &application) A2AMesonFieldPar.left="g5phi"; A2AMesonFieldPar.right="Peramb_unsmeared_sink"; A2AMesonFieldPar.output="DistilFields"; - A2AMesonFieldPar.gammas="all"; + A2AMesonFieldPar.gammas="Identity"; A2AMesonFieldPar.mom={"0 0 0"}; A2AMesonFieldPar.cacheBlock=2; A2AMesonFieldPar.block=4; application.createModule("DistilMesonSink",A2AMesonFieldPar); } +///////////////////////////////////////////////////////////// +// MesonFields +///////////////////////////////////////////////////////////// + +void test_MesonField(Application &application, const char * pszFileSuffix, + const char * pszObjectLeft = nullptr, const char * pszObjectRight = nullptr ) +{ + // DistilVectors parameters + if( pszObjectLeft == nullptr ) + pszObjectLeft = pszFileSuffix; + if( pszObjectRight == nullptr ) + pszObjectRight = pszObjectLeft; + MContraction::A2AMesonField::Par A2AMesonFieldPar; + A2AMesonFieldPar.left="DistilVecs"; + A2AMesonFieldPar.right=A2AMesonFieldPar.left; + A2AMesonFieldPar.left.append( pszObjectLeft ); + A2AMesonFieldPar.right.append( pszObjectRight ); + A2AMesonFieldPar.output="MesonSinks"; + A2AMesonFieldPar.output.append( pszFileSuffix ); + A2AMesonFieldPar.gammas="Identity"; + A2AMesonFieldPar.mom={"0 0 0"}; + A2AMesonFieldPar.cacheBlock=2; + A2AMesonFieldPar.block=4; + std::string sObjectName{"DistilMesonField"}; + sObjectName.append( pszFileSuffix ); + application.createModule(sObjectName, A2AMesonFieldPar); +} + ///////////////////////////////////////////////////////////// // g5*unsmeared ///////////////////////////////////////////////////////////// @@ -360,86 +379,11 @@ void test_g5_sinks(Application &application) g5_multiplyPar.Nt_inv=1; application.createModule("g5phi",g5_multiplyPar); } -#endif -///////////////////////////////////////////////////////////// -// MesonFields -///////////////////////////////////////////////////////////// - -void test_MesonFieldSL(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecsS_phi"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_phi"; - A2AMesonFieldPar.output="DistilFieldsS"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldS",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// MesonFields - phiphi -///////////////////////////////////////////////////////////// - -void test_MesonField(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_phi"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_phi"; - A2AMesonFieldPar.output="MesonSinksPhi"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonField",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// MesonFields - rhorho -///////////////////////////////////////////////////////////// - -void test_MesonFieldRho(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_rho"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.output="MesonSinksRho"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldRho",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// MesonFields - rhorhoall -///////////////////////////////////////////////////////////// - -void test_MesonFieldRhoAll(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - //A2AMesonFieldPar.left="DistilVecs_rho_all_tsrc"; - //A2AMesonFieldPar.right="DistilVecs_rho_all_tsrc"; - A2AMesonFieldPar.left="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.output="MesonSinksRhoAll"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldRhoAll",A2AMesonFieldPar); -} ///////////////////////////////////////////////////////////// // BaryonFields - phiphiphi - efficient ///////////////////////////////////////////////////////////// -#ifdef DISTIL_PRE_RELEASE void test_BaryonFieldPhi2(Application &application) { // DistilVectors parameters @@ -577,7 +521,6 @@ void test_PerambulatorsSolve(Application &application) PerambFromSolvePar.Distil.TI=8; PerambFromSolvePar.nvec=5; PerambFromSolvePar.Distil.Ns=4; - PerambFromSolvePar.Distil.Nt=8; PerambFromSolvePar.Distil.Nt_inv=1; application.createModule("PerambAslashS",PerambFromSolvePar); } @@ -602,25 +545,6 @@ void test_DistilVectorsAslashSeq(Application &application) DistilSinkPar.Nt_inv=1; application.createModule("DistilVecsAslashSeq",DistilSinkPar); } -///////////////////////////////////////////////////////////// -// MesonFields - aslaaaash -///////////////////////////////////////////////////////////// - -void test_MesonFieldAslashSeq(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecsAslashSeq"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecsAslashSeq"; - A2AMesonFieldPar.output="MesonSinksAslashSeq"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldAslashSeq",A2AMesonFieldPar); -} - bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { if( bGobbleWhiteSpace ) @@ -774,7 +698,7 @@ bool DebugEigenTest() std::array as={"Alpha", "Beta", "Gamma"}; MyTensor x(as, 2,1,4); DebugShowTensor(x, "x"); - x.WriteBinary(pszTestFileName); + x.write(pszTestFileName); // Test initialisation of an array of strings for( auto a : as ) std::cout << a << std::endl; @@ -787,7 +711,7 @@ bool DebugEigenTest() // Now see whether we can read a tensor back std::array Names2={"Alpha", "Gamma", "Delta"}; MyTensor y(Names2, 2,4,1); - y.ReadBinary(pszTestFileName); + y.read(pszTestFileName); DebugShowTensor(y, "y"); // Now see whether we can read a tensor back from an hdf5 file @@ -1037,15 +961,6 @@ int main(int argc, char *argv[]) { #ifdef DEBUG // Debug only - test of Eigen::Tensor - std::cout << "sizeof(int) = " << sizeof(int) - << ", sizeof(long) = " << sizeof(long) - << ", sizeof(size_t) = " << sizeof(size_t) - << ", sizeof(std::size_t) = " << sizeof(std::size_t) - << ", sizeof(std::streamsize) = " << sizeof(std::streamsize) - << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) - << ", sizeof(hsize_t) = " << sizeof(hsize_t) - << ", sizeof(unsigned long long) = " << sizeof(unsigned long long) - << std::endl; //if( DebugEigenTest() ) return 0; //if(DebugGridTensorTest()) return 0; //if(ConvertPeramb("PerambL_100_tsrc0.3000","PerambL_100_tsrc0.3000")) return 0; @@ -1105,21 +1020,21 @@ int main(int argc, char *argv[]) test_LapEvec( application ); test_Perambulators( application ); break; - case 3: // 3 + default: // 3 test_Global( application ); test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); break; - default: // 4 + case 4: test_Global( application ); test_SolverS( application ); test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); - test_MesonField( application ); - test_MesonFieldRhoAll( application ); + test_MesonField( application, "Phi", "_phi" ); + test_MesonField( application, "Rho", "_rho" ); break; case 5: // 3 test_Global( application ); @@ -1129,7 +1044,8 @@ int main(int argc, char *argv[]) test_DistilVectors( application ); test_PerambulatorsS( application ); test_DistilVectorsS( application ); - test_MesonFieldSL( application ); + test_MesonField( application, "SPhi", "S_phi" ); + test_MesonField( application, "SRho", "S_rho" ); break; #ifdef DISTIL_PRE_RELEASE case 6: // 3 @@ -1156,7 +1072,8 @@ int main(int argc, char *argv[]) test_LapEvec( application ); test_Perambulators( application ); test_DistilVectors( application ); - test_MesonField( application ); + test_MesonField( application, "Phi", "_phi" ); + test_MesonField( application, "Rho", "_rho" ); break; #ifdef DISTIL_PRE_RELEASE case 9: // 3 @@ -1192,7 +1109,7 @@ int main(int argc, char *argv[]) test_AslashSeq( application ); test_PerambulatorsSolve( application ); test_DistilVectorsAslashSeq( application ); - test_MesonFieldAslashSeq( application ); + test_MesonField( application, "AslashSeq" ); break; case 13: test_Global( application ); @@ -1201,10 +1118,9 @@ int main(int argc, char *argv[]) test_MultiPerambulators( application ); break; } - LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; - // execution application.saveParameterFile("test_distil.xml"); + LOG(Warning) << "These parameters are designed to run on a laptop usid --grid 4.4.4.8" << std::endl; application.run(); // epilogue From 334f29becbb7ac67af3af91c19e584ade1c6a2b3 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 30 Apr 2019 23:53:57 +0100 Subject: [PATCH 221/347] Fairly close to ready for release. Felix and I to review, then submit for release --- Hadrons/Distil.hpp | 62 +++-- Hadrons/Modules.hpp | 2 - Hadrons/Modules/MDistil/DistilSink.cc | 36 --- Hadrons/Modules/MDistil/DistilSink.hpp | 236 ---------------- Hadrons/Modules/MDistil/DistilSource.cc | 36 --- Hadrons/Modules/MDistil/DistilSource.hpp | 250 ----------------- Hadrons/Modules/MDistil/DistilVectors.hpp | 75 +++-- Hadrons/Modules/MDistil/LapEvec.hpp | 77 ++--- Hadrons/Modules/MDistil/Noises.hpp | 43 +-- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 94 ++----- Hadrons/Modules/MDistil/Perambulator.hpp | 105 +++---- Hadrons/Modules/MIO/LoadPerambulator.hpp | 12 +- Hadrons/modules.inc | 4 - tests/hadrons/Test_distil.cc | 294 +++++++------------- 14 files changed, 290 insertions(+), 1036 deletions(-) delete mode 100644 Hadrons/Modules/MDistil/DistilSink.cc delete mode 100644 Hadrons/Modules/MDistil/DistilSink.hpp delete mode 100644 Hadrons/Modules/MDistil/DistilSource.cc delete mode 100644 Hadrons/Modules/MDistil/DistilSource.hpp diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 3728f0d5..58bfc532 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -33,16 +33,10 @@ #include #include #include - -/****************************************************************************** - Needed to make sure envCreate() (see Hadrons) work with specialisations - with more than one parameter, eg obj - I imagine this exists already? - ******************************************************************************/ - -#ifndef COMMA -#define COMMA , -#endif +#include +#include +#include +#include /****************************************************************************** A consistent set of cross-platform methods for big endian <-> host byte ordering @@ -207,24 +201,53 @@ BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) -using DistilEP = Grid::Hadrons::EigenPack; +using LapEvecs = Grid::Hadrons::EigenPack; // Noise vector index order: nnoise, nt, nvec, ns using NoiseTensor = Eigen::Tensor; struct DistilParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, - int, TI, - int, LI, int, nnoise, int, tsrc, - int, SI, - int, Ns, - int, Nt_inv) + std::string, TI, + std::string, LI, + std::string, SI ) DistilParameters() = default; template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} + + // Numeric parameter is allowed to be empty (in which case it = Default), + // but assert during setup() if specified but not numeric + + static int ParameterDefault( const std::string & s, int Default, bool bCalledFromSetup ) + { + int i = Default; + if( s.length() > 0 ) { + std::istringstream ss( s ); + ss >> i; + if( bCalledFromSetup ) + assert( !ss.fail() && "Parameter should either be empty or integer" ); + } + return i; + } + + int getTI( const Environment & env, bool bCalledFromSetup ) const { + return ParameterDefault( TI, env.getDim(Tdir), bCalledFromSetup ); } }; +#define DISTIL_PARAMETERS_DEFINE( inSetup ) \ +const int Nt{env().getDim(Tdir)}; \ +const int nvec{par().nvec}; \ +const int Ns{Grid::QCD::Ns}; \ +const int nnoise{par().Distil.nnoise}; \ +const int tsrc{par().Distil.tsrc}; \ +const int TI{par().Distil.getTI(env(), inSetup)}; \ +const int LI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.LI, nvec, inSetup)}; \ +const int SI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.SI, Ns, inSetup)}; \ +const bool full_tdil{ TI == Nt }; \ +const bool exact_distillation{ full_tdil && LI == nvec }; \ +const int Nt_inv{ full_tdil ? 1 : TI } + class BFieldIO: Serializable{ public: using BaryonTensorSet = Eigen::Tensor; @@ -299,7 +322,7 @@ public: // Construct a named tensor explicitly specifying size of each dimension template - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) : tensor(firstDimension, otherDimensions...), IndexNames{NumIndices} { // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. @@ -343,8 +366,9 @@ template struct is_named_tensor -using Perambulator = NamedTensor; +//template +using Perambulator = NamedTensor; +static const std::array PerambIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; /****************************************************************************** Save NamedTensor binary format (NB: On-disk format is Big Endian) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 0ffe6b6d..774eafa4 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -39,10 +39,8 @@ #include #include #include -#include #include #include -#include #include #include #include diff --git a/Hadrons/Modules/MDistil/DistilSink.cc b/Hadrons/Modules/MDistil/DistilSink.cc deleted file mode 100644 index c438dd8b..00000000 --- a/Hadrons/Modules/MDistil/DistilSink.cc +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/DistilSink.cc - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 - -using namespace Grid; -using namespace Hadrons; -using namespace MDistil; - -template class Grid::Hadrons::MDistil::TDistilSink; diff --git a/Hadrons/Modules/MDistil/DistilSink.hpp b/Hadrons/Modules/MDistil/DistilSink.hpp deleted file mode 100644 index 9435bcc4..00000000 --- a/Hadrons/Modules/MDistil/DistilSink.hpp +++ /dev/null @@ -1,236 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/DistilSink.hpp - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 */ - -#ifndef Hadrons_MDistil_DistilSink_hpp_ -#define Hadrons_MDistil_DistilSink_hpp_ - -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation -#include - -BEGIN_HADRONS_NAMESPACE - - -/****************************************************************************** - * DistilSink * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) - -class DistilSinkPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilSinkPar, - std::string, perambulator, - std::string, eigenPack, - bool, multiFile, - int, tsrc, - int, nnoise, - int, LI, - int, SI, - int, TI, - int, nvec, - int, Ns, - int, Nt, - int, Nt_inv); -}; - -template -class TDistilSink: public Module -{ -public: - FERM_TYPE_ALIASES(FImpl,); -public: - // constructor - TDistilSink(const std::string name); - // destructor - virtual ~TDistilSink(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); -protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) -protected: - virtual void Cleanup(void); -}; - -MODULE_REGISTER_TMP(DistilSink, TDistilSink, MDistil); - -/****************************************************************************** - * TDistilSink implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -TDistilSink::TDistilSink(const std::string name) -: grid3d{nullptr}, grid4d{nullptr}, Module(name) -{} -// destructor -template -TDistilSink::~TDistilSink(void) -{ - Cleanup(); -}; - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TDistilSink::getInput(void) -{ - std::vector in; - - in.push_back(par().perambulator); - in.push_back(par().eigenPack); - - return in; -} - -template -std::vector TDistilSink::getOutput(void) -{ - std::vector out = {getName()}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void TDistilSink::setup(void) -{ - Cleanup(); - int nnoise=par().nnoise; - int LI=par().LI; - int Ns=par().Ns; - int SI=par().SI; - int Nt_inv=par().Nt_inv; - - envCreate(std::vector, getName(), 1, - nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - - grid4d = env().getGrid(); - std::vector latt_size = GridDefaultLatt(); - std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); - std::vector mpi_layout = GridDefaultMpi(); - std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); - latt_size[Nd-1] = 1; - simd_layout_3.push_back( 1 ); - mpi_layout[Nd-1] = 1; - grid3d = MakeLowerDimGrid(grid4d); - - - envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); -} - -// clean up any temporaries created by setup (that aren't stored in the environment) -template -void TDistilSink::Cleanup(void) -{ - if( grid3d != nullptr ) { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; -} - -// execution /////////////////////////////////////////////////////////////////// -template -void TDistilSink::execute(void) -{ - - auto &perambulator = envGet(Perambulator, par().perambulator); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - auto &phi = envGet(std::vector, getName()); - - envGetTmp(LatticeSpinColourVector, tmp2); - envGetTmp(LatticeColourVector, tmp_nospin); - envGetTmp(LatticeSpinColourVector, tmp3d); - envGetTmp(LatticeColourVector, tmp3d_nospin); - envGetTmp(LatticeSpinColourVector, sink_tslice); - envGetTmp(LatticeColourVector, evec3d); - - int Ntlocal = grid4d->LocalDimensions()[3]; - int Ntfirst = grid4d->LocalStarts()[3]; - - int tsrc=par().tsrc; - int nnoise=par().nnoise; - int LI=par().LI; - int SI=par().SI; - int Ns=par().Ns; - int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI - int Nt=par().Nt; - int TI=par().TI; - int nvec=par().nvec; - - bool full_tdil=(TI==Nt); - - int vecindex; - int t_inv; - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; - phi[vecindex] = zero; - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - sink_tslice=zero; - for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); - sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); - } - InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); - } - } - } - } - } - // TEST TO SEE WHETHER THIS MIGHT BE THE MEMORY LEAK - Cleanup(); - -} - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MDistil_DistilSink_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilSource.cc b/Hadrons/Modules/MDistil/DistilSource.cc deleted file mode 100644 index 3438b8ea..00000000 --- a/Hadrons/Modules/MDistil/DistilSource.cc +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/DistilSource.cc - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 - -using namespace Grid; -using namespace Hadrons; -using namespace MDistil; - -template class Grid::Hadrons::MDistil::TDistilSource; diff --git a/Hadrons/Modules/MDistil/DistilSource.hpp b/Hadrons/Modules/MDistil/DistilSource.hpp deleted file mode 100644 index da0c5028..00000000 --- a/Hadrons/Modules/MDistil/DistilSource.hpp +++ /dev/null @@ -1,250 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/DistilSource.hpp - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 */ - -#ifndef Hadrons_MDistil_DistilSource_hpp_ -#define Hadrons_MDistil_DistilSource_hpp_ - -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation -#include - -BEGIN_HADRONS_NAMESPACE - - -/****************************************************************************** - * DistilSource * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) - -class DistilSourcePar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilSourcePar, - std::string, noise, - std::string, eigenPack, - bool, multiFile, - int, tsrc, - int, nnoise, - int, LI, - int, SI, - int, TI, - int, nvec, - int, Ns, - int, Nt, - int, Nt_inv); -}; - -template -class TDistilSource: public Module -{ -public: - FERM_TYPE_ALIASES(FImpl,); -public: - // constructor - TDistilSource(const std::string name); - // destructor - virtual ~TDistilSource(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); -protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) -protected: - virtual void Cleanup(void); -}; - -MODULE_REGISTER_TMP(DistilSource, TDistilSource, MDistil); - -/****************************************************************************** - * TDistilSource implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -TDistilSource::TDistilSource(const std::string name) -: grid3d{nullptr}, grid4d{nullptr}, Module(name) -{} -// destructor -template -TDistilSource::~TDistilSource(void) -{ - Cleanup(); -}; - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TDistilSource::getInput(void) -{ - std::vector in; - - in.push_back(par().noise); - in.push_back(par().eigenPack); - - return in; -} - -template -std::vector TDistilSource::getOutput(void) -{ - std::vector out = {getName()}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void TDistilSource::setup(void) -{ - Cleanup(); - auto &noise = envGet(std::vector, par().noise); - - int nnoise=par().nnoise; - int LI=par().LI; - int Ns=par().Ns; - int SI=par().SI; - int Nt_inv=par().Nt_inv; - - envCreate(std::vector, getName(), 1, - nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - - grid4d = env().getGrid(); - std::vector latt_size = GridDefaultLatt(); - std::vector simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); - std::vector mpi_layout = GridDefaultMpi(); - std::vector simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); - latt_size[Nd-1] = 1; - simd_layout_3.push_back( 1 ); - mpi_layout[Nd-1] = 1; - grid3d = MakeLowerDimGrid(grid4d); - - - envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); -} - -// clean up any temporaries created by setup (that aren't stored in the environment) -template -void TDistilSource::Cleanup(void) -{ - if( grid3d != nullptr ) { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; -} - -// execution /////////////////////////////////////////////////////////////////// -template -void TDistilSource::execute(void) -{ - - auto &noise = envGet(std::vector, par().noise); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - auto &rho = envGet(std::vector, getName()); - - envGetTmp(LatticeSpinColourVector, tmp2); - envGetTmp(LatticeColourVector, tmp_nospin); - envGetTmp(LatticeSpinColourVector, tmp3d); - envGetTmp(LatticeColourVector, tmp3d_nospin); - envGetTmp(LatticeSpinColourVector, sink_tslice); - envGetTmp(LatticeColourVector, evec3d); - - - int Ntlocal = grid4d->LocalDimensions()[3]; - int Ntfirst = grid4d->LocalStarts()[3]; - - int tsrc=par().tsrc; - int nnoise=par().nnoise; - int LI=par().LI; - int Ns=par().Ns; - int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI - int Nt=par().Nt; - int TI=par().TI; - int nvec=par().nvec; - int SI=par().SI; - - bool full_tdil=(TI==Nt); - - int vecindex; - int t_inv; - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; - rho[vecindex] = zero; - tmp3d_nospin = zero; - for (int it = dt; it < Nt; it += TI){ - if (full_tdil) t_inv = tsrc; else t_inv = it; - if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += SI){ - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); - tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; - tmp3d=zero; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=zero; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Grid::QCD::Tdir); - rho[vecindex] += tmp2; - } - } - } - } - } - } - } - } - - - // TEST TO SEE WHETHER THIS MIGHT BE THE MEMORY LEAK - Cleanup(); - -} - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MDistil_DistilSource_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 4a76c68c..a9f6ad14 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -60,12 +60,8 @@ public: std::string, sink, bool, multiFile, int, tsrc, - int, LI, - int, SI, - int, TI, - int, nvec, - int, Ns, - int, Nt_inv); + std::string, nvec, + std::string, TI) }; template @@ -73,8 +69,6 @@ class TDistilVectors: public Module { public: FERM_TYPE_ALIASES(FImpl,); - // This is the type of perambulator I expect - using DistilPeramb = Perambulator; // constructor TDistilVectors(const std::string name); // destructor @@ -100,7 +94,6 @@ public: bool bMakeSink; std::string SourceName; std::string SinkName; - int nnoise; }; MODULE_REGISTER_TMP(DistilVectors, TDistilVectors, MDistil); @@ -171,19 +164,24 @@ void TDistilVectors::setup(void) { Cleanup(); auto &noise = envGet(NoiseTensor, NoiseVectorName); - auto &perambulator = envGet(DistilPeramb, PerambulatorName); + auto &perambulator = envGet(Perambulator, PerambulatorName); // We expect the perambulator to have been created with these indices - std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - for(int i = 0; i < DistilPeramb::NumIndices; i++ ) - assert( sIndexNames[i] == perambulator.IndexNames[i] && "Perambulator indices bad" ); + for(int i = 0; i < Perambulator::NumIndices; i++ ) + assert( PerambIndexNames[i] == perambulator.IndexNames[i] && "Perambulator indices bad" ); - nnoise = static_cast( noise.dimension(0) ); - LOG(Message) << "NoiseTensor has " << nnoise << " noise vectors" << std::endl; - int LI=par().LI; - int Ns=par().Ns; - int SI=par().SI; - int Nt_inv=par().Nt_inv; + const int Nt{ env().getDim(Tdir) }; + assert( Nt == static_cast( perambulator.tensor.dimension(0) ) && "Perambulator time dimensionality bad" ); + const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, true) }; + const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; + const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; + const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; + const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; + assert( nnoise >= static_cast( noise.dimension(0) ) && "Not enough noise vectors for perambulator" ); + // Nvec defaults to what's in the perambulator unless overriden + const int nvec_per{ static_cast( perambulator.tensor.dimension(1) ) }; + const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, nvec_per, true) }; + assert( nvec <= nvec_per && "Not enough distillation sub-space vectors" ); if( bMakeSource ) envCreate(std::vector, SourceName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); @@ -225,31 +223,30 @@ template void TDistilVectors::execute(void) { auto &noise = envGet(NoiseTensor, NoiseVectorName); - auto &perambulator = envGet(DistilPeramb, PerambulatorName); + auto &perambulator = envGet(Perambulator, PerambulatorName); auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); envGetTmp(LatticeSpinColourVector, tmp2); - //envGetTmp(LatticeColourVector, tmp_nospin); envGetTmp(LatticeSpinColourVector, tmp3d); - envGetTmp(LatticeColourVector, tmp3d_nospin); + envGetTmp(LatticeColourVector, tmp3d_nospin); envGetTmp(LatticeSpinColourVector, sink_tslice); - envGetTmp(LatticeColourVector, evec3d); - - - int Ntlocal = grid4d->LocalDimensions()[3]; - int Ntfirst = grid4d->LocalStarts()[3]; - - int tsrc=par().tsrc; - int LI=par().LI; - int Ns=par().Ns; - int Nt_inv=par().Nt_inv; // TODO: No input, but define through Nt, TI - const int Nt{grid4d->GlobalDimensions()[Tdir]}; - int TI=par().TI; - int nvec=par().nvec; - int SI=par().SI; - - bool full_tdil=(TI==Nt); - + envGetTmp(LatticeColourVector, evec3d); + + const int Ntlocal{ grid4d->LocalDimensions()[3] }; + const int Ntfirst{ grid4d->LocalStarts()[3] }; + + const int Ns{ Grid::QCD::Ns }; + const int Nt{ env().getDim(Tdir) }; + const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false ) }; + const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; + const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; + const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; + const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; + // Nvec defaults to what's in the perambulator unless overriden + const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, static_cast( perambulator.tensor.dimension(1) ), false)}; + const int tsrc{ par().tsrc }; + const bool full_tdil{ TI==Nt }; + int vecindex; int t_inv; if( bMakeSource ) { diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index c2139b0e..1b603ef6 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -51,7 +51,7 @@ BEGIN_MODULE_NAMESPACE(MDistil) struct StoutParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(StoutParameters, int, steps, - double, parm) // TODO: change name of this to rho + double, rho) // TODO: change name of this to rho StoutParameters() = default; template StoutParameters(Reader& Reader){read(Reader,"StoutSmearing",*this);} }; @@ -67,13 +67,10 @@ struct ChebyshevParameters: Serializable { struct LanczosParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters, - //int, Nstart, int, Nvec, int, Nk, - //int, Nm, // Not currently used int, Np, int, MaxIt, - //int, MinRes, double, resid, int, IRLLog) LanczosParameters() = default; @@ -82,19 +79,12 @@ struct LanczosParameters: Serializable { // These are the actual parameters passed to the module during construction -class LapEvecPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar, - std::string, gauge, - // std::string, ConfigFileDir, - // std::string, ConfigFileName, - //,std::string, EigenPackName - StoutParameters, Stout +struct LapEvecPar: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar + ,std::string, gauge + ,StoutParameters, Stout ,ChebyshevParameters, Cheby - ,LanczosParameters, Lanczos - //,DistilParameters, Distil - )//,SolverParameters, Solver) + ,LanczosParameters, Lanczos) }; /****************************************************************************** @@ -123,7 +113,7 @@ protected: // These variables are created in setup() and freed in Cleanup() GridCartesian * gridLD; // Owned by me, so I must delete it GridCartesian * gridHD; // Owned by environment (so I won't delete it) - int Nx, Ny, Nz, Nt; + std::string sGaugeName; protected: virtual void Cleanup(void); }; @@ -151,8 +141,12 @@ TLapEvec::~TLapEvec() template std::vector TLapEvec::getInput(void) { - std::vector in = {par().gauge}; - return in; + sGaugeName = par().gauge; + if( sGaugeName.size() == 0 ) { + sGaugeName = getName(); + sGaugeName.append( "_gauge" ); + } + return std::vector{ sGaugeName }; } template @@ -170,19 +164,16 @@ void TLapEvec::setup(void) Environment & e{env()}; gridHD = e.getGrid(); gridLD = MakeLowerDimGrid( gridHD ); - Nx = gridHD->_fdimensions[Xdir]; - Ny = gridHD->_fdimensions[Ydir]; - Nz = gridHD->_fdimensions[Zdir]; - Nt = gridHD->_fdimensions[Tdir]; + const int Nt{e.getDim(Tdir)}; // Temporaries - //envTmpLat(GaugeField, "Umu"); + envTmpLat(GaugeField, "Umu"); envTmpLat(GaugeField, "Umu_stout"); envTmpLat(GaugeField, "Umu_smear"); envTmp(LatticeGaugeField, "UmuNoTime",1,LatticeGaugeField(gridLD)); envTmp(LatticeColourVector, "src",1,LatticeColourVector(gridLD)); - envTmp(std::vector, "eig",1,std::vector(Nt)); + envTmp(std::vector, "eig",1,std::vector(Nt)); // Output objects - envCreate(DistilEP, getName(), 1, par().Lanczos.Nvec, gridHD ); + envCreate(LapEvecs, getName(), 1, par().Lanczos.Nvec, gridHD ); } // clean up any temporaries created by setup (that aren't stored in the environment) @@ -206,35 +197,21 @@ void TLapEvec::execute(void) { const ChebyshevParameters &ChebPar{par().Cheby}; const LanczosParameters &LPar{par().Lanczos}; - const int &nvec{LPar.Nvec}; - - //const bool exact_distillation{TI==Nt && LI==nvec}; - //const bool full_tdil{TI==Nt}; - //const int &Nt_inv{full_tdil ? 1 : TI}; - - // Assertions on the parameters we read - //assert(TI>1); - //assert(LI>1); - //if(exact_distillation) - //assert(nnoise==1); - //else - //assert(nnoise>1); // Disable IRL logging if requested LOG(Message) << "IRLLog=" << LPar.IRLLog << std::endl; const int PreviousIRLLogState{GridLogIRL.isActive()}; GridLogIRL.Active( LPar.IRLLog == 0 ? 0 : 1 ); - - auto &Umu = envGet(GaugeField, par().gauge); - envGetTmp(GaugeField, Umu_smear); // Stout smearing - Umu_smear = Umu; - LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu) << std::endl; + envGetTmp(GaugeField, Umu_smear); { + auto &Umu = envGet(GaugeField, sGaugeName); + LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu) << std::endl; + Umu_smear = Umu; const StoutParameters &Stout{par().Stout}; envGetTmp(GaugeField, Umu_stout); - Smear_Stout LS(Stout.parm, Tdir); // spatial smearing only + Smear_Stout LS(Stout.rho, Tdir); // spatial smearing only for (int i = 0; i < Stout.steps; i++) { LS.smear(Umu_stout, Umu_smear); Umu_smear = Umu_stout; @@ -246,8 +223,8 @@ void TLapEvec::execute(void) // Invert Peardon Nabla operator separately on each time-slice //////////////////////////////////////////////////////////////////////// - auto & eig4d = envGet(DistilEP, getName() ); - envGetTmp(std::vector, eig); // Eigenpack for each timeslice + auto & eig4d = envGet(LapEvecs, getName() ); + envGetTmp(std::vector, eig); // Eigenpack for each timeslice envGetTmp(LatticeGaugeField, UmuNoTime); // Gauge field without time dimension envGetTmp(LatticeColourVector, src); const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; @@ -267,12 +244,6 @@ void TLapEvec::execute(void) << " with parameters (alpha,beta) = (" << ChebPar.alpha << "," << ChebPar.beta << ")" << std::endl; Chebyshev Cheb(ChebPar.alpha,ChebPar.beta,ChebPar.PolyOrder); - //from Test_Cheby.cc - //if( Ntfirst == 0 && t==0) { - //std::ofstream of("cheby_" + std::to_string(ChebPar.alpha) + "_" + std::to_string(ChebPar.beta) + "_" + std::to_string(ChebPar.PolyOrder)); - //Cheb.csv(of); - //} - // Construct source vector according to Test_dwf_compressed_lanczos.cc src = 11.0; RealD nn = norm2(src); diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index fcb102f5..324318ec 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -52,9 +52,11 @@ class NoisesPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(NoisesPar, - std::string, UniqueIdentifier, + int, nnoise, int, nvec, - DistilParameters, Distil); + std::string, UniqueIdentifier, + std::string, TI, + std::string, LI) }; template @@ -103,36 +105,41 @@ std::vector TNoises::getOutput(void) } // setup /////////////////////////////////////////////////////////////////////// + template void TNoises::setup(void) { - const int Nt{env().getGrid()->GlobalDimensions()[Tdir]}; + const int Nt{env().getDim(Tdir)}; + const int Ns{Grid::QCD::Ns}; + const int nnoise{par().nnoise}; const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - //envCreate(std::vector, getName(), 1, nvec*Distil.Ns*Distil.Nt*Distil.nnoise); - envCreate(NoiseTensor, getName(), 1, Distil.nnoise, Nt, nvec, Distil.Ns); + const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, true) }; + const int LI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI, nvec, true) }; + envCreate(NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); } // execution /////////////////////////////////////////////////////////////////// template void TNoises::execute(void) { - const std::string &UniqueIdentifier{par().UniqueIdentifier}; - auto &noise = envGet(NoiseTensor, getName()); + const int Nt{env().getDim(Tdir)}; + const int Ns{Grid::QCD::Ns}; + const int nnoise{par().nnoise}; const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - const int nnoise{Distil.nnoise}; - const int Nt{env().getGrid()->GlobalDimensions()[Tdir]}; - const int Ns{Distil.Ns}; - const int TI{Distil.TI}; - const int LI{Distil.LI}; - const bool full_tdil{TI==Nt}; - const bool exact_distillation{full_tdil && LI==nvec}; + const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false) }; + const int LI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI, nvec, false) }; + const bool full_tdil{ TI == Nt }; \ + const bool exact_distillation{ full_tdil && LI == nvec }; \ + std::string UniqueIdentifier{par().UniqueIdentifier}; + if( UniqueIdentifier.length() == 0 ) { + UniqueIdentifier = getName(); + } + UniqueIdentifier.append( std::to_string( vm().getTrajectory() ) ); //maybe add more?? GridSerialRNG sRNG; - sRNG.SeedUniqueString(UniqueIdentifier + std::to_string(vm().getTrajectory())); //maybe add more?? + sRNG.SeedUniqueString(UniqueIdentifier); Real rn; - + auto &noise = envGet(NoiseTensor, getName()); for( int inoise = 0; inoise < nnoise; inoise++ ) { for( int t = 0; t < Nt; t++ ) { for( int ivec = 0; ivec < nvec; ivec++ ) { diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index e3160ee5..7c3f2729 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -47,21 +47,6 @@ BEGIN_HADRONS_NAMESPACE * PerambFromSolve * ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) -/* -struct DistilParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, - int, TI, - int, LI, - int, nnoise, - int, tsrc, - int, SI, - int, Ns, - int, Nt, - int, Nt_inv) - DistilParameters() = default; - template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} -}; -*/ class PerambFromSolvePar: Serializable { @@ -122,91 +107,54 @@ TPerambFromSolve::~TPerambFromSolve(void) template std::vector TPerambFromSolve::getInput(void) { - std::vector in; - - in.push_back(par().solve); - in.push_back(par().eigenPack); - - return in; + return std::vector{ par().solve, par().eigenPack }; } template std::vector TPerambFromSolve::getOutput(void) { - std::vector out = {getName()}; - - return out; + return std::vector{ getName() }; } // setup /////////////////////////////////////////////////////////////////////// template void TPerambFromSolve::setup(void) { - Cleanup(); - - const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - const int nnoise{Distil.nnoise}; - const int Nt_inv{Distil.Nt_inv}; - const int Ns{Distil.Ns}; - std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - - grid4d = env().getGrid(); - grid3d = MakeLowerDimGrid(grid4d); - const int Nt{grid4d->GlobalDimensions()[Tdir]}; - - - const int nvec_reduced{par().nvec_reduced}; - const int LI_reduced{par().LI_reduced}; - //envCreate(Perambulator, getName(), 1, - // sIndexNames,Distil.Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); - envCreate(Perambulator, getName(), 1, - sIndexNames,Nt,nvec_reduced,LI_reduced,Distil.nnoise,Distil.Nt_inv,Distil.SI); - envCreate(std::vector, getName() + "_noise", 1, - nvec*Distil.Ns*Nt*Distil.nnoise); - - envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); - envTmpLat(LatticeColourVector, "result_nospin"); - - + Cleanup(); + DISTIL_PARAMETERS_DEFINE( true ); + grid4d = env().getGrid(); + grid3d = MakeLowerDimGrid(grid4d); + const int nvec_reduced{par().nvec_reduced}; + const int LI_reduced{par().LI_reduced}; + //std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + envCreate(Perambulator, getName(), 1, PerambIndexNames,Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); + envCreate(NoiseTensor, getName() + "_noise", 1, nnoise, Nt, nvec, Ns ); + envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + envTmpLat(LatticeColourVector, "result_nospin"); } template void TPerambFromSolve::Cleanup(void) { - if( grid3d != nullptr ) { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; + if( grid3d != nullptr ) { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; } - // execution /////////////////////////////////////////////////////////////////// template void TPerambFromSolve::execute(void) { GridCartesian * grid4d = env().getGrid(); - const int Nt{grid4d->GlobalDimensions()[Tdir]}; const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - const int nvec_reduced{par().nvec_reduced}; const int LI_reduced{par().LI_reduced}; - const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - const int TI{Distil.TI}; - const int SI{Distil.SI}; - const int nnoise{Distil.nnoise}; - const int Nt_inv{Distil.Nt_inv}; - const int tsrc{Distil.tsrc}; - const int Ns{Distil.Ns}; - const bool full_tdil{TI==Nt}; - const bool exact_distillation{full_tdil && LI==nvec}; - auto &perambulator = envGet(Perambulator, getName()); + DISTIL_PARAMETERS_DEFINE( false ); + auto &perambulator = envGet(Perambulator, getName()); auto &solve = envGet(std::vector, par().solve); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 202efae0..325a2386 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -30,14 +30,6 @@ #ifndef Hadrons_MDistil_Perambulator_hpp_ #define Hadrons_MDistil_Perambulator_hpp_ -#include -#include -#include -#include -#include -#include -#include - // These are members of Distillation #include @@ -53,41 +45,42 @@ class PerambulatorPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambulatorPar, - std::string, eigenPack, + std::string, lapevec, + std::string, solver, std::string, noise, std::string, PerambFileName, //stem!!! - std::string, UniqueIdentifier, bool, multiFile, int, nvec, - DistilParameters, Distil, - std::string, solver); + DistilParameters, Distil); }; template class TPerambulator: public Module { public: - FERM_TYPE_ALIASES(FImpl,); - SOLVER_TYPE_ALIASES(FImpl,); - // constructor - TPerambulator(const std::string name); - // destructor - virtual ~TPerambulator(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); + FERM_TYPE_ALIASES(FImpl,); + SOLVER_TYPE_ALIASES(FImpl,); + // constructor + TPerambulator(const std::string name); + // destructor + virtual ~TPerambulator(void); + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) + virtual void Cleanup(void); protected: - virtual void Cleanup(void); -private: - unsigned int Ls_; + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) + // Other members + unsigned int Ls_; + std::string sLapEvecName; + std::string sNoiseName; }; // Can't name the module Perambulator, because that's what we've called the object @@ -113,21 +106,17 @@ TPerambulator::~TPerambulator(void) template std::vector TPerambulator::getInput(void) { - std::vector in; - - in.push_back(par().eigenPack); - in.push_back(par().solver); - in.push_back(par().noise); - - return in; + sLapEvecName = par().lapevec; + sNoiseName = par().noise; + if( sNoiseName.length() == 0 ) + sNoiseName = getName() + "_noise"; + return {sLapEvecName, par().solver, sNoiseName }; } template std::vector TPerambulator::getOutput(void) { - std::vector out = {getName(),getName() + "_unsmeared_sink"}; - - return out; + return {getName(), getName() + "_unsmeared_sink"}; } // setup /////////////////////////////////////////////////////////////////////// @@ -137,17 +126,9 @@ void TPerambulator::setup(void) Cleanup(); grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); - const int Nt{grid4d->GlobalDimensions()[Tdir]}; - const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - const int nnoise{Distil.nnoise}; - const int Nt_inv{Distil.Nt_inv}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; - const int Ns{Distil.Ns}; - std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + DISTIL_PARAMETERS_DEFINE( true ); - envCreate(Perambulator, getName(), 1, - sIndexNames,Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); + envCreate(Perambulator, getName(), 1, PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); envCreate(std::vector, getName() + "_unsmeared_sink", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); @@ -181,18 +162,7 @@ void TPerambulator::Cleanup(void) template void TPerambulator::execute(void) { - const int Nt{grid4d->GlobalDimensions()[Tdir]}; - const int nvec{par().nvec}; - const DistilParameters & Distil{par().Distil}; - const int LI{Distil.LI}; - const int SI{Distil.SI}; - const int TI{Distil.TI}; - const int nnoise{Distil.nnoise}; - const int tsrc{Distil.tsrc}; - const int Ns{Distil.Ns}; - const bool full_tdil{TI==Nt}; - const bool exact_distillation{full_tdil && LI==nvec}; - const int Nt_inv{full_tdil ? 1 : TI}; // TODO: PROBABLY BETTER: if (full_tdil) Nt_inv=1; else Nt_inv = TI; + DISTIL_PARAMETERS_DEFINE( false ); auto &solver=envGet(Solver, par().solver); auto &mat = solver.getFMat(); @@ -200,12 +170,9 @@ void TPerambulator::execute(void) envGetTmp(FermionField, v5dtmp); envGetTmp(FermionField, v5dtmp_sol); - const std::string &UniqueIdentifier{par().UniqueIdentifier}; - - //auto &noise = envGet(std::vector, par().noise); - auto &noise = envGet(NoiseTensor, par().noise); - auto &perambulator = envGet(Perambulator, getName()); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + auto &noise = envGet(NoiseTensor, sNoiseName); + auto &perambulator = envGet(Perambulator, getName()); + auto &epack = envGet(LapEvecs, sLapEvecName); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 6f7c5283..156fb5dc 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -101,20 +101,16 @@ std::vector TLoadPerambulator::getOutput(void) template void TLoadPerambulator::setup(void) { - GridCartesian * grid4d = env().getGrid(); - const int Nt{grid4d->GlobalDimensions()[Tdir]}; - const int nvec{par().nvec}; - const MDistil::DistilParameters & Distil{par().Distil}; - std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - envCreate(MDistil::Perambulator, getName(), 1, - sIndexNames,Nt,nvec,Distil.LI,Distil.nnoise,Distil.Nt_inv,Distil.SI); + DISTIL_PARAMETERS_DEFINE( true ); + //std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + envCreate(MDistil::Perambulator, getName(), 1, MDistil::PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); } // execution /////////////////////////////////////////////////////////////////// template void TLoadPerambulator::execute(void) { - auto &perambulator = envGet(MDistil::Perambulator, getName()); + auto &perambulator = envGet(MDistil::Perambulator, getName()); const std::string sPerambName{par().PerambFileName + "." + std::to_string(vm().getTrajectory())}; perambulator.read(sPerambName.c_str()); } diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index d87aad20..c13c18a2 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -43,9 +43,7 @@ modules_cc =\ Modules/MDistil/BC2.cc \ Modules/MDistil/Noises.cc \ Modules/MDistil/BContraction.cc \ - Modules/MDistil/DistilSource.cc \ Modules/MDistil/LapEvec.cc \ - Modules/MDistil/DistilSink.cc \ Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/Perambulator.cc \ Modules/MDistil/PerambFromSolve.cc \ @@ -118,10 +116,8 @@ modules_hpp =\ Modules/MScalar/FreeProp.hpp \ Modules/MScalar/Scalar.hpp \ Modules/MScalar/ChargedProp.hpp \ - Modules/MDistil/DistilSource.hpp \ Modules/MDistil/LapEvec.hpp \ Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/DistilSink.hpp \ Modules/MDistil/Noises.hpp \ Modules/MDistil/BC2.hpp \ Modules/MDistil/Perambulator.hpp \ diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index a791858e..bc4578f1 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -70,7 +70,7 @@ void test_SolverS(Application &application) { std::string boundary = "1 1 1 -1"; MAction::DWF::Par actionPar; - actionPar.gauge = "gauge"; + actionPar.gauge = "LapEvec_gauge"; actionPar.Ls = 16; actionPar.M5 = 1.8; actionPar.mass = 0.005; @@ -90,19 +90,16 @@ void test_SolverS(Application &application) void test_LapEvec(Application &application) { - const char szGaugeName[] = "gauge"; + const char szModuleName[] = "LapEvec"; // gauge field - application.createModule(szGaugeName); + std::string sGaugeName{szModuleName}; + sGaugeName.append( "_gauge" ); + application.createModule(sGaugeName); // Now make an instance of the LapEvec object MDistil::LapEvecPar p; - p.gauge = szGaugeName; - //p.EigenPackName = "ePack"; - //p.Distil.TI = 8; - //p.Distil.LI = 3; - //p.Distil.Nnoise = 2; - //p.Distil.tSrc = 0; + //p.gauge = sGaugeName; // This is the default for LapEvec, so no need to be explicit p.Stout.steps = 3; - p.Stout.parm = 0.2; + p.Stout.rho = 0.2; p.Cheby.PolyOrder = 11; p.Cheby.alpha = 0.3; p.Cheby.beta = 12.5; @@ -112,58 +109,90 @@ void test_LapEvec(Application &application) p.Lanczos.MaxIt = 1000; p.Lanczos.resid = 1e-2; p.Lanczos.IRLLog = 0; - application.createModule("LapEvec",p); + application.createModule(szModuleName,p); +} + +///////////////////////////////////////////////////////////// +// Noises +///////////////////////////////////////////////////////////// + +std::string test_Noises(Application &application, const std::string &sNoiseBaseName ) { + // DistilVectors parameters + MDistil::NoisesPar NoisePar; + NoisePar.nnoise = 1; + NoisePar.nvec = 5; + std::string sNoiseName{sNoiseBaseName + "_noise"}; + application.createModule(sNoiseName,NoisePar); + return sNoiseName; } ///////////////////////////////////////////////////////////// // Perambulators ///////////////////////////////////////////////////////////// -void test_Perambulators(Application &application) +void test_Perambulators( Application &application, const char * pszSuffix = nullptr ) { + std::string sModuleName{ "Peramb" }; + if( pszSuffix ) + sModuleName.append( pszSuffix ); + test_Noises(application, sModuleName); // Perambulator parameters MDistil::Peramb::Par PerambPar; - PerambPar.eigenPack="LapEvec"; - PerambPar.noise="Peramb_noise"; - PerambPar.PerambFileName="peramb.bin"; - PerambPar.UniqueIdentifier="full_dilution"; + PerambPar.lapevec = "LapEvec"; + PerambPar.PerambFileName = sModuleName + ".bin"; PerambPar.solver="CG_s"; PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=5; - PerambPar.Distil.SI=4; - PerambPar.Distil.TI=8; PerambPar.nvec=5; - PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt_inv=1; - //PerambPar.Solver.mass=0.005; - //PerambPar.Solver.M5=1.8; - //PerambPar.Ls=16; - //PerambPar.Solver.CGPrecision=1e-8; - //PerambPar.Solver.MaxIterations=10000; - application.createModule("Peramb",PerambPar); + application.createModule( sModuleName, PerambPar ); } + +///////////////////////////////////////////////////////////// +// DistilVectors +///////////////////////////////////////////////////////////// + +#define TEST_DISTIL_VECTORS_COMMON \ +std::string sModuleName{"DistilVecs"}; \ +if( pszSuffix ) \ + sModuleName.append( pszSuffix ); \ +std::string sPerambName{"Peramb"}; \ +if( pszSuffix ) \ + sPerambName.append( pszSuffix ); \ +MDistil::DistilVectors::Par DistilVecPar; \ +DistilVecPar.noise = sPerambName + "_noise"; \ +DistilVecPar.perambulator = sPerambName; \ +DistilVecPar.lapevec = "LapEvec"; \ +DistilVecPar.tsrc = 0; \ +if( pszNvec ) \ + DistilVecPar.nvec = pszNvec + +#define TEST_DISTIL_VECTORS_COMMON_END \ +application.createModule(sModuleName,DistilVecPar) + +void test_DistilVectors(Application &application, const char * pszSuffix = nullptr, const char * pszNvec = nullptr ) +{ + TEST_DISTIL_VECTORS_COMMON; + TEST_DISTIL_VECTORS_COMMON_END; +} + +void test_DistilVectorsSS(Application &application, const char * pszSink, const char * pszSource, + const char * pszSuffix = nullptr, const char * pszNvec = nullptr ) +{ + TEST_DISTIL_VECTORS_COMMON; + if( pszSink ) + DistilVecPar.sink = pszSink; + if( pszSource ) + DistilVecPar.source = pszSource; + TEST_DISTIL_VECTORS_COMMON_END; +} + ///////////////////////////////////////////////////////////// // Multiple Perambulators ///////////////////////////////////////////////////////////// void test_MultiPerambulators(Application &application) { - // Perambulator parameters - MDistil::Peramb::Par PerambPar; - PerambPar.eigenPack="LapEvec"; - PerambPar.UniqueIdentifier="full_dilution"; - PerambPar.PerambFileName="Peramb5"; - PerambPar.solver="CG_s"; - PerambPar.Distil.tsrc = 0; - PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=5; - PerambPar.Distil.SI=4; - PerambPar.Distil.TI=8; - PerambPar.nvec=5; - PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt_inv=1; - application.createModule("Peramb5",PerambPar); + test_Perambulators( application, "5" ); MDistil::PerambFromSolve::Par SolvePar; SolvePar.eigenPack="LapEvec"; SolvePar.PerambFileName="Peramb2"; @@ -175,35 +204,16 @@ void test_MultiPerambulators(Application &application) SolvePar.nvec=5; SolvePar.nvec_reduced=2; SolvePar.LI_reduced=2; - SolvePar.Distil.Ns=4; - SolvePar.Distil.Nt_inv=1; application.createModule("Peramb2",SolvePar); SolvePar.PerambFileName="Peramb3"; SolvePar.nvec_reduced=3; SolvePar.LI_reduced=3; application.createModule("Peramb3",SolvePar); - MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="Peramb5_noise"; - DistilVecPar.perambulator="Peramb2"; - DistilVecPar.lapevec ="LapEvec"; - DistilVecPar.tsrc = 0; - //DistilVecPar.nnoise = 1; - DistilVecPar.LI=2; - DistilVecPar.SI=4; - DistilVecPar.TI=8; - DistilVecPar.nvec=2; - DistilVecPar.Ns=4; - DistilVecPar.Nt_inv=1; - application.createModule("DistilVecs2",DistilVecPar); - DistilVecPar.perambulator="Peramb3"; - DistilVecPar.LI=3; - DistilVecPar.nvec=3; - application.createModule("DistilVecs3",DistilVecPar); - //DistilVecPar.perambulator="Peramb5_perambulator_light"; - DistilVecPar.perambulator="Peramb5"; - DistilVecPar.LI=5; - DistilVecPar.nvec=5; - application.createModule("DistilVecs5",DistilVecPar); + + test_DistilVectors( application, "2", "2" ); + test_DistilVectors( application, "3", "3" ); + test_DistilVectors( application, "5", "5" ); + MContraction::A2AMesonField::Par A2AMesonFieldPar; A2AMesonFieldPar.left="DistilVecs2_rho"; A2AMesonFieldPar.right="DistilVecs2_rho"; @@ -235,88 +245,6 @@ void test_MultiPerambulators(Application &application) application.createModule("DistilMesonFieldPhi5",A2AMesonFieldPar); } -void test_Noises(Application &application) { - // DistilVectors parameters - MDistil::NoisesPar NoisePar; - NoisePar.UniqueIdentifier = "full_dilution"; - NoisePar.nvec = 5; - NoisePar.Distil.TI = 8; - NoisePar.Distil.SI = 4; - NoisePar.Distil.LI = 5; - NoisePar.Distil.nnoise = 1; - NoisePar.Distil.Ns = 4; - NoisePar.Distil.Nt_inv = 1; - NoisePar.Distil.tsrc = 0; - application.createModule("Peramb_noise",NoisePar); -} - -///////////////////////////////////////////////////////////// -// DistilVectors -///////////////////////////////////////////////////////////// - -void test_DistilVectors(Application &application) -{ - test_Noises(application); - // DistilVectors parameters - MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="Peramb_noise"; - //DistilVecPar.perambulator="Peramb_perambulator_light"; - DistilVecPar.perambulator="Peramb"; - DistilVecPar.lapevec="LapEvec"; - DistilVecPar.tsrc = 0; - DistilVecPar.LI=5; - DistilVecPar.SI=4; - DistilVecPar.TI=8; - DistilVecPar.nvec=5; - DistilVecPar.Ns=4; - DistilVecPar.Nt_inv=1; - application.createModule("DistilVecs",DistilVecPar); -} -void test_PerambulatorsS(Application &application) -{ - // Perambulator parameters - MDistil::Peramb::Par PerambPar; - PerambPar.eigenPack="LapEvec"; - PerambPar.PerambFileName="perambS.bin"; - PerambPar.UniqueIdentifier="full_dilution"; - PerambPar.solver="CG_s"; - PerambPar.Distil.tsrc = 0; - PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=5; - PerambPar.Distil.SI=4; - PerambPar.Distil.TI=8; - PerambPar.nvec=5; - PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt_inv=1; - //PerambPar.Solver.mass=0.005; //strange mass??? - //PerambPar.Solver.M5=1.8; - //PerambPar.Ls=16; - //PerambPar.Solver.CGPrecision=1e-8; - //PerambPar.Solver.MaxIterations=10000; - application.createModule("PerambS",PerambPar); -} -///////////////////////////////////////////////////////////// -// DistilVectors -///////////////////////////////////////////////////////////// - -void test_DistilVectorsS(Application &application) -{ - // DistilVectors parameters - MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="PerambS_noise"; - //DistilVecPar.perambulator="PerambS_perambulator_light"; - DistilVecPar.perambulator="PerambS"; - DistilVecPar.lapevec="LapEvec"; - DistilVecPar.tsrc = 0; - //DistilVecPar.nnoise = 1; - DistilVecPar.LI=5; - DistilVecPar.SI=4; - DistilVecPar.TI=32; - DistilVecPar.nvec=5; - DistilVecPar.Ns=4; - DistilVecPar.Nt_inv=1; - application.createModule("DistilVecsS",DistilVecPar); -} ///////////////////////////////////////////////////////////// // MesonSink ///////////////////////////////////////////////////////////// @@ -516,35 +444,10 @@ void test_PerambulatorsSolve(Application &application) PerambFromSolvePar.PerambFileName="perambAslashS.bin"; PerambFromSolvePar.Distil.tsrc = 0; PerambFromSolvePar.Distil.nnoise = 1; - PerambFromSolvePar.Distil.LI=5; - PerambFromSolvePar.Distil.SI=4; - PerambFromSolvePar.Distil.TI=8; PerambFromSolvePar.nvec=5; - PerambFromSolvePar.Distil.Ns=4; - PerambFromSolvePar.Distil.Nt_inv=1; application.createModule("PerambAslashS",PerambFromSolvePar); } -///////////////////////////////////////////////////////////// -// DistilVectors -///////////////////////////////////////////////////////////// -void test_DistilVectorsAslashSeq(Application &application) -{ - // DistilVectors parameters - MDistil::DistilSink::Par DistilSinkPar; - DistilSinkPar.perambulator="PerambAslashS"; - DistilSinkPar.eigenPack="LapEvec"; - DistilSinkPar.tsrc = 0; - DistilSinkPar.nnoise = 1; - DistilSinkPar.LI=5; - DistilSinkPar.SI=4; - DistilSinkPar.TI=8; - DistilSinkPar.nvec=5; - DistilSinkPar.Ns=4; - DistilSinkPar.Nt=8; - DistilSinkPar.Nt_inv=1; - application.createModule("DistilVecsAslashSeq",DistilSinkPar); -} bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { if( bGobbleWhiteSpace ) @@ -702,7 +605,7 @@ bool DebugEigenTest() // Test initialisation of an array of strings for( auto a : as ) std::cout << a << std::endl; - Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; + Grid::Hadrons::MDistil::NamedTensor p{as,2,7,2}; DebugShowTensor(p, "p"); std::cout << "p.IndexNames follow" << std::endl; for( auto a : p.IndexNames ) @@ -950,7 +853,7 @@ bool DebugGridTensorTest( void ) bool ConvertPeramb(const char * pszSource, const char * pszDest) { std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - Grid::Hadrons::MDistil::Perambulator p(sIndexNames); + Grid::Hadrons::MDistil::Perambulator p(sIndexNames); p.ReadBinary( pszSource ); p.write(pszDest); return true; @@ -1016,21 +919,21 @@ int main(int argc, char *argv[]) break; case 2: test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); break; default: // 3 test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); break; case 4: test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_MesonField( application, "Phi", "_phi" ); @@ -1038,28 +941,28 @@ int main(int argc, char *argv[]) break; case 5: // 3 test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); - test_PerambulatorsS( application ); - test_DistilVectorsS( application ); + test_Perambulators( application, "S" ); + test_DistilVectors( application, "S" ); test_MesonField( application, "SPhi", "S_phi" ); test_MesonField( application, "SRho", "S_rho" ); break; #ifdef DISTIL_PRE_RELEASE case 6: // 3 test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); test_g5_sinks( application ); test_MesonSink( application ); break; case 7: // 3 test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_BaryonFieldPhi( application ); @@ -1068,8 +971,8 @@ int main(int argc, char *argv[]) #endif case 8: // 3 test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_MesonField( application, "Phi", "_phi" ); @@ -1083,8 +986,8 @@ int main(int argc, char *argv[]) break; case 10: // 3 test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); test_g5_sinks( application ); test_em( application ); @@ -1092,8 +995,8 @@ int main(int argc, char *argv[]) break; case 11: // 3 test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_BaryonFieldPhi2( application ); @@ -1102,26 +1005,31 @@ int main(int argc, char *argv[]) #endif case 12: // 3 test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); - test_PerambulatorsS( application ); + test_SolverS( application ); + test_Perambulators( application, "S" ); test_em( application ); test_AslashSeq( application ); test_PerambulatorsSolve( application ); - test_DistilVectorsAslashSeq( application ); + test_DistilVectorsSS( application, "AslashSeq", nullptr, "S" ); test_MesonField( application, "AslashSeq" ); break; case 13: test_Global( application ); - test_SolverS( application ); test_LapEvec( application ); + test_SolverS( application ); test_MultiPerambulators( application ); break; } // execution - application.saveParameterFile("test_distil.xml"); - LOG(Warning) << "These parameters are designed to run on a laptop usid --grid 4.4.4.8" << std::endl; - application.run(); + static const char XmlFileName[] = "test_distil.xml"; + application.saveParameterFile( XmlFileName ); + + const std::vector &lat{GridDefaultLatt()}; + if( lat.size() == 4 && lat[0] == 4 && lat[1] == 4 && lat[2] == 4 && lat[3] == 8 ) + application.run(); + else + LOG(Warning) << "The parameters in " << XmlFileName << " are designed to run on a laptop usid --grid 4.4.4.8" << std::endl; // epilogue LOG(Message) << "Grid is finalizing now" << std::endl; From e72e26c89983630780fad9051f8430cec7abfc01 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 1 May 2019 08:53:08 +0100 Subject: [PATCH 222/347] Get rid of unnecessary multiFile options --- Hadrons/Modules/MDistil/DistilVectors.hpp | 1 - Hadrons/Modules/MDistil/Perambulator.hpp | 1 - tests/hadrons/Test_distil.cc | 85 ++++++++++++----------- 3 files changed, 44 insertions(+), 43 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index a9f6ad14..29209567 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -58,7 +58,6 @@ public: std::string, lapevec, std::string, source, std::string, sink, - bool, multiFile, int, tsrc, std::string, nvec, std::string, TI) diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 325a2386..9e8cad21 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -49,7 +49,6 @@ public: std::string, solver, std::string, noise, std::string, PerambFileName, //stem!!! - bool, multiFile, int, nvec, DistilParameters, Distil); }; diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index bc4578f1..bf53f2b2 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -61,29 +61,6 @@ void test_Global(Application &application) application.setPar(globalPar); } - -///////////////////////////////////////////////////////////// -// Test creation Solver -///////////////////////////////////////////////////////////// - -void test_SolverS(Application &application) -{ - std::string boundary = "1 1 1 -1"; - MAction::DWF::Par actionPar; - actionPar.gauge = "LapEvec_gauge"; - actionPar.Ls = 16; - actionPar.M5 = 1.8; - actionPar.mass = 0.005; - actionPar.boundary = boundary; - actionPar.twist = "0. 0. 0. 0."; - application.createModule("DWF_s", actionPar); - MSolver::RBPrecCG::Par solverPar; - solverPar.action = "DWF_s"; - solverPar.residual = 1.0e-2; - solverPar.maxIteration = 10000; - application.createModule("CG_s", solverPar); -} - ///////////////////////////////////////////////////////////// // Test creation of laplacian eigenvectors ///////////////////////////////////////////////////////////// @@ -101,7 +78,7 @@ void test_LapEvec(Application &application) p.Stout.steps = 3; p.Stout.rho = 0.2; p.Cheby.PolyOrder = 11; - p.Cheby.alpha = 0.3; + p.Cheby.alpha = 0.55; p.Cheby.beta = 12.5; p.Lanczos.Nvec = 5; p.Lanczos.Nk = 6; @@ -112,6 +89,43 @@ void test_LapEvec(Application &application) application.createModule(szModuleName,p); } +///////////////////////////////////////////////////////////// +// Test creation Solver +///////////////////////////////////////////////////////////// + +std::string SolverName( const char * pSuffix = nullptr ) { + std::string sSolverName{ "CG" }; + if( pSuffix && pSuffix[0] ) { + sSolverName.append( "_" ); + sSolverName.append( pSuffix ); + } + return sSolverName; +} + +std::string test_Solver(Application &application, const char * pSuffix = nullptr ) +{ + std::string sActionName{ "DWF" }; + if( pSuffix && pSuffix[0] ) { + sActionName.append( "_" ); + sActionName.append( pSuffix ); + } + MAction::DWF::Par actionPar; + actionPar.gauge = "LapEvec_gauge"; + actionPar.Ls = 16; + actionPar.M5 = 1.8; + actionPar.mass = 0.005; + actionPar.boundary = "1 1 1 -1"; + actionPar.twist = "0. 0. 0. 0."; + application.createModule( sActionName, actionPar ); + MSolver::RBPrecCG::Par solverPar; + solverPar.action = sActionName; + solverPar.residual = 1.0e-2; + solverPar.maxIteration = 10000; + std::string sSolverName{ SolverName( pSuffix ) }; + application.createModule( sSolverName, solverPar ); + return sSolverName; +} + ///////////////////////////////////////////////////////////// // Noises ///////////////////////////////////////////////////////////// @@ -133,17 +147,17 @@ std::string test_Noises(Application &application, const std::string &sNoiseBaseN void test_Perambulators( Application &application, const char * pszSuffix = nullptr ) { std::string sModuleName{ "Peramb" }; - if( pszSuffix ) + if( pszSuffix && pszSuffix[0] ) sModuleName.append( pszSuffix ); - test_Noises(application, sModuleName); // Perambulator parameters MDistil::Peramb::Par PerambPar; PerambPar.lapevec = "LapEvec"; - PerambPar.PerambFileName = sModuleName + ".bin"; - PerambPar.solver="CG_s"; + PerambPar.PerambFileName = sModuleName; + PerambPar.solver = test_Solver( application, pszSuffix ); PerambPar.Distil.tsrc = 0; PerambPar.Distil.nnoise = 1; - PerambPar.nvec=5; + PerambPar.nvec = 5; + test_Noises(application, sModuleName); // I want these written after solver stuff application.createModule( sModuleName, PerambPar ); } @@ -920,20 +934,17 @@ int main(int argc, char *argv[]) case 2: test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); break; default: // 3 test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); break; case 4: test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_MesonField( application, "Phi", "_phi" ); @@ -942,7 +953,6 @@ int main(int argc, char *argv[]) case 5: // 3 test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_Perambulators( application, "S" ); @@ -954,7 +964,6 @@ int main(int argc, char *argv[]) case 6: // 3 test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); test_g5_sinks( application ); test_MesonSink( application ); @@ -962,7 +971,6 @@ int main(int argc, char *argv[]) case 7: // 3 test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_BaryonFieldPhi( application ); @@ -972,7 +980,6 @@ int main(int argc, char *argv[]) case 8: // 3 test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_MesonField( application, "Phi", "_phi" ); @@ -981,13 +988,12 @@ int main(int argc, char *argv[]) #ifdef DISTIL_PRE_RELEASE case 9: // 3 test_Global( application ); - test_SolverS( application ); + test_Solver( application ); test_Baryon2pt( application ); break; case 10: // 3 test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); test_g5_sinks( application ); test_em( application ); @@ -996,7 +1002,6 @@ int main(int argc, char *argv[]) case 11: // 3 test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application ); test_DistilVectors( application ); test_BaryonFieldPhi2( application ); @@ -1006,7 +1011,6 @@ int main(int argc, char *argv[]) case 12: // 3 test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_Perambulators( application, "S" ); test_em( application ); test_AslashSeq( application ); @@ -1017,7 +1021,6 @@ int main(int argc, char *argv[]) case 13: test_Global( application ); test_LapEvec( application ); - test_SolverS( application ); test_MultiPerambulators( application ); break; } From cfe5fa7a35121be865f8eb9f934ee9852a5d693f Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 1 May 2019 09:50:23 +0100 Subject: [PATCH 223/347] 1) Don't write Laplacian eigenvectors to disk 2) Add a test that loads perambulators from disk --- Hadrons/Modules/MDistil/LapEvec.hpp | 12 +++--- tests/hadrons/Test_distil.cc | 57 ++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 1b603ef6..cbbbebae 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -229,8 +229,6 @@ void TLapEvec::execute(void) envGetTmp(LatticeColourVector, src); const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; const int Ntfirst{gridHD->LocalStarts()[Tdir]}; - const char DefaultOperatorXml[] = "Michael"; - const char DefaultsolverXml[] = "Felix"; for(int t = Ntfirst; t < Ntfirst + Ntlocal; t++ ) { LOG(Message) << "------------------------------------------------------------" << std::endl; LOG(Message) << " Compute eigenpack, Timeslice = " << t << " / " << Ntfirst + Ntlocal << std::endl; @@ -266,16 +264,16 @@ void TLapEvec::execute(void) eig4d.eval[i] = eig[t].eval[i]; // TODO: Discuss: is this needed? Is there a better way? } } - + GridLogIRL.Active( PreviousIRLLogState ); +#if DEBUG // Now write out the 4d eigenvectors - eig4d.record.operatorXml = DefaultOperatorXml; - eig4d.record.solverXml = DefaultsolverXml; + eig4d.record.operatorXml = "Distillation"; + eig4d.record.solverXml = "CG"; std::string sEigenPackName(getName()); sEigenPackName.append("."); sEigenPackName.append(std::to_string(vm().getTrajectory())); eig4d.write(sEigenPackName,false); - - GridLogIRL.Active( PreviousIRLLogState ); +#endif } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index bf53f2b2..cb6475ee 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -61,6 +61,18 @@ void test_Global(Application &application) application.setPar(globalPar); } +///////////////////////////////////////////////////////////// +// Create a random gauge with the correct name +///////////////////////////////////////////////////////////// + +std::string test_Gauge(Application &application, const char * pszBaseName ) +{ + std::string sGaugeName{ pszBaseName }; + sGaugeName.append( "_gauge" ); + application.createModule( sGaugeName ); + return sGaugeName; +} + ///////////////////////////////////////////////////////////// // Test creation of laplacian eigenvectors ///////////////////////////////////////////////////////////// @@ -68,13 +80,8 @@ void test_Global(Application &application) void test_LapEvec(Application &application) { const char szModuleName[] = "LapEvec"; - // gauge field - std::string sGaugeName{szModuleName}; - sGaugeName.append( "_gauge" ); - application.createModule(sGaugeName); - // Now make an instance of the LapEvec object + test_Gauge( application, szModuleName ); MDistil::LapEvecPar p; - //p.gauge = sGaugeName; // This is the default for LapEvec, so no need to be explicit p.Stout.steps = 3; p.Stout.rho = 0.2; p.Cheby.PolyOrder = 11; @@ -144,11 +151,29 @@ std::string test_Noises(Application &application, const std::string &sNoiseBaseN // Perambulators ///////////////////////////////////////////////////////////// +std::string PerambulatorName( const char * pszSuffix = nullptr ) +{ + std::string sPerambulatorName{ "Peramb" }; + if( pszSuffix && pszSuffix[0] ) + sPerambulatorName.append( pszSuffix ); + return sPerambulatorName; +} + +void test_LoadPerambulators( Application &application, const char * pszSuffix = nullptr ) +{ + std::string sModuleName{ PerambulatorName( pszSuffix ) }; + MIO::LoadPerambulator::Par PerambPar; + PerambPar.PerambFileName = sModuleName; + PerambPar.Distil.tsrc = 0; + PerambPar.Distil.nnoise = 1; + PerambPar.nvec = 5; + test_Noises(application, sModuleName); // I want these written after solver stuff + application.createModule( sModuleName, PerambPar ); +} + void test_Perambulators( Application &application, const char * pszSuffix = nullptr ) { - std::string sModuleName{ "Peramb" }; - if( pszSuffix && pszSuffix[0] ) - sModuleName.append( pszSuffix ); + std::string sModuleName{ PerambulatorName( pszSuffix ) }; // Perambulator parameters MDistil::Peramb::Par PerambPar; PerambPar.lapevec = "LapEvec"; @@ -927,19 +952,25 @@ int main(int argc, char *argv[]) //const unsigned int nt = GridDefaultLatt()[Tp]; switch(iTestNum) { + case 0: + test_Global( application ); + test_LapEvec( application ); + break; case 1: test_Global( application ); test_LapEvec( application ); + test_Perambulators( application ); break; - case 2: + default: // 2 test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); + test_DistilVectors( application ); break; - default: // 3 + case 3: test_Global( application ); test_LapEvec( application ); - test_Perambulators( application ); + test_LoadPerambulators( application ); test_DistilVectors( application ); break; case 4: @@ -950,7 +981,7 @@ int main(int argc, char *argv[]) test_MesonField( application, "Phi", "_phi" ); test_MesonField( application, "Rho", "_rho" ); break; - case 5: // 3 + case 5: test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); From 8dc058762173cc104b1fa2500b57c789658099d9 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 1 May 2019 13:04:51 +0100 Subject: [PATCH 224/347] Post Michael / Felix review. Ready for Peter / Antonin review --- Hadrons/Distil.hpp | 23 ++++++++++++++++----- Hadrons/Modules/MDistil/DistilVectors.hpp | 10 ++++----- Hadrons/Modules/MDistil/LapEvec.hpp | 3 ++- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 16 +++++++------- Hadrons/Modules/MDistil/Perambulator.hpp | 7 +++---- Hadrons/Modules/MIO/LoadPerambulator.hpp | 4 ++-- tests/hadrons/Test_distil.cc | 6 +++--- 7 files changed, 41 insertions(+), 28 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 58bfc532..261a7cab 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -362,13 +362,13 @@ template struct template struct is_named_tensor, T>::value>::type> : public std::true_type {}; /****************************************************************************** - Perambulator object + PerambTensor object Endian_Scalar_Size can be removed once (deprecated) NamedTensor::ReadBinary & WriteBinary methods deleted ******************************************************************************/ //template -using Perambulator = NamedTensor; -static const std::array PerambIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; +using PerambTensor = NamedTensor; +static const std::array PerambIndexNames{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; /****************************************************************************** Save NamedTensor binary format (NB: On-disk format is Big Endian) @@ -552,7 +552,7 @@ void NamedTensor::ReadBinary(const std #else u32 -= GridChecksum::crc32(tensor.data(), TotalDataSize); #endif - assert( u32 == 0 && "NamedTensor error: Perambulator checksum invalid"); + assert( u32 == 0 && "NamedTensor error: PerambTensor checksum invalid"); } /****************************************************************************** @@ -593,7 +593,20 @@ void NamedTensor::read(Reader &r, cons const typename ET::Dimensions & NewDimensions{tensor.dimensions()}; for( int i=0; i < NumIndices_; i++ ) { assert(OldDimensions[i] == 0 || OldDimensions[i] == NewDimensions[i] && "NamedTensor::load dimension size"); - assert(OldIndexNames[i].size() == 0 || OldIndexNames[i] == IndexNames[i] && "NamedTensor::load dimension name"); + // Case insensitive name compare - TODO std:: c++ way of doing this + const int iOldLen{ static_cast( OldIndexNames[i].size() ) }; + const int iNewLen{ static_cast( IndexNames[i].size() ) }; + bool bSame{ iOldLen == 0 || iOldLen == iNewLen }; + for( int j = 0; j < iOldLen && bSame; j++ ) { + wchar_t c1 = OldIndexNames[i][j]; + if( c1 >= 'a' && c1 <= 'z' ) + c1 -= 'a' - 'A'; + wchar_t c2 = IndexNames[i][j]; + if( c2 >= 'a' && c1 <= 'z' ) + c2 -= 'a' - 'A'; + bSame = ( c1 == c2 ); + } + assert(bSame && "NamedTensor::load dimension name"); } } diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 29209567..e04b91fa 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -163,14 +163,14 @@ void TDistilVectors::setup(void) { Cleanup(); auto &noise = envGet(NoiseTensor, NoiseVectorName); - auto &perambulator = envGet(Perambulator, PerambulatorName); + auto &perambulator = envGet(PerambTensor, PerambulatorName); // We expect the perambulator to have been created with these indices - for(int i = 0; i < Perambulator::NumIndices; i++ ) - assert( PerambIndexNames[i] == perambulator.IndexNames[i] && "Perambulator indices bad" ); + for(int i = 0; i < PerambTensor::NumIndices; i++ ) + assert( PerambIndexNames[i] == perambulator.IndexNames[i] && "PerambTensor indices bad" ); const int Nt{ env().getDim(Tdir) }; - assert( Nt == static_cast( perambulator.tensor.dimension(0) ) && "Perambulator time dimensionality bad" ); + assert( Nt == static_cast( perambulator.tensor.dimension(0) ) && "PerambTensor time dimensionality bad" ); const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, true) }; const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; @@ -222,7 +222,7 @@ template void TDistilVectors::execute(void) { auto &noise = envGet(NoiseTensor, NoiseVectorName); - auto &perambulator = envGet(Perambulator, PerambulatorName); + auto &perambulator = envGet(PerambTensor, PerambulatorName); auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); envGetTmp(LatticeSpinColourVector, tmp2); diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index cbbbebae..a83c775a 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -205,11 +205,12 @@ void TLapEvec::execute(void) // Stout smearing envGetTmp(GaugeField, Umu_smear); + const StoutParameters &Stout{par().Stout}; + if( Stout.steps ) { auto &Umu = envGet(GaugeField, sGaugeName); LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu) << std::endl; Umu_smear = Umu; - const StoutParameters &Stout{par().Stout}; envGetTmp(GaugeField, Umu_stout); Smear_Stout LS(Stout.rho, Tdir); // spatial smearing only for (int i = 0; i < Stout.steps; i++) { diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 7c3f2729..05a9be53 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -56,8 +56,8 @@ public: std::string, PerambFileName, std::string, solve, int, nvec, - int, nvec_reduced, - int, LI_reduced, + std::string, nvec_reduced, + std::string, LI_reduced, DistilParameters, Distil); }; @@ -122,12 +122,12 @@ void TPerambFromSolve::setup(void) { Cleanup(); DISTIL_PARAMETERS_DEFINE( true ); + const int nvec_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().nvec_reduced, nvec, true) }; + const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, true) }; grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); - const int nvec_reduced{par().nvec_reduced}; - const int LI_reduced{par().LI_reduced}; //std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - envCreate(Perambulator, getName(), 1, PerambIndexNames,Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); + envCreate(PerambTensor, getName(), 1, PerambIndexNames,Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); envCreate(NoiseTensor, getName() + "_noise", 1, nnoise, Nt, nvec, Ns ); envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); @@ -151,10 +151,10 @@ void TPerambFromSolve::execute(void) GridCartesian * grid4d = env().getGrid(); const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - const int nvec_reduced{par().nvec_reduced}; - const int LI_reduced{par().LI_reduced}; DISTIL_PARAMETERS_DEFINE( false ); - auto &perambulator = envGet(Perambulator, getName()); + const int nvec_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().nvec_reduced, nvec, false) }; + const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, false) }; + auto &perambulator = envGet(PerambTensor, getName()); auto &solve = envGet(std::vector, par().solve); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 9e8cad21..8dc0ee92 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -82,8 +82,7 @@ protected: std::string sNoiseName; }; -// Can't name the module Perambulator, because that's what we've called the object -MODULE_REGISTER_TMP(Peramb, TPerambulator, MDistil); +MODULE_REGISTER_TMP(Perambulator, TPerambulator, MDistil); /****************************************************************************** * TPerambulator implementation * @@ -127,7 +126,7 @@ void TPerambulator::setup(void) grid3d = MakeLowerDimGrid(grid4d); DISTIL_PARAMETERS_DEFINE( true ); - envCreate(Perambulator, getName(), 1, PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); + envCreate(PerambTensor, getName(), 1, PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); envCreate(std::vector, getName() + "_unsmeared_sink", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); @@ -170,7 +169,7 @@ void TPerambulator::execute(void) envGetTmp(FermionField, v5dtmp_sol); auto &noise = envGet(NoiseTensor, sNoiseName); - auto &perambulator = envGet(Perambulator, getName()); + auto &perambulator = envGet(PerambTensor, getName()); auto &epack = envGet(LapEvecs, sLapEvecName); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 156fb5dc..719a041a 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -103,14 +103,14 @@ void TLoadPerambulator::setup(void) { DISTIL_PARAMETERS_DEFINE( true ); //std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - envCreate(MDistil::Perambulator, getName(), 1, MDistil::PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); + envCreate(MDistil::PerambTensor, getName(), 1, MDistil::PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); } // execution /////////////////////////////////////////////////////////////////// template void TLoadPerambulator::execute(void) { - auto &perambulator = envGet(MDistil::Perambulator, getName()); + auto &perambulator = envGet(MDistil::PerambTensor, getName()); const std::string sPerambName{par().PerambFileName + "." + std::to_string(vm().getTrajectory())}; perambulator.read(sPerambName.c_str()); } diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index cb6475ee..14dfed9b 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -175,7 +175,7 @@ void test_Perambulators( Application &application, const char * pszSuffix = null { std::string sModuleName{ PerambulatorName( pszSuffix ) }; // Perambulator parameters - MDistil::Peramb::Par PerambPar; + MDistil::Perambulator::Par PerambPar; PerambPar.lapevec = "LapEvec"; PerambPar.PerambFileName = sModuleName; PerambPar.solver = test_Solver( application, pszSuffix ); @@ -183,7 +183,7 @@ void test_Perambulators( Application &application, const char * pszSuffix = null PerambPar.Distil.nnoise = 1; PerambPar.nvec = 5; test_Noises(application, sModuleName); // I want these written after solver stuff - application.createModule( sModuleName, PerambPar ); + application.createModule( sModuleName, PerambPar ); } ///////////////////////////////////////////////////////////// @@ -892,7 +892,7 @@ bool DebugGridTensorTest( void ) bool ConvertPeramb(const char * pszSource, const char * pszDest) { std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - Grid::Hadrons::MDistil::Perambulator p(sIndexNames); + Grid::Hadrons::MDistil::PerambTensor p(sIndexNames); p.ReadBinary( pszSource ); p.write(pszDest); return true; From a3fe57f430c7019881909345a2d3d5ecc61307c4 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 1 May 2019 18:11:37 +0100 Subject: [PATCH 225/347] NamedTensor writes to tag NamedTensor by default (not filename) - so still usable in case user renames file. Also tweaked tensor index name checking (which is used to ensure tensor is correct type) --- Hadrons/Distil.hpp | 71 ++++++++++++++--------- Hadrons/Modules/MDistil/DistilVectors.hpp | 3 +- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 261a7cab..c3183893 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -347,11 +347,15 @@ public: Grid::SliceShare( gridLowDim, gridHighDim, tensor.data(), (int) (tensor.size() * sizeof(Scalar_))); } - // load and save - not virtual - probably all changes + bool ValidateIndexNames( int iNumNames, const std::string * MatchNames ) const; + + // Read/Write in any format template inline void read (Reader &r, const char * pszTag = nullptr); template inline void write(Writer &w, const char * pszTag = nullptr) const; + // Read/Write in default format, i.e. HDF5 if present, else binary inline void read (const char * filename, const char * pszTag = nullptr); inline void write(const char * filename, const char * pszTag = nullptr) const; + // Original I/O implementation. This will be removed when we're sure it's no longer needed EIGEN_DEPRECATED inline void ReadBinary (const std::string filename); // To be removed EIGEN_DEPRECATED inline void WriteBinary(const std::string filename); // To be removed }; @@ -563,18 +567,43 @@ template template void NamedTensor::write(Writer &w, const char * pszTag)const{ if( pszTag == nullptr ) - pszTag = "tensor"; + pszTag = "NamedTensor"; + LOG(Message) << "Writing NamedTensor to tag " << pszTag << std::endl; write(w, pszTag, *this); } template void NamedTensor::write(const char * filename, const char * pszTag)const{ - const std::string sTag{pszTag == nullptr ? filename : pszTag}; std::string sFileName{filename}; sFileName.append( MDistil::FileExtension ); - LOG(Message) << "Writing NamedTensor to " << sFileName << ", tag " << sTag << std::endl; - MDistil::Default_Writer w(sFileName); - write(w, sTag.c_str()); + LOG(Message) << "Writing NamedTensor to file " << sFileName << std::endl; + MDistil::Default_Writer w( sFileName ); + write( w, pszTag ); +} + +/****************************************************************************** + Validate named tensor index names + ******************************************************************************/ + +template +bool NamedTensor::ValidateIndexNames( int iNumNames, const std::string * MatchNames ) const { + bool bSame{ iNumNames == NumIndices_ && IndexNames.size() == NumIndices_ }; + for( int i = 0; bSame && i < NumIndices_; i++ ) { + // Case insensitive name compare + const int iMatchLen{ static_cast( MatchNames[i].size() ) }; + const int iNewLen { static_cast( IndexNames[i].size() ) }; + bSame = ( iMatchLen == iNewLen ); + for( int j = 0; bSame && j < iMatchLen; j++ ) { + wchar_t c1 = MatchNames[i][j]; + if( c1 >= 'a' && c1 <= 'z' ) + c1 -= 'a' - 'A'; + wchar_t c2 = IndexNames[i][j]; + if( c2 >= 'a' && c1 <= 'z' ) + c2 -= 'a' - 'A'; + bSame = ( c1 == c2 ); + } + } + return bSame; } /****************************************************************************** @@ -584,40 +613,26 @@ void NamedTensor::write(const char * f template template void NamedTensor::read(Reader &r, const char * pszTag) { - // Grab index names and dimensions if( pszTag == nullptr ) - pszTag = "tensor"; + pszTag = "NamedTensor"; + // Grab index names and dimensions std::vector OldIndexNames{std::move(IndexNames)}; typename ET::Dimensions OldDimensions{tensor.dimensions()}; + LOG(Message) << "Reading NamedTensor from tag " << pszTag << std::endl; read(r, pszTag, *this); const typename ET::Dimensions & NewDimensions{tensor.dimensions()}; - for( int i=0; i < NumIndices_; i++ ) { + for( int i = 0; i < NumIndices_; i++ ) assert(OldDimensions[i] == 0 || OldDimensions[i] == NewDimensions[i] && "NamedTensor::load dimension size"); - // Case insensitive name compare - TODO std:: c++ way of doing this - const int iOldLen{ static_cast( OldIndexNames[i].size() ) }; - const int iNewLen{ static_cast( IndexNames[i].size() ) }; - bool bSame{ iOldLen == 0 || iOldLen == iNewLen }; - for( int j = 0; j < iOldLen && bSame; j++ ) { - wchar_t c1 = OldIndexNames[i][j]; - if( c1 >= 'a' && c1 <= 'z' ) - c1 -= 'a' - 'A'; - wchar_t c2 = IndexNames[i][j]; - if( c2 >= 'a' && c1 <= 'z' ) - c2 -= 'a' - 'A'; - bSame = ( c1 == c2 ); - } - assert(bSame && "NamedTensor::load dimension name"); - } + assert( ValidateIndexNames( OldIndexNames.size(), &OldIndexNames[0] ) && "NamedTensor::load dimension name" ); } template void NamedTensor::read(const char * filename, const char * pszTag) { - const std::string sTag{pszTag == nullptr ? filename : pszTag}; std::string sFileName{filename}; sFileName.append( MDistil::FileExtension ); - LOG(Message) << "Reading NamedTensor from " << sFileName << ", tag " << sTag << std::endl; - MDistil::Default_Reader r(sFileName); - read(r, sTag.c_str()); + LOG(Message) << "Reading NamedTensor from file " << sFileName << std::endl; + MDistil::Default_Reader r( sFileName ); + read( r, pszTag ); } /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index e04b91fa..f508d72b 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -166,8 +166,7 @@ void TDistilVectors::setup(void) auto &perambulator = envGet(PerambTensor, PerambulatorName); // We expect the perambulator to have been created with these indices - for(int i = 0; i < PerambTensor::NumIndices; i++ ) - assert( PerambIndexNames[i] == perambulator.IndexNames[i] && "PerambTensor indices bad" ); + assert( perambulator.ValidateIndexNames( PerambIndexNames.size(), &PerambIndexNames[0] ) && "Perambulator index names bad" ); const int Nt{ env().getDim(Tdir) }; assert( Nt == static_cast( perambulator.tensor.dimension(0) ) && "PerambTensor time dimensionality bad" ); From 311c35a15c505310e9d0984ea322e833182e128b Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 1 May 2019 18:22:08 +0100 Subject: [PATCH 226/347] Looking for fixes for Intel '17 compiler errors. std::cout << complex number ? --- Hadrons/Distil.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index c3183893..02569298 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -665,11 +665,11 @@ inline void RotateEigen(std::vector & evec) peekSite(cv0, evec[0], siteFirst); auto & cplx0 = cv0()()(0); if( std::imag(cplx0) == 0 ) - std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; + std::cout << GridLogMessage << "RotateEigen() : Site 0 : already meets phase convention" << std::endl; else { const auto cplx0_mag{std::abs(cplx0)}; const auto phase{std::conj(cplx0 / cplx0_mag)}; - std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (std::arg(phase) / 3.14159265) << " pi" << std::endl; + std::cout << GridLogMessage << "RotateEigen() : Site 0 : =" << cplx0_mag << " => phase=" << (std::arg(phase) / 3.14159265) << " pi" << std::endl; { // TODO: Only really needed on the master slice for( int k = 0 ; k < evec.size() ; k++ ) From 62692b68b998f4c8ee8282a5366d69ded44d6c21 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 1 May 2019 20:45:16 +0100 Subject: [PATCH 227/347] I'd forgotten that Intel '17 doesn't like auto var{value}; syntax --- Hadrons/Distil.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 02569298..41fcf437 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -103,7 +103,7 @@ inline void SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Bu //#ifndef USE_LOCAL_SLICES // assert(0); // Can't do this without MPI (should really test whether MPI is defined) //#else - const auto MyRank{gridHighDim->ThisRank()}; + const auto MyRank = gridHighDim->ThisRank(); std::vector reqs(0); int MySlice{coor[dimSpreadOut]}; char * const _buffer{(char *)Buffer}; @@ -113,9 +113,9 @@ inline void SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Bu int RecvSlice = ( MySlice - i + NumSlices ) % NumSlices; char * const RecvData{_buffer + RecvSlice * SliceSize}; coor[dimSpreadOut] = SendSlice; - const auto SendRank{gridHighDim->RankFromProcessorCoor(coor)}; + const auto SendRank = gridHighDim->RankFromProcessorCoor(coor); coor[dimSpreadOut] = RecvSlice; - const auto RecvRank{gridHighDim->RankFromProcessorCoor(coor)}; + const auto RecvRank = gridHighDim->RankFromProcessorCoor(coor); std::cout << GridLogMessage << "Send slice " << MySlice << " (" << MyRank << ") to " << SendSlice << " (" << SendRank << "), receive slice from " << RecvSlice << " (" << RecvRank << ")" << std::endl; gridHighDim->SendToRecvFromBegin(reqs,MyData,SendRank,RecvData,RecvRank,SliceSize); @@ -389,7 +389,7 @@ void NamedTensor::WriteBinary(const st assert((sizeof(Scalar_) % Endian_Scalar_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Endian_Scalar_Size" ); // Size of the data (in bytes) const uint32_t Scalar_Size{sizeof(Scalar_)}; - const auto NumElements{tensor.size()}; + const auto NumElements = tensor.size(); const std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; uint64_t u64 = htobe64(static_cast(TotalDataSize)); w.write(reinterpret_cast(&u64), sizeof(u64)); @@ -518,7 +518,7 @@ void NamedTensor::ReadBinary(const std TotalDataSize = NumElements * Scalar_Size; } else { // dimensions together with names - const auto & TensorDims{tensor.dimensions()}; + const auto & TensorDims = tensor.dimensions(); for( int d = 0; d < NumIndices_; d++ ) { // size of dimension r.read(reinterpret_cast(&u16), sizeof(u16)); @@ -665,11 +665,11 @@ inline void RotateEigen(std::vector & evec) peekSite(cv0, evec[0], siteFirst); auto & cplx0 = cv0()()(0); if( std::imag(cplx0) == 0 ) - std::cout << GridLogMessage << "RotateEigen() : Site 0 : already meets phase convention" << std::endl; + std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; else { - const auto cplx0_mag{std::abs(cplx0)}; - const auto phase{std::conj(cplx0 / cplx0_mag)}; - std::cout << GridLogMessage << "RotateEigen() : Site 0 : =" << cplx0_mag << " => phase=" << (std::arg(phase) / 3.14159265) << " pi" << std::endl; + const auto cplx0_mag = std::abs(cplx0); + const auto phase = std::conj(cplx0 / cplx0_mag); + std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (std::arg(phase) / 3.14159265) << " pi" << std::endl; { // TODO: Only really needed on the master slice for( int k = 0 ; k < evec.size() ; k++ ) From b7ead6c16a93e3d3232301c743a187b081181300 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Thu, 2 May 2019 18:20:49 +0100 Subject: [PATCH 228/347] Fixed bug: iff stout smearing disabled then gauge field uninitialised --- Hadrons/Modules/MDistil/LapEvec.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index a83c775a..58416bcf 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -205,20 +205,19 @@ void TLapEvec::execute(void) // Stout smearing envGetTmp(GaugeField, Umu_smear); + Umu_smear = envGet(GaugeField, sGaugeName); // The smeared field starts off as the Gauge field + LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; const StoutParameters &Stout{par().Stout}; if( Stout.steps ) { - auto &Umu = envGet(GaugeField, sGaugeName); - LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu) << std::endl; - Umu_smear = Umu; envGetTmp(GaugeField, Umu_stout); Smear_Stout LS(Stout.rho, Tdir); // spatial smearing only for (int i = 0; i < Stout.steps; i++) { LS.smear(Umu_stout, Umu_smear); Umu_smear = Umu_stout; } + LOG(Message) << "Smeared plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; } - LOG(Message) << "Smeared plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; //////////////////////////////////////////////////////////////////////// // Invert Peardon Nabla operator separately on each time-slice From 0efe63f6fadb46511635f8f00be6731470a13186 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Thu, 2 May 2019 19:37:59 +0100 Subject: [PATCH 229/347] 3D smearing fix --- Grid/qcd/smearing/StoutSmearing.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Grid/qcd/smearing/StoutSmearing.h b/Grid/qcd/smearing/StoutSmearing.h index d867fe52..78c05b12 100644 --- a/Grid/qcd/smearing/StoutSmearing.h +++ b/Grid/qcd/smearing/StoutSmearing.h @@ -70,7 +70,7 @@ public: /*! Construct stout smearing object from explicitly specified rho matrix */ Smear_Stout(const std::vector& rho_) : OwnedBase{new Smear_APE(rho_)}, SmearBase{OwnedBase.get()} { - std::cout << GridLogDebug << "Stout smearing constructor : Smear_Stout(const std::vector& rho_)" << std::endl + std::cout << GridLogDebug << "Stout smearing constructor : Smear_Stout(const std::vector& " << rho_ << " )" << std::endl assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } @@ -78,7 +78,7 @@ public: Smear_Stout(double rho, int orthogdim = -1) //: OwnedBase{(orthogdim<0 || orthogdim>=Nd) ? new Smear_APE(rho) : new Smear_APE(rho3D(rho,orthogdim))}, : OrthogDim{orthogdim}, SmearRho{ rho3D(rho,orthogdim) }, OwnedBase{ new Smear_APE(SmearRho) }, SmearBase{OwnedBase.get()} { - std::cout << GridLogDebug << "Stout smearing constructor : Smear_StoutSmear_Stout(double rho, int orthogdim = -1)\nrho3d=" << SmearRho << std::endl; + std::cout << GridLogDebug << "Stout smearing constructor : Smear_StoutSmear_Stout(double " << rho << ", int " << OrthogDim << " )\nrho3d=" << SmearRho << std::endl; assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } @@ -108,8 +108,8 @@ public: tmp * adj(Umu)); // iq_mu = Ta(Omega_mu) to match the signs with the paper exponentiate_iQ(tmp, iq_mu); - pokeLorentz(u_smr, tmp * Umu, mu); // u_smr = exp(iQ_mu)*U_mu } + pokeLorentz(u_smr, tmp * Umu, mu); // u_smr = exp(iQ_mu)*U_mu } std::cout << GridLogDebug << "Stout smearing completed" << std::endl; }; From ec24a1f828bc385fdc01a8e9b27c444db881887b Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 3 May 2019 16:03:56 +0100 Subject: [PATCH 230/347] Fixed 2 bugs in LapEvec: 1) InsertLocalSlice 2) ensure convergence assertion stops entire machine --- Hadrons/Modules/MDistil/LapEvec.hpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 58416bcf..e3186b70 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -164,14 +164,13 @@ void TLapEvec::setup(void) Environment & e{env()}; gridHD = e.getGrid(); gridLD = MakeLowerDimGrid( gridHD ); - const int Nt{e.getDim(Tdir)}; + const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; // Temporaries - envTmpLat(GaugeField, "Umu"); envTmpLat(GaugeField, "Umu_stout"); envTmpLat(GaugeField, "Umu_smear"); envTmp(LatticeGaugeField, "UmuNoTime",1,LatticeGaugeField(gridLD)); envTmp(LatticeColourVector, "src",1,LatticeColourVector(gridLD)); - envTmp(std::vector, "eig",1,std::vector(Nt)); + envTmp(std::vector, "eig",1,std::vector(Ntlocal)); // Output objects envCreate(LapEvecs, getName(), 1, par().Lanczos.Nvec, gridHD ); } @@ -229,14 +228,15 @@ void TLapEvec::execute(void) envGetTmp(LatticeColourVector, src); const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; const int Ntfirst{gridHD->LocalStarts()[Tdir]}; - for(int t = Ntfirst; t < Ntfirst + Ntlocal; t++ ) { + uint32_t ConvergenceErrors{0}; + for(int t = 0; t < Ntlocal; t++ ) { LOG(Message) << "------------------------------------------------------------" << std::endl; - LOG(Message) << " Compute eigenpack, Timeslice = " << t << " / " << Ntfirst + Ntlocal << std::endl; + LOG(Message) << " Compute eigenpack, local timeslice = " << t << " / " << Ntlocal << std::endl; LOG(Message) << "------------------------------------------------------------" << std::endl; eig[t].resize(LPar.Nk+LPar.Np,gridLD); // Construct smearing operator - ExtractSliceLocal(UmuNoTime,Umu_smear,0,t-Ntfirst,Grid::QCD::Tdir); // switch to 3d/4d objects + ExtractSliceLocal(UmuNoTime,Umu_smear,0,t,Grid::QCD::Tdir); // switch to 3d/4d objects LinOpPeardonNabla PeardonNabla(UmuNoTime); LOG(Debug) << "Chebyshev preconditioning to order " << ChebPar.PolyOrder << " with parameters (alpha,beta) = (" << ChebPar.alpha << "," << ChebPar.beta << ")" << std::endl; @@ -253,18 +253,24 @@ void TLapEvec::execute(void) IRL(PeardonNablaCheby,PeardonNabla,LPar.Nvec,LPar.Nk,LPar.Nk+LPar.Np,LPar.resid,LPar.MaxIt); int Nconv = 0; IRL.calc(eig[t].eval,eig[t].evec,src,Nconv); - assert( Nconv >= LPar.Nvec && "MDistil::LapEvec : Error - not enough eigenvectors converged" ); - if( Nconv > LPar.Nvec ) + if( Nconv < LPar.Nvec ) { + // NB: Can't assert here since we are processing local slices - i.e. not all nodes would assert + ConvergenceErrors = 1; + LOG(Error) << "MDistil::LapEvec : Not enough eigenvectors converged. If this occurs in practice, we should modify the eigensolver to iterate once more to ensure the second convergence test does not take us below the requested number of eigenvectors" << std::endl; + } + if( Nconv != LPar.Nvec ) eig[t].resize( LPar.Nvec, gridLD ); RotateEigen( eig[t].evec ); // Rotate the eigenvectors into our phase convention for (int i=0;iGlobalSum(ConvergenceErrors); + assert(ConvergenceErrors==0 && "The eingensolver failed to find enough eigenvectors on at least one node"); #if DEBUG // Now write out the 4d eigenvectors eig4d.record.operatorXml = "Distillation"; From 9ae4d369f3b5f397786de21edecb8f6c99aebdf1 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 3 May 2019 22:00:50 +0100 Subject: [PATCH 231/347] Use the definition of the Perambulator Index names given in Hadrons::MDistil --- tests/hadrons/Test_distil.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index 14dfed9b..ab7bdcac 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -891,8 +891,7 @@ bool DebugGridTensorTest( void ) } bool ConvertPeramb(const char * pszSource, const char * pszDest) { - std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - Grid::Hadrons::MDistil::PerambTensor p(sIndexNames); + Grid::Hadrons::MDistil::PerambTensor p(Hadrons::MDistil::PerambIndexNames); p.ReadBinary( pszSource ); p.write(pszDest); return true; From a865caf0d219976de46ea2508c5bb13641037f21 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 3 May 2019 22:17:25 +0100 Subject: [PATCH 232/347] Forgot a const in IndexName only version of NamedTensor constructor --- Hadrons/Distil.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 41fcf437..b420c725 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -335,7 +335,7 @@ public: EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor() : IndexNames{NumIndices_} {} // Construct a named tensor without specifying size of each dimension (because it will be loaded from file) - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(std::array &IndexNames_) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::array &IndexNames_) : IndexNames{NumIndices_} { for( int i = 0; i < NumIndices_; i++ ) From c16916cc459312508b693cff8e2d20892c167e1b Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 6 May 2019 10:35:42 +0100 Subject: [PATCH 233/347] Multiple local slice fixes --- Hadrons/Modules/MDistil/DistilVectors.hpp | 4 ++-- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 2 +- Hadrons/Modules/MDistil/Perambulator.hpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index f508d72b..81e7ba9e 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -261,7 +261,7 @@ void TDistilVectors::execute(void) if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += SI){ - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Grid::QCD::Tdir); //tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); tmp3d=zero; @@ -289,7 +289,7 @@ void TDistilVectors::execute(void) for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { sink_tslice=zero; for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Grid::QCD::Tdir); sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); } InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Grid::QCD::Tdir); diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index e3186b70..9877ee2d 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -263,7 +263,7 @@ void TLapEvec::execute(void) RotateEigen( eig[t].evec ); // Rotate the eigenvectors into our phase convention for (int i=0;i::execute(void) for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); for (int ivec = 0; ivec < nvec_reduced; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Grid::QCD::Tdir); pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; } diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 8dc0ee92..d297ca16 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -206,7 +206,7 @@ void TPerambulator::execute(void) if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += SI){ - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv,3); + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Grid::QCD::Tdir); //tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); tmp3d=zero; @@ -235,7 +235,7 @@ void TPerambulator::execute(void) for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Grid::QCD::Tdir); for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t,3); + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Grid::QCD::Tdir); pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); } } From acd5a01b659f5e8059da081184d774f7972dd54d Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 16 May 2019 15:11:50 +0100 Subject: [PATCH 234/347] some work on baryons --- Grid/qcd/utils/A2Autils.h | 4 ++-- Hadrons/Distil.hpp | 2 +- Hadrons/Modules/MDistil/BC2.hpp | 21 +++++++++++++++++---- Hadrons/Modules/MDistil/Baryon2pt.hpp | 4 ++-- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index bfe03bc0..9e2bd7e2 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -186,7 +186,7 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, auto v2 = conjugate(two[j]._odata[ss]); // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 //auto v2g = v2*Gamma(Gamma::Algebra::SigmaXZ); - auto v2g=v2; + //auto v2g=v2; for(int k=0;k::NucleonFieldMom(Eigen::Tensor &mat, } } - grid->GlobalSumVector(&mat(0,0,0,0,0,0),Nmom*Nt*oneBlock*twoBlock*threeBlock); + grid->GlobalSumVector(&mat(0,0,0,0,0,0),Nmom*Nt*oneBlock*twoBlock*threeBlock*4); } diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index b420c725..5a5a4a23 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -250,7 +250,7 @@ const int Nt_inv{ full_tdil ? 1 : TI } class BFieldIO: Serializable{ public: - using BaryonTensorSet = Eigen::Tensor; + using BaryonTensorSet = Eigen::Tensor; GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, BaryonTensorSet, BField ); }; diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp index 14ade917..5c3433cc 100644 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -158,6 +158,7 @@ void TBC2::execute(void) auto &one = envGet(std::vector, par().one); auto &two = envGet(std::vector, par().two); auto &three = envGet(std::vector, par().three); + const std::string &output{par().output}; int N_1 = one.size(); int N_2 = two.size(); @@ -172,8 +173,8 @@ void TBC2::execute(void) } - int Nmom=1; - int Nt=8; + int Nmom = mom_.size(); + const int Nt{env().getDim(Tdir)}; int parity = 1; int orthogDim=3; @@ -199,17 +200,29 @@ void TBC2::execute(void) } hasPhase_ = true; stopTimer("Momentum phases"); -} + } envCache(std::vector, momphName_, 1, mom_.size(), envGetGrid(ComplexField)); Eigen::Tensor m(Nmom,Nt,N_1,N_2,N_3,4); A2Autils::NucleonFieldMom(m, &one[0], &two[0], &three[0], ph, parity, orthogDim); + //A2Autils::NucleonFieldMom(m, one, two, three, ph, parity, orthogDim); for (int is=0 ; is < 4 ; is++){ for (int t=0 ; t < Nt ; t++){ - std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << m(0,t,is,0,0,0) << std::endl; + std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << m(0,t,0,0,0,is) << std::endl; } } + BFieldIO BField_save; + BField_save.BField = m; + + + GridCartesian * grid = env().getGrid(); + if(grid->IsBoss()) { + std::string filename ="./" + output + ".h5"; + std::cout << "Writing to file " << filename << std::endl; + Grid::Hdf5Writer writer(filename); + write(writer,"BaryonField",BField_save.BField); + } } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index 4964c6db..02ac892a 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -144,7 +144,7 @@ void TBaryon2pt::execute(void) // using BaryonTensorSet = Eigen::Tensor; BFieldIO BFieldL; - BFieldL.BField.resize(Nmom,Ngamma,Nt,4,N_1,N_2,N_3); + BFieldL.BField.resize(Nmom,Nt,N_1,N_2,N_3,4); std::string filenameL ="./" + inputL + ".h5"; std::cout << "Reading from file " << filenameL << std::endl; @@ -152,7 +152,7 @@ void TBaryon2pt::execute(void) read(readerL,"BaryonField",BFieldL.BField); BFieldIO BFieldR; - BFieldR.BField.resize(Nmom,Ngamma,Nt,4,N_1,N_2,N_3); + BFieldR.BField.resize(Nmom,Nt,N_1,N_2,N_3,4); std::string filenameR ="./" + inputR + ".h5"; std::cout << "Reading from file " << filenameR << std::endl; From 10a052d695211b4e7ef95dd2fac3206ab8f9e365 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 17 May 2019 09:59:01 +0100 Subject: [PATCH 235/347] 3 issues preventing compilation under clang. Marked these with FELIX_ISSUE and made minimal change to make compile (as fix not obvious) --- Hadrons/Modules/MDistil/BContraction.hpp | 3 ++- Hadrons/Modules/MDistil/Baryon2pt.hpp | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp index 21d28e3a..5cfd8967 100644 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ b/Hadrons/Modules/MDistil/BContraction.hpp @@ -261,8 +261,9 @@ void TBContraction::execute(void) } BFieldIO BField_save; +#ifdef FELIX_ISSUE BField_save.BField = BField3; - +#endif std::string filename ="./" + output + ".h5"; std::cout << "Writing to file " << filename << std::endl; Hdf5Writer writer(filename); diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index 02ac892a..3004e331 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -186,8 +186,16 @@ void TBaryon2pt::execute(void) Eigen::array, 3> product_dims = { Eigen::IndexPair(0,epsilon[pairs[ipair]][0]),Eigen::IndexPair(1,epsilon[pairs[ipair]][1]) ,Eigen::IndexPair(2,epsilon[pairs[ipair]][2]) }; for (int imom=0 ; imom < Nmom ; imom++){ std::cout << imom << std::endl; - Eigen::Tensor B6L = BFieldL.BField.chip(imom,0); - Eigen::Tensor B6R = BFieldR.BField.chip(imom,0); + Eigen::Tensor B6L +#ifdef FELIX_ISSUE + = BFieldL.BField.chip(imom,0) +#endif + ; + Eigen::Tensor B6R +#ifdef FELIX_ISSUE + = BFieldR.BField.chip(imom,0) +#endif + ; for (int ig=0 ; ig < Ngamma ; ig++){ Eigen::Tensor B5L = B6L.chip(ig,0); Eigen::Tensor B5R = B6R.chip(ig,0); From 435653490e39ba50d5bcd15bc4426551013039af Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 17 May 2019 10:50:15 +0100 Subject: [PATCH 236/347] fixed contraction issue --- Grid/qcd/utils/A2Autils.h | 10 +++++++++- Hadrons/Modules/MDistil/Baryon2pt.hpp | 24 ++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 9e2bd7e2..81c53800 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -183,7 +183,8 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, for(int j=0;j C gamma_5 = - i gamma_1 gamma_3 //auto v2g = v2*Gamma(Gamma::Algebra::SigmaXZ); //auto v2g=v2; @@ -209,6 +210,13 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, + pv1()(s1)(2) * v2()(s2)(0) * gv3()(s2)(1) - pv1()(s1)(2) * v2()(s2)(1) * gv3()(s2)(0); }} + + /*if (i+j+k == 0) { + Serializable::WriteMember(std::cout, pv1); + Serializable::WriteMember(std::cout, v2); + Serializable::WriteMember(std::cout, gv3); + Serializable::WriteMember(std::cout, vv); + }*/ // After getting the sitewise product do the mom phase loop int base = Nmom*i+Nmom*oneBlock*j+Nmom*oneBlock*twoBlock*k+Nmom*oneBlock*twoBlock*threeBlock*r; diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index 02ac892a..d4420ecc 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -186,20 +186,16 @@ void TBaryon2pt::execute(void) Eigen::array, 3> product_dims = { Eigen::IndexPair(0,epsilon[pairs[ipair]][0]),Eigen::IndexPair(1,epsilon[pairs[ipair]][1]) ,Eigen::IndexPair(2,epsilon[pairs[ipair]][2]) }; for (int imom=0 ; imom < Nmom ; imom++){ std::cout << imom << std::endl; - Eigen::Tensor B6L = BFieldL.BField.chip(imom,0); - Eigen::Tensor B6R = BFieldR.BField.chip(imom,0); - for (int ig=0 ; ig < Ngamma ; ig++){ - Eigen::Tensor B5L = B6L.chip(ig,0); - Eigen::Tensor B5R = B6R.chip(ig,0); - for (int t=0 ; t < Nt ; t++){ - Eigen::Tensor B4L = B5L.chip(t,0); - Eigen::Tensor B4R = B5R.chip(tsrc,0); - for (int is=0 ; is < 4 ; is++){ - Eigen::Tensor B3L = B4L.chip(is,0); - Eigen::Tensor B3R = B4R.chip(is,0); - Eigen::Tensor C2 = B3L.contract(B3R,product_dims); - corr(imom,t) += static_cast(epsilon_sgn[pairs[ipair]])*C2(0); - } + Eigen::Tensor B5L = BFieldL.BField.chip(imom,0); + Eigen::Tensor B5R = BFieldR.BField.chip(imom,0); + for (int t=0 ; t < Nt ; t++){ + Eigen::Tensor B4L = B5L.chip(t,0); + Eigen::Tensor B4R = B5R.chip(tsrc,0); + for (int is=0 ; is < 4 ; is++){ + Eigen::Tensor B3L = B4L.chip(is,0); + Eigen::Tensor B3R = B4R.chip(is,0); + Eigen::Tensor C2 = B3L.contract(B3R,product_dims); + corr(imom,t) += static_cast(epsilon_sgn[pairs[ipair]])*C2(0); } } } From 9ff459816f61b665c5b87a095e0ba052ab42d620 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 1 Jun 2019 13:50:27 +0100 Subject: [PATCH 237/347] ReadBinary needs to do case insensitive name comparison (since I changed the default case of perambulator column names) --- Hadrons/Distil.hpp | 40 ++++++++++++++++++------------- documentation/GridXcode/readme.md | 2 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 5a5a4a23..9c809dbb 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -268,6 +268,25 @@ using Default_Writer = Grid::BinaryWriter; static const char * FileExtension = ".dat"; #endif +/****************************************************************************** + Case insensitive compare of two strings + ******************************************************************************/ + +bool CompareCaseInsensitive( const std::string &s1, const std::string &s2 ) { + auto Len = s1.size(); + bool bSame{ Len == s2.size() }; + for( int j = 0; bSame && j < Len; j++ ) { + wchar_t c1 = s1[j]; + if( c1 >= 'a' && c1 <= 'z' ) + c1 -= 'a' - 'A'; + wchar_t c2 = s2[j]; + if( c2 >= 'a' && c1 <= 'z' ) + c2 -= 'a' - 'A'; + bSame = ( c1 == c2 ); + } + return bSame; +} + /****************************************************************************** NamedTensor object This is an Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) @@ -299,7 +318,7 @@ public: ); public: // Named tensors are intended to be a superset of Eigen tensor - inline operator ET&() const { return tensor; } + inline operator ET&() { return tensor; } template inline const Scalar_& operator()(const std::array &Indices) const { return tensor.operator()(Indices); } @@ -506,7 +525,7 @@ void NamedTensor::ReadBinary(const std std::string s( l, '?' ); r.read(&s[0], l); // skip forward to matching name - while( IndexNames[d].size() > 0 && s != IndexNames[d] ) + while( IndexNames[d].size() > 0 && !CompareCaseInsensitive( s, IndexNames[d] ) ) assert(++d < NumIndices && "NamedTensor error: dimension name" ); if( IndexNames[d].size() == 0 ) IndexNames[d] = s; @@ -588,21 +607,8 @@ void NamedTensor::write(const char * f template bool NamedTensor::ValidateIndexNames( int iNumNames, const std::string * MatchNames ) const { bool bSame{ iNumNames == NumIndices_ && IndexNames.size() == NumIndices_ }; - for( int i = 0; bSame && i < NumIndices_; i++ ) { - // Case insensitive name compare - const int iMatchLen{ static_cast( MatchNames[i].size() ) }; - const int iNewLen { static_cast( IndexNames[i].size() ) }; - bSame = ( iMatchLen == iNewLen ); - for( int j = 0; bSame && j < iMatchLen; j++ ) { - wchar_t c1 = MatchNames[i][j]; - if( c1 >= 'a' && c1 <= 'z' ) - c1 -= 'a' - 'A'; - wchar_t c2 = IndexNames[i][j]; - if( c2 >= 'a' && c1 <= 'z' ) - c2 -= 'a' - 'A'; - bSame = ( c1 == c2 ); - } - } + for( int i = 0; bSame && i < NumIndices_; i++ ) + bSame = CompareCaseInsensitive( MatchNames[i], IndexNames[i] ); return bSame; } diff --git a/documentation/GridXcode/readme.md b/documentation/GridXcode/readme.md index 031ec72a..9a5e3407 100644 --- a/documentation/GridXcode/readme.md +++ b/documentation/GridXcode/readme.md @@ -298,7 +298,7 @@ The easiest way to link to all required libraries is to obtain a list of all lib and pasting the output ***with `-lGrid -lHadrons ` prepended*** (including the `-l` switches) directly into `OTHER_LDFLAGS`, e.g.: - -lGrid -lHadrons -lmpi -lhdf5_cpp -lz -lcrypto -llime -lfftw3f -lfftw3 -lmpfr -lgmp -lstdc++ -lm -lz -lhdf5 + -lGrid -lHadrons -lmpi -lhdf5_cpp -lhdf5 -lz -lcrypto -llime -lfftw3f -lfftw3 -lmpfr -lgmp -lm ## Make additional configurations From 54edb9906e6ae4a27439877a9d445f30aa8dc7f1 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 3 Jun 2019 15:20:46 +0100 Subject: [PATCH 238/347] Housekeeping. #include ---> #include --- .../fermion/ImprovedStaggeredFermion.cc | 2 +- .../qcd/action/fermion/StaggeredKernelsAsm.cc | 14 ++++---- .../action/fermion/StaggeredKernelsHand.cc | 2 +- Grid/qcd/action/fermion/WilsonKernelsAsm.cc | 4 +-- Hadrons/Distil.hpp | 36 +++++++++---------- documentation/GridXcode/readme.md | 3 +- 6 files changed, 29 insertions(+), 32 deletions(-) diff --git a/Grid/qcd/action/fermion/ImprovedStaggeredFermion.cc b/Grid/qcd/action/fermion/ImprovedStaggeredFermion.cc index 4a0f7e63..883db902 100644 --- a/Grid/qcd/action/fermion/ImprovedStaggeredFermion.cc +++ b/Grid/qcd/action/fermion/ImprovedStaggeredFermion.cc @@ -26,7 +26,7 @@ See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#include +#include namespace Grid { namespace QCD { diff --git a/Grid/qcd/action/fermion/StaggeredKernelsAsm.cc b/Grid/qcd/action/fermion/StaggeredKernelsAsm.cc index 990ac126..9711c487 100644 --- a/Grid/qcd/action/fermion/StaggeredKernelsAsm.cc +++ b/Grid/qcd/action/fermion/StaggeredKernelsAsm.cc @@ -26,11 +26,11 @@ Author: paboyle See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#include +#include #ifdef AVX512 -#include -#include +#include +#include #endif // Interleave operations from two directions @@ -679,7 +679,7 @@ void StaggeredKernels::DhopSiteAsm(StencilImpl &st, LebesgueOrder &lo, gauge3 =(uint64_t)&UU._odata[sU]( T ); // This is the single precision 5th direction vectorised kernel -#include +#include template <> void StaggeredKernels::DhopSiteAsm(StencilImpl &st, LebesgueOrder &lo, DoubledGaugeField &U, DoubledGaugeField &UUU, SiteSpinor *buf, int LLs, int sU, @@ -732,7 +732,7 @@ template <> void StaggeredKernels::DhopSiteAsm(StencilImpl } -#include +#include template <> void StaggeredKernels::DhopSiteAsm(StencilImpl &st, LebesgueOrder &lo, DoubledGaugeField &U, DoubledGaugeField &UUU, SiteSpinor *buf, int LLs, int sU, @@ -816,7 +816,7 @@ template <> void StaggeredKernels::DhopSiteAsm(StencilImpl // This is the single precision 5th direction vectorised kernel -#include +#include template <> void StaggeredKernels::DhopSiteAsm(StencilImpl &st, LebesgueOrder &lo, DoubledGaugeField &U, DoubledGaugeField &UUU, SiteSpinor *buf, int LLs, int sU, @@ -884,7 +884,7 @@ template <> void StaggeredKernels::DhopSiteAsm(StencilImpl &st, #endif } -#include +#include template <> void StaggeredKernels::DhopSiteAsm(StencilImpl &st, LebesgueOrder &lo, DoubledGaugeField &U, DoubledGaugeField &UUU, SiteSpinor *buf, int LLs, int sU, diff --git a/Grid/qcd/action/fermion/StaggeredKernelsHand.cc b/Grid/qcd/action/fermion/StaggeredKernelsHand.cc index 47ebdd86..f304b00f 100644 --- a/Grid/qcd/action/fermion/StaggeredKernelsHand.cc +++ b/Grid/qcd/action/fermion/StaggeredKernelsHand.cc @@ -26,7 +26,7 @@ Author: paboyle See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#include +#include #define LOAD_CHI(b) \ diff --git a/Grid/qcd/action/fermion/WilsonKernelsAsm.cc b/Grid/qcd/action/fermion/WilsonKernelsAsm.cc index cd5d2430..55911988 100644 --- a/Grid/qcd/action/fermion/WilsonKernelsAsm.cc +++ b/Grid/qcd/action/fermion/WilsonKernelsAsm.cc @@ -81,8 +81,8 @@ WilsonKernels::AsmDhopSiteDagExt(StencilImpl &st,LebesgueOrder & lo,Doubl assert(0); } -#include -#include +#include +#include #define INSTANTIATE_ASM(A)\ template void WilsonKernels::AsmDhopSite(StencilImpl &st,LebesgueOrder & lo,DoubledGaugeField &U, SiteHalfSpinor *buf,\ diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 9c809dbb..edb7cbf6 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -268,25 +268,6 @@ using Default_Writer = Grid::BinaryWriter; static const char * FileExtension = ".dat"; #endif -/****************************************************************************** - Case insensitive compare of two strings - ******************************************************************************/ - -bool CompareCaseInsensitive( const std::string &s1, const std::string &s2 ) { - auto Len = s1.size(); - bool bSame{ Len == s2.size() }; - for( int j = 0; bSame && j < Len; j++ ) { - wchar_t c1 = s1[j]; - if( c1 >= 'a' && c1 <= 'z' ) - c1 -= 'a' - 'A'; - wchar_t c2 = s2[j]; - if( c2 >= 'a' && c1 <= 'z' ) - c2 -= 'a' - 'A'; - bSame = ( c1 == c2 ); - } - return bSame; -} - /****************************************************************************** NamedTensor object This is an Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) @@ -377,6 +358,23 @@ public: // Original I/O implementation. This will be removed when we're sure it's no longer needed EIGEN_DEPRECATED inline void ReadBinary (const std::string filename); // To be removed EIGEN_DEPRECATED inline void WriteBinary(const std::string filename); // To be removed + + // Case insensitive compare of two strings + // Pesumably this exists already? Where should this go? + static inline bool CompareCaseInsensitive( const std::string &s1, const std::string &s2 ) { + auto Len = s1.size(); + bool bSame{ Len == s2.size() }; + for( int j = 0; bSame && j < Len; j++ ) { + wchar_t c1 = s1[j]; + if( c1 >= 'a' && c1 <= 'z' ) + c1 -= 'a' - 'A'; + wchar_t c2 = s2[j]; + if( c2 >= 'a' && c1 <= 'z' ) + c2 -= 'a' - 'A'; + bSame = ( c1 == c2 ); + } + return bSame; + } }; // Is this a named tensor diff --git a/documentation/GridXcode/readme.md b/documentation/GridXcode/readme.md index 9a5e3407..8d9d7ad8 100644 --- a/documentation/GridXcode/readme.md +++ b/documentation/GridXcode/readme.md @@ -262,7 +262,6 @@ Set HEADER_SEARCH_PATHS to: $Grid/build$(CONFIGURATION)/Grid $Grid - $Grid/Grid followed by (***the order is important***) the locations reported by `grid-config --cxxflags`, ignoring duplicates, e.g.: @@ -272,7 +271,7 @@ followed by (***the order is important***) the locations reported by `grid-confi **Note: the easiest way to set this value is to put it all on one line, space separated, and edit the text to the right of `HEADER_SEARCH_PATHS`**, i.e.: - $Grid/build$(CONFIGURATION)/Grid $Grid $Grid/Grid $GridPre/openmpi/include $GridPkg/include $GridPre/lime/include + $Grid/build$(CONFIGURATION)/Grid $Grid $GridPre/openmpi/include $GridPkg/include $GridPre/lime/include #### LIBRARY_SEARCH_PATHS From fe72dc099ba2fe65bdcfd31bd75377a5246512d7 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 4 Jun 2019 16:12:24 +0100 Subject: [PATCH 239/347] Upgrade to Mojave forced me to reinstall MacPorts. These are the ports I installed to get Grid working --- documentation/GridXcode/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/GridXcode/readme.md b/documentation/GridXcode/readme.md index 8d9d7ad8..e48b0a34 100644 --- a/documentation/GridXcode/readme.md +++ b/documentation/GridXcode/readme.md @@ -119,13 +119,13 @@ Install [MacPorts][MacPorts] if you haven't done so already, and then install pa These are the `portname`s for mandatory Grid libraries: -* git +* git-flow-avh * gmp * mpfr and these are the `portname`s for optional Grid libraries: -* fftw-3 +* fftw-3-single * hdf5 * lapack * doxygen From 9a8a63467e071b88913b211ae11a7bea9242026d Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 12 Jun 2019 15:25:59 +0100 Subject: [PATCH 240/347] BC2 now runs. setup() runs twice, which had resulted in doubling up of momenta. Also fixed initialisation of momentum phases. --- Grid/qcd/utils/A2Autils.h | 12 +++++------ Hadrons/Modules/MDistil/BC2.hpp | 37 +++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 81c53800..73b9d16f 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -43,9 +43,9 @@ public: static void NucleonFieldMom(Eigen::Tensor &mat, - const FermionField *one, - const FermionField *two, - const FermionField *three, + const std::vector &one, + const std::vector &two, + const std::vector &three, const std::vector &mom, int parity, int orthogdim); @@ -112,9 +112,9 @@ public: template void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, - const FermionField *one, - const FermionField *two, - const FermionField *three, + const std::vector &one, + const std::vector &two, + const std::vector &three, const std::vector &mom, int parity, int orthogdim) diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp index 5c3433cc..d3c4475a 100644 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -56,7 +56,7 @@ BEGIN_MODULE_NAMESPACE(MDistil) // 3 - s - free spin index // 4 - i - left distillation mode index // 5 - j - middle distillation mode index - // 6 - k - left distillation mode index + // 6 - k - right distillation mode index // template // using BaryonTensorSet = Eigen::TensorMap>; @@ -133,6 +133,7 @@ std::vector TBC2::getOutput(void) template void TBC2::setup(void) { + if(!mom_.size()) { for (auto &pstr: par().mom) { auto p = strToVec(pstr); @@ -143,8 +144,19 @@ void TBC2::setup(void) } mom_.push_back(p); } - envCache(std::vector, momphName_, 1, - par().mom.size(), envGetGrid(ComplexField)); + } + //envCache(std::vector, momphName_, 1, par().mom.size(), envGetGrid(ComplexField)); + static GridCartesian * MyGrid{env().getGrid()}; + if( MyGrid == envGetGrid(ComplexField) ) + LOG(Message) << "envGetGrid(ComplexField) == env().getGrid()" << std::endl; + else + LOG(Message) << "envGetGrid(ComplexField) != env().getGrid()" << std::endl; + envTmp(std::vector, "ph", 1, std::vector()); + envGetTmp(std::vector, ph); + if(!ph.size()) { + for (unsigned int j = 0; j < par().mom.size(); ++j) + ph.push_back(ComplexField(MyGrid)); + } envTmpLat(ComplexField, "coor"); } @@ -153,16 +165,14 @@ void TBC2::setup(void) template void TBC2::execute(void) { - - auto &one = envGet(std::vector, par().one); auto &two = envGet(std::vector, par().two); auto &three = envGet(std::vector, par().three); const std::string &output{par().output}; - int N_1 = one.size(); - int N_2 = two.size(); - int N_3 = three.size(); + int N_1 = static_cast(one.size()); + int N_2 = static_cast(two.size()); + int N_3 = static_cast(three.size()); LOG(Message) << "Computing distillation baryon fields" << std::endl; LOG(Message) << "One: '" << par().one << "' Two: '" << par().two << "' Three: '" << par().three << "'" << std::endl; @@ -173,13 +183,14 @@ void TBC2::execute(void) } - int Nmom = mom_.size(); + int Nmom = static_cast(mom_.size()); const int Nt{env().getDim(Tdir)}; int parity = 1; int orthogDim=3; - auto &ph = envGet(std::vector, momphName_); + //auto &ph = envGet(std::vector, momphName_); + envGetTmp(std::vector, ph); if (!hasPhase_) { @@ -201,11 +212,11 @@ void TBC2::execute(void) hasPhase_ = true; stopTimer("Momentum phases"); } - envCache(std::vector, momphName_, 1, mom_.size(), envGetGrid(ComplexField)); + //envCache(std::vector, momphName_, 1, mom_.size(), envGetGrid(ComplexField)); Eigen::Tensor m(Nmom,Nt,N_1,N_2,N_3,4); - A2Autils::NucleonFieldMom(m, &one[0], &two[0], &three[0], ph, parity, orthogDim); - //A2Autils::NucleonFieldMom(m, one, two, three, ph, parity, orthogDim); + //A2Autils::NucleonFieldMom(m, &one[0], &two[0], &three[0], ph, parity, orthogDim); + A2Autils::NucleonFieldMom(m, one, two, three, ph, parity, orthogDim); for (int is=0 ; is < 4 ; is++){ for (int t=0 ; t < Nt ; t++){ std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << m(0,t,0,0,0,is) << std::endl; From 015340d60ce0f519236667ab2650a79bca498547 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 19 Jun 2019 09:37:03 +0100 Subject: [PATCH 241/347] Elided superfluous copy on write --- Hadrons/Modules/MDistil/BC2.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp index d3c4475a..6d30ad22 100644 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -223,8 +223,8 @@ void TBC2::execute(void) } } - BFieldIO BField_save; - BField_save.BField = m; + //BFieldIO BField_save; + //BField_save.BField = m; GridCartesian * grid = env().getGrid(); @@ -232,7 +232,8 @@ void TBC2::execute(void) std::string filename ="./" + output + ".h5"; std::cout << "Writing to file " << filename << std::endl; Grid::Hdf5Writer writer(filename); - write(writer,"BaryonField",BField_save.BField); + //write(writer,"BaryonField",BField_save.BField); + write(writer,"BaryonField",m); } } From c28c5fc61b9075c342205e994bb84c94c90e8878 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 19 Jun 2019 10:31:41 +0100 Subject: [PATCH 242/347] Inserted four extra parameters just to make this test compile. Needs to be fixed properly --- tests/forces/Test_dwf_gpforce_eofa.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/forces/Test_dwf_gpforce_eofa.cc b/tests/forces/Test_dwf_gpforce_eofa.cc index 3afeaa43..91c78904 100644 --- a/tests/forces/Test_dwf_gpforce_eofa.cc +++ b/tests/forces/Test_dwf_gpforce_eofa.cc @@ -89,7 +89,10 @@ int main (int argc, char** argv) FermionAction Rop(U, *FGrid, *FrbGrid, *UGrid, *UrbGrid, mb, mf, mb, -1.0, 1, M5, params); OneFlavourRationalParams Params(0.95, 100.0, 5000, 1.0e-12, 12); ConjugateGradient CG(1.0e-12, 5000); - ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, true); + assert(0 && "MM 2019/06/19 Inserted four extra parameters to make this test compile. Needs to be fixed properly"); + ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, + CG, CG, CG, CG, // Inserted this line to make this test compile. Needs attention to make sure it works + Params, true); Meofa.refresh(U, RNG5); RealD S = Meofa.S(U); // pdag M p From 2d940a598c2488ee4293c03988d2ed57c5a4016b Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 19 Jun 2019 10:37:50 +0100 Subject: [PATCH 243/347] Inserted four extra parameters just to make this test compile. Needs to be fixed properly --- tests/forces/Test_dwf_force_eofa.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/forces/Test_dwf_force_eofa.cc b/tests/forces/Test_dwf_force_eofa.cc index f17579ae..e8c417ef 100644 --- a/tests/forces/Test_dwf_force_eofa.cc +++ b/tests/forces/Test_dwf_force_eofa.cc @@ -84,7 +84,10 @@ int main (int argc, char** argv) DomainWallEOFAFermionR Rop(U, *FGrid, *FrbGrid, *UGrid, *UrbGrid, mb, mf, mb, -1.0, 1, M5); OneFlavourRationalParams Params(0.95, 100.0, 5000, 1.0e-12, 12); ConjugateGradient CG(1.0e-12, 5000); - ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, Params, true); + assert(0 && "MM 2019/06/19 Inserted four extra parameters to make this test compile. Needs to be fixed properly"); + ExactOneFlavourRatioPseudoFermionAction Meofa(Lop, Rop, CG, + CG, CG, CG, CG, // Inserted this line to make this test compile. Needs attention to make sure it works + Params, true); Meofa.refresh(U, RNG5); RealD S = Meofa.S(U); // pdag M p From 5fc01882056d3681d0064fbd6360d81ad9c75e33 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Mon, 1 Jul 2019 14:51:59 +0100 Subject: [PATCH 244/347] started saving sinks --- Hadrons/Modules/MDistil/Perambulator.hpp | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index d297ca16..0da47c57 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -257,6 +257,43 @@ void TPerambulator::execute(void) //perambulator.WriteBinary(sPerambName); perambulator.write(sPerambName.c_str()); } + + const int X{grid4d->GlobalDimensions()[0]}; + const int Y{grid4d->GlobalDimensions()[1]}; + const int Z{grid4d->GlobalDimensions()[2]}; + const int T{grid4d->GlobalDimensions()[3]}; + + + if(grid4d->IsBoss()) { + Eigen::Tensor sink(nnoise,LI,Nt_inv,SI,X,Y,Z,T,3,4); + + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + for (int ix=0; ix < X; ix++) { + for (int iy=0; iy < Y; iy++) { + for (int iz=0; iz < Z; iz++) { + for (int it=0; it < T; it++) { + std::vector site({ix,iy,iz,it}); + for (int ic=0; ic < 3; ic++) { + for (int is=0; is < 4; is++) { + //peekSite(sink[inoise,dk,dt,ds,ix,iy,iz,it,ic,is],unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))]()(is)(ic),site); // Build fails when uncommenting + + }} + }}}} + } + } + } + } + + std::string filename ="./" + par().PerambFileName + "_sink.h5"; + std::cout << "Writing to file " << filename << std::endl; + Grid::Hdf5Writer writer(filename); + write(writer,"unsmeared_sink",sink); + } + + } END_MODULE_NAMESPACE From ae3abbe53d3875c657aeeec0e25b82bd0417726e Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 1 Jul 2019 17:28:27 +0100 Subject: [PATCH 245/347] Added the ability for Perambulator module to save unsmeared sinks through the addition of two optional parameters: UnsmearedSinkFileName: If present, specifies the filename to write to UnsmearedSinkMultiFile: defaults to true to write each sink vector to a different file, but can be set to 0 for a single file --- Hadrons/Modules/MDistil/Perambulator.hpp | 70 +++++++++++++----------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 0da47c57..6eaa40a1 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -49,6 +49,8 @@ public: std::string, solver, std::string, noise, std::string, PerambFileName, //stem!!! + std::string, UnsmearedSinkFileName, // Filename to save unsmeared sink + std::string, UnsmearedSinkMultiFile, // One file per vector? int, nvec, DistilParameters, Distil); }; @@ -125,7 +127,10 @@ void TPerambulator::setup(void) grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); DISTIL_PARAMETERS_DEFINE( true ); - + const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; + if( !UnsmearedSinkFileName.empty() ) + bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, true ) != 0 ); + envCreate(PerambTensor, getName(), 1, PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); envCreate(std::vector, getName() + "_unsmeared_sink", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); @@ -244,7 +249,7 @@ void TPerambulator::execute(void) } } } - } + } std::cout << "perambulator done" << std::endl; perambulator.SliceShare( grid3d, grid4d ); @@ -258,42 +263,43 @@ void TPerambulator::execute(void) perambulator.write(sPerambName.c_str()); } - const int X{grid4d->GlobalDimensions()[0]}; - const int Y{grid4d->GlobalDimensions()[1]}; - const int Z{grid4d->GlobalDimensions()[2]}; - const int T{grid4d->GlobalDimensions()[3]}; + // Save the unsmeared sink as well if requested + /*const int X{grid4d->GlobalDimensions()[0]}; + const int Y{grid4d->GlobalDimensions()[1]}; + const int Z{grid4d->GlobalDimensions()[2]}; + const int T{grid4d->GlobalDimensions()[3]}; - - if(grid4d->IsBoss()) { - Eigen::Tensor sink(nnoise,LI,Nt_inv,SI,X,Y,Z,T,3,4); - - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - for (int ix=0; ix < X; ix++) { + if(grid4d->IsBoss()) { + Eigen::Tensor sink(nnoise,LI,Nt_inv,SI,X,Y,Z,T,3,4); + + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + for (int ix=0; ix < X; ix++) { for (int iy=0; iy < Y; iy++) { - for (int iz=0; iz < Z; iz++) { - for (int it=0; it < T; it++) { - std::vector site({ix,iy,iz,it}); - for (int ic=0; ic < 3; ic++) { - for (int is=0; is < 4; is++) { - //peekSite(sink[inoise,dk,dt,ds,ix,iy,iz,it,ic,is],unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))]()(is)(ic),site); // Build fails when uncommenting - - }} - }}}} - } + for (int iz=0; iz < Z; iz++) { + for (int it=0; it < T; it++) { + std::vector site({ix,iy,iz,it}); + for (int ic=0; ic < 3; ic++) { + for (int is=0; is < 4; is++) { + //peekSite(sink[inoise,dk,dt,ds,ix,iy,iz,it,ic,is],unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))]()(is)(ic),site); // Build fails when uncommenting + + }} + }}}} } } } + }*/ - std::string filename ="./" + par().PerambFileName + "_sink.h5"; - std::cout << "Writing to file " << filename << std::endl; - Grid::Hdf5Writer writer(filename); - write(writer,"unsmeared_sink",sink); - } - - + const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; + if( !UnsmearedSinkFileName.empty() ) { + bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); + std::cout << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; + //Grid::Hdf5Writer writer(filename); + //write(writer,"unsmeared_sink",sink); + A2AVectorsIo::write(UnsmearedSinkFileName, unsmeared_sink, bMulti, vm().getTrajectory()); + } } END_MODULE_NAMESPACE From 2c1a077369235e3d69af2669bcadce840bef23c1 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 2 Jul 2019 17:55:28 +0100 Subject: [PATCH 246/347] continued on baryons --- Hadrons/Modules/MDistil/BC2.hpp | 2 +- Hadrons/Modules/MDistil/Baryon2pt.hpp | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp index 6d30ad22..a10d4eaa 100644 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ b/Hadrons/Modules/MDistil/BC2.hpp @@ -190,7 +190,7 @@ void TBC2::execute(void) int orthogDim=3; //auto &ph = envGet(std::vector, momphName_); - envGetTmp(std::vector, ph); + envGetTmp(std::vector, ph); if (!hasPhase_) { diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp index d4420ecc..520357bf 100644 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ b/Hadrons/Modules/MDistil/Baryon2pt.hpp @@ -131,15 +131,15 @@ void TBaryon2pt::execute(void) const std::string &output{par().output}; int Nmom=1; - int Nt=64; + int Nt=32; int Nc=3; //Num colours std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; int Ngamma=3; - int N_1=20; - int N_2=20; - int N_3=20; + int N_1=12; + int N_2=12; + int N_3=12; // using BaryonTensorSet = Eigen::Tensor; @@ -192,8 +192,8 @@ void TBaryon2pt::execute(void) Eigen::Tensor B4L = B5L.chip(t,0); Eigen::Tensor B4R = B5R.chip(tsrc,0); for (int is=0 ; is < 4 ; is++){ - Eigen::Tensor B3L = B4L.chip(is,0); - Eigen::Tensor B3R = B4R.chip(is,0); + Eigen::Tensor B3L = B4L.chip(is,3); + Eigen::Tensor B3R = B4R.chip(is,3); Eigen::Tensor C2 = B3L.contract(B3R,product_dims); corr(imom,t) += static_cast(epsilon_sgn[pairs[ipair]])*C2(0); } @@ -204,13 +204,14 @@ void TBaryon2pt::execute(void) std::cout << "C2(t=" << t << ") = " << corr(0,t) << std::endl; } - C2IO C2_save; + /* C2IO C2_save; C2_save.C2 = corr; std::string filename ="./" + output + ".h5"; std::cout << "Writing to file " << filename << std::endl; Hdf5Writer writer(filename); write(writer,"C2",C2_save.C2); + */ } END_MODULE_NAMESPACE From b7d0cf675154111e2a22038e55d3781b39182436 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 4 Jul 2019 22:06:37 +0100 Subject: [PATCH 247/347] buxfix in diquark sum / baryons --- Grid/qcd/utils/A2Autils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 73b9d16f..22815ac1 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -196,6 +196,7 @@ void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, SpinVector_v vv; for(int s1=0;s1::NucleonFieldMom(Eigen::Tensor &mat, - pv1()(s1)(1) * v2g()(s2)(0) * v3()(s2)(2) + pv1()(s1)(2) * v2g()(s2)(0) * v3()(s2)(1) - pv1()(s1)(2) * v2g()(s2)(1) * v3()(s2)(0); */ - vv()(s1)() = pv1()(s1)(0) * v2()(s2)(1) * gv3()(s2)(2) //Cross product + vv()(s1)() += pv1()(s1)(0) * v2()(s2)(1) * gv3()(s2)(2) //Cross product - pv1()(s1)(0) * v2()(s2)(2) * gv3()(s2)(1) + pv1()(s1)(1) * v2()(s2)(2) * gv3()(s2)(0) - pv1()(s1)(1) * v2()(s2)(0) * gv3()(s2)(2) From 3848da7c50a0a4e3556162e86ebe200fea0f200e Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Mon, 8 Jul 2019 17:43:14 +0100 Subject: [PATCH 248/347] added nucleon module (non-distillation) --- Hadrons/Modules.hpp | 139 ++++++------ Hadrons/Modules/MContraction/Nucleon.cc | 35 +++ Hadrons/Modules/MContraction/Nucleon.hpp | 168 ++++++++++++++ Hadrons/modules.inc | 266 ++++++++++++----------- 4 files changed, 407 insertions(+), 201 deletions(-) create mode 100644 Hadrons/Modules/MContraction/Nucleon.cc create mode 100644 Hadrons/Modules/MContraction/Nucleon.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 4a62cf02..07d24378 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,80 +1,81 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include #include +#include +#include #include #include +#include #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include +#include #include -#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/Modules/MContraction/Nucleon.cc b/Hadrons/Modules/MContraction/Nucleon.cc new file mode 100644 index 00000000..304ff07a --- /dev/null +++ b/Hadrons/Modules/MContraction/Nucleon.cc @@ -0,0 +1,35 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MContraction/Nucleon.cc + +Copyright (C) 2015-2019 + +Author: Antonin Portelli + +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 + +using namespace Grid; +using namespace Hadrons; +using namespace MContraction; + +template class Grid::Hadrons::MContraction::TNucleon; + diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp new file mode 100644 index 00000000..d2a5129d --- /dev/null +++ b/Hadrons/Modules/MContraction/Nucleon.hpp @@ -0,0 +1,168 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MContraction/Nucleon.hpp + +Copyright (C) 2015-2019 + +Author: Antonin Portelli +Author: Felix Erben + +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 */ + +#ifndef Hadrons_MContraction_Nucleon_hpp_ +#define Hadrons_MContraction_Nucleon_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * Nucleon * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MContraction) + +class NucleonPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(NucleonPar, + std::string, q1, + std::string, q2, + std::string, q3, + std::string, output); +}; + +template +class TNucleon: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl1, 1); + FERM_TYPE_ALIASES(FImpl2, 2); + FERM_TYPE_ALIASES(FImpl3, 3); + class Result: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(Result, + std::vector>>, corr); + }; +public: + // constructor + TNucleon(const std::string name); + // destructor + virtual ~TNucleon(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); +protected: + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(Nucleon, ARG(TNucleon), MContraction); + +/****************************************************************************** + * TNucleon implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TNucleon::TNucleon(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TNucleon::getInput(void) +{ + std::vector input = {par().q1, par().q2, par().q3}; + + return input; +} + +template +std::vector TNucleon::getOutput(void) +{ + std::vector out = {}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TNucleon::setup(void) +{ + envTmpLat(LatticeComplex, "c"); + envTmpLat(LatticeComplex, "diquark"); +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TNucleon::execute(void) +{ + LOG(Message) << "Computing nucleon contractions '" << getName() << "' using" + << " quarks '" << par().q1 << "', '" << par().q2 << "', and '" + << par().q3 << "'" << std::endl; + + auto &q1 = envGet(PropagatorField1, par().q1); + auto &q2 = envGet(PropagatorField2, par().q2); + auto &q3 = envGet(PropagatorField3, par().q2); + envGetTmp(LatticeComplex, c); + //envGetTmp(LatticeComplex, quark2); + //envGetTmp(LatticeComplex, quark3); + envGetTmp(LatticeComplex, diquark); + Result result; + + // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 + Gamma Cg5(Gamma::Algebra::SigmaXZ); + Gamma g4(Gamma::Algebra::GammaT); + + std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; + std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + for (int ie_src=0; ie_src < 6 ; ie_src++){ + int c1_src = epsilon[ie_src][0]; + int c2_src = epsilon[ie_src][1]; + int c3_src = epsilon[ie_src][2]; + for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ + int c1_snk = epsilon[ie_snk][0]; + int c2_snk = epsilon[ie_snk][1]; + int c3_snk = epsilon[ie_snk][2]; + auto f1 = peekColour(q1,c1_snk,c1_src); + auto f2 = peekColour(q2,c2_snk,c2_src); + auto f3 = peekColour(q3,c3_snk,c3_src); + diquark = trace(f2 * Cg5 * f3); + //diquark = q2()()(c2,1) * Cg5 * q3()()(c3,2); + auto temp = Cg5 * f1 * diquark; + auto g4_temp = g4 * temp; + int parity = 1; + c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * (double)parity * trace(temp + g4_temp); + } + } + + // saveResult(par().output, "meson", result); +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MContraction_Nucleon_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 5cb77b9e..aa6d3af8 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,161 +1,163 @@ modules_cc =\ - Modules/MContraction/Baryon.cc \ - Modules/MContraction/Meson.cc \ - Modules/MContraction/WeakMesonDecayKl2.cc \ - Modules/MContraction/WeakEye3pt.cc \ - Modules/MContraction/A2ALoop.cc \ - Modules/MContraction/WeakNonEye3pt.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/Gamma3pt.cc \ + Modules/MScalarSUN/Grad.cc \ + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/Div.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/TwoPoint.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MScalar/FreeProp.cc \ + Modules/MScalar/ChargedProp.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadCosmHol.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MIO/LoadA2AVectors.cc \ + Modules/MIO/LoadPerambulator.cc \ + Modules/MSink/Smear.cc \ + Modules/MSink/Point.cc \ Modules/MFermion/FreeProp.cc \ Modules/MFermion/GaugeProp.cc \ Modules/MFermion/EMLepton.cc \ - Modules/MSource/Momentum.cc \ - Modules/MSource/Point.cc \ - Modules/MSource/Wall.cc \ - Modules/MSource/SeqConserved.cc \ - Modules/MSource/SeqGamma.cc \ - Modules/MSource/SeqAslash.cc \ - Modules/MSource/Z2.cc \ - Modules/MSink/Point.cc \ - Modules/MSink/Smear.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MGauge/Random.cc \ + Modules/MGauge/StoutSmearing3D.cc \ + Modules/MGauge/StochEm.cc \ Modules/MGauge/StoutSmearing.cc \ Modules/MGauge/Unit.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MGauge/Random.cc \ Modules/MGauge/Electrify.cc \ - Modules/MGauge/StoutSmearing3D.cc \ + Modules/MGauge/UnitEm.cc \ Modules/MGauge/FundtoHirep.cc \ Modules/MGauge/GaugeFix.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ Modules/MUtilities/RandomVectors.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MScalar/FreeProp.cc \ - Modules/MScalar/ChargedProp.cc \ - Modules/MDistil/DistilVectors.cc \ - Modules/MDistil/g5_multiply.cc \ - Modules/MDistil/BC2.cc \ - Modules/MDistil/Noises.cc \ - Modules/MDistil/BContraction.cc \ - Modules/MDistil/LapEvec.cc \ - Modules/MDistil/Baryon2pt.cc \ - Modules/MDistil/Perambulator.cc \ Modules/MDistil/PerambFromSolve.cc \ - Modules/MNPR/Amputate.cc \ - Modules/MNPR/Bilinear.cc \ - Modules/MNPR/FourQuark.cc \ - Modules/MAction/Wilson.cc \ + Modules/MDistil/g5_multiply.cc \ + Modules/MDistil/LapEvec.cc \ + Modules/MDistil/Perambulator.cc \ + Modules/MDistil/Noises.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MDistil/BContraction.cc \ + Modules/MDistil/Baryon2pt.cc \ + Modules/MDistil/BC2.cc \ + Modules/MSource/Momentum.cc \ + Modules/MSource/SeqAslash.cc \ + Modules/MSource/Z2.cc \ + Modules/MSource/Point.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Wall.cc \ + Modules/MSource/SeqConserved.cc \ + Modules/MContraction/WeakEye3pt.cc \ + Modules/MContraction/Meson.cc \ + Modules/MContraction/A2AAslashField.cc \ + Modules/MContraction/Baryon.cc \ + Modules/MContraction/Nucleon.cc \ + Modules/MContraction/WeakNonEye3pt.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/WeakMesonDecayKl2.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MContraction/A2ALoop.cc \ + Modules/MContraction/Gamma3pt.cc \ Modules/MAction/MobiusDWF.cc \ - Modules/MAction/ZMobiusDWF.cc \ Modules/MAction/WilsonClover.cc \ + Modules/MAction/Wilson.cc \ Modules/MAction/DWF.cc \ Modules/MAction/ScaledDWF.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/StochFreeField.cc \ - Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ - Modules/MScalarSUN/Div.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadPerambulator.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MIO/LoadA2AVectors.cc + Modules/MAction/ZMobiusDWF.cc \ + Modules/MSolver/A2AVectors.cc \ + Modules/MSolver/RBPrecCG.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/A2AAslashVectors.cc \ + Modules/MNPR/Bilinear.cc \ + Modules/MNPR/FourQuark.cc \ + Modules/MNPR/Amputate.cc modules_hpp =\ - Modules/MContraction/WeakEye3pt.hpp \ - Modules/MContraction/Baryon.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/A2ALoop.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/WeakMesonDecayKl2.hpp \ - Modules/MContraction/WeakNonEye3pt.hpp \ - Modules/MFermion/FreeProp.hpp \ - Modules/MFermion/GaugeProp.hpp \ - Modules/MFermion/EMLepton.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/SeqAslash.hpp \ - Modules/MSource/Point.hpp \ - Modules/MSource/Wall.hpp \ - Modules/MSource/Z2.hpp \ - Modules/MSource/SeqConserved.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MSink/Point.hpp \ - Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ - Modules/MSolver/A2AAslashVectors.hpp \ - Modules/MSolver/Guesser.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MGauge/UnitEm.hpp \ - Modules/MGauge/StoutSmearing.hpp \ - Modules/MGauge/Unit.hpp \ - Modules/MGauge/Random.hpp \ - Modules/MGauge/GaugeFix.hpp \ - Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/StoutSmearing3D.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MUtilities/PrecisionCast.hpp \ - Modules/MUtilities/RandomVectors.hpp \ - Modules/MScalar/FreeProp.hpp \ - Modules/MScalar/Scalar.hpp \ - Modules/MScalar/ChargedProp.hpp \ - Modules/MDistil/LapEvec.hpp \ - Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/Noises.hpp \ - Modules/MDistil/BC2.hpp \ - Modules/MDistil/Perambulator.hpp \ - Modules/MDistil/PerambFromSolve.hpp \ - Modules/MDistil/BContraction.hpp \ - Modules/MDistil/Baryon2pt.hpp \ - Modules/MDistil/g5_multiply.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ - Modules/MAction/Wilson.hpp \ - Modules/MAction/WilsonClover.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ - Modules/MAction/ScaledDWF.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ Modules/MScalarSUN/StochFreeField.hpp \ Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/TransProj.hpp \ Modules/MScalarSUN/Div.hpp \ Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/Utils.hpp \ Modules/MScalarSUN/EMT.hpp \ Modules/MScalarSUN/TwoPoint.hpp \ Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MIO/LoadEigenPack.hpp \ - Modules/MIO/LoadNersc.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MScalar/FreeProp.hpp \ + Modules/MScalar/Scalar.hpp \ + Modules/MScalar/ChargedProp.hpp \ Modules/MIO/LoadPerambulator.hpp \ + Modules/MIO/LoadEigenPack.hpp \ Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadCosmHol.hpp \ Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadBinary.hpp + Modules/MIO/LoadCosmHol.hpp \ + Modules/MIO/LoadBinary.hpp \ + Modules/MIO/LoadNersc.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MSink/Point.hpp \ + Modules/MFermion/FreeProp.hpp \ + Modules/MFermion/GaugeProp.hpp \ + Modules/MFermion/EMLepton.hpp \ + Modules/MGauge/FundtoHirep.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/StoutSmearing.hpp \ + Modules/MGauge/Unit.hpp \ + Modules/MGauge/GaugeFix.hpp \ + Modules/MGauge/StoutSmearing3D.hpp \ + Modules/MGauge/StochEm.hpp \ + Modules/MGauge/Electrify.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MUtilities/RandomVectors.hpp \ + Modules/MUtilities/PrecisionCast.hpp \ + Modules/MDistil/Noises.hpp \ + Modules/MDistil/Perambulator.hpp \ + Modules/MDistil/BC2.hpp \ + Modules/MDistil/g5_multiply.hpp \ + Modules/MDistil/PerambFromSolve.hpp \ + Modules/MDistil/Baryon2pt.hpp \ + Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/BContraction.hpp \ + Modules/MDistil/DistilVectors.hpp \ + Modules/MSource/SeqConserved.hpp \ + Modules/MSource/SeqAslash.hpp \ + Modules/MSource/Z2.hpp \ + Modules/MSource/Wall.hpp \ + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MContraction/WeakMesonDecayKl2.hpp \ + Modules/MContraction/Nucleon.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/WeakEye3pt.hpp \ + Modules/MContraction/WeakNonEye3pt.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MContraction/Meson.hpp \ + Modules/MContraction/A2ALoop.hpp \ + Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/A2AMesonField.hpp \ + Modules/MAction/WilsonClover.hpp \ + Modules/MAction/ScaledDWF.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MAction/Wilson.hpp \ + Modules/MAction/DWF.hpp \ + Modules/MAction/ZMobiusDWF.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MSolver/A2AVectors.hpp \ + Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/Guesser.hpp \ + Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MNPR/Amputate.hpp From cc3346073e004cfa1df019a9f677b397fad2c9dd Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 9 Jul 2019 17:30:32 +0100 Subject: [PATCH 249/347] continued work on baryons --- Hadrons/Modules/MContraction/Nucleon.hpp | 35 +++++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp index d2a5129d..41e1348d 100644 --- a/Hadrons/Modules/MContraction/Nucleon.hpp +++ b/Hadrons/Modules/MContraction/Nucleon.hpp @@ -134,10 +134,11 @@ void TNucleon::execute(void) // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 Gamma Cg5(Gamma::Algebra::SigmaXZ); - Gamma g4(Gamma::Algebra::GammaT); + Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + // This is the \delta_{123}^{123} part for (int ie_src=0; ie_src < 6 ; ie_src++){ int c1_src = epsilon[ie_src][0]; int c2_src = epsilon[ie_src][1]; @@ -146,18 +147,38 @@ void TNucleon::execute(void) int c1_snk = epsilon[ie_snk][0]; int c2_snk = epsilon[ie_snk][1]; int c3_snk = epsilon[ie_snk][2]; - auto f1 = peekColour(q1,c1_snk,c1_src); - auto f2 = peekColour(q2,c2_snk,c2_src); - auto f3 = peekColour(q3,c3_snk,c3_src); - diquark = trace(f2 * Cg5 * f3); - //diquark = q2()()(c2,1) * Cg5 * q3()()(c3,2); - auto temp = Cg5 * f1 * diquark; + auto Dcc = peekColour(q1,c1_snk,c1_src); //D_{gamma' gamma} + auto Daa = peekColour(q2,c2_snk,c2_src); //D_{alpha' alpha} + auto Dbb = peekColour(q3,c3_snk,c3_src); //D_{beta' beta} + diquark = trace(Cg5 * Daa * Cg5 * Dbb); //Daa transposed???? + //diquark = q2()()(c2,1) * Cg5 * q3()()(c3,2); //Why does this not work?? + auto temp = Dcc * diquark; auto g4_temp = g4 * temp; int parity = 1; c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * (double)parity * trace(temp + g4_temp); } } + + // This is the \delta_{123}^{213} part + for (int ie_src=0; ie_src < 6 ; ie_src++){ + int c1_src = epsilon[ie_src][0]; + int c2_src = epsilon[ie_src][1]; + int c3_src = epsilon[ie_src][2]; + for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ + int c1_snk = epsilon[ie_snk][0]; + int c2_snk = epsilon[ie_snk][1]; + int c3_snk = epsilon[ie_snk][2]; + auto Dca = peekColour(q1,c1_snk,c2_src); //D_{gamma' alpha} + auto Dac = peekColour(q2,c2_snk,c1_src); //D_{alpha' gamma} + auto Dbb = peekColour(q3,c3_snk,c3_src); //D_{beta' beta} + auto temp = Dca * Cg5 * Dbb * Cg5 * Dac; //(Dbb*Cg5) transposed??? + auto g4_temp = g4 * temp; + int parity = 1; + c -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * (double)parity * trace(temp + g4_temp); + } + } + // saveResult(par().output, "meson", result); } From 98cf20cf0653abed5c11508c60b6279caeeacd74 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 9 Jul 2019 17:42:36 +0100 Subject: [PATCH 250/347] continued work on baryons --- Hadrons/Modules/MContraction/Nucleon.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp index 41e1348d..2caa8208 100644 --- a/Hadrons/Modules/MContraction/Nucleon.hpp +++ b/Hadrons/Modules/MContraction/Nucleon.hpp @@ -127,11 +127,12 @@ void TNucleon::execute(void) auto &q2 = envGet(PropagatorField2, par().q2); auto &q3 = envGet(PropagatorField3, par().q2); envGetTmp(LatticeComplex, c); - //envGetTmp(LatticeComplex, quark2); - //envGetTmp(LatticeComplex, quark3); envGetTmp(LatticeComplex, diquark); Result result; - + int nt = env().getDim(Tp); + result.corr.resize(nt); + + std::vector buf; // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 Gamma Cg5(Gamma::Algebra::SigmaXZ); Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) @@ -179,7 +180,13 @@ void TNucleon::execute(void) } } - // saveResult(par().output, "meson", result); + /* sliceSum(c,buf,Tp); + for (unsigned int t = 0; t < buf.size(); ++t) + { + result.corr[t] = TensorRemove(buf[t]); + }*/ + + // saveResult(par().output, "baryon", result); } END_MODULE_NAMESPACE From dc2240d2d8164f572eadb2d6c909613447ab92ce Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Wed, 10 Jul 2019 11:34:16 +0100 Subject: [PATCH 251/347] why does sliceSum in Nucleon.hpp not work --- Hadrons/Modules/MContraction/Nucleon.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp index 2caa8208..125bfd37 100644 --- a/Hadrons/Modules/MContraction/Nucleon.hpp +++ b/Hadrons/Modules/MContraction/Nucleon.hpp @@ -180,13 +180,13 @@ void TNucleon::execute(void) } } - /* sliceSum(c,buf,Tp); + sliceSum(c,buf,Tp); for (unsigned int t = 0; t < buf.size(); ++t) { - result.corr[t] = TensorRemove(buf[t]); - }*/ + //result.corr[t] = TensorRemove(buf[t]); + } - // saveResult(par().output, "baryon", result); + saveResult(par().output, "baryon", result); } END_MODULE_NAMESPACE From cd659525e164471336b6b812612892e9a92f7e45 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 10 Jul 2019 12:08:37 +0100 Subject: [PATCH 252/347] You probably want to add this to the build. And you may need to do a bootstrap --- Hadrons/modules.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index aa6d3af8..bcab1232 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -63,6 +63,7 @@ modules_cc =\ Modules/MContraction/A2AMesonField.cc \ Modules/MContraction/A2ALoop.cc \ Modules/MContraction/Gamma3pt.cc \ + Modules/MContraction/Nucleon.cc \ Modules/MAction/MobiusDWF.cc \ Modules/MAction/WilsonClover.cc \ Modules/MAction/Wilson.cc \ From 7bc4a06f3fa478fe93055c2d68874bbd030198b5 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 10 Jul 2019 12:29:33 +0100 Subject: [PATCH 253/347] This is probably what you want ... --- Hadrons/Modules/MContraction/Nucleon.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp index 125bfd37..1c510c7b 100644 --- a/Hadrons/Modules/MContraction/Nucleon.hpp +++ b/Hadrons/Modules/MContraction/Nucleon.hpp @@ -62,7 +62,7 @@ public: { public: GRID_SERIALIZABLE_CLASS_MEMBERS(Result, - std::vector>>, corr); + std::vector, corr); }; public: // constructor @@ -183,7 +183,7 @@ void TNucleon::execute(void) sliceSum(c,buf,Tp); for (unsigned int t = 0; t < buf.size(); ++t) { - //result.corr[t] = TensorRemove(buf[t]); + result.corr[t] = TensorRemove(buf[t]); } saveResult(par().output, "baryon", result); From ec4aa978aba12e949e7736acbed2ce81fa88812e Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 11 Jul 2019 14:01:41 +0100 Subject: [PATCH 254/347] why cant I spinTranspose --- Hadrons/Modules/MContraction/Nucleon.hpp | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp index 1c510c7b..8f69aaf2 100644 --- a/Hadrons/Modules/MContraction/Nucleon.hpp +++ b/Hadrons/Modules/MContraction/Nucleon.hpp @@ -141,15 +141,16 @@ void TNucleon::execute(void) std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; // This is the \delta_{123}^{123} part for (int ie_src=0; ie_src < 6 ; ie_src++){ - int c1_src = epsilon[ie_src][0]; - int c2_src = epsilon[ie_src][1]; - int c3_src = epsilon[ie_src][2]; + int c1_src = epsilon[ie_src][0]; //a + int c2_src = epsilon[ie_src][1]; //b + int c3_src = epsilon[ie_src][2]; //c for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ - int c1_snk = epsilon[ie_snk][0]; - int c2_snk = epsilon[ie_snk][1]; - int c3_snk = epsilon[ie_snk][2]; + int c1_snk = epsilon[ie_snk][0]; //a' + int c2_snk = epsilon[ie_snk][1]; //b' + int c3_snk = epsilon[ie_snk][2]; //c' auto Dcc = peekColour(q1,c1_snk,c1_src); //D_{gamma' gamma} auto Daa = peekColour(q2,c2_snk,c2_src); //D_{alpha' alpha} + //auto test = transposeSpin(Daa); //Does not work... auto Dbb = peekColour(q3,c3_snk,c3_src); //D_{beta' beta} diquark = trace(Cg5 * Daa * Cg5 * Dbb); //Daa transposed???? //diquark = q2()()(c2,1) * Cg5 * q3()()(c3,2); //Why does this not work?? @@ -163,13 +164,13 @@ void TNucleon::execute(void) // This is the \delta_{123}^{213} part for (int ie_src=0; ie_src < 6 ; ie_src++){ - int c1_src = epsilon[ie_src][0]; - int c2_src = epsilon[ie_src][1]; - int c3_src = epsilon[ie_src][2]; + int c1_src = epsilon[ie_src][0]; //a + int c2_src = epsilon[ie_src][1]; //b + int c3_src = epsilon[ie_src][2]; //c for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ - int c1_snk = epsilon[ie_snk][0]; - int c2_snk = epsilon[ie_snk][1]; - int c3_snk = epsilon[ie_snk][2]; + int c1_snk = epsilon[ie_snk][0]; //a' + int c2_snk = epsilon[ie_snk][1]; //b' + int c3_snk = epsilon[ie_snk][2]; //c' auto Dca = peekColour(q1,c1_snk,c2_src); //D_{gamma' alpha} auto Dac = peekColour(q2,c2_snk,c1_src); //D_{alpha' gamma} auto Dbb = peekColour(q3,c3_snk,c3_src); //D_{beta' beta} From 12afb0395f20ded5d33850b761259cbbc65f3203 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Thu, 11 Jul 2019 17:42:26 +0100 Subject: [PATCH 255/347] Debugging transposeSpin - seems just not to be implemented for Lattice --- Hadrons/Modules/MContraction/Nucleon.hpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp index 8f69aaf2..149d53a0 100644 --- a/Hadrons/Modules/MContraction/Nucleon.hpp +++ b/Hadrons/Modules/MContraction/Nucleon.hpp @@ -115,6 +115,11 @@ void TNucleon::setup(void) envTmpLat(LatticeComplex, "diquark"); } +#ifdef DEBUG +template struct DebugShowType { DebugShowType() { T t = (void***) nullptr; } }; +#define DEBUG_SHOW_TYPE(x) DebugShowType __DEBUG_SHOW_TYPE__##x +#endif + // execution /////////////////////////////////////////////////////////////////// template void TNucleon::execute(void) @@ -150,6 +155,18 @@ void TNucleon::execute(void) int c3_snk = epsilon[ie_snk][2]; //c' auto Dcc = peekColour(q1,c1_snk,c1_src); //D_{gamma' gamma} auto Daa = peekColour(q2,c2_snk,c2_src); //D_{alpha' alpha} + // DEBUG Just defining a few types so I can see what these things are + //auto Daa_debug1 = transposeSpin( q1 ); + // Current compilation settings tell me that FImpl is WilsonImplR (see FermionOperatorImpl.h, line 163) + WilsonImplR::PropagatorField &Debug_q_1{ q1 }; + //DEBUG_SHOW_TYPE( q1 ); + // The propagator field is an alias for + Lattice, Ns> >> &Debug_q_2{ q1 }; + // So then Daa is one of these + Lattice, Ns> >> &Debug_Daa_1{ Daa }; + // Which means I should be able to do this + //Lattice, Ns> >> Debug_Daa_2 = transposeSpin(Daa); + // END DEBUG //auto test = transposeSpin(Daa); //Does not work... auto Dbb = peekColour(q3,c3_snk,c3_src); //D_{beta' beta} diquark = trace(Cg5 * Daa * Cg5 * Dbb); //Daa transposed???? From fa747173d1b25fad0e840ae04a255676871aa44e Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sun, 14 Jul 2019 11:08:00 +0100 Subject: [PATCH 256/347] Debugging references were to l-values, so added const to stop errors --- Hadrons/Modules/MContraction/Nucleon.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp index 149d53a0..6c1633c7 100644 --- a/Hadrons/Modules/MContraction/Nucleon.hpp +++ b/Hadrons/Modules/MContraction/Nucleon.hpp @@ -158,12 +158,12 @@ void TNucleon::execute(void) // DEBUG Just defining a few types so I can see what these things are //auto Daa_debug1 = transposeSpin( q1 ); // Current compilation settings tell me that FImpl is WilsonImplR (see FermionOperatorImpl.h, line 163) - WilsonImplR::PropagatorField &Debug_q_1{ q1 }; + const WilsonImplR::PropagatorField &Debug_q_1{ q1 }; //DEBUG_SHOW_TYPE( q1 ); // The propagator field is an alias for - Lattice, Ns> >> &Debug_q_2{ q1 }; + const Lattice, Ns> >> &Debug_q_2{ q1 }; // So then Daa is one of these - Lattice, Ns> >> &Debug_Daa_1{ Daa }; + const Lattice, Ns> >> &Debug_Daa_1{ Daa }; // Which means I should be able to do this //Lattice, Ns> >> Debug_Daa_2 = transposeSpin(Daa); // END DEBUG From 5a62ebe7b180759ae25060e34926448f50d5fb26 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Mon, 15 Jul 2019 15:26:30 +0100 Subject: [PATCH 257/347] general baryons case added --- Hadrons/Modules/MContraction/Baryon.hpp | 120 ++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 7 deletions(-) diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index fc78ab80..977009c0 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -7,7 +7,7 @@ Source file: Hadrons/Modules/MContraction/Baryon.hpp Copyright (C) 2015-2019 Author: Antonin Portelli -Author: Lanny91 +Author: Felix Erben 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 @@ -62,7 +62,7 @@ public: { public: GRID_SERIALIZABLE_CLASS_MEMBERS(Result, - std::vector>>, corr); + std::vector, corr); }; public: // constructor @@ -112,13 +112,14 @@ template void TBaryon::setup(void) { envTmpLat(LatticeComplex, "c"); + envTmpLat(LatticeComplex, "diquark"); } // execution /////////////////////////////////////////////////////////////////// template void TBaryon::execute(void) { - LOG(Message) << "Computing baryon contractions '" << getName() << "' using" + LOG(Message) << "Computing nucleon contractions '" << getName() << "' using" << " quarks '" << par().q1 << "', '" << par().q2 << "', and '" << par().q3 << "'" << std::endl; @@ -126,11 +127,116 @@ void TBaryon::execute(void) auto &q2 = envGet(PropagatorField2, par().q2); auto &q3 = envGet(PropagatorField3, par().q2); envGetTmp(LatticeComplex, c); + envGetTmp(LatticeComplex, diquark); Result result; - - // FIXME: do contractions - - // saveResult(par().output, "meson", result); + int nt = env().getDim(Tp); + result.corr.resize(nt); + + std::vector buf; + // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 + Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 + Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 + Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) + + std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; + std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + + char left[] = "uud"; + char right[] = "uud"; + std::vector wick_contraction = {0,0,0,0,0,0}; + + for (int ie=0; ie < 6 ; ie++) + if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]) + wick_contraction[ie]=1; + + + int parity = 1; + + + for (int ie_src=0; ie_src < 6 ; ie_src++){ + int a_src = epsilon[ie_src][0]; //a + int b_src = epsilon[ie_src][1]; //b + int c_src = epsilon[ie_src][2]; //c + for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ + int a_snk = epsilon[ie_snk][0]; //a' + int b_snk = epsilon[ie_snk][1]; //b' + int c_snk = epsilon[ie_snk][2]; //c' + auto Daa = peekColour(q2,a_snk,a_src); //D_{alpha' alpha} + auto Dbb = peekColour(q3,b_snk,b_src); //D_{beta' beta} + auto Dcc = peekColour(q1,c_snk,c_src); //D_{gamma' gamma} + auto Dab = peekColour(q2,a_snk,b_src); //D_{alpha' beta} + auto Dac = peekColour(q2,a_snk,c_src); //D_{alpha' gamma} + auto Dba = peekColour(q3,b_snk,a_src); //D_{beta' alpha} + auto Dbc = peekColour(q3,b_snk,c_src); //D_{beta' gamma} + auto Dca = peekColour(q1,c_snk,a_src); //D_{gamma' alpha} + auto Dcb = peekColour(q1,c_snk,b_src); //D_{gamma' beta} + // This needs lees peekColours for some baryons, but does not compile - worth the effort? + /*if (wick_contraction[0] || wick_contraction[4]) + auto Daa = peekColour(q2,a_snk,a_src); //D_{alpha' alpha} + if (wick_contraction[0] || wick_contraction[5]) + auto Dbb = peekColour(q3,b_snk,b_src); //D_{beta' beta} + if (wick_contraction[0] || wick_contraction[3]) + auto Dcc = peekColour(q1,c_snk,c_src); //D_{gamma' gamma} + if (wick_contraction[1] || wick_contraction[3]) + auto Dab = peekColour(q2,a_snk,b_src); //D_{alpha' beta} + if (wick_contraction[2] || wick_contraction[5]) + auto Dac = peekColour(q2,a_snk,c_src); //D_{alpha' gamma} + if (wick_contraction[2] || wick_contraction[3]) + auto Dba = peekColour(q3,b_snk,a_src); //D_{beta' alpha} + if (wick_contraction[1] || wick_contraction[4]) + auto Dbc = peekColour(q3,b_snk,c_src); //D_{beta' gamma} + if (wick_contraction[1] || wick_contraction[5]) + auto Dca = peekColour(q1,c_snk,a_src); //D_{gamma' alpha} + if (wick_contraction[2] || wick_contraction[4]) + auto Dcb = peekColour(q1,c_snk,b_src); //D_{gamma' beta}*/ + // This is the \delta_{123}^{123} part + if (wick_contraction[0]){ + diquark = trace(GammaB * Daa * GammaB * Dbb); //1st GammaB and Daa transposed???? + auto temp = GammaA * Dcc * diquark; + auto g4_temp = GammaA * g4 * temp; + c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); + } + // This is the \delta_{123}^{231} part + if (wick_contraction[1]){ + auto temp = GammaA * Dca * GammaB * Dab * GammaB * Dbc; //Dab transposed??? + auto g4_temp = GammaA * g4 * temp; + c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); + } + // This is the \delta_{123}^{312} part + if (wick_contraction[2]){ + auto temp = GammaA * Dcb * GammaB * Dba * GammaB * Dac; //both GammaB and Dba transposed??? + auto g4_temp = GammaA * g4 * temp; + c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); + } + // This is the \delta_{123}^{132} part + if (wick_contraction[3]){ + diquark = trace(GammaB * Dba * GammaB * Dab); //2nd GammaB and Dab transposed???? + auto temp = GammaA * Dcc * diquark; + auto g4_temp = GammaA * g4 * temp; + c -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); + } + // This is the \delta_{123}^{321} part + if (wick_contraction[4]){ + auto temp = GammaA * Dcb * GammaB * Daa * GammaB * Dbc; //1st GammaB and Daa transposed??? + auto g4_temp = GammaA * g4 * temp; + c -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); + } + // This is the \delta_{123}^{213} part + if (wick_contraction[5]){ + auto temp = GammaA * Dca * GammaB * Dbb * GammaB * Dac; //(Dbb*GammaB) transposed??? + auto g4_temp = GammaA * g4 * temp; + c -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); + } + } + } + + sliceSum(c,buf,Tp); + for (unsigned int t = 0; t < buf.size(); ++t) + { + result.corr[t] = TensorRemove(buf[t]); + } + + saveResult(par().output, "baryon", result); } END_MODULE_NAMESPACE From feb029fb660e869d0296dbc57a63fdf5d380d3b9 Mon Sep 17 00:00:00 2001 From: ferben Date: Thu, 18 Jul 2019 14:24:16 +0100 Subject: [PATCH 258/347] new utils for baryons --- Grid/qcd/utils/BaryonUtils.h | 155 +++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 Grid/qcd/utils/BaryonUtils.h diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h new file mode 100644 index 00000000..020a3e07 --- /dev/null +++ b/Grid/qcd/utils/BaryonUtils.h @@ -0,0 +1,155 @@ +#pragma once +//#include +#include + +namespace Grid { +namespace QCD { + +#undef DELTA_F_EQ_2 + +template +class BaryonUtils +{ +public: + typedef typename FImpl::ComplexField ComplexField; + typedef typename FImpl::FermionField FermionField; + typedef typename FImpl::PropagatorField PropagatorField; + + typedef typename FImpl::SiteSpinor vobj; + typedef typename vobj::scalar_object sobj; + typedef typename vobj::scalar_type scalar_type; + typedef typename vobj::vector_type vector_type; + + + static void ContractBaryons(const PropagatorField &q1, + const PropagatorField &q2, + const PropagatorField &q3, + ComplexField &baryon_corr); +}; + + + +template +void BaryonUtils::ContractBaryons(const PropagatorField &q1, + const PropagatorField &q2, + const PropagatorField &q3, + ComplexField &baryon_corr) +{ + assert(gamma0.size()==gamma1.size()); + int Ng = gamma0.size(); + + GridBase *grid = q1._grid; + + // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 + Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 + Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 + Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) + + std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; + std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + char left[] = "uud"; + char right[] = "uud"; + std::vector wick_contraction = {0,0,0,0,0,0}; + + for (int ie=0; ie < 6 ; ie++) + if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]) + wick_contraction[ie]=1; + + + int parity = 1; + + + parallel_for(int ss=0;ssoSites();ss++){ + + typedef typename ComplexField::vector_object vobj; + + auto D1 = q1._odata[ss]; + auto D2 = q2._odata[ss]; + auto D3 = q3._odata[ss]; + + auto gD1a = GammaA * GammaA * D1; + auto gD1b = GammaA * g4 * GammaA * D1; + auto pD1 = 0.5* (gD1a + (double)parity * dD1b); + auto gD3 = GammaB * D3; + + vobj result=zero; + + for (int ie_src=0; ie_src < 6 ; ie_src++){ + int a_src = epsilon[ie_src][0]; //a + int b_src = epsilon[ie_src][1]; //b + int c_src = epsilon[ie_src][2]; //c + for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ + int a_snk = epsilon[ie_snk][0]; //a' + int b_snk = epsilon[ie_snk][1]; //b' + int c_snk = epsilon[ie_snk][2]; //c' + //This is the \delta_{123}^{123} part + if (wick_contraction[0]){ + auto D2g = D2 * GammaB; + for (int alpha_snk=0; alpha_snk Date: Thu, 18 Jul 2019 14:29:04 +0100 Subject: [PATCH 259/347] new utils for baryons --- Grid/qcd/utils/BaryonUtils.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 020a3e07..1e8fb253 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -35,9 +35,6 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1, const PropagatorField &q3, ComplexField &baryon_corr) { - assert(gamma0.size()==gamma1.size()); - int Ng = gamma0.size(); - GridBase *grid = q1._grid; // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 From 11a8668d1976d785db002116dec9a3f588d244ff Mon Sep 17 00:00:00 2001 From: ferben Date: Thu, 18 Jul 2019 14:44:55 +0100 Subject: [PATCH 260/347] bugfix in Baryonutils --- Grid/qcd/utils/BaryonUtils.h | 2 +- Hadrons/Modules/MContraction/Baryon.hpp | 26 ++++++------------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 1e8fb253..38506f3a 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -66,7 +66,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1, auto gD1a = GammaA * GammaA * D1; auto gD1b = GammaA * g4 * GammaA * D1; - auto pD1 = 0.5* (gD1a + (double)parity * dD1b); + auto pD1 = 0.5* (gD1a + (double)parity * gD1b); auto gD3 = GammaB * D3; vobj result=zero; diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index 977009c0..8e7cbbac 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -33,6 +33,7 @@ See the full license in the file "LICENSE" in the top level distribution directo #include #include #include +#include "/home/ferben/code/grid_devel/Grid/Grid/qcd/utils/BaryonUtils.h" BEGIN_HADRONS_NAMESPACE @@ -134,7 +135,7 @@ void TBaryon::execute(void) std::vector buf; // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 - Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 +/* Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) @@ -170,25 +171,6 @@ void TBaryon::execute(void) auto Dbc = peekColour(q3,b_snk,c_src); //D_{beta' gamma} auto Dca = peekColour(q1,c_snk,a_src); //D_{gamma' alpha} auto Dcb = peekColour(q1,c_snk,b_src); //D_{gamma' beta} - // This needs lees peekColours for some baryons, but does not compile - worth the effort? - /*if (wick_contraction[0] || wick_contraction[4]) - auto Daa = peekColour(q2,a_snk,a_src); //D_{alpha' alpha} - if (wick_contraction[0] || wick_contraction[5]) - auto Dbb = peekColour(q3,b_snk,b_src); //D_{beta' beta} - if (wick_contraction[0] || wick_contraction[3]) - auto Dcc = peekColour(q1,c_snk,c_src); //D_{gamma' gamma} - if (wick_contraction[1] || wick_contraction[3]) - auto Dab = peekColour(q2,a_snk,b_src); //D_{alpha' beta} - if (wick_contraction[2] || wick_contraction[5]) - auto Dac = peekColour(q2,a_snk,c_src); //D_{alpha' gamma} - if (wick_contraction[2] || wick_contraction[3]) - auto Dba = peekColour(q3,b_snk,a_src); //D_{beta' alpha} - if (wick_contraction[1] || wick_contraction[4]) - auto Dbc = peekColour(q3,b_snk,c_src); //D_{beta' gamma} - if (wick_contraction[1] || wick_contraction[5]) - auto Dca = peekColour(q1,c_snk,a_src); //D_{gamma' alpha} - if (wick_contraction[2] || wick_contraction[4]) - auto Dcb = peekColour(q1,c_snk,b_src); //D_{gamma' beta}*/ // This is the \delta_{123}^{123} part if (wick_contraction[0]){ diquark = trace(GammaB * Daa * GammaB * Dbb); //1st GammaB and Daa transposed???? @@ -229,8 +211,12 @@ void TBaryon::execute(void) } } } +*/ + + ContractBaryons(q1,q2,q3,c); sliceSum(c,buf,Tp); + for (unsigned int t = 0; t < buf.size(); ++t) { result.corr[t] = TensorRemove(buf[t]); From 97d61f25646ceaf53785d88e4580bb94ace376db Mon Sep 17 00:00:00 2001 From: ferben Date: Thu, 18 Jul 2019 14:57:10 +0100 Subject: [PATCH 261/347] bugfix in Baryonutils --- Hadrons/Modules/MContraction/Baryon.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index 8e7cbbac..bb63b6bb 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -33,7 +33,7 @@ See the full license in the file "LICENSE" in the top level distribution directo #include #include #include -#include "/home/ferben/code/grid_devel/Grid/Grid/qcd/utils/BaryonUtils.h" +#include BEGIN_HADRONS_NAMESPACE From 9d82855c5d8e7cc69ea018cc705596f0a415a167 Mon Sep 17 00:00:00 2001 From: ferben Date: Thu, 18 Jul 2019 15:45:43 +0100 Subject: [PATCH 262/347] bugfix in Baryonutils --- Hadrons/Modules/MContraction/Baryon.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index bb63b6bb..257da82c 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -213,7 +213,7 @@ void TBaryon::execute(void) } */ - ContractBaryons(q1,q2,q3,c); + BaryonUtils::ContractBaryons(q1,q2,q3,c); sliceSum(c,buf,Tp); From 56cefadf9bd951134ac93654c257631801b00cf9 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 18 Jul 2019 17:46:43 +0100 Subject: [PATCH 263/347] gamma matrices as input --- Grid/qcd/utils/BaryonUtils.h | 13 +++++++++---- Hadrons/Modules/MContraction/Baryon.hpp | 20 ++++++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 38506f3a..ea2034de 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -24,6 +24,8 @@ public: static void ContractBaryons(const PropagatorField &q1, const PropagatorField &q2, const PropagatorField &q3, + const Gamma GammaA, + const Gamma GammaB, ComplexField &baryon_corr); }; @@ -33,19 +35,22 @@ template void BaryonUtils::ContractBaryons(const PropagatorField &q1, const PropagatorField &q2, const PropagatorField &q3, + const Gamma GammaA, + const Gamma GammaB, ComplexField &baryon_corr) { GridBase *grid = q1._grid; // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 - Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 - Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 + //Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 + //Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 + //Gamma GammaB(Gamma::Algebra::GammaZGamma5); //Still hardcoded CgX = i gamma_3 gamma_5 Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - char left[] = "uud"; - char right[] = "uud"; + char left[] = "sss"; + char right[] = "sss"; std::vector wick_contraction = {0,0,0,0,0,0}; for (int ie=0; ie < 6 ; ie++) diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index 257da82c..5f3c9b71 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -49,6 +49,7 @@ public: std::string, q1, std::string, q2, std::string, q3, + std::string, gamma, std::string, output); }; @@ -132,7 +133,7 @@ void TBaryon::execute(void) Result result; int nt = env().getDim(Tp); result.corr.resize(nt); - + const std::string gamma{ par().gamma }; std::vector buf; // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 /* Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 @@ -213,7 +214,22 @@ void TBaryon::execute(void) } */ - BaryonUtils::ContractBaryons(q1,q2,q3,c); + Gamma GammaA(Gamma::Algebra::Identity); + Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 + if (gamma.compare("X") ==0){ + std::cout << "using interpolator C gamma_X"; + Gamma GammaB(Gamma::Algebra::GammaZGamma5); //Still hardcoded CgX = i gamma_3 gamma_5 + } + if (gamma.compare("Y") ==0){ + std::cout << "using interpolator C gamma_Y"; + Gamma GammaB(Gamma::Algebra::GammaT); //Still hardcoded CgX = - gamma_4 + } + if (gamma.compare("Z")==0){ + std::cout << "using interpolator C gamma_Z"; + Gamma GammaB(Gamma::Algebra::GammaXGamma5); //Still hardcoded CgX = i gamma_1 gamma_5 + } + + BaryonUtils::ContractBaryons(q1,q2,q3,GammaA,GammaB,c); sliceSum(c,buf,Tp); From 6d4fb35d844ed8563615e6fd64f1627c8ccde17d Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 19 Jul 2019 10:33:03 +0100 Subject: [PATCH 264/347] Ready for testing --- Hadrons/Modules/MContraction/Baryon.hpp | 47 +++++++++++++++++-------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index 5f3c9b71..561a91f4 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -79,6 +79,8 @@ protected: virtual void setup(void); // execution virtual void execute(void); + // Which gamma algebra was specified + Gamma::Algebra al; }; MODULE_REGISTER_TMP(Baryon, ARG(TBaryon), MContraction); @@ -115,6 +117,35 @@ void TBaryon::setup(void) { envTmpLat(LatticeComplex, "c"); envTmpLat(LatticeComplex, "diquark"); + // Translate the full string naming the desired gamma structure into the one we need to use + const std::string gamma{ par().gamma }; + int iGamma = 0; + while( gamma.compare( Gamma::name[iGamma] ) ) + assert( ++iGamma < Gamma::nGamma && "Invalid gamma structure specified" ); + switch( iGamma ) { + case Gamma::Algebra::GammaX: + std::cout << "using interpolator C gamma_X"; + al = Gamma::Algebra::GammaZGamma5; //Still hardcoded CgX = i gamma_3 gamma_5 + break; + case Gamma::Algebra::GammaY: + std::cout << "using interpolator C gamma_Y"; + al = Gamma::Algebra::GammaT; //Still hardcoded CgX = - gamma_4 + break; + case Gamma::Algebra::GammaZ: + std::cout << "using interpolator C gamma_Z"; + al = Gamma::Algebra::GammaXGamma5; //Still hardcoded CgX = i gamma_1 gamma_5 + break; + default: + { + LOG(Message) << "Unsupported gamma structure " << gamma << " = " << iGamma << std::endl; + assert( 0 && "Unsupported gamma structure" ); + // or you could do something like + al = static_cast( iGamma ); + break; + } + } + LOG(Message) << "Gamma structure " << gamma << " = " << iGamma + << " translated to " << Gamma::name[al] << std::endl; } // execution /////////////////////////////////////////////////////////////////// @@ -214,20 +245,8 @@ void TBaryon::execute(void) } */ - Gamma GammaA(Gamma::Algebra::Identity); - Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 - if (gamma.compare("X") ==0){ - std::cout << "using interpolator C gamma_X"; - Gamma GammaB(Gamma::Algebra::GammaZGamma5); //Still hardcoded CgX = i gamma_3 gamma_5 - } - if (gamma.compare("Y") ==0){ - std::cout << "using interpolator C gamma_Y"; - Gamma GammaB(Gamma::Algebra::GammaT); //Still hardcoded CgX = - gamma_4 - } - if (gamma.compare("Z")==0){ - std::cout << "using interpolator C gamma_Z"; - Gamma GammaB(Gamma::Algebra::GammaXGamma5); //Still hardcoded CgX = i gamma_1 gamma_5 - } + const Gamma GammaA{ Gamma::Algebra::Identity }; + const Gamma GammaB{ al }; BaryonUtils::ContractBaryons(q1,q2,q3,GammaA,GammaB,c); From e138bc7204d66215692abd1c5d4d127ccd60b6af Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 19 Jul 2019 11:16:35 +0100 Subject: [PATCH 265/347] debug output --- Hadrons/Modules/MContraction/Baryon.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index 561a91f4..c717f4a5 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -120,19 +120,20 @@ void TBaryon::setup(void) // Translate the full string naming the desired gamma structure into the one we need to use const std::string gamma{ par().gamma }; int iGamma = 0; + std::cout << "Gamma input " << gamma << std::endl; while( gamma.compare( Gamma::name[iGamma] ) ) assert( ++iGamma < Gamma::nGamma && "Invalid gamma structure specified" ); switch( iGamma ) { case Gamma::Algebra::GammaX: - std::cout << "using interpolator C gamma_X"; + std::cout << "using interpolator C gamma_X" << std::endl; al = Gamma::Algebra::GammaZGamma5; //Still hardcoded CgX = i gamma_3 gamma_5 break; case Gamma::Algebra::GammaY: - std::cout << "using interpolator C gamma_Y"; + std::cout << "using interpolator C gamma_Y" << std::endl; al = Gamma::Algebra::GammaT; //Still hardcoded CgX = - gamma_4 break; case Gamma::Algebra::GammaZ: - std::cout << "using interpolator C gamma_Z"; + std::cout << "using interpolator C gamma_Z" << std::endl; al = Gamma::Algebra::GammaXGamma5; //Still hardcoded CgX = i gamma_1 gamma_5 break; default: From e7050a7aede342c854cd4076c7d8f8e455e81271 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 19 Jul 2019 11:58:56 +0100 Subject: [PATCH 266/347] Support gamma structure names that have trailing white space --- Hadrons/Modules/MContraction/Baryon.hpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index c717f4a5..52508864 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -120,9 +120,20 @@ void TBaryon::setup(void) // Translate the full string naming the desired gamma structure into the one we need to use const std::string gamma{ par().gamma }; int iGamma = 0; - std::cout << "Gamma input " << gamma << std::endl; - while( gamma.compare( Gamma::name[iGamma] ) ) - assert( ++iGamma < Gamma::nGamma && "Invalid gamma structure specified" ); + do + { + const char * pGammaName = Gamma::name[iGamma]; + int iLen = 0; + while( pGammaName[iLen] && pGammaName[iLen] != ' ' ) + iLen++; + if( !gamma.compare( 0, gamma.size(), pGammaName, iLen ) ) + break; + } + while( ++iGamma < Gamma::nGamma ); + if( iGamma >= Gamma::nGamma ) { + LOG(Message) << "Unrecognised gamma structure \"" << gamma << "\"" << std::endl; + assert( 0 && "Invalid gamma structure specified" ); + } switch( iGamma ) { case Gamma::Algebra::GammaX: std::cout << "using interpolator C gamma_X" << std::endl; From f5ad4f3de8003843cfe01b27ccfa59ac1d51f04c Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 26 Jul 2019 19:46:55 +0100 Subject: [PATCH 267/347] Added the ability to write a version of the validated XML file excluding any of the module IDs supplied in a separate exclude file --- Hadrons/Application.cc | 21 +++++++---- Hadrons/Application.hpp | 3 +- Hadrons/Utilities/HadronsXmlValidate.cc | 47 ++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/Hadrons/Application.cc b/Hadrons/Application.cc index e4264f84..9a2451a8 100644 --- a/Hadrons/Application.cc +++ b/Hadrons/Application.cc @@ -180,7 +180,7 @@ void Application::parseParameterFile(const std::string parameterFileName) pop(reader); } -void Application::saveParameterFile(const std::string parameterFileName, unsigned int prec) +void Application::saveParameterFile(const std::string ¶meterFileName, const std::vector &Except, unsigned int prec) { LOG(Message) << "Saving application to '" << parameterFileName << "'..." << std::endl; if (env().getGrid()->IsBoss()) @@ -194,18 +194,27 @@ void Application::saveParameterFile(const std::string parameterFileName, unsigne push(writer, "modules"); for (unsigned int i = 0; i < nMod; ++i) { - push(writer, "module"); id.name = vm().getModuleName(i); - id.type = vm().getModule(i)->getRegisteredName(); - write(writer, "id", id); - vm().getModule(i)->saveParameters(writer, "options"); - pop(writer); + if( std::find( Except.begin(), Except.end(), id.name ) == Except.end() ) + { + push(writer, "module"); + id.type = vm().getModule(i)->getRegisteredName(); + write(writer, "id", id); + vm().getModule(i)->saveParameters(writer, "options"); + pop(writer); + } } pop(writer); pop(writer); } } +void Application::saveParameterFile(const std::string ¶meterFileName, unsigned int prec) +{ + const std::vector Except; + saveParameterFile(parameterFileName, Except, prec); +} + // schedule computation //////////////////////////////////////////////////////// void Application::schedule(void) { diff --git a/Hadrons/Application.hpp b/Hadrons/Application.hpp index 36179c5f..38442ea3 100644 --- a/Hadrons/Application.hpp +++ b/Hadrons/Application.hpp @@ -81,7 +81,8 @@ public: void run(void); // XML parameter file I/O void parseParameterFile(const std::string parameterFileName); - void saveParameterFile(const std::string parameterFileName, unsigned int prec=15); + void saveParameterFile(const std::string ¶meterFileName, unsigned int prec=15); + void saveParameterFile(const std::string ¶meterFileName, const std::vector &Except, unsigned int prec=15); // schedule computation void schedule(void); void saveSchedule(const std::string filename); diff --git a/Hadrons/Utilities/HadronsXmlValidate.cc b/Hadrons/Utilities/HadronsXmlValidate.cc index 73cf3139..a6100f2b 100644 --- a/Hadrons/Utilities/HadronsXmlValidate.cc +++ b/Hadrons/Utilities/HadronsXmlValidate.cc @@ -27,24 +27,52 @@ See the full license in the file "LICENSE" in the top level distribution directo /* END LEGAL */ #include +#include +#include +#include using namespace Grid; using namespace QCD; using namespace Hadrons; +// Does the specified file exist? +bool FileExists(const std::string& Filename) +{ + struct stat buf; + return stat(Filename.c_str(), &buf) != -1; +} + +void Shorten( Application &app, const std::string &FileList, const std::string OutFileName ) +{ + std::vector Except; + std::ifstream list{ FileList }; + for( std::string s; std::getline(list, s); ) { + //const std::string::size_type l{ s.find_first_of( '.' ) }; + //if( l != std::string::npos ) + //s.resize( l ); + if( s.length() ) + Except.push_back( s ); + } + std::sort( Except.begin(), Except.end() ); + for( const std::string &s : Except ) + std::cout << s << std::endl; + app.saveParameterFile( OutFileName, Except ); +} + int main(int argc, char *argv[]) { // parse command line std::string parameterFileName; - if (argc != 2) + if (argc != 2 && argc != 4) { - std::cerr << "usage: " << argv[0] << " "; + std::cerr << "usage: " << argv[0] << " [filelist.txt output.xml]"; std::cerr << std::endl; std::exit(EXIT_FAILURE); } parameterFileName = argv[1]; - + if( argc == 4 ) + Grid_init(&argc, &argv); try { Application application(parameterFileName); @@ -54,11 +82,22 @@ int main(int argc, char *argv[]) vm.getModuleGraph(); LOG(Message) << "Application valid (check XML warnings though)" << std::endl; + if( argc == 4 ) { + const std::string FileList{ argv[3] }; + const std::string OutFileName{ argv[2] }; + if( !FileExists( FileList ) ) + std::cout << "File list \"" << FileList << "\" does not exist" << std::endl; + else { + Shorten( application, FileList, OutFileName ); + } + } } catch (const std::exception& e) { Exceptions::abort(e); } - + if( argc == 4 ) + Grid_finalize(); + return EXIT_SUCCESS; } From e66d48c14246f3bf367283816c146c3c19c671f5 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Tue, 30 Jul 2019 13:46:59 +0100 Subject: [PATCH 268/347] second way to compute baryons - qdp style --- Grid/qcd/utils/BaryonUtils.h | 58 ++++++- Hadrons/Modules.hpp | 2 + Hadrons/Modules/MContraction/Baryon.hpp | 86 +--------- Hadrons/Modules/MContraction/Baryon2.cc | 35 ++++ Hadrons/Modules/MContraction/Baryon2.hpp | 206 +++++++++++++++++++++++ Hadrons/modules.inc | 4 +- 6 files changed, 306 insertions(+), 85 deletions(-) create mode 100644 Hadrons/Modules/MContraction/Baryon2.cc create mode 100644 Hadrons/Modules/MContraction/Baryon2.hpp diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index ea2034de..468c1f78 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -27,6 +27,9 @@ public: const Gamma GammaA, const Gamma GammaB, ComplexField &baryon_corr); + + static LatticeSpinColourMatrix quarkContract13(const PropagatorField &q1, + const PropagatorField &q2); }; @@ -139,19 +142,70 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1, }}} } - if (ie_src==0 && ie_snk==0){ + /*if (ie_src==0 && ie_snk==0){ baryon_corr._odata[ss] = result; } else { baryon_corr._odata[ss] += result; - } + }*/ } } + baryon_corr._odata[ss] = result; + } //end loop over lattice sites } +//QDP / CHROMA - style diquark construction +// (q_out)^{c'c}_{alpha,beta} = epsilon^{abc} epsilon^{a'b'c'} (q1)^{aa'}_{rho alpha}^* (q2)^{bb'}_{rho beta} +template +LatticeSpinColourMatrix BaryonUtils::quarkContract13(const PropagatorField &q1, + const PropagatorField &q2) +{ + GridBase *grid = q1._grid; + + + std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; + std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + std::vector wick_contraction = {0,0,0,0,0,0}; + + LatticeSpinColourMatrix q_out=zero; + + parallel_for(int ss=0;ssoSites();ss++){ + + typedef typename ComplexField::vector_object vobj; + + auto D1 = q1._odata[ss]; + auto D2 = q2._odata[ss]; + //auto D_out = q_out._odata[ss]; + //D_out=zero; + + SpinColourMatrix D_out=zero; + + for (int ie_src=0; ie_src < 6 ; ie_src++){ + int a_src = epsilon[ie_src][0]; //a + int b_src = epsilon[ie_src][1]; //b + int c_src = epsilon[ie_src][2]; //c + for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ + int a_snk = epsilon[ie_snk][0]; //a' + int b_snk = epsilon[ie_snk][1]; //b' + int c_snk = epsilon[ie_snk][2]; //c' + for (int alpha=0; alpha #include #include +#include #include #include #include +#include #include #include #include diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp index 52508864..f78759d8 100644 --- a/Hadrons/Modules/MContraction/Baryon.hpp +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -164,13 +164,13 @@ void TBaryon::setup(void) template void TBaryon::execute(void) { - LOG(Message) << "Computing nucleon contractions '" << getName() << "' using" - << " quarks '" << par().q1 << "', '" << par().q2 << "', and '" - << par().q3 << "'" << std::endl; + LOG(Message) << "Computing baryon contractions '" << getName() << "' using" + << " quarks '" << par().q1 << "', and a diquark formed of ('" << par().q2 << "', and '" + << par().q3 << "')" << std::endl; auto &q1 = envGet(PropagatorField1, par().q1); auto &q2 = envGet(PropagatorField2, par().q2); - auto &q3 = envGet(PropagatorField3, par().q2); + auto &q3 = envGet(PropagatorField3, par().q3); envGetTmp(LatticeComplex, c); envGetTmp(LatticeComplex, diquark); Result result; @@ -178,84 +178,6 @@ void TBaryon::execute(void) result.corr.resize(nt); const std::string gamma{ par().gamma }; std::vector buf; - // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 -/* Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 - Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 - Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) - - std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - - char left[] = "uud"; - char right[] = "uud"; - std::vector wick_contraction = {0,0,0,0,0,0}; - - for (int ie=0; ie < 6 ; ie++) - if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]) - wick_contraction[ie]=1; - - - int parity = 1; - - - for (int ie_src=0; ie_src < 6 ; ie_src++){ - int a_src = epsilon[ie_src][0]; //a - int b_src = epsilon[ie_src][1]; //b - int c_src = epsilon[ie_src][2]; //c - for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ - int a_snk = epsilon[ie_snk][0]; //a' - int b_snk = epsilon[ie_snk][1]; //b' - int c_snk = epsilon[ie_snk][2]; //c' - auto Daa = peekColour(q2,a_snk,a_src); //D_{alpha' alpha} - auto Dbb = peekColour(q3,b_snk,b_src); //D_{beta' beta} - auto Dcc = peekColour(q1,c_snk,c_src); //D_{gamma' gamma} - auto Dab = peekColour(q2,a_snk,b_src); //D_{alpha' beta} - auto Dac = peekColour(q2,a_snk,c_src); //D_{alpha' gamma} - auto Dba = peekColour(q3,b_snk,a_src); //D_{beta' alpha} - auto Dbc = peekColour(q3,b_snk,c_src); //D_{beta' gamma} - auto Dca = peekColour(q1,c_snk,a_src); //D_{gamma' alpha} - auto Dcb = peekColour(q1,c_snk,b_src); //D_{gamma' beta} - // This is the \delta_{123}^{123} part - if (wick_contraction[0]){ - diquark = trace(GammaB * Daa * GammaB * Dbb); //1st GammaB and Daa transposed???? - auto temp = GammaA * Dcc * diquark; - auto g4_temp = GammaA * g4 * temp; - c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); - } - // This is the \delta_{123}^{231} part - if (wick_contraction[1]){ - auto temp = GammaA * Dca * GammaB * Dab * GammaB * Dbc; //Dab transposed??? - auto g4_temp = GammaA * g4 * temp; - c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); - } - // This is the \delta_{123}^{312} part - if (wick_contraction[2]){ - auto temp = GammaA * Dcb * GammaB * Dba * GammaB * Dac; //both GammaB and Dba transposed??? - auto g4_temp = GammaA * g4 * temp; - c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); - } - // This is the \delta_{123}^{132} part - if (wick_contraction[3]){ - diquark = trace(GammaB * Dba * GammaB * Dab); //2nd GammaB and Dab transposed???? - auto temp = GammaA * Dcc * diquark; - auto g4_temp = GammaA * g4 * temp; - c -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); - } - // This is the \delta_{123}^{321} part - if (wick_contraction[4]){ - auto temp = GammaA * Dcb * GammaB * Daa * GammaB * Dbc; //1st GammaB and Daa transposed??? - auto g4_temp = GammaA * g4 * temp; - c -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); - } - // This is the \delta_{123}^{213} part - if (wick_contraction[5]){ - auto temp = GammaA * Dca * GammaB * Dbb * GammaB * Dac; //(Dbb*GammaB) transposed??? - auto g4_temp = GammaA * g4 * temp; - c -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * trace(GammaA * temp + (double)parity * g4_temp); - } - } - } -*/ const Gamma GammaA{ Gamma::Algebra::Identity }; const Gamma GammaB{ al }; diff --git a/Hadrons/Modules/MContraction/Baryon2.cc b/Hadrons/Modules/MContraction/Baryon2.cc new file mode 100644 index 00000000..b8d3bc52 --- /dev/null +++ b/Hadrons/Modules/MContraction/Baryon2.cc @@ -0,0 +1,35 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MContraction/Baryon2.cc + +Copyright (C) 2015-2019 + +Author: Antonin Portelli + +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 + +using namespace Grid; +using namespace Hadrons; +using namespace MContraction; + +template class Grid::Hadrons::MContraction::TBaryon2; + diff --git a/Hadrons/Modules/MContraction/Baryon2.hpp b/Hadrons/Modules/MContraction/Baryon2.hpp new file mode 100644 index 00000000..df928472 --- /dev/null +++ b/Hadrons/Modules/MContraction/Baryon2.hpp @@ -0,0 +1,206 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MContraction/Baryon2.hpp + +Copyright (C) 2015-2019 + +Author: Antonin Portelli +Author: Felix Erben + +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 */ + +#ifndef Hadrons_MContraction_Baryon2_hpp_ +#define Hadrons_MContraction_Baryon2_hpp_ + +#include +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * Baryon2 * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MContraction) + +class Baryon2Par: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(Baryon2Par, + std::string, q1, + std::string, q2, + std::string, q3, + std::string, gamma, + std::string, output); +}; + +template +class TBaryon2: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl1, 1); + FERM_TYPE_ALIASES(FImpl2, 2); + FERM_TYPE_ALIASES(FImpl3, 3); + class Result: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(Result, + std::vector, corr); + }; +public: + // constructor + TBaryon2(const std::string name); + // destructor + virtual ~TBaryon2(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); +protected: + // setup + virtual void setup(void); + // execution + virtual void execute(void); + // Which gamma algebra was specified + Gamma::Algebra al; +}; + +MODULE_REGISTER_TMP(Baryon2, ARG(TBaryon2), MContraction); + +/****************************************************************************** + * TBaryon2 implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TBaryon2::TBaryon2(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TBaryon2::getInput(void) +{ + std::vector input = {par().q1, par().q2, par().q3}; + + return input; +} + +template +std::vector TBaryon2::getOutput(void) +{ + std::vector out = {}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TBaryon2::setup(void) +{ + envTmpLat(LatticeComplex, "c"); + envTmpLat(LatticeComplex, "diquark"); + // Translate the full string naming the desired gamma structure into the one we need to use + const std::string gamma{ par().gamma }; + int iGamma = 0; + do + { + const char * pGammaName = Gamma::name[iGamma]; + int iLen = 0; + while( pGammaName[iLen] && pGammaName[iLen] != ' ' ) + iLen++; + if( !gamma.compare( 0, gamma.size(), pGammaName, iLen ) ) + break; + } + while( ++iGamma < Gamma::nGamma ); + if( iGamma >= Gamma::nGamma ) { + LOG(Message) << "Unrecognised gamma structure \"" << gamma << "\"" << std::endl; + assert( 0 && "Invalid gamma structure specified" ); + } + switch( iGamma ) { + case Gamma::Algebra::GammaX: + std::cout << "using interpolator C gamma_X" << std::endl; + al = Gamma::Algebra::GammaZGamma5; //Still hardcoded CgX = i gamma_3 gamma_5 + break; + case Gamma::Algebra::GammaY: + std::cout << "using interpolator C gamma_Y" << std::endl; + al = Gamma::Algebra::GammaT; //Still hardcoded CgX = - gamma_4 + break; + case Gamma::Algebra::GammaZ: + std::cout << "using interpolator C gamma_Z" << std::endl; + al = Gamma::Algebra::GammaXGamma5; //Still hardcoded CgX = i gamma_1 gamma_5 + break; + default: + { + LOG(Message) << "Unsupported gamma structure " << gamma << " = " << iGamma << std::endl; + assert( 0 && "Unsupported gamma structure" ); + // or you could do something like + al = static_cast( iGamma ); + break; + } + } + LOG(Message) << "Gamma structure " << gamma << " = " << iGamma + << " translated to " << Gamma::name[al] << std::endl; +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TBaryon2::execute(void) +{ + LOG(Message) << "Computing baryon contractions '" << getName() << "' using" + << " quarks '" << par().q1 << "', and a diquark formed of ('" << par().q2 << "', and '" + << par().q3 << "')" << std::endl; + + auto &q1 = envGet(PropagatorField1, par().q1); + auto &q2 = envGet(PropagatorField2, par().q2); + auto &q3 = envGet(PropagatorField3, par().q3); + envGetTmp(LatticeComplex, c); + //envGetTmp(LatticeComplex, diquark); + Result result; + int nt = env().getDim(Tp); + result.corr.resize(nt); + const std::string gamma{ par().gamma }; + std::vector buf; + + const Gamma GammaA{ Gamma::Algebra::Identity }; + const Gamma GammaB{ al }; + + LatticeSpinColourMatrix diquark; + + diquark = BaryonUtils::quarkContract13(q2*GammaB,GammaB*q3); + + //result = trace(GammaA*GammaA * traceColour(q1*traceSpin(diquark))) + 2.0 * trace(GammaA*GammaA*traceColour(q1*diquark)); + result = trace(q1*diquark); + + sliceSum(c,buf,Tp); + + for (unsigned int t = 0; t < buf.size(); ++t) + { + result.corr[t] = TensorRemove(buf[t]); + } + + saveResult(par().output, "baryon", result); +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MContraction_Baryon2_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index bcab1232..a18afd53 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -55,6 +55,7 @@ modules_cc =\ Modules/MContraction/WeakEye3pt.cc \ Modules/MContraction/Meson.cc \ Modules/MContraction/A2AAslashField.cc \ + Modules/MContraction/Baryon2.cc \ Modules/MContraction/Baryon.cc \ Modules/MContraction/Nucleon.cc \ Modules/MContraction/WeakNonEye3pt.cc \ @@ -63,7 +64,6 @@ modules_cc =\ Modules/MContraction/A2AMesonField.cc \ Modules/MContraction/A2ALoop.cc \ Modules/MContraction/Gamma3pt.cc \ - Modules/MContraction/Nucleon.cc \ Modules/MAction/MobiusDWF.cc \ Modules/MAction/WilsonClover.cc \ Modules/MAction/Wilson.cc \ @@ -138,9 +138,11 @@ modules_hpp =\ Modules/MContraction/WeakMesonDecayKl2.hpp \ Modules/MContraction/Nucleon.hpp \ Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/Baryon2.hpp \ Modules/MContraction/WeakEye3pt.hpp \ Modules/MContraction/WeakNonEye3pt.hpp \ Modules/MContraction/Baryon.hpp \ + Modules/MContraction/Baryon_old.hpp \ Modules/MContraction/Meson.hpp \ Modules/MContraction/A2ALoop.hpp \ Modules/MContraction/Gamma3pt.hpp \ From 6f400218427927c24b0d083306f523348c105876 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Thu, 1 Aug 2019 19:57:59 +0100 Subject: [PATCH 269/347] Fixed compiler errors: TODO: Felix, please validate --- Grid/qcd/utils/BaryonUtils.h | 27 +++++++----------------- Hadrons/Modules.hpp | 1 - Hadrons/Modules/MContraction/Baryon2.hpp | 2 +- Hadrons/modules.inc | 1 - 4 files changed, 9 insertions(+), 22 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 468c1f78..c658a220 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -165,24 +165,19 @@ LatticeSpinColourMatrix BaryonUtils::quarkContract13(const PropagatorFiel { GridBase *grid = q1._grid; - std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; std::vector wick_contraction = {0,0,0,0,0,0}; - LatticeSpinColourMatrix q_out=zero; + // TODO: Felix, made a few changes to fix this as there were compiler errors. Please validate! + LatticeSpinColourMatrix q_out(grid); + // q_out = zero; TODO: Don't think you need this, as you'll set each site explicitly anyway parallel_for(int ss=0;ssoSites();ss++){ - - typedef typename ComplexField::vector_object vobj; - - auto D1 = q1._odata[ss]; - auto D2 = q2._odata[ss]; - //auto D_out = q_out._odata[ss]; - //D_out=zero; - - SpinColourMatrix D_out=zero; - + const auto & D1 = q1._odata[ss]; + const auto & D2 = q2._odata[ss]; + auto & D_out = q_out._odata[ss]; + D_out=zero; for (int ie_src=0; ie_src < 6 ; ie_src++){ int a_src = epsilon[ie_src][0]; //a int b_src = epsilon[ie_src][1]; //b @@ -198,14 +193,8 @@ LatticeSpinColourMatrix BaryonUtils::quarkContract13(const PropagatorFiel }}} } } - - q_out._odata[ss]=D_out; - - } //end loop over lattice sites - + } return q_out; - } - }} diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 889763ea..c8c6cd99 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -60,7 +60,6 @@ #include #include #include -#include #include #include #include diff --git a/Hadrons/Modules/MContraction/Baryon2.hpp b/Hadrons/Modules/MContraction/Baryon2.hpp index df928472..3004248b 100644 --- a/Hadrons/Modules/MContraction/Baryon2.hpp +++ b/Hadrons/Modules/MContraction/Baryon2.hpp @@ -182,7 +182,7 @@ void TBaryon2::execute(void) const Gamma GammaA{ Gamma::Algebra::Identity }; const Gamma GammaB{ al }; - LatticeSpinColourMatrix diquark; + LatticeSpinColourMatrix diquark( q1._grid ); // TODO: Felix, I added "q1._grid". I presume this is correct? diquark = BaryonUtils::quarkContract13(q2*GammaB,GammaB*q3); diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index a18afd53..1a88f14c 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -142,7 +142,6 @@ modules_hpp =\ Modules/MContraction/WeakEye3pt.hpp \ Modules/MContraction/WeakNonEye3pt.hpp \ Modules/MContraction/Baryon.hpp \ - Modules/MContraction/Baryon_old.hpp \ Modules/MContraction/Meson.hpp \ Modules/MContraction/A2ALoop.hpp \ Modules/MContraction/Gamma3pt.hpp \ From 723457d46751c66246e586e20a3a4e4944f41832 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Thu, 1 Aug 2019 20:35:55 +0100 Subject: [PATCH 270/347] Contractor updates ready for test on Tesseract: 1) Move definitions of serialisable objects into header for re-use by external programs/utilities 2) Add "-s" switch for "Simple" correlators, i.e. only include A2AMatrix info for the actual fields included in each contraction --- Hadrons/Utilities/Contractor.cc | 115 +++++++++++-------------------- Hadrons/Utilities/Contractor.hpp | 70 +++++++++++++++++++ 2 files changed, 109 insertions(+), 76 deletions(-) diff --git a/Hadrons/Utilities/Contractor.cc b/Hadrons/Utilities/Contractor.cc index cfa4492e..15c970b9 100644 --- a/Hadrons/Utilities/Contractor.cc +++ b/Hadrons/Utilities/Contractor.cc @@ -25,7 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#include + +#include #include #include #include @@ -34,67 +35,6 @@ using namespace Grid; using namespace QCD; using namespace Hadrons; -#define TIME_MOD(t) (((t) + par.global.nt) % par.global.nt) - -namespace Contractor -{ - class TrajRange: Serializable - { - public: - GRID_SERIALIZABLE_CLASS_MEMBERS(TrajRange, - unsigned int, start, - unsigned int, end, - unsigned int, step); - }; - - class GlobalPar: Serializable - { - public: - GRID_SERIALIZABLE_CLASS_MEMBERS(GlobalPar, - TrajRange, trajCounter, - unsigned int, nt, - std::string, diskVectorDir, - std::string, output); - }; - - class A2AMatrixPar: Serializable - { - public: - GRID_SERIALIZABLE_CLASS_MEMBERS(A2AMatrixPar, - std::string, file, - std::string, dataset, - unsigned int, cacheSize, - std::string, name); - }; - - class ProductPar: Serializable - { - public: - GRID_SERIALIZABLE_CLASS_MEMBERS(ProductPar, - std::string, terms, - std::vector, times, - std::string, translations, - bool, translationAverage); - }; - - class CorrelatorResult: Serializable - { - public: - GRID_SERIALIZABLE_CLASS_MEMBERS(CorrelatorResult, - std::vector, a2aMatrix, - ProductPar, contraction, - std::vector, times, - std::vector, correlator); - }; -} - -struct ContractorPar -{ - Contractor::GlobalPar global; - std::vector a2aMatrix; - std::vector product; -}; - void makeTimeSeq(std::vector> &timeSeq, const std::vector> ×, std::vector ¤t, @@ -119,10 +59,10 @@ void makeTimeSeq(std::vector> &timeSeq, { std::vector current(times.size()); - makeTimeSeq(timeSeq, times, current, times.size()); + makeTimeSeq(timeSeq, times, current, static_cast(times.size())); } -void saveCorrelator(const Contractor::CorrelatorResult &result, const std::string dir, +void saveCorrelator(const Contractor::CorrelatorResult &result, const std::string dir, const unsigned int dt, const unsigned int traj) { std::string fileStem = "", filename; @@ -239,31 +179,52 @@ inline std::ostream & operator<< (std::ostream& s, const Bytes &&b) int main(int argc, char* argv[]) { // parse command line - std::string parFilename; + std::string parFilename; + bool SimpleCorrelator{ false }; + int ArgCount{ 0 }; + bool bCmdLineError{ false }; + for( int i = 1; i < argc; i++ ) { + if( argv[i][0] == '-' ) { + if( argv[i][1] == 's' && argv[i][2] == 0 ) + SimpleCorrelator = true; + else { + std::cerr << "Urecognised switch \"" << argv[i] << "\"" << std::endl; + bCmdLineError = true; + } + } else { + switch( ++ArgCount ) { + case 1: + parFilename = argv[i]; + break; + default: + std::cerr << "Unused argument \"" << argv[i] << "\"" << std::endl; + break; + } + } + } - if (argc != 2) + if (ArgCount != 1 or bCmdLineError) { - std::cerr << "usage: " << argv[0] << " "; - std::cerr << std::endl; + std::cerr << "usage: " << argv[0] << " " + "\n\t-s\tSimple Correlators (only describe A2AMatrices used for contraction)" + << std::endl; return EXIT_FAILURE; } - parFilename = argv[1]; // parse parameter file - ContractorPar par; + Contractor::ContractorPar par; unsigned int nMat, nCont; XmlReader reader(parFilename); read(reader, "global", par.global); read(reader, "a2aMatrix", par.a2aMatrix); read(reader, "product", par.product); - nMat = par.a2aMatrix.size(); - nCont = par.product.size(); + nMat = static_cast(par.a2aMatrix.size()); + nCont = static_cast(par.product.size()); // create diskvectors std::map> a2aMat; - unsigned int cacheSize; for (auto &p: par.a2aMatrix) { @@ -282,7 +243,7 @@ int main(int argc, char* argv[]) for (auto &p: par.a2aMatrix) { std::string filename = p.file; - double t, size; + double t; tokenReplace(filename, "traj", traj); std::cout << "======== Loading '" << filename << "'" << std::endl; @@ -306,7 +267,7 @@ int main(int argc, char* argv[]) std::vector> lastTerm(par.global.nt); A2AMatrix prod, buf, tmp; TimerArray tAr; - double fusec, busec, flops, bytes, tusec; + double fusec, busec, flops, bytes; Contractor::CorrelatorResult result; tAr.startTimer("Total"); @@ -328,7 +289,9 @@ int main(int argc, char* argv[]) } for (auto &m: par.a2aMatrix) { - if (std::find(result.a2aMatrix.begin(), result.a2aMatrix.end(), m) == result.a2aMatrix.end()) + // For simple correlators, only include A2AMatrix info for correlators in this contraction + if ( ( !SimpleCorrelator or std::find( term.begin(), term.end(), m.name ) != term.end() ) + and std::find(result.a2aMatrix.begin(), result.a2aMatrix.end(), m) == result.a2aMatrix.end()) { result.a2aMatrix.push_back(m); tokenReplace(result.a2aMatrix.back().file, "traj", traj); diff --git a/Hadrons/Utilities/Contractor.hpp b/Hadrons/Utilities/Contractor.hpp index decd13aa..27e55b50 100644 --- a/Hadrons/Utilities/Contractor.hpp +++ b/Hadrons/Utilities/Contractor.hpp @@ -36,4 +36,74 @@ BEGIN_HADRONS_NAMESPACE END_HADRONS_NAMESPACE +#define BEGIN_CONTRACTOR_NAMESPACE namespace Contractor{ +BEGIN_CONTRACTOR_NAMESPACE + +using Grid::Serializable; +using Grid::Reader; +using Grid::Writer; +using Grid::ComplexD; + +class TrajRange: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(TrajRange, + unsigned int, start, + unsigned int, end, + unsigned int, step); +}; + +class GlobalPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(GlobalPar, + TrajRange, trajCounter, + unsigned int, nt, + std::string, diskVectorDir, + std::string, output); +}; + +class A2AMatrixPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(A2AMatrixPar, + std::string, file, + std::string, dataset, + unsigned int, cacheSize, + std::string, name); +}; + +class ProductPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(ProductPar, + std::string, terms, + std::vector, times, + std::string, translations, + bool, translationAverage); +}; + +class CorrelatorResult: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(CorrelatorResult, + std::vector, a2aMatrix, + ProductPar, contraction, + std::vector, times, + std::vector, correlator); +}; + +struct ContractorPar +{ + Contractor::GlobalPar global; + std::vector a2aMatrix; + std::vector product; +}; + +// Useful ... so long as there's a ContractorPar named par in scope +#define TIME_MOD(t) (((t) + par.global.nt) % par.global.nt) + +#define END_CONTRACTOR_NAMESPACE } +END_CONTRACTOR_NAMESPACE + #endif // Hadrons_Contractor_hpp_ From e598178d9481c1dbfedacbda3f52abc866f1f620 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Thu, 1 Aug 2019 20:51:51 +0100 Subject: [PATCH 271/347] TODO: Felix, please fix. I commented this out because of compiler errors --- Hadrons/Modules/MContraction/Baryon2.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MContraction/Baryon2.hpp b/Hadrons/Modules/MContraction/Baryon2.hpp index 3004248b..6c2b476d 100644 --- a/Hadrons/Modules/MContraction/Baryon2.hpp +++ b/Hadrons/Modules/MContraction/Baryon2.hpp @@ -187,7 +187,8 @@ void TBaryon2::execute(void) diquark = BaryonUtils::quarkContract13(q2*GammaB,GammaB*q3); //result = trace(GammaA*GammaA * traceColour(q1*traceSpin(diquark))) + 2.0 * trace(GammaA*GammaA*traceColour(q1*diquark)); - result = trace(q1*diquark); + //result = trace(q1*diquark); // TODO: Apologies, Felix - compiler errors + assert( 0 && "TODO: Felix, please fix prior line - compiler errors" ); sliceSum(c,buf,Tp); From 310867d46ae5a7cd0f86c0b1d0ca400b201658e4 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 2 Aug 2019 11:25:29 +0100 Subject: [PATCH 272/347] Additional option to specify the separator used between terms in correlator --- Hadrons/Utilities/Contractor.cc | 39 ++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/Hadrons/Utilities/Contractor.cc b/Hadrons/Utilities/Contractor.cc index 15c970b9..0ce5ecdb 100644 --- a/Hadrons/Utilities/Contractor.cc +++ b/Hadrons/Utilities/Contractor.cc @@ -35,6 +35,9 @@ using namespace Grid; using namespace QCD; using namespace Hadrons; +// Separator to be used between contraction terms only (underscores elsewhere) +std::string Separator{ "_" }; + void makeTimeSeq(std::vector> &timeSeq, const std::vector> ×, std::vector ¤t, @@ -70,12 +73,12 @@ void saveCorrelator(const Contractor::CorrelatorResult &result, const std::strin for (unsigned int i = 0; i < terms.size() - 1; i++) { - fileStem += terms[i] + "_" + std::to_string(result.times[i]) + "_"; + fileStem += terms[i] + "_" + std::to_string(result.times[i]) + Separator; } fileStem += terms.back(); if (!result.contraction.translationAverage) { - fileStem += "_dt_" + std::to_string(dt); + fileStem += Separator + "dt_" + std::to_string(dt); } filename = dir + "/" + RESULT_FILE_NAME(fileStem, traj); std::cout << "Saving correlator to '" << filename << "'" << std::endl; @@ -180,18 +183,36 @@ int main(int argc, char* argv[]) { // parse command line std::string parFilename; - bool SimpleCorrelator{ false }; + bool bOnlyWriteUsedA2AMatrices{ false }; int ArgCount{ 0 }; bool bCmdLineError{ false }; for( int i = 1; i < argc; i++ ) { if( argv[i][0] == '-' ) { - if( argv[i][1] == 's' && argv[i][2] == 0 ) - SimpleCorrelator = true; - else { + // Switches + bool bSwitchOK = false; + switch( argv[i][1] ) { + case 'a': + if( argv[i][2] == 0 ) { + bOnlyWriteUsedA2AMatrices = true; + bSwitchOK = true; + std::cout << "Only A2AMatrices used in each contraction will be written" << std::endl; + } + break; + case 's': + if( argv[i][2] ) + Separator = &argv[i][2]; + else + Separator = "."; + bSwitchOK = true; + std::cout << "Using \"" << Separator << "\" as name separator" << std::endl; + break; + } + if( !bSwitchOK ) { std::cerr << "Urecognised switch \"" << argv[i] << "\"" << std::endl; bCmdLineError = true; } } else { + // Arguments switch( ++ArgCount ) { case 1: parFilename = argv[i]; @@ -206,7 +227,9 @@ int main(int argc, char* argv[]) if (ArgCount != 1 or bCmdLineError) { std::cerr << "usage: " << argv[0] << " " - "\n\t-s\tSimple Correlators (only describe A2AMatrices used for contraction)" + "\n\t-a\tSimple Correlators (only describe A2AMatrices used for contraction)" + "\n\t-s[sep]\tSeparator \"sep\" used between name components." + "\n\t\tDefaults to \"_\", or \".\" if -s specified without sep" << std::endl; return EXIT_FAILURE; @@ -290,7 +313,7 @@ int main(int argc, char* argv[]) for (auto &m: par.a2aMatrix) { // For simple correlators, only include A2AMatrix info for correlators in this contraction - if ( ( !SimpleCorrelator or std::find( term.begin(), term.end(), m.name ) != term.end() ) + if ( ( !bOnlyWriteUsedA2AMatrices or std::find( term.begin(), term.end(), m.name ) != term.end() ) and std::find(result.a2aMatrix.begin(), result.a2aMatrix.end(), m) == result.a2aMatrix.end()) { result.a2aMatrix.push_back(m); From cad76827b0ad1428f5b620a5d29db4045a7b2cc5 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 2 Aug 2019 15:47:20 +0100 Subject: [PATCH 273/347] Be consistent about separator usage. Log start / stop / duration --- Hadrons/Utilities/Contractor.cc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Hadrons/Utilities/Contractor.cc b/Hadrons/Utilities/Contractor.cc index 0ce5ecdb..b2002a73 100644 --- a/Hadrons/Utilities/Contractor.cc +++ b/Hadrons/Utilities/Contractor.cc @@ -26,6 +26,8 @@ See the full license in the file "LICENSE" in the top level distribution directo *************************************************************************************/ /* END LEGAL */ +#include +#include #include #include #include @@ -73,12 +75,12 @@ void saveCorrelator(const Contractor::CorrelatorResult &result, const std::strin for (unsigned int i = 0; i < terms.size() - 1; i++) { - fileStem += terms[i] + "_" + std::to_string(result.times[i]) + Separator; + fileStem += terms[i] + Separator + std::to_string(result.times[i]) + Separator; } fileStem += terms.back(); if (!result.contraction.translationAverage) { - fileStem += Separator + "dt_" + std::to_string(dt); + fileStem += Separator + "dt" + Separator + std::to_string(dt); } filename = dir + "/" + RESULT_FILE_NAME(fileStem, traj); std::cout << "Saving correlator to '" << filename << "'" << std::endl; @@ -235,6 +237,11 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } + // Log what file we're processing and when we started + const std::chrono::system_clock::time_point start{ std::chrono::system_clock::now() }; + std::time_t now = std::chrono::system_clock::to_time_t( start ); + std::cout << "Start " << parFilename << " " << std::ctime( &now ) << std::endl; + // parse parameter file Contractor::ContractorPar par; unsigned int nMat, nCont; @@ -436,6 +443,13 @@ int main(int argc, char* argv[]) printTimeProfile(tAr.getTimings(), tAr.getTimer("Total")); } } - + + // Mention that we're finished, what the time is and how long it took + const std::chrono::system_clock::time_point stop{ std::chrono::system_clock::now() }; + now = std::chrono::system_clock::to_time_t( stop ); + const std::chrono::duration duration_seconds = stop - start; + const double hours{ ( duration_seconds.count() + 0.5 ) / 3600 }; + std::cout << "Stop " << parFilename << " " << std::ctime( &now ) + << "Total duration " << std::fixed << std::setprecision(1) << hours << " hours." << std::endl; return EXIT_SUCCESS; } From ed23f6be206235179f4e5a4393378f36dc5cf935 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 2 Aug 2019 15:59:18 +0100 Subject: [PATCH 274/347] Remove blank line from log --- Hadrons/Utilities/Contractor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Utilities/Contractor.cc b/Hadrons/Utilities/Contractor.cc index b2002a73..dfb167a8 100644 --- a/Hadrons/Utilities/Contractor.cc +++ b/Hadrons/Utilities/Contractor.cc @@ -240,7 +240,7 @@ int main(int argc, char* argv[]) // Log what file we're processing and when we started const std::chrono::system_clock::time_point start{ std::chrono::system_clock::now() }; std::time_t now = std::chrono::system_clock::to_time_t( start ); - std::cout << "Start " << parFilename << " " << std::ctime( &now ) << std::endl; + std::cout << "Start " << parFilename << " " << std::ctime( &now ); // parse parameter file Contractor::ContractorPar par; From 8d97e2a02a86575d1fb127ad6743b4edb23cab41 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 2 Aug 2019 19:23:18 +0100 Subject: [PATCH 275/347] Say which A2AMatrix is being loaded, and which contraction is being performed (m of n) --- Hadrons/Utilities/Contractor.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Hadrons/Utilities/Contractor.cc b/Hadrons/Utilities/Contractor.cc index dfb167a8..7d643778 100644 --- a/Hadrons/Utilities/Contractor.cc +++ b/Hadrons/Utilities/Contractor.cc @@ -244,14 +244,13 @@ int main(int argc, char* argv[]) // parse parameter file Contractor::ContractorPar par; - unsigned int nMat, nCont; XmlReader reader(parFilename); read(reader, "global", par.global); read(reader, "a2aMatrix", par.a2aMatrix); read(reader, "product", par.product); - nMat = static_cast(par.a2aMatrix.size()); - nCont = static_cast(par.product.size()); + const unsigned int nMat { static_cast(par.a2aMatrix.size()) }; + const unsigned int nCont { static_cast(par.product.size()) }; // create diskvectors std::map> a2aMat; @@ -270,13 +269,15 @@ int main(int argc, char* argv[]) std::cout << ":::::::: Trajectory " << traj << std::endl; // load data + int iSeq = 0; for (auto &p: par.a2aMatrix) { std::string filename = p.file; double t; tokenReplace(filename, "traj", traj); - std::cout << "======== Loading '" << filename << "'" << std::endl; + std::cout << "======== Loading '" << filename << "'" + << "\nA2AMatrix " << ++iSeq << " of " << nMat << " = " << p.name << std::endl; A2AMatrixIo a2aIo(filename, p.dataset, par.global.nt); @@ -288,6 +289,7 @@ int main(int argc, char* argv[]) // contract EigenDiskVector::Matrix buf; + iSeq = 0; for (auto &p: par.product) { std::vector term = strToVec(p.terms); @@ -301,7 +303,7 @@ int main(int argc, char* argv[]) Contractor::CorrelatorResult result; tAr.startTimer("Total"); - std::cout << "======== Contraction tr("; + std::cout << "======== Contraction " << ++iSeq << " of " << nCont << " tr("; for (unsigned int g = 0; g < term.size(); ++g) { std::cout << term[g] << ((g == term.size() - 1) ? ')' : '*'); From 29df60c0cbe51c36311e19e4f62500e5df7117cd Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Mon, 5 Aug 2019 14:10:04 +0100 Subject: [PATCH 276/347] some debugging stuff --- Grid/qcd/utils/BaryonUtils.h | 185 +++++++++++++++++++++-- Hadrons/Modules.hpp | 1 + Hadrons/Modules/MContraction/Baryon2.hpp | 11 +- Hadrons/modules.inc | 2 + 4 files changed, 181 insertions(+), 18 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 468c1f78..19fb03c5 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -15,12 +15,25 @@ public: typedef typename FImpl::FermionField FermionField; typedef typename FImpl::PropagatorField PropagatorField; + typedef typename FImpl::SitePropagator pobj; typedef typename FImpl::SiteSpinor vobj; typedef typename vobj::scalar_object sobj; typedef typename vobj::scalar_type scalar_type; typedef typename vobj::vector_type vector_type; + static void ContractBaryons_debug(const PropagatorField &q1, + const PropagatorField &q2, + const PropagatorField &q3, + const Gamma GammaA, + const Gamma GammaB, + ComplexField &bc1, + ComplexField &bc2, + ComplexField &bc3, + ComplexField &bc4, + ComplexField &bc5, + ComplexField &bc6, + ComplexField &baryon_corr); static void ContractBaryons(const PropagatorField &q1, const PropagatorField &q2, const PropagatorField &q3, @@ -28,11 +41,159 @@ public: const Gamma GammaB, ComplexField &baryon_corr); - static LatticeSpinColourMatrix quarkContract13(const PropagatorField &q1, - const PropagatorField &q2); +// static PropagatorField quarkContract13(const PropagatorField &q1, +// const PropagatorField &q2); + static void quarkContract13(const PropagatorField &q1, + const PropagatorField &q2, + PropagatorField &q_out); }; +template +void BaryonUtils::ContractBaryons_debug(const PropagatorField &q1, + const PropagatorField &q2, + const PropagatorField &q3, + const Gamma GammaA, + const Gamma GammaB, + ComplexField &bc1, + ComplexField &bc2, + ComplexField &bc3, + ComplexField &bc4, + ComplexField &bc5, + ComplexField &bc6, + ComplexField &baryon_corr) +{ + GridBase *grid = q1._grid; + + // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 + //Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 + //Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 + //Gamma GammaB(Gamma::Algebra::GammaZGamma5); //Still hardcoded CgX = i gamma_3 gamma_5 + Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) + + std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; + std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; + char left[] = "sss"; + char right[] = "sss"; + std::vector wick_contraction = {0,0,0,0,0,0}; + + for (int ie=0; ie < 6 ; ie++) + if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]) + wick_contraction[ie]=1; + + + int parity = 1; + + + parallel_for(int ss=0;ssoSites();ss++){ + + typedef typename ComplexField::vector_object vobj; + + auto D1 = q1._odata[ss]; + auto D2 = q2._odata[ss]; + auto D3 = q3._odata[ss]; + + auto gD1a = GammaA * GammaA * D1; + auto gD1b = GammaA * g4 * GammaA * D1; + auto pD1 = 0.5* (gD1a + (double)parity * gD1b); + auto gD3 = GammaB * D3; + + vobj result=zero; + vobj result1=zero; + vobj result2=zero; + vobj result3=zero; + vobj result4=zero; + vobj result5=zero; + vobj result6=zero; + + for (int ie_src=0; ie_src < 6 ; ie_src++){ + int a_src = epsilon[ie_src][0]; //a + int b_src = epsilon[ie_src][1]; //b + int c_src = epsilon[ie_src][2]; //c + for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ + int a_snk = epsilon[ie_snk][0]; //a' + int b_snk = epsilon[ie_snk][1]; //b' + int c_snk = epsilon[ie_snk][2]; //c' + //This is the \delta_{123}^{123} part + if (wick_contraction[0]){ + auto D2g = D2 * GammaB; + for (int alpha_snk=0; alpha_snk void BaryonUtils::ContractBaryons(const PropagatorField &q1, @@ -78,7 +239,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1, auto gD3 = GammaB * D3; vobj result=zero; - + for (int ie_src=0; ie_src < 6 ; ie_src++){ int a_src = epsilon[ie_src][0]; //a int b_src = epsilon[ie_src][1]; //b @@ -153,24 +314,24 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1, baryon_corr._odata[ss] = result; } //end loop over lattice sites - - } //QDP / CHROMA - style diquark construction // (q_out)^{c'c}_{alpha,beta} = epsilon^{abc} epsilon^{a'b'c'} (q1)^{aa'}_{rho alpha}^* (q2)^{bb'}_{rho beta} template -LatticeSpinColourMatrix BaryonUtils::quarkContract13(const PropagatorField &q1, - const PropagatorField &q2) +//typename FImpl::PropagatorField BaryonUtils::quarkContract13(const PropagatorField &q1, +// const PropagatorField &q2) +void BaryonUtils::quarkContract13(const PropagatorField &q1, + const PropagatorField &q2, + PropagatorField &q_out) { GridBase *grid = q1._grid; std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - std::vector wick_contraction = {0,0,0,0,0,0}; - LatticeSpinColourMatrix q_out=zero; + //PropagatorField q_out;//=zero; parallel_for(int ss=0;ssoSites();ss++){ @@ -181,7 +342,7 @@ LatticeSpinColourMatrix BaryonUtils::quarkContract13(const PropagatorFiel //auto D_out = q_out._odata[ss]; //D_out=zero; - SpinColourMatrix D_out=zero; + pobj D_out=zero; for (int ie_src=0; ie_src < 6 ; ie_src++){ int a_src = epsilon[ie_src][0]; //a @@ -194,7 +355,7 @@ LatticeSpinColourMatrix BaryonUtils::quarkContract13(const PropagatorFiel for (int alpha=0; alpha::quarkContract13(const PropagatorFiel } //end loop over lattice sites - return q_out; + //return q_out; } diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 889763ea..047f0aff 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include diff --git a/Hadrons/Modules/MContraction/Baryon2.hpp b/Hadrons/Modules/MContraction/Baryon2.hpp index df928472..7bf977cf 100644 --- a/Hadrons/Modules/MContraction/Baryon2.hpp +++ b/Hadrons/Modules/MContraction/Baryon2.hpp @@ -116,7 +116,7 @@ template void TBaryon2::setup(void) { envTmpLat(LatticeComplex, "c"); - envTmpLat(LatticeComplex, "diquark"); + envTmpLat(PropagatorField1, "diquark"); // Translate the full string naming the desired gamma structure into the one we need to use const std::string gamma{ par().gamma }; int iGamma = 0; @@ -172,7 +172,7 @@ void TBaryon2::execute(void) auto &q2 = envGet(PropagatorField2, par().q2); auto &q3 = envGet(PropagatorField3, par().q3); envGetTmp(LatticeComplex, c); - //envGetTmp(LatticeComplex, diquark); + envGetTmp(PropagatorField1, diquark); //ACTUALLY MIX OF 2 AND 3!!!! Result result; int nt = env().getDim(Tp); result.corr.resize(nt); @@ -182,12 +182,11 @@ void TBaryon2::execute(void) const Gamma GammaA{ Gamma::Algebra::Identity }; const Gamma GammaB{ al }; - LatticeSpinColourMatrix diquark; - - diquark = BaryonUtils::quarkContract13(q2*GammaB,GammaB*q3); + //diquark = BaryonUtils::quarkContract13(q2*GammaB,GammaB*q3); + BaryonUtils::quarkContract13(q2*GammaB,GammaB*q3,diquark); //result = trace(GammaA*GammaA * traceColour(q1*traceSpin(diquark))) + 2.0 * trace(GammaA*GammaA*traceColour(q1*diquark)); - result = trace(q1*diquark); + //c = trace(q1*traceSpin(diquark)); //NO TRACESPIN??? sliceSum(c,buf,Tp); diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index a18afd53..36e6429b 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -55,6 +55,7 @@ modules_cc =\ Modules/MContraction/WeakEye3pt.cc \ Modules/MContraction/Meson.cc \ Modules/MContraction/A2AAslashField.cc \ + Modules/MContraction/SelfContract.cc \ Modules/MContraction/Baryon2.cc \ Modules/MContraction/Baryon.cc \ Modules/MContraction/Nucleon.cc \ @@ -147,6 +148,7 @@ modules_hpp =\ Modules/MContraction/A2ALoop.hpp \ Modules/MContraction/Gamma3pt.hpp \ Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/SelfContract.hpp \ Modules/MContraction/A2AMesonField.hpp \ Modules/MAction/WilsonClover.hpp \ Modules/MAction/ScaledDWF.hpp \ From 51bed48cd2223dd87d8d9d36fd217ea8296a62df Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Mon, 5 Aug 2019 17:46:42 +0100 Subject: [PATCH 277/347] added selfcontract module --- Hadrons/Modules/MContraction/Baryon2.hpp | 4 +- Hadrons/Modules/MContraction/SelfContract.cc | 7 + Hadrons/Modules/MContraction/SelfContract.hpp | 147 ++++++++++++++++++ 3 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 Hadrons/Modules/MContraction/SelfContract.cc create mode 100644 Hadrons/Modules/MContraction/SelfContract.hpp diff --git a/Hadrons/Modules/MContraction/Baryon2.hpp b/Hadrons/Modules/MContraction/Baryon2.hpp index 1d434d0a..3195b18d 100644 --- a/Hadrons/Modules/MContraction/Baryon2.hpp +++ b/Hadrons/Modules/MContraction/Baryon2.hpp @@ -116,7 +116,7 @@ template void TBaryon2::setup(void) { envTmpLat(LatticeComplex, "c"); - envTmpLat(PropagatorField1, "diquark"); + // envTmpLat(PropagatorField1, "diquark"); // Translate the full string naming the desired gamma structure into the one we need to use const std::string gamma{ par().gamma }; int iGamma = 0; @@ -172,7 +172,7 @@ void TBaryon2::execute(void) auto &q2 = envGet(PropagatorField2, par().q2); auto &q3 = envGet(PropagatorField3, par().q3); envGetTmp(LatticeComplex, c); - envGetTmp(PropagatorField1, diquark); //ACTUALLY MIX OF 2 AND 3!!!! +// envGetTmp(PropagatorField1, diquark); //ACTUALLY MIX OF 2 AND 3!!!! Result result; int nt = env().getDim(Tp); result.corr.resize(nt); diff --git a/Hadrons/Modules/MContraction/SelfContract.cc b/Hadrons/Modules/MContraction/SelfContract.cc new file mode 100644 index 00000000..b89d55fe --- /dev/null +++ b/Hadrons/Modules/MContraction/SelfContract.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MContraction; + +template class Grid::Hadrons::MContraction::TSelfContract; diff --git a/Hadrons/Modules/MContraction/SelfContract.hpp b/Hadrons/Modules/MContraction/SelfContract.hpp new file mode 100644 index 00000000..3fe8bcc6 --- /dev/null +++ b/Hadrons/Modules/MContraction/SelfContract.hpp @@ -0,0 +1,147 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MContraction/Baryon.hpp + +Copyright (C) 2015-2019 + +Author: Antonin Portelli +Author: Felix Erben + +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 */ + +#ifndef Hadrons_MContraction_SelfContract_hpp_ +#define Hadrons_MContraction_SelfContract_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * SelfContract * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MContraction) + +class SelfContractPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(SelfContractPar, + std::string, q_self, + Gamma::Algebra, gamma, + std::string, sink, + std::string, output); +}; + +template +class TSelfContract: public Module +{ + FERM_TYPE_ALIASES(FImpl,); + BASIC_TYPE_ALIASES(ScalarImplCR, Scalar); + SINK_TYPE_ALIASES(Scalar); + class Result: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(Result, + Gamma::Algebra, gamma, + std::vector, corr); + }; +public: + // constructor + TSelfContract(const std::string name); + // destructor + virtual ~TSelfContract(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); +protected: + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(SelfContract, TSelfContract, MContraction); + +/****************************************************************************** + * TSelfContract implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TSelfContract::TSelfContract(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TSelfContract::getInput(void) +{ + std::vector in = {par().q_self, par().sink}; + + return in; +} + +template +std::vector TSelfContract::getOutput(void) +{ + std::vector out = {}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TSelfContract::setup(void) +{ + envTmpLat(LatticeComplex, "c"); +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TSelfContract::execute(void) +{ + LOG(Message) << "Computing self-contracted loop '" << getName() + << "' using '" << par().q_self << "' with " << par().gamma + << " insertion and sink " << par().sink << std::endl; + + auto &q_self = envGet(PropagatorField, par().q_self); + Gamma gamma(par().gamma); + std::vector buf; + Result result; + envGetTmp(LatticeComplex, c); + SinkFnScalar &sink = envGet(SinkFnScalar, par().sink); + + c = trace(gamma*q_self); + buf = sink(c); + result.gamma = par().gamma; + result.corr.resize(buf.size()); + for (unsigned int t = 0; t < buf.size(); ++t) + { + result.corr[t] = TensorRemove(buf[t]); + } + saveResult(par().output, "selfContr", result); +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MContraction_SelfContract_hpp_ From 04a661cafe10abc0d69d5a88ae7e6f6d45321388 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 10 Sep 2019 14:49:24 +0100 Subject: [PATCH 278/347] Remove unused modules BC2 and Baryon2 --- Hadrons/Modules.hpp | 2 - Hadrons/Modules/MContraction/Baryon2.cc | 35 ---- Hadrons/Modules/MContraction/Baryon2.hpp | 207 ------------------- Hadrons/Modules/MDistil/BC2.cc | 36 ---- Hadrons/Modules/MDistil/BC2.hpp | 244 ----------------------- Hadrons/modules.inc | 4 - 6 files changed, 528 deletions(-) delete mode 100644 Hadrons/Modules/MContraction/Baryon2.cc delete mode 100644 Hadrons/Modules/MContraction/Baryon2.hpp delete mode 100644 Hadrons/Modules/MDistil/BC2.cc delete mode 100644 Hadrons/Modules/MDistil/BC2.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 85f265cf..098227ee 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -56,7 +55,6 @@ #include #include #include -#include #include #include #include diff --git a/Hadrons/Modules/MContraction/Baryon2.cc b/Hadrons/Modules/MContraction/Baryon2.cc deleted file mode 100644 index b8d3bc52..00000000 --- a/Hadrons/Modules/MContraction/Baryon2.cc +++ /dev/null @@ -1,35 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: Hadrons/Modules/MContraction/Baryon2.cc - -Copyright (C) 2015-2019 - -Author: Antonin Portelli - -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 - -using namespace Grid; -using namespace Hadrons; -using namespace MContraction; - -template class Grid::Hadrons::MContraction::TBaryon2; - diff --git a/Hadrons/Modules/MContraction/Baryon2.hpp b/Hadrons/Modules/MContraction/Baryon2.hpp deleted file mode 100644 index 3195b18d..00000000 --- a/Hadrons/Modules/MContraction/Baryon2.hpp +++ /dev/null @@ -1,207 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: Hadrons/Modules/MContraction/Baryon2.hpp - -Copyright (C) 2015-2019 - -Author: Antonin Portelli -Author: Felix Erben - -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 */ - -#ifndef Hadrons_MContraction_Baryon2_hpp_ -#define Hadrons_MContraction_Baryon2_hpp_ - -#include -#include -#include -#include - -BEGIN_HADRONS_NAMESPACE - -/****************************************************************************** - * Baryon2 * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MContraction) - -class Baryon2Par: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(Baryon2Par, - std::string, q1, - std::string, q2, - std::string, q3, - std::string, gamma, - std::string, output); -}; - -template -class TBaryon2: public Module -{ -public: - FERM_TYPE_ALIASES(FImpl1, 1); - FERM_TYPE_ALIASES(FImpl2, 2); - FERM_TYPE_ALIASES(FImpl3, 3); - class Result: Serializable - { - public: - GRID_SERIALIZABLE_CLASS_MEMBERS(Result, - std::vector, corr); - }; -public: - // constructor - TBaryon2(const std::string name); - // destructor - virtual ~TBaryon2(void) {}; - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); -protected: - // setup - virtual void setup(void); - // execution - virtual void execute(void); - // Which gamma algebra was specified - Gamma::Algebra al; -}; - -MODULE_REGISTER_TMP(Baryon2, ARG(TBaryon2), MContraction); - -/****************************************************************************** - * TBaryon2 implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -TBaryon2::TBaryon2(const std::string name) -: Module(name) -{} - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TBaryon2::getInput(void) -{ - std::vector input = {par().q1, par().q2, par().q3}; - - return input; -} - -template -std::vector TBaryon2::getOutput(void) -{ - std::vector out = {}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void TBaryon2::setup(void) -{ - envTmpLat(LatticeComplex, "c"); - // envTmpLat(PropagatorField1, "diquark"); - // Translate the full string naming the desired gamma structure into the one we need to use - const std::string gamma{ par().gamma }; - int iGamma = 0; - do - { - const char * pGammaName = Gamma::name[iGamma]; - int iLen = 0; - while( pGammaName[iLen] && pGammaName[iLen] != ' ' ) - iLen++; - if( !gamma.compare( 0, gamma.size(), pGammaName, iLen ) ) - break; - } - while( ++iGamma < Gamma::nGamma ); - if( iGamma >= Gamma::nGamma ) { - LOG(Message) << "Unrecognised gamma structure \"" << gamma << "\"" << std::endl; - assert( 0 && "Invalid gamma structure specified" ); - } - switch( iGamma ) { - case Gamma::Algebra::GammaX: - std::cout << "using interpolator C gamma_X" << std::endl; - al = Gamma::Algebra::GammaZGamma5; //Still hardcoded CgX = i gamma_3 gamma_5 - break; - case Gamma::Algebra::GammaY: - std::cout << "using interpolator C gamma_Y" << std::endl; - al = Gamma::Algebra::GammaT; //Still hardcoded CgX = - gamma_4 - break; - case Gamma::Algebra::GammaZ: - std::cout << "using interpolator C gamma_Z" << std::endl; - al = Gamma::Algebra::GammaXGamma5; //Still hardcoded CgX = i gamma_1 gamma_5 - break; - default: - { - LOG(Message) << "Unsupported gamma structure " << gamma << " = " << iGamma << std::endl; - assert( 0 && "Unsupported gamma structure" ); - // or you could do something like - al = static_cast( iGamma ); - break; - } - } - LOG(Message) << "Gamma structure " << gamma << " = " << iGamma - << " translated to " << Gamma::name[al] << std::endl; -} - -// execution /////////////////////////////////////////////////////////////////// -template -void TBaryon2::execute(void) -{ - LOG(Message) << "Computing baryon contractions '" << getName() << "' using" - << " quarks '" << par().q1 << "', and a diquark formed of ('" << par().q2 << "', and '" - << par().q3 << "')" << std::endl; - - auto &q1 = envGet(PropagatorField1, par().q1); - auto &q2 = envGet(PropagatorField2, par().q2); - auto &q3 = envGet(PropagatorField3, par().q3); - envGetTmp(LatticeComplex, c); -// envGetTmp(PropagatorField1, diquark); //ACTUALLY MIX OF 2 AND 3!!!! - Result result; - int nt = env().getDim(Tp); - result.corr.resize(nt); - const std::string gamma{ par().gamma }; - std::vector buf; - - const Gamma GammaA{ Gamma::Algebra::Identity }; - const Gamma GammaB{ al }; - - LatticeSpinColourMatrix diquark( q1._grid ); // TODO: Felix, I added "q1._grid". I presume this is correct? - - diquark = BaryonUtils::quarkContract13(q2*GammaB,GammaB*q3); - - //result = trace(GammaA*GammaA * traceColour(q1*traceSpin(diquark))) + 2.0 * trace(GammaA*GammaA*traceColour(q1*diquark)); - //result = trace(q1*diquark); // TODO: Apologies, Felix - compiler errors - assert( 0 && "TODO: Felix, please fix prior line - compiler errors" ); - - sliceSum(c,buf,Tp); - - for (unsigned int t = 0; t < buf.size(); ++t) - { - result.corr[t] = TensorRemove(buf[t]); - } - - saveResult(par().output, "baryon", result); -} - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MContraction_Baryon2_hpp_ diff --git a/Hadrons/Modules/MDistil/BC2.cc b/Hadrons/Modules/MDistil/BC2.cc deleted file mode 100644 index 187a2340..00000000 --- a/Hadrons/Modules/MDistil/BC2.cc +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/BC2.cc - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 - -using namespace Grid; -using namespace Hadrons; -using namespace MDistil; - -template class Grid::Hadrons::MDistil::TBC2; diff --git a/Hadrons/Modules/MDistil/BC2.hpp b/Hadrons/Modules/MDistil/BC2.hpp deleted file mode 100644 index a10d4eaa..00000000 --- a/Hadrons/Modules/MDistil/BC2.hpp +++ /dev/null @@ -1,244 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/BC2.hpp - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 */ - -#ifndef Hadrons_MDistil_BC2_hpp_ -#define Hadrons_MDistil_BC2_hpp_ - -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation -#include - -BEGIN_HADRONS_NAMESPACE - -/****************************************************************************** - * BC2 * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) - - // general baryon tensor set based on Eigen tensors and Grid-allocated memory - // Dimensions: - // 0 - ext - external field (momentum, EM field, ...) - // 1 - str - dirac structure - // 2 - t - timeslice - // 3 - s - free spin index - // 4 - i - left distillation mode index - // 5 - j - middle distillation mode index - // 6 - k - right distillation mode index - // template - // using BaryonTensorSet = Eigen::TensorMap>; - -class BC2Par: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(BC2Par, - std::string, one, - std::string, two, - std::string, three, - std::string, output, - int, parity, - std::vector, mom); -}; - -template -class TBC2: public Module -{ -public: - FERM_TYPE_ALIASES(FImpl,); -public: - // constructor - TBC2(const std::string name); - // destructor - virtual ~TBC2(void) {}; - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); -private: - bool hasPhase_{false}; - std::string momphName_; - std::vector gamma12_; - std::vector gamma23_; - std::vector> mom_; -protected: - GridCartesian * grid4d; - GridCartesian * grid3d; -}; - -MODULE_REGISTER_TMP(BC2, TBC2, MDistil); - -/****************************************************************************** - * TBC2 implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -TBC2::TBC2(const std::string name) -: Module(name) -, momphName_(name + "_momph") -{} - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TBC2::getInput(void) -{ - std::vector in = {par().one, par().two, par().three}; - - return in; -} - -template -std::vector TBC2::getOutput(void) -{ - std::vector out = {}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void TBC2::setup(void) -{ - if(!mom_.size()) { - for (auto &pstr: par().mom) - { - auto p = strToVec(pstr); - - if (p.size() != env().getNd() - 1) - { - HADRONS_ERROR(Size, "Momentum has " + std::to_string(p.size()) + " components instead of " + std::to_string(env().getNd() - 1)); - } - mom_.push_back(p); - } - } - //envCache(std::vector, momphName_, 1, par().mom.size(), envGetGrid(ComplexField)); - static GridCartesian * MyGrid{env().getGrid()}; - if( MyGrid == envGetGrid(ComplexField) ) - LOG(Message) << "envGetGrid(ComplexField) == env().getGrid()" << std::endl; - else - LOG(Message) << "envGetGrid(ComplexField) != env().getGrid()" << std::endl; - envTmp(std::vector, "ph", 1, std::vector()); - envGetTmp(std::vector, ph); - if(!ph.size()) { - for (unsigned int j = 0; j < par().mom.size(); ++j) - ph.push_back(ComplexField(MyGrid)); - } - - envTmpLat(ComplexField, "coor"); -} - -// execution /////////////////////////////////////////////////////////////////// -template -void TBC2::execute(void) -{ - auto &one = envGet(std::vector, par().one); - auto &two = envGet(std::vector, par().two); - auto &three = envGet(std::vector, par().three); - const std::string &output{par().output}; - - int N_1 = static_cast(one.size()); - int N_2 = static_cast(two.size()); - int N_3 = static_cast(three.size()); - - LOG(Message) << "Computing distillation baryon fields" << std::endl; - LOG(Message) << "One: '" << par().one << "' Two: '" << par().two << "' Three: '" << par().three << "'" << std::endl; - LOG(Message) << "Momenta:" << std::endl; - for (auto &p: mom_) - { - LOG(Message) << " " << p << std::endl; - } - - - int Nmom = static_cast(mom_.size()); - const int Nt{env().getDim(Tdir)}; - - int parity = 1; - int orthogDim=3; - - //auto &ph = envGet(std::vector, momphName_); - envGetTmp(std::vector, ph); - - if (!hasPhase_) - { - startTimer("Momentum phases"); - for (unsigned int j = 0; j < Nmom; ++j) - { - Complex i(0.0,1.0); - std::vector p; - - envGetTmp(ComplexField, coor); - ph[j] = zero; - for(unsigned int mu = 0; mu < mom_[j].size(); mu++) - { - LatticeCoordinate(coor, mu); - ph[j] = ph[j] + (mom_[j][mu]/env().getDim(mu))*coor; - } - ph[j] = exp((Real)(2*M_PI)*i*ph[j]); - } - hasPhase_ = true; - stopTimer("Momentum phases"); - } - //envCache(std::vector, momphName_, 1, mom_.size(), envGetGrid(ComplexField)); - - Eigen::Tensor m(Nmom,Nt,N_1,N_2,N_3,4); - //A2Autils::NucleonFieldMom(m, &one[0], &two[0], &three[0], ph, parity, orthogDim); - A2Autils::NucleonFieldMom(m, one, two, three, ph, parity, orthogDim); - for (int is=0 ; is < 4 ; is++){ - for (int t=0 ; t < Nt ; t++){ - std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << m(0,t,0,0,0,is) << std::endl; - } - } - - //BFieldIO BField_save; - //BField_save.BField = m; - - - GridCartesian * grid = env().getGrid(); - if(grid->IsBoss()) { - std::string filename ="./" + output + ".h5"; - std::cout << "Writing to file " << filename << std::endl; - Grid::Hdf5Writer writer(filename); - //write(writer,"BaryonField",BField_save.BField); - write(writer,"BaryonField",m); - } -} - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MDistil_BC2_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 5fd2f6d8..53e71ee3 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -44,7 +44,6 @@ modules_cc =\ Modules/MDistil/DistilVectors.cc \ Modules/MDistil/BContraction.cc \ Modules/MDistil/Baryon2pt.cc \ - Modules/MDistil/BC2.cc \ Modules/MSource/Momentum.cc \ Modules/MSource/SeqAslash.cc \ Modules/MSource/Z2.cc \ @@ -56,7 +55,6 @@ modules_cc =\ Modules/MContraction/Meson.cc \ Modules/MContraction/A2AAslashField.cc \ Modules/MContraction/SelfContract.cc \ - Modules/MContraction/Baryon2.cc \ Modules/MContraction/Baryon.cc \ Modules/MContraction/Nucleon.cc \ Modules/MContraction/WeakNonEye3pt.cc \ @@ -122,7 +120,6 @@ modules_hpp =\ Modules/MUtilities/PrecisionCast.hpp \ Modules/MDistil/Noises.hpp \ Modules/MDistil/Perambulator.hpp \ - Modules/MDistil/BC2.hpp \ Modules/MDistil/g5_multiply.hpp \ Modules/MDistil/PerambFromSolve.hpp \ Modules/MDistil/Baryon2pt.hpp \ @@ -139,7 +136,6 @@ modules_hpp =\ Modules/MContraction/WeakMesonDecayKl2.hpp \ Modules/MContraction/Nucleon.hpp \ Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/Baryon2.hpp \ Modules/MContraction/WeakEye3pt.hpp \ Modules/MContraction/WeakNonEye3pt.hpp \ Modules/MContraction/Baryon.hpp \ From bf52e7cc9668123b829afb701f75a4f81d809c69 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 13 Sep 2019 18:11:10 +0100 Subject: [PATCH 279/347] Latest BaryonUtils.h from Felix + my fixes --- Grid/qcd/utils/BaryonUtils.h | 291 +++++------------------------------ 1 file changed, 37 insertions(+), 254 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 5fc045a8..cad547b1 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -48,228 +48,62 @@ public: typedef typename vobj::scalar_type scalar_type; typedef typename vobj::vector_type vector_type; - - static void ContractBaryons_debug(const PropagatorField &q1, - const PropagatorField &q2, - const PropagatorField &q3, - const Gamma GammaA, - const Gamma GammaB, - ComplexField &bc1, - ComplexField &bc2, - ComplexField &bc3, - ComplexField &bc4, - ComplexField &bc5, - ComplexField &bc6, - ComplexField &baryon_corr); - static void ContractBaryons(const PropagatorField &q1, - const PropagatorField &q2, - const PropagatorField &q3, + static void ContractBaryons(const PropagatorField &q1_src, + const PropagatorField &q2_src, + const PropagatorField &q3_src, const Gamma GammaA, const Gamma GammaB, + const char quarks_snk[], + const char quarks_src[], + const int parity, ComplexField &baryon_corr); - static LatticeSpinColourMatrix quarkContract13(const PropagatorField &q1, - const PropagatorField &q2); }; + template -void BaryonUtils::ContractBaryons_debug(const PropagatorField &q1, - const PropagatorField &q2, - const PropagatorField &q3, +void BaryonUtils::ContractBaryons(const PropagatorField &q1_src, + const PropagatorField &q2_src, + const PropagatorField &q3_src, const Gamma GammaA, const Gamma GammaB, - ComplexField &bc1, - ComplexField &bc2, - ComplexField &bc3, - ComplexField &bc4, - ComplexField &bc5, - ComplexField &bc6, + const char quarks_snk[], + const char quarks_src[], + const int parity, ComplexField &baryon_corr) { - GridBase *grid = q1.Grid(); - // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 - //Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 - //Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 - //Gamma GammaB(Gamma::Algebra::GammaZGamma5); //Still hardcoded CgX = i gamma_3 gamma_5 + assert(parity==1 || parity == -1 && "Parity must be +1 or -1"); + + GridBase *grid = q1_src.Grid(); + Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - char left[] = "sss"; - char right[] = "sss"; std::vector wick_contraction = {0,0,0,0,0,0}; for (int ie=0; ie < 6 ; ie++) - if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]) + if (quarks_src[0] == quarks_snk[epsilon[ie][0]] && quarks_src[1] == quarks_snk[epsilon[ie][1]] && quarks_src[2] == quarks_snk[epsilon[ie][2]]) wick_contraction[ie]=1; - const int parity{ 1 }; - - LatticeView v1(q1); - LatticeView v2(q2); - LatticeView v3(q3); - + typedef typename ComplexField::vector_object vobj; + LatticeView vbaryon_corr{ baryon_corr }; accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - using CF_vobj = typename ComplexField::vector_object; - const auto &D1{ v1[ss] }; - const auto &D2{ v2[ss] }; - const auto &D3{ v3[ss] }; + LatticeView v1(q1_src); + LatticeView v2(q2_src); + LatticeView v3(q3_src); + auto D1 = v1[ss]; + auto D2 = v2[ss]; + auto D3 = v3[ss]; auto gD1a = GammaA * GammaA * D1; auto gD1b = GammaA * g4 * GammaA * D1; auto pD1 = 0.5* (gD1a + (double)parity * gD1b); auto gD3 = GammaB * D3; - CF_vobj result { 0 }; - CF_vobj result1{ 0 }; - CF_vobj result2{ 0 }; - CF_vobj result3{ 0 }; - CF_vobj result4{ 0 }; - CF_vobj result5{ 0 }; - CF_vobj result6{ 0 }; - - for (int ie_src=0; ie_src < 6 ; ie_src++){ - int a_src = epsilon[ie_src][0]; //a - int b_src = epsilon[ie_src][1]; //b - int c_src = epsilon[ie_src][2]; //c - for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ - int a_snk = epsilon[ie_snk][0]; //a' - int b_snk = epsilon[ie_snk][1]; //b' - int c_snk = epsilon[ie_snk][2]; //c' - //This is the \delta_{123}^{123} part - if (wick_contraction[0]){ - auto D2g = D2 * GammaB; - for (int alpha_snk=0; alpha_snk vbaryon_corr(baryon_corr); - vbaryon_corr[ss] = result; - - LatticeView vbc1(bc1); - LatticeView vbc2(bc2); - LatticeView vbc3(bc3); - LatticeView vbc4(bc4); - LatticeView vbc5(bc5); - LatticeView vbc6(bc6); - vbc1[ss] = result1; - vbc2[ss] = result2; - vbc3[ss] = result3; - vbc4[ss] = result4; - vbc5[ss] = result5; - vbc6[ss] = result6; - } );//end loop over lattice sites -} - -template -void BaryonUtils::ContractBaryons(const PropagatorField &q1, - const PropagatorField &q2, - const PropagatorField &q3, - const Gamma GammaA, - const Gamma GammaB, - ComplexField &baryon_corr) -{ - GridBase *grid = q1.Grid(); - - // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 - //Gamma GammaA(Gamma::Algebra::Identity); //Still hardcoded 1 - //Gamma GammaB(Gamma::Algebra::SigmaXZ); //Still hardcoded Cg5 - //Gamma GammaB(Gamma::Algebra::GammaZGamma5); //Still hardcoded CgX = i gamma_3 gamma_5 - Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) - - std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - char left[] = "sss"; - char right[] = "sss"; - std::vector wick_contraction = {0,0,0,0,0,0}; - - for (int ie=0; ie < 6 ; ie++) - if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]) - wick_contraction[ie]=1; - - const int parity{ 1 }; - - LatticeView v1(q1); - LatticeView v2(q2); - LatticeView v3(q3); - - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - - const auto &D1{ v1[ss] }; - const auto &D2{ v2[ss] }; - const auto &D3{ v3[ss] }; - - auto gD1a = GammaA * GammaA * D1; - auto gD1b = GammaA * g4 * GammaA * D1; - auto pD1 = 0.5* (gD1a + (double)parity * gD1b); - auto gD3 = GammaB * D3; - - using CF_vobj = typename ComplexField::vector_object; - CF_vobj result{ 0 }; + vobj result{ 0 }; for (int ie_src=0; ie_src < 6 ; ie_src++){ int a_src = epsilon[ie_src][0]; //a @@ -279,7 +113,7 @@ void BaryonUtils::ContractBaryons(const PropagatorField &q1, int a_snk = epsilon[ie_snk][0]; //a' int b_snk = epsilon[ie_snk][1]; //b' int c_snk = epsilon[ie_snk][2]; //c' - //This is the \delta_{123}^{123} part + //This is the \delta_{456}^{123} part if (wick_contraction[0]){ auto D2g = D2 * GammaB; for (int alpha_snk=0; alpha_snk::ContractBaryons(const PropagatorField &q1, result()()() += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * pD1()(gamma_src,gamma_src)(c_snk,c_src)*D2g()(alpha_snk,beta_src)(a_snk,a_src)*gD3()(alpha_snk,beta_src)(b_snk,b_src); }}} } - //This is the \delta_{123}^{231} part + //This is the \delta_{456}^{231} part if (wick_contraction[1]){ auto pD1g = pD1 * GammaB; for (int alpha_snk=0; alpha_snk::ContractBaryons(const PropagatorField &q1, result()()() += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * pD1g()(gamma_src,beta_src)(c_snk,a_src)*D2()(alpha_snk,beta_src)(a_snk,b_src)*gD3()(alpha_snk,gamma_src)(b_snk,c_src); }}} } - //This is the \delta_{123}^{312} part + //This is the \delta_{456}^{312} part if (wick_contraction[2]){ auto gD3g = gD3 * GammaB; for (int alpha_snk=0; alpha_snk::ContractBaryons(const PropagatorField &q1, result()()() += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * pD1()(gamma_src,beta_src)(c_snk,b_src)*D2()(alpha_snk,gamma_src)(a_snk,c_src)*gD3g()(alpha_snk,beta_src)(b_snk,a_src); }}} } - //This is the \delta_{123}^{132} part + //This is the \delta_{456}^{132} part if (wick_contraction[3]){ auto gD3g = gD3 * GammaB; for (int alpha_snk=0; alpha_snk::ContractBaryons(const PropagatorField &q1, result()()() -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * pD1()(gamma_src,beta_src)(c_snk,b_src)*D2g()(alpha_snk,beta_src)(a_snk,a_src)*gD3()(alpha_snk,gamma_src)(b_snk,c_src); }}} } - //This is the \delta_{123}^{213} part + //This is the \delta_{456}^{213} part if (wick_contraction[5]){ auto pD1g = pD1 * GammaB; for (int alpha_snk=0; alpha_snk::ContractBaryons(const PropagatorField &q1, result()()() -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * pD1g()(gamma_src,beta_src)(c_snk,a_src)*D2()(alpha_snk,gamma_src)(a_snk,c_src)*gD3()(alpha_snk,beta_src)(b_snk,b_src); }}} } - - /*if (ie_src==0 && ie_snk==0){ - baryon_corr._odata[ss] = result; - } else { - baryon_corr._odata[ss] += result; - }*/ - } } - LatticeView vbaryon_corr(baryon_corr); - vbaryon_corr[ss] = result; - } ); //end loop over lattice sites + vbaryon_corr[ss] = result; + + } );//end loop over lattice sites } - -//QDP / CHROMA - style diquark construction -// (q_out)^{c'c}_{alpha,beta} = epsilon^{abc} epsilon^{a'b'c'} (q1)^{aa'}_{rho alpha}^* (q2)^{bb'}_{rho beta} -template -LatticeSpinColourMatrix BaryonUtils::quarkContract13(const PropagatorField &q1, - const PropagatorField &q2) -{ - GridBase *grid = q1.Grid(); - - std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - - // TODO: Felix, made a few changes to fix this. Please validate! - LatticeSpinColourMatrix q_out(grid); - // q_out = 0; TODO: Don't think you need this, as you'll set each site explicitly anyway - LatticeView v1(q1); - LatticeView v2(q2); - LatticeView vw( q_out ); - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - const auto & D1{ v1[ss] }; - const auto & D2{ v2[ss] }; - auto & D_out { vw[ss] }; - D_out = 0; - for (int ie_src=0; ie_src < 6 ; ie_src++){ - int a_src = epsilon[ie_src][0]; //a - int b_src = epsilon[ie_src][1]; //b - int c_src = epsilon[ie_src][2]; //c - for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ - int a_snk = epsilon[ie_snk][0]; //a' - int b_snk = epsilon[ie_snk][1]; //b' - int c_snk = epsilon[ie_snk][2]; //c' - for (int alpha=0; alpha Date: Mon, 16 Sep 2019 15:34:47 +0100 Subject: [PATCH 280/347] Fix location of Grid.h and remove reference to QCD namespace --- .../instantiation/ImprovedStaggeredFermionInstantiation.cc | 2 +- .../ImprovedStaggeredFermionInstantiation.cc.master | 2 +- .../ImprovedStaggeredFermionInstantiationStaggeredImplD.cc | 2 +- .../ImprovedStaggeredFermionInstantiationStaggeredImplF.cc | 2 +- Hadrons/Utilities/Contractor.cc | 1 - Hadrons/Utilities/HadronsXmlValidate.cc | 1 - 6 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Grid/qcd/action/fermion/instantiation/ImprovedStaggeredFermionInstantiation.cc b/Grid/qcd/action/fermion/instantiation/ImprovedStaggeredFermionInstantiation.cc index a617f6cb..b1afc20e 100644 --- a/Grid/qcd/action/fermion/instantiation/ImprovedStaggeredFermionInstantiation.cc +++ b/Grid/qcd/action/fermion/instantiation/ImprovedStaggeredFermionInstantiation.cc @@ -26,7 +26,7 @@ See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#include +#include NAMESPACE_BEGIN(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/ImprovedStaggeredFermionInstantiation.cc.master b/Grid/qcd/action/fermion/instantiation/ImprovedStaggeredFermionInstantiation.cc.master index 2023adc2..d35b7349 100644 --- a/Grid/qcd/action/fermion/instantiation/ImprovedStaggeredFermionInstantiation.cc.master +++ b/Grid/qcd/action/fermion/instantiation/ImprovedStaggeredFermionInstantiation.cc.master @@ -26,7 +26,7 @@ See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#include +#include #include NAMESPACE_BEGIN(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/StaggeredImplD/ImprovedStaggeredFermionInstantiationStaggeredImplD.cc b/Grid/qcd/action/fermion/instantiation/StaggeredImplD/ImprovedStaggeredFermionInstantiationStaggeredImplD.cc index 2023adc2..d35b7349 100644 --- a/Grid/qcd/action/fermion/instantiation/StaggeredImplD/ImprovedStaggeredFermionInstantiationStaggeredImplD.cc +++ b/Grid/qcd/action/fermion/instantiation/StaggeredImplD/ImprovedStaggeredFermionInstantiationStaggeredImplD.cc @@ -26,7 +26,7 @@ See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#include +#include #include NAMESPACE_BEGIN(Grid); diff --git a/Grid/qcd/action/fermion/instantiation/StaggeredImplF/ImprovedStaggeredFermionInstantiationStaggeredImplF.cc b/Grid/qcd/action/fermion/instantiation/StaggeredImplF/ImprovedStaggeredFermionInstantiationStaggeredImplF.cc index 2023adc2..d35b7349 100644 --- a/Grid/qcd/action/fermion/instantiation/StaggeredImplF/ImprovedStaggeredFermionInstantiationStaggeredImplF.cc +++ b/Grid/qcd/action/fermion/instantiation/StaggeredImplF/ImprovedStaggeredFermionInstantiationStaggeredImplF.cc @@ -26,7 +26,7 @@ See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ -#include +#include #include NAMESPACE_BEGIN(Grid); diff --git a/Hadrons/Utilities/Contractor.cc b/Hadrons/Utilities/Contractor.cc index 9ed76c5a..053b4ceb 100644 --- a/Hadrons/Utilities/Contractor.cc +++ b/Hadrons/Utilities/Contractor.cc @@ -34,7 +34,6 @@ See the full license in the file "LICENSE" in the top level distribution directo #include using namespace Grid; -using namespace QCD; using namespace Hadrons; // Separator to be used between contraction terms only (underscores elsewhere) diff --git a/Hadrons/Utilities/HadronsXmlValidate.cc b/Hadrons/Utilities/HadronsXmlValidate.cc index a6100f2b..637f4484 100644 --- a/Hadrons/Utilities/HadronsXmlValidate.cc +++ b/Hadrons/Utilities/HadronsXmlValidate.cc @@ -32,7 +32,6 @@ See the full license in the file "LICENSE" in the top level distribution directo #include using namespace Grid; -using namespace QCD; using namespace Hadrons; // Does the specified file exist? From 2f3dd0703d6aa3f3c26b9bbc3d5d0451526c0ba8 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 16 Sep 2019 17:00:46 +0100 Subject: [PATCH 281/347] Ensure Distillation test (Test_distil) works --- Hadrons/Distil.hpp | 1 - Hadrons/Modules/MDistil/DistilVectors.hpp | 1 - Hadrons/Modules/MDistil/Noises.hpp | 2 -- tests/hadrons/Test_distil.cc | 2 +- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 31f59cd8..9a53760a 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -238,7 +238,6 @@ struct DistilParameters: Serializable { #define DISTIL_PARAMETERS_DEFINE( inSetup ) \ const int Nt{env().getDim(Tdir)}; \ const int nvec{par().nvec}; \ -const int Ns{Ns}; \ const int nnoise{par().Distil.nnoise}; \ const int tsrc{par().Distil.tsrc}; \ const int TI{par().Distil.getTI(env(), inSetup)}; \ diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index ed31ad45..2b549b6c 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -233,7 +233,6 @@ void TDistilVectors::execute(void) const int Ntlocal{ grid4d->LocalDimensions()[3] }; const int Ntfirst{ grid4d->LocalStarts()[3] }; - const int Ns{ Ns }; const int Nt{ env().getDim(Tdir) }; const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false ) }; const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index de4101f3..91cb35ca 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -110,7 +110,6 @@ template void TNoises::setup(void) { const int Nt{env().getDim(Tdir)}; - //const int Ns{Grid::Ns}; const int nnoise{par().nnoise}; const int nvec{par().nvec}; const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, true) }; @@ -123,7 +122,6 @@ template void TNoises::execute(void) { const int Nt{env().getDim(Tdir)}; - //const int Ns{Grid::Ns}; const int nnoise{par().nnoise}; const int nvec{par().nvec}; const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false) }; diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index ab7bdcac..d60eb2b7 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -1058,7 +1058,7 @@ int main(int argc, char *argv[]) static const char XmlFileName[] = "test_distil.xml"; application.saveParameterFile( XmlFileName ); - const std::vector &lat{GridDefaultLatt()}; + const Grid::Coordinate &lat{GridDefaultLatt()}; if( lat.size() == 4 && lat[0] == 4 && lat[1] == 4 && lat[2] == 4 && lat[3] == 8 ) application.run(); else From eb293e99099644958f4707a2e89d28ee62f4aada Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 16 Sep 2019 20:29:37 +0100 Subject: [PATCH 282/347] Restore Baryons modules per develop branch --- Hadrons/Modules.hpp | 1 + Hadrons/Modules/MContraction/Baryon.cc | 35 ++++++ Hadrons/Modules/MContraction/Baryon.hpp | 140 ++++++++++++++++++++++++ Hadrons/modules.inc | 2 + 4 files changed, 178 insertions(+) create mode 100644 Hadrons/Modules/MContraction/Baryon.cc create mode 100644 Hadrons/Modules/MContraction/Baryon.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 1d353f80..14a0b6d8 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include diff --git a/Hadrons/Modules/MContraction/Baryon.cc b/Hadrons/Modules/MContraction/Baryon.cc new file mode 100644 index 00000000..84e0f984 --- /dev/null +++ b/Hadrons/Modules/MContraction/Baryon.cc @@ -0,0 +1,35 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MContraction/Baryon.cc + +Copyright (C) 2015-2019 + +Author: Antonin Portelli + +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 + +using namespace Grid; +using namespace Hadrons; +using namespace MContraction; + +template class Grid::Hadrons::MContraction::TBaryon; + diff --git a/Hadrons/Modules/MContraction/Baryon.hpp b/Hadrons/Modules/MContraction/Baryon.hpp new file mode 100644 index 00000000..fc78ab80 --- /dev/null +++ b/Hadrons/Modules/MContraction/Baryon.hpp @@ -0,0 +1,140 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MContraction/Baryon.hpp + +Copyright (C) 2015-2019 + +Author: Antonin Portelli +Author: Lanny91 + +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 */ + +#ifndef Hadrons_MContraction_Baryon_hpp_ +#define Hadrons_MContraction_Baryon_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * Baryon * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MContraction) + +class BaryonPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(BaryonPar, + std::string, q1, + std::string, q2, + std::string, q3, + std::string, output); +}; + +template +class TBaryon: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl1, 1); + FERM_TYPE_ALIASES(FImpl2, 2); + FERM_TYPE_ALIASES(FImpl3, 3); + class Result: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(Result, + std::vector>>, corr); + }; +public: + // constructor + TBaryon(const std::string name); + // destructor + virtual ~TBaryon(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); +protected: + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(Baryon, ARG(TBaryon), MContraction); + +/****************************************************************************** + * TBaryon implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TBaryon::TBaryon(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TBaryon::getInput(void) +{ + std::vector input = {par().q1, par().q2, par().q3}; + + return input; +} + +template +std::vector TBaryon::getOutput(void) +{ + std::vector out = {}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TBaryon::setup(void) +{ + envTmpLat(LatticeComplex, "c"); +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TBaryon::execute(void) +{ + LOG(Message) << "Computing baryon contractions '" << getName() << "' using" + << " quarks '" << par().q1 << "', '" << par().q2 << "', and '" + << par().q3 << "'" << std::endl; + + auto &q1 = envGet(PropagatorField1, par().q1); + auto &q2 = envGet(PropagatorField2, par().q2); + auto &q3 = envGet(PropagatorField3, par().q2); + envGetTmp(LatticeComplex, c); + Result result; + + // FIXME: do contractions + + // saveResult(par().output, "meson", result); +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MContraction_Baryon_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index a63006ad..0d9cf070 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -51,6 +51,7 @@ modules_cc =\ Modules/MContraction/A2AMesonField.cc \ Modules/MContraction/Gamma3pt.cc \ Modules/MContraction/A2AAslashField.cc \ + Modules/MContraction/Baryon.cc \ Modules/MContraction/DiscLoop.cc \ Modules/MContraction/Meson.cc \ Modules/MContraction/Nucleon.cc \ @@ -135,6 +136,7 @@ modules_hpp =\ Modules/MContraction/WeakNonEye3pt.hpp \ Modules/MContraction/DiscLoop.hpp \ Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/Baryon.hpp \ Modules/MContraction/Meson.hpp \ Modules/MContraction/Nucleon.hpp \ Modules/MContraction/SelfContract.hpp \ From 911fbb0f36b77eb839b8fbb90eaf9fcc2ef700b6 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 17 Sep 2019 13:06:52 +0100 Subject: [PATCH 283/347] Cleanup modules that are no longer required --- Hadrons/Modules.hpp | 3 - Hadrons/Modules/MContraction/Nucleon.cc | 35 --- Hadrons/Modules/MContraction/Nucleon.hpp | 214 ------------------ Hadrons/Modules/MContraction/SelfContract.cc | 7 - Hadrons/Modules/MContraction/SelfContract.hpp | 147 ------------ Hadrons/Modules/MDistil/g5_multiply.cc | 36 --- Hadrons/Modules/MDistil/g5_multiply.hpp | 150 ------------ Hadrons/modules.inc | 6 - 8 files changed, 598 deletions(-) delete mode 100644 Hadrons/Modules/MContraction/Nucleon.cc delete mode 100644 Hadrons/Modules/MContraction/Nucleon.hpp delete mode 100644 Hadrons/Modules/MContraction/SelfContract.cc delete mode 100644 Hadrons/Modules/MContraction/SelfContract.hpp delete mode 100644 Hadrons/Modules/MDistil/g5_multiply.cc delete mode 100644 Hadrons/Modules/MDistil/g5_multiply.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 14a0b6d8..34ecc63a 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -56,8 +56,6 @@ #include #include #include -#include -#include #include #include #include @@ -65,7 +63,6 @@ #include #include #include -#include #include #include #include diff --git a/Hadrons/Modules/MContraction/Nucleon.cc b/Hadrons/Modules/MContraction/Nucleon.cc deleted file mode 100644 index 304ff07a..00000000 --- a/Hadrons/Modules/MContraction/Nucleon.cc +++ /dev/null @@ -1,35 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: Hadrons/Modules/MContraction/Nucleon.cc - -Copyright (C) 2015-2019 - -Author: Antonin Portelli - -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 - -using namespace Grid; -using namespace Hadrons; -using namespace MContraction; - -template class Grid::Hadrons::MContraction::TNucleon; - diff --git a/Hadrons/Modules/MContraction/Nucleon.hpp b/Hadrons/Modules/MContraction/Nucleon.hpp deleted file mode 100644 index 6c1633c7..00000000 --- a/Hadrons/Modules/MContraction/Nucleon.hpp +++ /dev/null @@ -1,214 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: Hadrons/Modules/MContraction/Nucleon.hpp - -Copyright (C) 2015-2019 - -Author: Antonin Portelli -Author: Felix Erben - -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 */ - -#ifndef Hadrons_MContraction_Nucleon_hpp_ -#define Hadrons_MContraction_Nucleon_hpp_ - -#include -#include -#include - -BEGIN_HADRONS_NAMESPACE - -/****************************************************************************** - * Nucleon * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MContraction) - -class NucleonPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(NucleonPar, - std::string, q1, - std::string, q2, - std::string, q3, - std::string, output); -}; - -template -class TNucleon: public Module -{ -public: - FERM_TYPE_ALIASES(FImpl1, 1); - FERM_TYPE_ALIASES(FImpl2, 2); - FERM_TYPE_ALIASES(FImpl3, 3); - class Result: Serializable - { - public: - GRID_SERIALIZABLE_CLASS_MEMBERS(Result, - std::vector, corr); - }; -public: - // constructor - TNucleon(const std::string name); - // destructor - virtual ~TNucleon(void) {}; - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); -protected: - // setup - virtual void setup(void); - // execution - virtual void execute(void); -}; - -MODULE_REGISTER_TMP(Nucleon, ARG(TNucleon), MContraction); - -/****************************************************************************** - * TNucleon implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -TNucleon::TNucleon(const std::string name) -: Module(name) -{} - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TNucleon::getInput(void) -{ - std::vector input = {par().q1, par().q2, par().q3}; - - return input; -} - -template -std::vector TNucleon::getOutput(void) -{ - std::vector out = {}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void TNucleon::setup(void) -{ - envTmpLat(LatticeComplex, "c"); - envTmpLat(LatticeComplex, "diquark"); -} - -#ifdef DEBUG -template struct DebugShowType { DebugShowType() { T t = (void***) nullptr; } }; -#define DEBUG_SHOW_TYPE(x) DebugShowType __DEBUG_SHOW_TYPE__##x -#endif - -// execution /////////////////////////////////////////////////////////////////// -template -void TNucleon::execute(void) -{ - LOG(Message) << "Computing nucleon contractions '" << getName() << "' using" - << " quarks '" << par().q1 << "', '" << par().q2 << "', and '" - << par().q3 << "'" << std::endl; - - auto &q1 = envGet(PropagatorField1, par().q1); - auto &q2 = envGet(PropagatorField2, par().q2); - auto &q3 = envGet(PropagatorField3, par().q2); - envGetTmp(LatticeComplex, c); - envGetTmp(LatticeComplex, diquark); - Result result; - int nt = env().getDim(Tp); - result.corr.resize(nt); - - std::vector buf; - // C = i gamma_2 gamma_4 => C gamma_5 = - i gamma_1 gamma_3 - Gamma Cg5(Gamma::Algebra::SigmaXZ); - Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) - - std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - // This is the \delta_{123}^{123} part - for (int ie_src=0; ie_src < 6 ; ie_src++){ - int c1_src = epsilon[ie_src][0]; //a - int c2_src = epsilon[ie_src][1]; //b - int c3_src = epsilon[ie_src][2]; //c - for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ - int c1_snk = epsilon[ie_snk][0]; //a' - int c2_snk = epsilon[ie_snk][1]; //b' - int c3_snk = epsilon[ie_snk][2]; //c' - auto Dcc = peekColour(q1,c1_snk,c1_src); //D_{gamma' gamma} - auto Daa = peekColour(q2,c2_snk,c2_src); //D_{alpha' alpha} - // DEBUG Just defining a few types so I can see what these things are - //auto Daa_debug1 = transposeSpin( q1 ); - // Current compilation settings tell me that FImpl is WilsonImplR (see FermionOperatorImpl.h, line 163) - const WilsonImplR::PropagatorField &Debug_q_1{ q1 }; - //DEBUG_SHOW_TYPE( q1 ); - // The propagator field is an alias for - const Lattice, Ns> >> &Debug_q_2{ q1 }; - // So then Daa is one of these - const Lattice, Ns> >> &Debug_Daa_1{ Daa }; - // Which means I should be able to do this - //Lattice, Ns> >> Debug_Daa_2 = transposeSpin(Daa); - // END DEBUG - //auto test = transposeSpin(Daa); //Does not work... - auto Dbb = peekColour(q3,c3_snk,c3_src); //D_{beta' beta} - diquark = trace(Cg5 * Daa * Cg5 * Dbb); //Daa transposed???? - //diquark = q2()()(c2,1) * Cg5 * q3()()(c3,2); //Why does this not work?? - auto temp = Dcc * diquark; - auto g4_temp = g4 * temp; - int parity = 1; - c += epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * (double)parity * trace(temp + g4_temp); - } - } - - - // This is the \delta_{123}^{213} part - for (int ie_src=0; ie_src < 6 ; ie_src++){ - int c1_src = epsilon[ie_src][0]; //a - int c2_src = epsilon[ie_src][1]; //b - int c3_src = epsilon[ie_src][2]; //c - for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ - int c1_snk = epsilon[ie_snk][0]; //a' - int c2_snk = epsilon[ie_snk][1]; //b' - int c3_snk = epsilon[ie_snk][2]; //c' - auto Dca = peekColour(q1,c1_snk,c2_src); //D_{gamma' alpha} - auto Dac = peekColour(q2,c2_snk,c1_src); //D_{alpha' gamma} - auto Dbb = peekColour(q3,c3_snk,c3_src); //D_{beta' beta} - auto temp = Dca * Cg5 * Dbb * Cg5 * Dac; //(Dbb*Cg5) transposed??? - auto g4_temp = g4 * temp; - int parity = 1; - c -= epsilon_sgn[ie_src] * epsilon_sgn[ie_snk] * 0.5 * (double)parity * trace(temp + g4_temp); - } - } - - sliceSum(c,buf,Tp); - for (unsigned int t = 0; t < buf.size(); ++t) - { - result.corr[t] = TensorRemove(buf[t]); - } - - saveResult(par().output, "baryon", result); -} - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MContraction_Nucleon_hpp_ diff --git a/Hadrons/Modules/MContraction/SelfContract.cc b/Hadrons/Modules/MContraction/SelfContract.cc deleted file mode 100644 index b89d55fe..00000000 --- a/Hadrons/Modules/MContraction/SelfContract.cc +++ /dev/null @@ -1,7 +0,0 @@ -#include - -using namespace Grid; -using namespace Hadrons; -using namespace MContraction; - -template class Grid::Hadrons::MContraction::TSelfContract; diff --git a/Hadrons/Modules/MContraction/SelfContract.hpp b/Hadrons/Modules/MContraction/SelfContract.hpp deleted file mode 100644 index 3fe8bcc6..00000000 --- a/Hadrons/Modules/MContraction/SelfContract.hpp +++ /dev/null @@ -1,147 +0,0 @@ -/************************************************************************************* - -Grid physics library, www.github.com/paboyle/Grid - -Source file: Hadrons/Modules/MContraction/Baryon.hpp - -Copyright (C) 2015-2019 - -Author: Antonin Portelli -Author: Felix Erben - -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 */ - -#ifndef Hadrons_MContraction_SelfContract_hpp_ -#define Hadrons_MContraction_SelfContract_hpp_ - -#include -#include -#include - -BEGIN_HADRONS_NAMESPACE - -/****************************************************************************** - * SelfContract * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MContraction) - -class SelfContractPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(SelfContractPar, - std::string, q_self, - Gamma::Algebra, gamma, - std::string, sink, - std::string, output); -}; - -template -class TSelfContract: public Module -{ - FERM_TYPE_ALIASES(FImpl,); - BASIC_TYPE_ALIASES(ScalarImplCR, Scalar); - SINK_TYPE_ALIASES(Scalar); - class Result: Serializable - { - public: - GRID_SERIALIZABLE_CLASS_MEMBERS(Result, - Gamma::Algebra, gamma, - std::vector, corr); - }; -public: - // constructor - TSelfContract(const std::string name); - // destructor - virtual ~TSelfContract(void) {}; - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); -protected: - // setup - virtual void setup(void); - // execution - virtual void execute(void); -}; - -MODULE_REGISTER_TMP(SelfContract, TSelfContract, MContraction); - -/****************************************************************************** - * TSelfContract implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -TSelfContract::TSelfContract(const std::string name) -: Module(name) -{} - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TSelfContract::getInput(void) -{ - std::vector in = {par().q_self, par().sink}; - - return in; -} - -template -std::vector TSelfContract::getOutput(void) -{ - std::vector out = {}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void TSelfContract::setup(void) -{ - envTmpLat(LatticeComplex, "c"); -} - -// execution /////////////////////////////////////////////////////////////////// -template -void TSelfContract::execute(void) -{ - LOG(Message) << "Computing self-contracted loop '" << getName() - << "' using '" << par().q_self << "' with " << par().gamma - << " insertion and sink " << par().sink << std::endl; - - auto &q_self = envGet(PropagatorField, par().q_self); - Gamma gamma(par().gamma); - std::vector buf; - Result result; - envGetTmp(LatticeComplex, c); - SinkFnScalar &sink = envGet(SinkFnScalar, par().sink); - - c = trace(gamma*q_self); - buf = sink(c); - result.gamma = par().gamma; - result.corr.resize(buf.size()); - for (unsigned int t = 0; t < buf.size(); ++t) - { - result.corr[t] = TensorRemove(buf[t]); - } - saveResult(par().output, "selfContr", result); -} - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MContraction_SelfContract_hpp_ diff --git a/Hadrons/Modules/MDistil/g5_multiply.cc b/Hadrons/Modules/MDistil/g5_multiply.cc deleted file mode 100644 index 420b6768..00000000 --- a/Hadrons/Modules/MDistil/g5_multiply.cc +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/g5_multiply.cc - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 - -using namespace Grid; -using namespace Hadrons; -using namespace MDistil; - -template class Grid::Hadrons::MDistil::Tg5_multiply; diff --git a/Hadrons/Modules/MDistil/g5_multiply.hpp b/Hadrons/Modules/MDistil/g5_multiply.hpp deleted file mode 100644 index 498d50da..00000000 --- a/Hadrons/Modules/MDistil/g5_multiply.hpp +++ /dev/null @@ -1,150 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/g5_multiply.hpp - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 */ - -#ifndef Hadrons_MDistil_g5_multiply_hpp_ -#define Hadrons_MDistil_g5_multiply_hpp_ - -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation -#include - -BEGIN_HADRONS_NAMESPACE - -/****************************************************************************** - * g5_multiply * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) - -class g5_multiplyPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(g5_multiplyPar, - std::string, input, - int, nnoise, - int, LI, - int, Ns, - int, Nt_inv); -}; - -template -class Tg5_multiply: public Module -{ - public: - FERM_TYPE_ALIASES(FImpl,); - -public: - // constructor - Tg5_multiply(const std::string name); - // destructor - virtual ~Tg5_multiply(void) {}; - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); -}; - -MODULE_REGISTER_TMP(g5_multiply, Tg5_multiply, MDistil); - -/****************************************************************************** - * Tg5_multiply implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -Tg5_multiply::Tg5_multiply(const std::string name) -: Module(name) -{} - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector Tg5_multiply::getInput(void) -{ - std::vector in; - - in.push_back(par().input); - - return in; -} - -template -std::vector Tg5_multiply::getOutput(void) -{ - std::vector out = {getName()}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void Tg5_multiply::setup(void) -{ - int nnoise=par().nnoise; - int LI=par().LI; - int Ns=par().Ns; - int Nt_inv=par().Nt_inv; - - envCreate(std::vector, getName(), 1, - nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); - -} - -// execution /////////////////////////////////////////////////////////////////// -template -void Tg5_multiply::execute(void) -{ - auto &input_vector = envGet(std::vector, par().input); - auto &output_vector = envGet(std::vector, getName()); - - Gamma g5(Gamma::Algebra::Gamma5); - - int nnoise=par().nnoise; - int LI=par().LI; - int Ns=par().Ns; - int Nt_inv=par().Nt_inv; - int Ntot = nnoise*LI*Ns*Nt_inv; - for (int i =0; i Date: Tue, 17 Sep 2019 13:10:59 +0100 Subject: [PATCH 284/347] Cleanup tests that are no longer required --- tests/hadrons/Test_24.cc | 774 ------------------------------ tests/hadrons/Test_tesseract.cc | 823 -------------------------------- 2 files changed, 1597 deletions(-) delete mode 100644 tests/hadrons/Test_24.cc delete mode 100644 tests/hadrons/Test_tesseract.cc diff --git a/tests/hadrons/Test_24.cc b/tests/hadrons/Test_24.cc deleted file mode 100644 index d7e5db4c..00000000 --- a/tests/hadrons/Test_24.cc +++ /dev/null @@ -1,774 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Tests/Hadrons/Test_hadrons_distil.cc - - Copyright (C) 2015-2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 -#include -#include - -using namespace Grid; -using namespace Hadrons; - -///////////////////////////////////////////////////////////// -// Test creation of laplacian eigenvectors -///////////////////////////////////////////////////////////// -int Nconf = 3160; - -void test_Global(Application &application) -{ - // global parameters - Application::GlobalPar globalPar; - globalPar.trajCounter.start = Nconf; - globalPar.trajCounter.end = Nconf + 20; - globalPar.trajCounter.step = 20; - globalPar.runId = "test"; - application.setPar(globalPar); -} - -///////////////////////////////////////////////////////////// -// Test creation of laplacian eigenvectors -///////////////////////////////////////////////////////////// - -void test_LapEvec(Application &application) -{ - const char szGaugeName[] = "gauge"; - // gauge field - application.createModule(szGaugeName); - // Now make an instance of the LapEvec object - MDistil::LapEvecPar p; - //p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; - //p.ConfigFileName="ckpoint_lat.3000"; - p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; - p.ConfigFileName="ckpoint_lat." + std::to_string(Nconf); - p.gauge = szGaugeName; - //p.EigenPackName = "ePack"; - //p.Distil.TI = 8; - //p.Distil.LI = 3; - //p.Distil.Nnoise = 2; - //p.Distil.tSrc = 0; - p.Stout.steps = 3; - p.Stout.parm = 0.2; - p.Cheby.PolyOrder = 11; - p.Cheby.alpha = 0.3; - p.Cheby.beta = 12.5; - p.Lanczos.Nvec = 50; - p.Lanczos.Nk = 60; - p.Lanczos.Np = 20; - p.Lanczos.MaxIt = 1000; - p.Lanczos.resid = 1e-8; - application.createModule("LapEvec",p); -} - -///////////////////////////////////////////////////////////// -// Perambulators -///////////////////////////////////////////////////////////// - -void test_Perambulators(Application &application) -{ - // PerambLight parameters - MDistil::Peramb::Par PerambPar; - PerambPar.eigenPack="LapEvec"; - PerambPar.PerambFileName="peramb_" + std::to_string(Nconf) + ".bin"; - PerambPar.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; - PerambPar.ConfigFileName="ckpoint_lat." + std::to_string(Nconf); - PerambPar.UniqueIdentifier="full_dilution"; - PerambPar.Distil.tsrc = 0; - PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=50; - PerambPar.Distil.SI=4; - PerambPar.Distil.TI=64; - PerambPar.nvec=50; - PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt=64; - PerambPar.Distil.Nt_inv=1; - PerambPar.Solver.mass=0.005; - PerambPar.Solver.M5=1.8; - PerambPar.Ls=16; - PerambPar.Solver.CGPrecision=1e-7; - PerambPar.Solver.MaxIterations=10000; - application.createModule("Peramb",PerambPar); -} -///////////////////////////////////////////////////////////// -// DistilVectors -///////////////////////////////////////////////////////////// - -void test_DistilVectors(Application &application) -{ - // DistilVectors parameters - MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="Peramb_noise"; - //DistilVecPar.perambulator="Peramb_perambulator_light"; - DistilVecPar.perambulator="Peramb"; - DistilVecPar.eigenPack="LapEvec"; - DistilVecPar.tsrc = 0; - DistilVecPar.nnoise = 1; - DistilVecPar.LI=50; - DistilVecPar.SI=4; - DistilVecPar.TI=64; - DistilVecPar.nvec=50; - DistilVecPar.Ns=4; - DistilVecPar.Nt=64; - DistilVecPar.Nt_inv=1; - application.createModule("DistilVecs",DistilVecPar); -} -void test_PerambulatorsS(Application &application) -{ - // Peramb parameters - MDistil::Peramb::Par PerambPar; - PerambPar.eigenPack="LapEvec"; - PerambPar.PerambFileName="perambS.bin"; - PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; - PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; - PerambPar.UniqueIdentifier="full_dilution"; - PerambPar.Distil.tsrc = 0; - PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=50; - PerambPar.Distil.SI=4; - PerambPar.Distil.TI=64; - PerambPar.nvec=50; - PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt=64; - PerambPar.Distil.Nt_inv=1; - PerambPar.Solver.mass=0.04; //strange mass??? - PerambPar.Solver.M5=1.8; - PerambPar.Ls=16; - PerambPar.Solver.CGPrecision=1e-8; - PerambPar.Solver.MaxIterations=10000; - application.createModule("PerambS",PerambPar); -} -///////////////////////////////////////////////////////////// -// DistilVectors -///////////////////////////////////////////////////////////// - -void test_DistilVectorsS(Application &application) -{ - // DistilVectors parameters - MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="PerambS_noise"; - //DistilVecPar.perambulator="PerambS_perambulator_light"; - DistilVecPar.perambulator="PerambS"; - DistilVecPar.eigenPack="LapEvec"; - DistilVecPar.tsrc = 0; - DistilVecPar.nnoise = 1; - DistilVecPar.LI=50; - DistilVecPar.SI=4; - DistilVecPar.TI=64; - DistilVecPar.nvec=50; - DistilVecPar.Ns=4; - DistilVecPar.Nt=64; - DistilVecPar.Nt_inv=1; - application.createModule("DistilVecsS",DistilVecPar); -} -///////////////////////////////////////////////////////////// -// MesonSink -///////////////////////////////////////////////////////////// - -void test_MesonSink(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="Peramb_unsmeared_sink"; - A2AMesonFieldPar.right="Peramb_unsmeared_sink"; - A2AMesonFieldPar.output="DistilFields"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonSink",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// g5*unsmeared -///////////////////////////////////////////////////////////// -void test_g5_sinks(Application &application) -{ - MDistil::g5_multiply::Par g5_multiplyPar; - g5_multiplyPar.input="Peramb_unsmeared_sink"; - g5_multiplyPar.nnoise = 1; - g5_multiplyPar.LI=50; - g5_multiplyPar.Ns=4; - g5_multiplyPar.Nt_inv=1; - application.createModule("g5phi",g5_multiplyPar); -} -///////////////////////////////////////////////////////////// -// MesonFields -///////////////////////////////////////////////////////////// - -void test_MesonFieldSL(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecsS_phi"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_phi"; - A2AMesonFieldPar.output="DistilFieldsS"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldS",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// MesonFields - phiphi -///////////////////////////////////////////////////////////// - -void test_MesonField(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_phi"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_phi"; - A2AMesonFieldPar.output="MesonSinksPhi"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonField",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// MesonFields - rhorho -///////////////////////////////////////////////////////////// - -void test_MesonFieldRho(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_rho"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.output="MesonSinksRho"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldRho",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// BaryonFields - phiphiphi -///////////////////////////////////////////////////////////// - -void test_BaryonFieldPhi(Application &application) -{ - // DistilVectors parameters - MDistil::BContraction::Par BContractionPar; - BContractionPar.one="DistilVecs_phi"; - BContractionPar.two="DistilVecs_phi"; - BContractionPar.three="DistilVecs_phi"; - BContractionPar.output="BaryonFieldPhi"; - BContractionPar.parity=1; - BContractionPar.mom={"0 0 0"}; - application.createModule("BaryonFieldPhi",BContractionPar); -} -///////////////////////////////////////////////////////////// -// BaryonFields - rhorhorho -///////////////////////////////////////////////////////////// - -void test_BaryonFieldRho(Application &application) -{ - // DistilVectors parameters - MDistil::BContraction::Par BContractionPar; - BContractionPar.one="DistilVecs_rho"; - BContractionPar.two="DistilVecs_rho"; - BContractionPar.three="DistilVecs_rho"; - BContractionPar.output="BaryonFieldRho"; - BContractionPar.parity=1; - BContractionPar.mom={"0 0 0"}; - application.createModule("BaryonFieldRho",BContractionPar); -} -///////////////////////////////////////////////////////////// -// BaryonContraction -///////////////////////////////////////////////////////////// - -void test_Baryon2pt(Application &application) -{ - // DistilVectors parameters - MDistil::Baryon2pt::Par Baryon2ptPar; - Baryon2ptPar.inputL="BaryonFieldPhi"; - Baryon2ptPar.inputR="BaryonFieldRho"; - Baryon2ptPar.quarksL="uud"; - Baryon2ptPar.quarksR="uud"; - Baryon2ptPar.output="C2_baryon"; - application.createModule("C2_b",Baryon2ptPar); -} - -///////////////////////////////////////////////////////////// -// emField -///////////////////////////////////////////////////////////// -void test_em(Application &application) -{ - MGauge::StochEm::Par StochEmPar; - StochEmPar.gauge=PhotonR::Gauge::feynman; - StochEmPar.zmScheme=PhotonR::ZmScheme::qedL; - application.createModule("Em",StochEmPar); -} -///////////////////////////////////////////////////////////// -// MesonA2ASlash -///////////////////////////////////////////////////////////// -void test_Aslash(Application &application) -{ - MContraction::A2AAslashField::Par A2AAslashFieldPar; - A2AAslashFieldPar.left="Peramb_unsmeared_sink"; - A2AAslashFieldPar.right="Peramb_unsmeared_sink"; - A2AAslashFieldPar.output="unsmeared_Aslash"; - A2AAslashFieldPar.emField={"Em"}; - A2AAslashFieldPar.cacheBlock=2; - A2AAslashFieldPar.block=4; - application.createModule("Aslash_field",A2AAslashFieldPar); -} - -bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) -{ - if( bGobbleWhiteSpace ) - while( std::isspace(static_cast(*pstr)) ) - pstr++; - const char * p = pstr; - bool bMinus = false; - char c = * p++; - if( c == '+' ) - c = * p++; - else if( c == '-' ) { - bMinus = true; - c = * p++; - } - int n = c - '0'; - if( n < 0 || n > 9 ) - return false; - while( * p >= '0' && * p <= '9' ) { - n = n * 10 + ( * p ) - '0'; - p++; - } - if( bMinus ) - n *= -1; - ri = n; - pstr = p; - return true; -} - -#ifdef DEBUG - -typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; - -template -void DebugShowTensor(T &x, const char * n) -{ - const MyTensor::Index s{x.size()}; - std::cout << n << ".size() = " << s << std::endl; - std::cout << n << ".NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; - std::cout << n << ".NumIndices = " << x.NumIndices << std::endl; - const auto d{x.dimensions()}; - //std::cout << n << ".dimensions().size() = " << d.size() << std::endl; - std::cout << "Dimensions are "; - for(auto i = 0; i < x.NumDimensions ; i++) - std::cout << "[" << d[i] << "]"; - std::cout << std::endl; - MyTensor::Index SizeCalculated{1}; - std::cout << "Dimensions again"; - for(int i=0 ; i < x.NumDimensions ; i++ ) { - std::cout << " : [" << i << /*", " << x.IndexNames[i] << */"]=" << x.dimension(i); - SizeCalculated *= d[i]; - } - std::cout << std::endl; - std::cout << "SizeCalculated = " << SizeCalculated << std::endl;\ - assert( SizeCalculated == s ); - // Initialise - assert( x.NumDimensions == 3 ); - for( int i = 0 ; i < d[0] ; i++ ) - for( int j = 0 ; j < d[1] ; j++ ) - for( int k = 0 ; k < d[2] ; k++ ) { - x(i,j,k) = std::complex(SizeCalculated, -SizeCalculated); - SizeCalculated--; - } - // Show raw data - std::cout << "Data follow : " << std::endl; - typename T::Scalar * p = x.data(); - for( auto i = 0 ; i < s ; i++ ) { - if( i ) std::cout << ", "; - std::cout << n << ".data()[" << i << "]=" << * p++; - } - std::cout << std::endl; -} - -// Test whether typedef and underlying types are the same - -void DebugTestTypeEqualities(void) -{ - Real r1; - RealD r2; - double r3; - const std::type_info &tr1{typeid(r1)}; - const std::type_info &tr2{typeid(r2)}; - const std::type_info &tr3{typeid(r3)}; - if( tr1 == tr2 && tr2 == tr3 ) - std::cout << "r1, r2 and r3 are the same type" << std::endl; - else - std::cout << "r1, r2 and r3 are different types" << std::endl; - std::cout << "r1 is a " << tr1.name() << std::endl; - std::cout << "r2 is a " << tr2.name() << std::endl; - std::cout << "r3 is a " << tr3.name() << std::endl; - - // These are the same - Complex c1; - std::complex c2; - const std::type_info &tc1{typeid(c1)}; - const std::type_info &tc2{typeid(c2)}; - const std::type_info &tc3{typeid(SpinVector::scalar_type)}; - if( tc1 == tc2 && tc2 == tc3) - std::cout << "c1, c2 and SpinVector::scalar_type are the same type" << std::endl; - else - std::cout << "c1, c2 and SpinVector::scalar_type are different types" << std::endl; - std::cout << "c1 is a " << tc1.name() << std::endl; - std::cout << "c2 is a " << tc2.name() << std::endl; - std::cout << "SpinVector::scalar_type is a " << tc3.name() << std::endl; - - // These are the same - SpinVector s1; - iSpinVector s2; - iScalar, Ns> > s3; - const std::type_info &ts1{typeid(s1)}; - const std::type_info &ts2{typeid(s2)}; - const std::type_info &ts3{typeid(s3)}; - if( ts1 == ts2 && ts2 == ts3 ) - std::cout << "s1, s2 and s3 are the same type" << std::endl; - else - std::cout << "s1, s2 and s3 are different types" << std::endl; - std::cout << "s1 is a " << ts1.name() << std::endl; - std::cout << "s2 is a " << ts2.name() << std::endl; - std::cout << "s3 is a " << ts3.name() << std::endl; - - // These are the same - SpinColourVector sc1; - iSpinColourVector sc2; - const std::type_info &tsc1{typeid(sc1)}; - const std::type_info &tsc2{typeid(sc2)}; - if( tsc1 == tsc2 ) - std::cout << "sc1 and sc2 are the same type" << std::endl; - else - std::cout << "sc1 and sc2 are different types" << std::endl; - std::cout << "sc1 is a " << tsc1.name() << std::endl; - std::cout << "sc2 is a " << tsc2.name() << std::endl; -} - -bool DebugEigenTest() -{ - { - Eigen::TensorFixedSize,Eigen::Sizes<3,4,5>> x; - DebugShowTensor(x, "fixed"); - } - const char pszTestFileName[] = "test_tensor.bin"; - std::array as={"Alpha", "Beta", "Gamma"}; - MyTensor x(as, 2,1,4); - DebugShowTensor(x, "x"); - x.WriteBinary(pszTestFileName); - DebugShowTensor(x, "x"); - // Test initialisation of an array of strings - for( auto a : as ) - std::cout << a << std::endl; - Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; - DebugShowTensor(p, "p"); - std::cout << "p.IndexNames follow" << std::endl; - for( auto a : p.IndexNames ) - std::cout << a << std::endl; - // Now see whether we can read a tensor back - std::array Names2={"Alpha", "Gamma", "Delta"}; - MyTensor y(Names2, 2,4,1); - y.ReadBinary(pszTestFileName); - DebugShowTensor(y, "y"); - - // Testing whether typedef produces the same type - yes it does - - DebugTestTypeEqualities(); - std::cout << std::endl; - - // How to access members of SpinColourVector - SpinColourVector sc; - for( int s = 0 ; s < Ns ; s++ ) { - auto cv{sc()(s)}; - iVector c2{sc()(s)}; - std::cout << " cv is a " << typeid(cv).name() << std::endl; - std::cout << " c2 is a " << typeid(c2).name() << std::endl; - for( int c = 0 ; c < Nc ; c++ ) { - Complex & z{cv(c)}; - std::cout << " sc[spin=" << s << ", colour=" << c << "] = " << z << std::endl; - } - } - // We could have removed the Lorentz index independently, but much easier to do as we do above - iVector,Ns> sc2{sc()}; - std::cout << "sc() is a " << typeid(sc()).name() << std::endl; - std::cout << "sc2 is a " << typeid(sc2 ).name() << std::endl; - - // Or you can access elements directly - std::complex z = sc()(0)(0); - std::cout << "z = " << z << std::endl; - sc()(3)(2) = std::complex{3.141,-3.141}; - std::cout << "sc()(3)(2) = " << sc()(3)(2) << std::endl; - - return true; -} - -template -void DebugGridTensorTest_print( int i ) -{ - std::cout << i << " : " << EigenIO::is_tensor::value - << ", rank " << EigenIO::Traits::rank - << ", rank_non_trivial " << EigenIO::Traits::rank_non_trivial - << ", count " << EigenIO::Traits::count - << ", scalar_size " << EigenIO::Traits::scalar_size - << ", size " << EigenIO::Traits::size - << std::endl; -} - -// begin() and end() are the minimum necessary to support range-for loops -// should really turn this into an iterator ... -template -class TestObject { -public: - using value_type = T; -private: - value_type * m_p; -public: - TestObject() { - m_p = reinterpret_cast(std::malloc(N * sizeof(value_type))); - } - ~TestObject() { std::free(m_p); } - inline value_type * begin(void) { return m_p; } - inline value_type * end(void) { return m_p + N; } -}; - -template -void EigenSliceExample() -{ - std::cout << "Eigen example, Options = " << Options << std::endl; - using T2 = Eigen::Tensor; - T2 a(4, 3); - a.setValues({{0, 100, 200}, {300, 400, 500}, - {600, 700, 800}, {900, 1000, 1100}}); - std::cout << "a\n" << a << std::endl; - DumpMemoryOrder( a, "a" ); - Eigen::array offsets = {0, 1}; - Eigen::array extents = {4, 2}; - T2 slice = a.slice(offsets, extents); - std::cout << "slice\n" << slice << std::endl; - DumpMemoryOrder( slice, "slice" ); - std::cout << "\n========================================" << std::endl; -} - -template -void EigenSliceExample2() -{ - using TestScalar = std::complex; - using T3 = Eigen::Tensor; - using T2 = Eigen::Tensor; - T3 a(2,3,4); - - std::cout << "Initialising a:"; - for_all( a, [&](TestScalar &c, float f, const std::array &Dims ){ - c = TestScalar{f,-f}; - std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; - } ); - std::cout << std::endl; - //std::cout << "Validating a:"; - float z = 0; - for( int i = 0 ; i < a.dimension(0) ; i++ ) - for( int j = 0 ; j < a.dimension(1) ; j++ ) - for( int k = 0 ; k < a.dimension(2) ; k++ ) { - TestScalar w{z, -z}; - //std::cout << " a(" << i << "," << j << "," << k << ")=" << w; - assert( a(i,j,k) == w ); - z++; - } - //std::cout << std::endl; - //std::cout << "a initialised to:\n" << a << std::endl; - DumpMemoryOrder( a, "a" ); - std::cout << "for_all(a):"; - for_all( a, [&](TestScalar c, typename T3::Index n, const std::array &Dims ){ - std::cout << " (" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")<" << n << ">=" << c; - } ); - std::cout << std::endl; - Eigen::array offsets = {0,1,1}; - Eigen::array extents = {1,2,2}; - T3 b; - b = a.slice( offsets, extents );//.reshape(NewExtents); - std::cout << "b = a.slice( offsets, extents ):\n" << b << std::endl; - DumpMemoryOrder( b, "b" ); - T2 c(3,4); - c = a.chip(0,1); - std::cout << "c = a.chip(0,0):\n" << c << std::endl; - DumpMemoryOrder( c, "c" ); - //T2 d = b.reshape(extents); - //std::cout << "b.reshape(extents) is:\n" << d << std::endl; - std::cout << "\n========================================" << std::endl; -} - -void DebugFelixTensorTest( void ) -{ - unsigned int Nmom = 2; - unsigned int Nt = 2; - unsigned int N_1 = 2; - unsigned int N_2 = 2; - unsigned int N_3 = 2; - using BaryonTensorSet = Eigen::Tensor; - BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); - std::vector Memory(Nmom * Nt * N_1 * N_2 * N_3 * 2); - using BaryonTensorMap = Eigen::TensorMap; - BaryonTensorMap BField4 (&Memory[0], Nmom,4,Nt,N_1,N_2,N_3); - - EigenSliceExample(); - EigenSliceExample<0>(); - EigenSliceExample2(); - EigenSliceExample2<0>(); -} - -bool DebugGridTensorTest( void ) -{ - DebugFelixTensorTest(); - typedef Complex t1; - typedef iScalar t2; - typedef iVector t3; - typedef iMatrix t4; - typedef iVector,4> t5; - typedef iScalar t6; - typedef iMatrix t7; - typedef iMatrix,4>,2> t8; - int i = 1; - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - - //using TOC7 = TestObject, 7>; - using TOC7 = t7; - TOC7 toc7; - constexpr std::complex Inc{1,-1}; - std::complex Start{Inc}; - for( auto &x : toc7 ) { - x = Start; - Start += Inc; - } - i = 0; - std::cout << "toc7:"; - for( auto x : toc7 ) std::cout << " [" << i++ << "]=" << x; - std::cout << std::endl; - - t2 o2; - auto a2 = TensorRemove(o2); - //t3 o3; - //t4 o4; - //auto a3 = TensorRemove(o3); - //auto a4 = TensorRemove(o4); - - return true; -} -#endif - -int main(int argc, char *argv[]) -{ -#ifdef DEBUG - // Debug only - test of Eigen::Tensor - std::cout << "sizeof(int) = " << sizeof(int) - << ", sizeof(long) = " << sizeof(long) - << ", sizeof(size_t) = " << sizeof(size_t) - << ", sizeof(std::size_t) = " << sizeof(std::size_t) - << ", sizeof(std::streamsize) = " << sizeof(std::streamsize) - << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; - if( DebugEigenTest() ) return 0; - if(DebugGridTensorTest()) return 0; -#endif - - // Decode command-line parameters. 1st one is which test to run - int iTestNum = -1; - - for(int i = 1 ; i < argc ; i++ ) { - std::cout << "argv[" << i << "]=\"" << argv[i] << "\"" << std::endl; - const char * p = argv[i]; - if( * p == '/' || * p == '-' ) { - p++; - char c = * p++; - switch(toupper(c)) { - case 'T': - if( bNumber( iTestNum, p ) ) { - std::cout << "Test " << iTestNum << " requested"; - if( * p ) - std::cout << " (ignoring trailer \"" << p << "\")"; - std::cout << std::endl; - } - else - std::cout << "Invalid test \"" << &argv[i][2] << "\"" << std::endl; - break; - default: - std::cout << "Ignoring switch \"" << &argv[i][1] << "\"" << std::endl; - break; - } - } - } - - // initialization ////////////////////////////////////////////////////////// - Grid_init(&argc, &argv); - HadronsLogError.Active(GridLogError.isActive()); - HadronsLogWarning.Active(GridLogWarning.isActive()); - HadronsLogMessage.Active(GridLogMessage.isActive()); - HadronsLogIterative.Active(GridLogIterative.isActive()); - HadronsLogDebug.Active(GridLogDebug.isActive()); - LOG(Message) << "Grid initialized" << std::endl; - - // run setup /////////////////////////////////////////////////////////////// - Application application; - - // For now perform free propagator test - replace this with distillation test(s) - LOG(Message) << "====== Creating xml for test " << iTestNum << " ======" << std::endl; - //const unsigned int nt = GridDefaultLatt()[Tp]; - - switch(iTestNum) { - default: - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application ); - test_MesonSink( application ); - test_g5_sinks( application ); - test_em( application ); - test_Aslash( application ); - test_DistilVectors( application ); - test_MesonField( application ); - test_MesonFieldRho( application ); - break; - } - LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; - - // execution - application.saveParameterFile("test_hadrons_distil.xml"); - application.run(); - - // epilogue - LOG(Message) << "Grid is finalizing now" << std::endl; - Grid_finalize(); - - return EXIT_SUCCESS; -} diff --git a/tests/hadrons/Test_tesseract.cc b/tests/hadrons/Test_tesseract.cc deleted file mode 100644 index 77a5175c..00000000 --- a/tests/hadrons/Test_tesseract.cc +++ /dev/null @@ -1,823 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Tests/Hadrons/Test_hadrons_distil.cc - - Copyright (C) 2015-2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 -#include -#include - -using namespace Grid; -using namespace Hadrons; - -///////////////////////////////////////////////////////////// -// Test creation of laplacian eigenvectors -///////////////////////////////////////////////////////////// - -void test_Global(Application &application) -{ - // global parameters - Application::GlobalPar globalPar; - globalPar.trajCounter.start = 1100; - globalPar.trajCounter.end = 1120; - globalPar.trajCounter.step = 20; - globalPar.runId = "test"; - application.setPar(globalPar); -} - -///////////////////////////////////////////////////////////// -// Test creation of laplacian eigenvectors -///////////////////////////////////////////////////////////// - -void test_LapEvec(Application &application) -{ - const char szGaugeName[] = "gauge"; - // gauge field - application.createModule(szGaugeName); - // Now make an instance of the LapEvec object - MDistil::LapEvecPar p; - //p.ConfigFileDir="/home/dp008/dp008/dc-rich6/Scripts/ConfigsDeflQED/"; - //p.ConfigFileName="ckpoint_lat.3000"; - p.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; - p.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; - p.gauge = szGaugeName; - //p.EigenPackName = "ePack"; - //p.Distil.TI = 8; - //p.Distil.LI = 3; - //p.Distil.Nnoise = 2; - //p.Distil.tSrc = 0; - p.Stout.steps = 3; - p.Stout.parm = 0.2; - p.Cheby.PolyOrder = 11; - p.Cheby.alpha = 0.5; - p.Cheby.beta = 12.5; - p.Lanczos.Nvec = 6; - p.Lanczos.Nk = 15; - p.Lanczos.Np = 5; - p.Lanczos.MaxIt = 1000; - p.Lanczos.resid = 1e-5; - application.createModule("LapEvec",p); -} - -///////////////////////////////////////////////////////////// -// Perambulators -///////////////////////////////////////////////////////////// - -void test_Perambulators(Application &application) -{ - // Perambulator parameters - MDistil::Peramb::Par PerambPar; - PerambPar.eigenPack="LapEvec"; - PerambPar.PerambFileName="peramb.bin"; - PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; - PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; - PerambPar.UniqueIdentifier="full_dilution"; - PerambPar.Distil.tsrc = 0; - PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=6; - PerambPar.Distil.SI=4; - PerambPar.Distil.TI=32; - PerambPar.nvec=6; - PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt=32; - PerambPar.Distil.Nt_inv=1; - PerambPar.Solver.mass=0.5; - PerambPar.Solver.M5=1.8; - PerambPar.Ls=16; - PerambPar.Solver.CGPrecision=1e-2; - PerambPar.Solver.MaxIterations=10000; - application.createModule("Peramb",PerambPar); -} -///////////////////////////////////////////////////////////// -// DistilVectors -///////////////////////////////////////////////////////////// - -void test_DistilVectors(Application &application) -{ - // DistilVectors parameters - MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="Peramb_noise"; - DistilVecPar.perambulator="Peramb_perambulator_light"; - DistilVecPar.eigenPack="LapEvec"; - DistilVecPar.tsrc = 0; - DistilVecPar.nnoise = 1; - DistilVecPar.LI=6; - DistilVecPar.SI=4; - DistilVecPar.TI=32; - DistilVecPar.nvec=6; - DistilVecPar.Ns=4; - DistilVecPar.Nt=32; - DistilVecPar.Nt_inv=1; - application.createModule("DistilVecs",DistilVecPar); -} -void test_PerambulatorsS(Application &application) -{ - // Perambulator parameters - MDistil::Peramb::Par PerambPar; - PerambPar.eigenPack="LapEvec"; - PerambPar.PerambFileName="perambS.bin"; - PerambPar.ConfigFileDir="/home/dp008/dp008/paboyle/A2A/run/"; - PerambPar.ConfigFileName="ckpoint_lat.IEEE64BIG.1100"; - PerambPar.UniqueIdentifier="full_dilution"; - PerambPar.Distil.tsrc = 0; - PerambPar.Distil.nnoise = 1; - PerambPar.Distil.LI=3; - PerambPar.Distil.SI=4; - PerambPar.Distil.TI=8; - PerambPar.nvec=3; - PerambPar.Distil.Ns=4; - PerambPar.Distil.Nt=8; - PerambPar.Distil.Nt_inv=1; - PerambPar.Solver.mass=0.04; //strange mass??? - PerambPar.Solver.M5=1.8; - PerambPar.Ls=16; - PerambPar.Solver.CGPrecision=1e-8; - PerambPar.Solver.MaxIterations=10000; - application.createModule("PerambS",PerambPar); -} -///////////////////////////////////////////////////////////// -// DistilVectors -///////////////////////////////////////////////////////////// - -void test_DistilVectorsS(Application &application) -{ - // DistilVectors parameters - MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise="PerambS_noise"; - DistilVecPar.perambulator="PerambS_perambulator_light"; - DistilVecPar.eigenPack="LapEvec"; - DistilVecPar.tsrc = 0; - DistilVecPar.nnoise = 1; - DistilVecPar.LI=3; - DistilVecPar.SI=4; - DistilVecPar.TI=8; - DistilVecPar.nvec=3; - DistilVecPar.Ns=4; - DistilVecPar.Nt=8; - DistilVecPar.Nt_inv=1; - application.createModule("DistilVecsS",DistilVecPar); -} -///////////////////////////////////////////////////////////// -// MesonSink -///////////////////////////////////////////////////////////// - -void test_MesonSink(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="Peramb_unsmeared_sink"; - A2AMesonFieldPar.right="Peramb_unsmeared_sink"; - A2AMesonFieldPar.output="DistilFields"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonSink",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// g5*unsmeared -///////////////////////////////////////////////////////////// - -void test_g5_sinks(Application &application) -{ - // DistilVectors parameters - MDistil::g5_multiply::Par g5_multiplyPar; - g5_multiplyPar.input="Peramb_unsmeared_sink"; - g5_multiplyPar.nnoise = 1; - g5_multiplyPar.LI=5; - g5_multiplyPar.Ns=4; - g5_multiplyPar.Nt_inv=1; - application.createModule("g5phi",g5_multiplyPar); -} -///////////////////////////////////////////////////////////// -// MesonFields -///////////////////////////////////////////////////////////// - -void test_MesonFieldSL(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecsS_phi"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_phi"; - A2AMesonFieldPar.output="DistilFieldsS"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldS",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// MesonFields - phiphi -///////////////////////////////////////////////////////////// - -void test_MesonField(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_phi"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_phi"; - A2AMesonFieldPar.output="MesonSinksPhi"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonField",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// MesonFields - rhorho -///////////////////////////////////////////////////////////// - -void test_MesonFieldRho(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_rho"; - //A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.right="DistilVecs_rho"; - A2AMesonFieldPar.output="MesonSinksRho"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldRho",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// MesonFields - rhorhoAll -///////////////////////////////////////////////////////////// - -void test_MesonFieldRhoAll(Application &application) -{ - // DistilVectors parameters - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs_rho_all_tsrc"; - A2AMesonFieldPar.right="DistilVecs_rho_all_tsrc"; - A2AMesonFieldPar.output="MesonSinksRhoAll"; - A2AMesonFieldPar.gammas="all"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldRhoAll",A2AMesonFieldPar); -} -///////////////////////////////////////////////////////////// -// BaryonFields - phiphiphi - efficient -///////////////////////////////////////////////////////////// - -void test_BaryonFieldPhi2(Application &application) -{ - // DistilVectors parameters - MDistil::BC2::Par BC2Par; - BC2Par.one="DistilVecs_phi"; - BC2Par.two="DistilVecs_phi"; - BC2Par.three="DistilVecs_phi"; - BC2Par.output="BaryonFieldPhi2"; - BC2Par.parity=1; - BC2Par.mom={"0 0 0"}; - application.createModule("BaryonFieldPhi2",BC2Par); -} -///////////////////////////////////////////////////////////// -// BaryonFields - rhorhorho - efficient -///////////////////////////////////////////////////////////// - -void test_BaryonFieldRho2(Application &application) -{ - // DistilVectors parameters - MDistil::BC2::Par BC2Par; - BC2Par.one="DistilVecs_rho"; - BC2Par.two="DistilVecs_rho"; - BC2Par.three="DistilVecs_rho"; - BC2Par.output="BaryonFieldRho2"; - BC2Par.parity=1; - BC2Par.mom={"0 0 0"}; - application.createModule("BaryonFieldRho2",BC2Par); -} -///////////////////////////////////////////////////////////// -// BaryonFields - phiphiphi -///////////////////////////////////////////////////////////// - -void test_BaryonFieldPhi(Application &application) -{ - // DistilVectors parameters - MDistil::BContraction::Par BContractionPar; - BContractionPar.one="DistilVecs_phi"; - BContractionPar.two="DistilVecs_phi"; - BContractionPar.three="DistilVecs_phi"; - BContractionPar.output="BaryonFieldPhi"; - BContractionPar.parity=1; - BContractionPar.mom={"0 0 0"}; - application.createModule("BaryonFieldPhi",BContractionPar); -} -///////////////////////////////////////////////////////////// -// BaryonFields - rhorhorho -///////////////////////////////////////////////////////////// - -void test_BaryonFieldRho(Application &application) -{ - // DistilVectors parameters - MDistil::BContraction::Par BContractionPar; - BContractionPar.one="DistilVecs_rho"; - BContractionPar.two="DistilVecs_rho"; - BContractionPar.three="DistilVecs_rho"; - BContractionPar.output="BaryonFieldRho"; - BContractionPar.parity=1; - BContractionPar.mom={"0 0 0"}; - application.createModule("BaryonFieldRho",BContractionPar); -} -///////////////////////////////////////////////////////////// -// BaryonContraction -///////////////////////////////////////////////////////////// - -void test_Baryon2pt(Application &application) -{ - // DistilVectors parameters - MDistil::Baryon2pt::Par Baryon2ptPar; - Baryon2ptPar.inputL="BaryonFieldPhi"; - Baryon2ptPar.inputR="BaryonFieldRho"; - Baryon2ptPar.quarksL="uud"; - Baryon2ptPar.quarksR="uud"; - Baryon2ptPar.output="C2_baryon"; - application.createModule("C2_b",Baryon2ptPar); -} - -///////////////////////////////////////////////////////////// -// emField -///////////////////////////////////////////////////////////// -void test_em(Application &application) -{ - MGauge::StochEm::Par StochEmPar; - StochEmPar.gauge=PhotonR::Gauge::feynman; - StochEmPar.zmScheme=PhotonR::ZmScheme::qedL; - application.createModule("Em",StochEmPar); -} -///////////////////////////////////////////////////////////// -// MesonA2ASlash -///////////////////////////////////////////////////////////// -void test_Aslash(Application &application) -{ - MContraction::A2AAslashField::Par A2AAslashFieldPar; - A2AAslashFieldPar.left="Peramb_unsmeared_sink"; - A2AAslashFieldPar.right="Peramb_unsmeared_sink"; - A2AAslashFieldPar.output="unsmeared_Aslash"; - A2AAslashFieldPar.emField={"Em"}; - A2AAslashFieldPar.cacheBlock=2; - A2AAslashFieldPar.block=4; - application.createModule("Aslash_field",A2AAslashFieldPar); -} - -bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) -{ - if( bGobbleWhiteSpace ) - while( std::isspace(static_cast(*pstr)) ) - pstr++; - const char * p = pstr; - bool bMinus = false; - char c = * p++; - if( c == '+' ) - c = * p++; - else if( c == '-' ) { - bMinus = true; - c = * p++; - } - int n = c - '0'; - if( n < 0 || n > 9 ) - return false; - while( * p >= '0' && * p <= '9' ) { - n = n * 10 + ( * p ) - '0'; - p++; - } - if( bMinus ) - n *= -1; - ri = n; - pstr = p; - return true; -} - -#ifdef DEBUG - -typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; - -template -void DebugShowTensor(T &x, const char * n) -{ - const MyTensor::Index s{x.size()}; - std::cout << n << ".size() = " << s << std::endl; - std::cout << n << ".NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; - std::cout << n << ".NumIndices = " << x.NumIndices << std::endl; - const auto d{x.dimensions()}; - //std::cout << n << ".dimensions().size() = " << d.size() << std::endl; - std::cout << "Dimensions are "; - for(auto i = 0; i < x.NumDimensions ; i++) - std::cout << "[" << d[i] << "]"; - std::cout << std::endl; - MyTensor::Index SizeCalculated{1}; - std::cout << "Dimensions again"; - for(int i=0 ; i < x.NumDimensions ; i++ ) { - std::cout << " : [" << i << /*", " << x.IndexNames[i] << */"]=" << x.dimension(i); - SizeCalculated *= d[i]; - } - std::cout << std::endl; - std::cout << "SizeCalculated = " << SizeCalculated << std::endl;\ - assert( SizeCalculated == s ); - // Initialise - assert( x.NumDimensions == 3 ); - for( int i = 0 ; i < d[0] ; i++ ) - for( int j = 0 ; j < d[1] ; j++ ) - for( int k = 0 ; k < d[2] ; k++ ) { - x(i,j,k) = std::complex(SizeCalculated, -SizeCalculated); - SizeCalculated--; - } - // Show raw data - std::cout << "Data follow : " << std::endl; - typename T::Scalar * p = x.data(); - for( auto i = 0 ; i < s ; i++ ) { - if( i ) std::cout << ", "; - std::cout << n << ".data()[" << i << "]=" << * p++; - } - std::cout << std::endl; -} - -// Test whether typedef and underlying types are the same - -void DebugTestTypeEqualities(void) -{ - Real r1; - RealD r2; - double r3; - const std::type_info &tr1{typeid(r1)}; - const std::type_info &tr2{typeid(r2)}; - const std::type_info &tr3{typeid(r3)}; - if( tr1 == tr2 && tr2 == tr3 ) - std::cout << "r1, r2 and r3 are the same type" << std::endl; - else - std::cout << "r1, r2 and r3 are different types" << std::endl; - std::cout << "r1 is a " << tr1.name() << std::endl; - std::cout << "r2 is a " << tr2.name() << std::endl; - std::cout << "r3 is a " << tr3.name() << std::endl; - - // These are the same - Complex c1; - std::complex c2; - const std::type_info &tc1{typeid(c1)}; - const std::type_info &tc2{typeid(c2)}; - const std::type_info &tc3{typeid(SpinVector::scalar_type)}; - if( tc1 == tc2 && tc2 == tc3) - std::cout << "c1, c2 and SpinVector::scalar_type are the same type" << std::endl; - else - std::cout << "c1, c2 and SpinVector::scalar_type are different types" << std::endl; - std::cout << "c1 is a " << tc1.name() << std::endl; - std::cout << "c2 is a " << tc2.name() << std::endl; - std::cout << "SpinVector::scalar_type is a " << tc3.name() << std::endl; - - // These are the same - SpinVector s1; - iSpinVector s2; - iScalar, Ns> > s3; - const std::type_info &ts1{typeid(s1)}; - const std::type_info &ts2{typeid(s2)}; - const std::type_info &ts3{typeid(s3)}; - if( ts1 == ts2 && ts2 == ts3 ) - std::cout << "s1, s2 and s3 are the same type" << std::endl; - else - std::cout << "s1, s2 and s3 are different types" << std::endl; - std::cout << "s1 is a " << ts1.name() << std::endl; - std::cout << "s2 is a " << ts2.name() << std::endl; - std::cout << "s3 is a " << ts3.name() << std::endl; - - // These are the same - SpinColourVector sc1; - iSpinColourVector sc2; - const std::type_info &tsc1{typeid(sc1)}; - const std::type_info &tsc2{typeid(sc2)}; - if( tsc1 == tsc2 ) - std::cout << "sc1 and sc2 are the same type" << std::endl; - else - std::cout << "sc1 and sc2 are different types" << std::endl; - std::cout << "sc1 is a " << tsc1.name() << std::endl; - std::cout << "sc2 is a " << tsc2.name() << std::endl; -} - -bool DebugEigenTest() -{ - { - Eigen::TensorFixedSize,Eigen::Sizes<3,4,5>> x; - DebugShowTensor(x, "fixed"); - } - const char pszTestFileName[] = "test_tensor.bin"; - std::array as={"Alpha", "Beta", "Gamma"}; - MyTensor x(as, 2,1,4); - DebugShowTensor(x, "x"); - x.WriteBinary(pszTestFileName); - DebugShowTensor(x, "x"); - // Test initialisation of an array of strings - for( auto a : as ) - std::cout << a << std::endl; - Grid::Hadrons::MDistil::Perambulator p{as,2,7,2}; - DebugShowTensor(p, "p"); - std::cout << "p.IndexNames follow" << std::endl; - for( auto a : p.IndexNames ) - std::cout << a << std::endl; - // Now see whether we can read a tensor back - std::array Names2={"Alpha", "Gamma", "Delta"}; - MyTensor y(Names2, 2,4,1); - y.ReadBinary(pszTestFileName); - DebugShowTensor(y, "y"); - - // Testing whether typedef produces the same type - yes it does - - DebugTestTypeEqualities(); - std::cout << std::endl; - - // How to access members of SpinColourVector - SpinColourVector sc; - for( int s = 0 ; s < Ns ; s++ ) { - auto cv{sc()(s)}; - iVector c2{sc()(s)}; - std::cout << " cv is a " << typeid(cv).name() << std::endl; - std::cout << " c2 is a " << typeid(c2).name() << std::endl; - for( int c = 0 ; c < Nc ; c++ ) { - Complex & z{cv(c)}; - std::cout << " sc[spin=" << s << ", colour=" << c << "] = " << z << std::endl; - } - } - // We could have removed the Lorentz index independently, but much easier to do as we do above - iVector,Ns> sc2{sc()}; - std::cout << "sc() is a " << typeid(sc()).name() << std::endl; - std::cout << "sc2 is a " << typeid(sc2 ).name() << std::endl; - - // Or you can access elements directly - std::complex z = sc()(0)(0); - std::cout << "z = " << z << std::endl; - sc()(3)(2) = std::complex{3.141,-3.141}; - std::cout << "sc()(3)(2) = " << sc()(3)(2) << std::endl; - - return true; -} - -template -void DebugGridTensorTest_print( int i ) -{ - std::cout << i << " : " << EigenIO::is_tensor::value - << ", rank " << EigenIO::Traits::rank - << ", rank_non_trivial " << EigenIO::Traits::rank_non_trivial - << ", count " << EigenIO::Traits::count - << ", scalar_size " << EigenIO::Traits::scalar_size - << ", size " << EigenIO::Traits::size - << std::endl; -} - -// begin() and end() are the minimum necessary to support range-for loops -// should really turn this into an iterator ... -template -class TestObject { -public: - using value_type = T; -private: - value_type * m_p; -public: - TestObject() { - m_p = reinterpret_cast(std::malloc(N * sizeof(value_type))); - } - ~TestObject() { std::free(m_p); } - inline value_type * begin(void) { return m_p; } - inline value_type * end(void) { return m_p + N; } -}; - -template -void EigenSliceExample() -{ - std::cout << "Eigen example, Options = " << Options << std::endl; - using T2 = Eigen::Tensor; - T2 a(4, 3); - a.setValues({{0, 100, 200}, {300, 400, 500}, - {600, 700, 800}, {900, 1000, 1100}}); - std::cout << "a\n" << a << std::endl; - DumpMemoryOrder( a, "a" ); - Eigen::array offsets = {0, 1}; - Eigen::array extents = {4, 2}; - T2 slice = a.slice(offsets, extents); - std::cout << "slice\n" << slice << std::endl; - DumpMemoryOrder( slice, "slice" ); - std::cout << "\n========================================" << std::endl; -} - -template -void EigenSliceExample2() -{ - using TestScalar = std::complex; - using T3 = Eigen::Tensor; - using T2 = Eigen::Tensor; - T3 a(2,3,4); - - std::cout << "Initialising a:"; - for_all( a, [&](TestScalar &c, float f, const std::array &Dims ){ - c = TestScalar{f,-f}; - std::cout << " a(" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")=" << c; - } ); - std::cout << std::endl; - //std::cout << "Validating a:"; - float z = 0; - for( int i = 0 ; i < a.dimension(0) ; i++ ) - for( int j = 0 ; j < a.dimension(1) ; j++ ) - for( int k = 0 ; k < a.dimension(2) ; k++ ) { - TestScalar w{z, -z}; - //std::cout << " a(" << i << "," << j << "," << k << ")=" << w; - assert( a(i,j,k) == w ); - z++; - } - //std::cout << std::endl; - //std::cout << "a initialised to:\n" << a << std::endl; - DumpMemoryOrder( a, "a" ); - std::cout << "for_all(a):"; - for_all( a, [&](TestScalar c, typename T3::Index n, const std::array &Dims ){ - std::cout << " (" << Dims[0] << "," << Dims[1] << "," << Dims[2] << ")<" << n << ">=" << c; - } ); - std::cout << std::endl; - Eigen::array offsets = {0,1,1}; - Eigen::array extents = {1,2,2}; - T3 b; - b = a.slice( offsets, extents );//.reshape(NewExtents); - std::cout << "b = a.slice( offsets, extents ):\n" << b << std::endl; - DumpMemoryOrder( b, "b" ); - T2 c(3,4); - c = a.chip(0,1); - std::cout << "c = a.chip(0,0):\n" << c << std::endl; - DumpMemoryOrder( c, "c" ); - //T2 d = b.reshape(extents); - //std::cout << "b.reshape(extents) is:\n" << d << std::endl; - std::cout << "\n========================================" << std::endl; -} - -void DebugFelixTensorTest( void ) -{ - unsigned int Nmom = 2; - unsigned int Nt = 2; - unsigned int N_1 = 2; - unsigned int N_2 = 2; - unsigned int N_3 = 2; - using BaryonTensorSet = Eigen::Tensor; - BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); - std::vector Memory(Nmom * Nt * N_1 * N_2 * N_3 * 2); - using BaryonTensorMap = Eigen::TensorMap; - BaryonTensorMap BField4 (&Memory[0], Nmom,4,Nt,N_1,N_2,N_3); - - EigenSliceExample(); - EigenSliceExample<0>(); - EigenSliceExample2(); - EigenSliceExample2<0>(); -} - -bool DebugGridTensorTest( void ) -{ - DebugFelixTensorTest(); - typedef Complex t1; - typedef iScalar t2; - typedef iVector t3; - typedef iMatrix t4; - typedef iVector,4> t5; - typedef iScalar t6; - typedef iMatrix t7; - typedef iMatrix,4>,2> t8; - int i = 1; - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - - //using TOC7 = TestObject, 7>; - using TOC7 = t7; - TOC7 toc7; - constexpr std::complex Inc{1,-1}; - std::complex Start{Inc}; - for( auto &x : toc7 ) { - x = Start; - Start += Inc; - } - i = 0; - std::cout << "toc7:"; - for( auto x : toc7 ) std::cout << " [" << i++ << "]=" << x; - std::cout << std::endl; - - t2 o2; - auto a2 = TensorRemove(o2); - //t3 o3; - //t4 o4; - //auto a3 = TensorRemove(o3); - //auto a4 = TensorRemove(o4); - - return true; -} -#endif - -int main(int argc, char *argv[]) -{ -#ifdef DEBUG - // Debug only - test of Eigen::Tensor - std::cout << "sizeof(int) = " << sizeof(int) - << ", sizeof(long) = " << sizeof(long) - << ", sizeof(size_t) = " << sizeof(size_t) - << ", sizeof(std::size_t) = " << sizeof(std::size_t) - << ", sizeof(std::streamsize) = " << sizeof(std::streamsize) - << ", sizeof(Eigen::Index) = " << sizeof(Eigen::Index) << std::endl; - if( DebugEigenTest() ) return 0; - if(DebugGridTensorTest()) return 0; -#endif - - // Decode command-line parameters. 1st one is which test to run - int iTestNum = -1; - - for(int i = 1 ; i < argc ; i++ ) { - std::cout << "argv[" << i << "]=\"" << argv[i] << "\"" << std::endl; - const char * p = argv[i]; - if( * p == '/' || * p == '-' ) { - p++; - char c = * p++; - switch(toupper(c)) { - case 'T': - if( bNumber( iTestNum, p ) ) { - std::cout << "Test " << iTestNum << " requested"; - if( * p ) - std::cout << " (ignoring trailer \"" << p << "\")"; - std::cout << std::endl; - } - else - std::cout << "Invalid test \"" << &argv[i][2] << "\"" << std::endl; - break; - default: - std::cout << "Ignoring switch \"" << &argv[i][1] << "\"" << std::endl; - break; - } - } - } - - // initialization ////////////////////////////////////////////////////////// - Grid_init(&argc, &argv); - HadronsLogError.Active(GridLogError.isActive()); - HadronsLogWarning.Active(GridLogWarning.isActive()); - HadronsLogMessage.Active(GridLogMessage.isActive()); - HadronsLogIterative.Active(GridLogIterative.isActive()); - HadronsLogDebug.Active(GridLogDebug.isActive()); - LOG(Message) << "Grid initialized" << std::endl; - - // run setup /////////////////////////////////////////////////////////////// - Application application; - - // For now perform free propagator test - replace this with distillation test(s) - LOG(Message) << "====== Creating xml for test " << iTestNum << " ======" << std::endl; - //const unsigned int nt = GridDefaultLatt()[Tp]; - - switch(iTestNum) { - default: - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application ); - test_MesonSink( application ); - test_g5_sinks( application ); - test_em( application ); - test_Aslash( application ); - test_DistilVectors( application ); - test_MesonField( application ); - test_MesonFieldRho( application ); - test_MesonFieldRhoAll( application ); - break; - } - LOG(Message) << "====== XML creation for test " << iTestNum << " complete ======" << std::endl; - - // execution - application.saveParameterFile("test_hadrons_distil.xml"); - application.run(); - - // epilogue - LOG(Message) << "Grid is finalizing now" << std::endl; - Grid_finalize(); - - return EXIT_SUCCESS; -} From 313762822200ed278f68503c149ec992c86a575c Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 17 Sep 2019 13:19:20 +0100 Subject: [PATCH 285/347] BaryonUtils.h is now part of Baryons --- Grid/qcd/utils/BaryonUtils.h | 176 ----------------------------------- 1 file changed, 176 deletions(-) delete mode 100644 Grid/qcd/utils/BaryonUtils.h diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h deleted file mode 100644 index cad547b1..00000000 --- a/Grid/qcd/utils/BaryonUtils.h +++ /dev/null @@ -1,176 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: ./lib/qcd/utils/BaryonUtils.h - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 */ -#pragma once -//#include -#include - -NAMESPACE_BEGIN(Grid); - -#undef DELTA_F_EQ_2 - -template -class BaryonUtils -{ -public: - typedef typename FImpl::ComplexField ComplexField; - typedef typename FImpl::FermionField FermionField; - typedef typename FImpl::PropagatorField PropagatorField; - - typedef typename FImpl::SitePropagator pobj; - typedef typename FImpl::SiteSpinor vobj; - typedef typename vobj::scalar_object sobj; - typedef typename vobj::scalar_type scalar_type; - typedef typename vobj::vector_type vector_type; - - static void ContractBaryons(const PropagatorField &q1_src, - const PropagatorField &q2_src, - const PropagatorField &q3_src, - const Gamma GammaA, - const Gamma GammaB, - const char quarks_snk[], - const char quarks_src[], - const int parity, - ComplexField &baryon_corr); - -}; - - -template -void BaryonUtils::ContractBaryons(const PropagatorField &q1_src, - const PropagatorField &q2_src, - const PropagatorField &q3_src, - const Gamma GammaA, - const Gamma GammaB, - const char quarks_snk[], - const char quarks_src[], - const int parity, - ComplexField &baryon_corr) -{ - - assert(parity==1 || parity == -1 && "Parity must be +1 or -1"); - - GridBase *grid = q1_src.Grid(); - - Gamma g4(Gamma::Algebra::GammaT); //needed for parity P_\pm = 0.5*(1 \pm \gamma_4) - - std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - std::vector wick_contraction = {0,0,0,0,0,0}; - - for (int ie=0; ie < 6 ; ie++) - if (quarks_src[0] == quarks_snk[epsilon[ie][0]] && quarks_src[1] == quarks_snk[epsilon[ie][1]] && quarks_src[2] == quarks_snk[epsilon[ie][2]]) - wick_contraction[ie]=1; - - typedef typename ComplexField::vector_object vobj; - LatticeView vbaryon_corr{ baryon_corr }; - accelerator_for(ss, grid->oSites(), grid->Nsimd(), { - - LatticeView v1(q1_src); - LatticeView v2(q2_src); - LatticeView v3(q3_src); - auto D1 = v1[ss]; - auto D2 = v2[ss]; - auto D3 = v3[ss]; - - auto gD1a = GammaA * GammaA * D1; - auto gD1b = GammaA * g4 * GammaA * D1; - auto pD1 = 0.5* (gD1a + (double)parity * gD1b); - auto gD3 = GammaB * D3; - - vobj result{ 0 }; - - for (int ie_src=0; ie_src < 6 ; ie_src++){ - int a_src = epsilon[ie_src][0]; //a - int b_src = epsilon[ie_src][1]; //b - int c_src = epsilon[ie_src][2]; //c - for (int ie_snk=0; ie_snk < 6 ; ie_snk++){ - int a_snk = epsilon[ie_snk][0]; //a' - int b_snk = epsilon[ie_snk][1]; //b' - int c_snk = epsilon[ie_snk][2]; //c' - //This is the \delta_{456}^{123} part - if (wick_contraction[0]){ - auto D2g = D2 * GammaB; - for (int alpha_snk=0; alpha_snk Date: Wed, 2 Oct 2019 13:16:58 +0100 Subject: [PATCH 286/347] Remove references to unused modules (now part of separate Baryons branch) --- Hadrons/Modules.hpp | 2 -- Hadrons/modules.inc | 4 ---- 2 files changed, 6 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 34ecc63a..409135ac 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -56,8 +56,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 0faecb62..ab8ba209 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -55,8 +55,6 @@ modules_cc =\ Modules/MContraction/DiscLoop.cc \ Modules/MContraction/Meson.cc \ Modules/MContraction/WeakEye3pt.cc \ - Modules/MDistil/BContraction.cc \ - Modules/MDistil/Baryon2pt.cc \ Modules/MDistil/DistilVectors.cc \ Modules/MDistil/LapEvec.cc \ Modules/MDistil/Noises.cc \ @@ -135,8 +133,6 @@ modules_hpp =\ Modules/MContraction/A2AAslashField.hpp \ Modules/MContraction/Baryon.hpp \ Modules/MContraction/Meson.hpp \ - Modules/MDistil/BContraction.hpp \ - Modules/MDistil/Baryon2pt.hpp \ Modules/MDistil/DistilVectors.hpp \ Modules/MDistil/LapEvec.hpp \ Modules/MDistil/Noises.hpp \ From 92e25488f83704d80494817a844db7b0e41b2409 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 2 Oct 2019 14:13:35 +0100 Subject: [PATCH 287/347] Added MomentumPhase Hadrons module from z2_momentum branch (thankyou, Felix) so I can run Z_2 wall with momenta easily --- Hadrons/Modules.hpp | 1 + Hadrons/Modules/MSource/MomentumPhase.cc | 35 +++++ Hadrons/Modules/MSource/MomentumPhase.hpp | 160 ++++++++++++++++++++++ Hadrons/modules.inc | 2 + 4 files changed, 198 insertions(+) create mode 100644 Hadrons/Modules/MSource/MomentumPhase.cc create mode 100644 Hadrons/Modules/MSource/MomentumPhase.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 409135ac..6b5f2336 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/Hadrons/Modules/MSource/MomentumPhase.cc b/Hadrons/Modules/MSource/MomentumPhase.cc new file mode 100644 index 00000000..f1ff48d6 --- /dev/null +++ b/Hadrons/Modules/MSource/MomentumPhase.cc @@ -0,0 +1,35 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MSource/MomentumPhase.cc + +Copyright (C) 2015-2019 + +Author: Felix Erben + +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 + +using namespace Grid; +using namespace Hadrons; +using namespace MSource; + +template class Grid::Hadrons::MSource::TMomentumPhase; +template class Grid::Hadrons::MSource::TMomentumPhase; diff --git a/Hadrons/Modules/MSource/MomentumPhase.hpp b/Hadrons/Modules/MSource/MomentumPhase.hpp new file mode 100644 index 00000000..f2f94af1 --- /dev/null +++ b/Hadrons/Modules/MSource/MomentumPhase.hpp @@ -0,0 +1,160 @@ +/************************************************************************************* + +Grid physics library, www.github.com/paboyle/Grid + +Source file: Hadrons/Modules/MSource/MomentumPhase.hpp + +Copyright (C) 2015-2019 + +Author: Felix Erben + +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 */ + +#ifndef Hadrons_MSource_MomentumPhase_hpp_ +#define Hadrons_MSource_MomentumPhase_hpp_ + +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/* + + Multiply source field by momentum phase + ----------------------------- + * can be used to multiply a Z2-wall source by a phase e^{ipx}, which is needed for + * 2pt-correlation functions of mesons with nonzero momenta. + + */ + +/****************************************************************************** + * MomentumPhase * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MSource) + +class MomentumPhasePar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(MomentumPhasePar, + std::string, src, + std::string, mom); +}; + +template +class TMomentumPhase: public Module +{ +public: + FERM_TYPE_ALIASES(FImpl,); +public: + // constructor + TMomentumPhase(const std::string name); + // destructor + virtual ~TMomentumPhase(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); +protected: + // setup + virtual void setup(void); + // execution + virtual void execute(void); +private: + bool hasPhase_{false}; + std::string momphName_, tName_; +}; + +MODULE_REGISTER_TMP(MomentumPhase, TMomentumPhase, MSource); +MODULE_REGISTER_TMP(ZMomentumPhase, TMomentumPhase, MSource); + +/****************************************************************************** + * TMomentumPhase implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TMomentumPhase::TMomentumPhase(const std::string name) +: Module(name) +, momphName_ (name + "_momph") +, tName_ (name + "_t") +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TMomentumPhase::getInput(void) +{ + std::vector in = {par().src}; + + return in; +} + +template +std::vector TMomentumPhase::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TMomentumPhase::setup(void) +{ + envCreateLat(PropagatorField, getName()); + envCache(Lattice>, tName_, 1, envGetGrid(LatticeComplex)); + envCacheLat(LatticeComplex, momphName_); + envTmpLat(LatticeComplex, "coor"); +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TMomentumPhase::execute(void) +{ + LOG(Message) << "Multiplying source field " << par().src + << " by momentum phase " + << par().mom << std::endl; + auto &out = envGet(PropagatorField, getName()); + auto &src = envGet(PropagatorField, par().src); + auto &ph = envGet(LatticeComplex, momphName_); + auto &t = envGet(Lattice>, tName_); + + if (!hasPhase_) + { + Complex i(0.0,1.0); + std::vector p; + + envGetTmp(LatticeComplex, coor); + p = strToVec(par().mom); + ph = Zero(); + for(unsigned int mu = 0; mu < env().getNd(); mu++) + { + LatticeCoordinate(coor, mu); + ph = ph + (p[mu]/env().getDim(mu))*coor; + } + ph = exp((Real)(2*M_PI)*i*ph); + LatticeCoordinate(t, Tp); + hasPhase_ = true; + } + out = ph*src; +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MSource_MomentumPhase_hpp_ diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index ab8ba209..d5414b6b 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -8,6 +8,7 @@ modules_cc =\ Modules/MSource/Gauss.cc \ Modules/MSource/SeqGamma.cc \ Modules/MSource/Momentum.cc \ + Modules/MSource/MomentumPhase.cc \ Modules/MScalarSUN/TwoPoint.cc \ Modules/MScalarSUN/TransProj.cc \ Modules/MScalarSUN/TwoPointNPR.cc \ @@ -77,6 +78,7 @@ modules_cc =\ modules_hpp =\ Modules/MSource/Gauss.hpp \ Modules/MSource/Momentum.hpp \ + Modules/MSource/MomentumPhase.hpp \ Modules/MSource/SeqAslash.hpp \ Modules/MSource/Z2.hpp \ Modules/MSource/Point.hpp \ From 86939dbf1a750034d6bd79fc605d3a9454ddfa21 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 4 Oct 2019 13:59:59 +0100 Subject: [PATCH 288/347] Removed unnecessary function (for getting a parameter) --- Hadrons/Distil.hpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 9a53760a..83727df3 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -230,9 +230,6 @@ struct DistilParameters: Serializable { } return i; } - - int getTI( const Environment & env, bool bCalledFromSetup ) const { - return ParameterDefault( TI, env.getDim(Tdir), bCalledFromSetup ); } }; #define DISTIL_PARAMETERS_DEFINE( inSetup ) \ @@ -240,9 +237,9 @@ const int Nt{env().getDim(Tdir)}; \ const int nvec{par().nvec}; \ const int nnoise{par().Distil.nnoise}; \ const int tsrc{par().Distil.tsrc}; \ -const int TI{par().Distil.getTI(env(), inSetup)}; \ +const int TI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.TI, Nt, inSetup)}; \ const int LI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.LI, nvec, inSetup)}; \ -const int SI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.SI, Ns, inSetup)}; \ +const int SI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.SI, Ns, inSetup)}; \ const bool full_tdil{ TI == Nt }; \ const bool exact_distillation{ full_tdil && LI == nvec }; \ const int Nt_inv{ full_tdil ? 1 : TI } From 9d96899aa876dc31c80d3c9157c156fe0eb02bcd Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 7 Oct 2019 13:05:04 +0100 Subject: [PATCH 289/347] Doc bugfix --- documentation/GridXcode/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/GridXcode/readme.md b/documentation/GridXcode/readme.md index e48b0a34..8cbf9112 100644 --- a/documentation/GridXcode/readme.md +++ b/documentation/GridXcode/readme.md @@ -369,7 +369,7 @@ Instead: 3. From a terminal session, locate and run your executable using `mpirun` (*the mangled name of the project build products will not be exactly the same as this example*): - `$GridPre/openmpi-3.1.3/bin/mpirun -np 2 ~/Library/Developer/Xcode/DerivedData/HelloGrid-fiyyuveptaqelbbvllomcgjyvghr/Build/Products/Debug/HelloGrid --grid 4.4.4.8 --mpi 1.1.1.2` + `$GridPre/openmpi/bin/mpirun -np 2 ~/Library/Developer/Xcode/DerivedData/HelloGrid-fiyyuveptaqelbbvllomcgjyvghr/Build/Products/Debug/HelloGrid --grid 4.4.4.8 --mpi 1.1.1.2` The Xcode debugger will attach to the first process. From 88d6ff8f1d1f9949ece551ced37a5b8170f72d15 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 7 Oct 2019 17:36:11 +0100 Subject: [PATCH 290/347] Peter's bugfix in ImplicitlyRestartedLanczos.h My bugfix in MomentumPhase.hpp --- Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h | 2 +- Hadrons/Modules/MSource/MomentumPhase.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h b/Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h index 47dcee52..75fb872f 100644 --- a/Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h +++ b/Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h @@ -64,7 +64,7 @@ void basisRotate(std::vector &basis,Eigen::MatrixXd& Qt,int j0, int j1, i thread_region { - std::vector < vobj , commAllocator > B(Nm); // Thread private + std::vector < vobj > B(Nm); // Thread private thread_for_in_region(ss, grid->oSites(),{ for(int j=j0; j::execute(void) envGetTmp(LatticeComplex, coor); p = strToVec(par().mom); - ph = zero; + ph = Zero(); for(unsigned int mu = 0; mu < env().getNd(); mu++) { LatticeCoordinate(coor, mu); From 519ce19128f312eb075d558ba88f694f39090d7e Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 14 Oct 2019 22:27:08 +0100 Subject: [PATCH 291/347] Fixes to enable GPU build. NB: Contractor and ContractorBenchmark still not working --- Grid/qcd/utils/A2Autils.h | 2 +- Hadrons/Distil.hpp | 21 ++++++++++++++++----- Hadrons/Modules/MNPR/Amputate.hpp | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 4d79d060..7e1ee10a 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -649,7 +649,7 @@ void A2Autils::MesonField(TensorType &mat, int ij_dx = m+Nmom*i + Nmom*Lblock * j + Nmom*Lblock * Rblock * lt; for(int mu=0;mu & evec) auto grid = evec[0].Grid(); Coordinate siteFirst(grid->Nd(),0); peekSite(cv0, evec[0], siteFirst); - auto & cplx0 = cv0()()(0); + Grid::Complex cplx0 = cv0()()(0); +#ifdef GRID_NVCC + if( cplx0.imag() == 0 ) +#else if( std::imag(cplx0) == 0 ) +#endif std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; else { - const auto cplx0_mag = std::abs(cplx0); - const auto phase = std::conj(cplx0 / cplx0_mag); - std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (std::arg(phase) / 3.14159265) << " pi" << std::endl; +#ifdef GRID_NVCC + const Real cplx0_mag = thrust::abs(cplx0); + const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); + const Real argphase = thrust::arg(phase); +#else + const Real cplx0_mag = std::abs(cplx0); + const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); + const Real argphase = std::arg(phase); +#endif + std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / 3.14159265) << " pi" << std::endl; { // TODO: Only really needed on the master slice for( int k = 0 ; k < evec.size() ; k++ ) diff --git a/Hadrons/Modules/MNPR/Amputate.hpp b/Hadrons/Modules/MNPR/Amputate.hpp index 6258e515..107e95df 100644 --- a/Hadrons/Modules/MNPR/Amputate.hpp +++ b/Hadrons/Modules/MNPR/Amputate.hpp @@ -187,7 +187,7 @@ void TAmputate::execute(void) result.Vamp.resize(Gamma::nGamma/2); for( int mu=0; mu < Gamma::nGamma/2; mu++){ Gamma::Algebra gam = mu; - result.Vamp[mu] = 1/12.0*trace(adj(Gamma(mu*2+1))*g5*Sout_inv*g5*vertex[mu]*Sin_inv); + result.Vamp[mu] = 1/12.0*trace(adj(Gamma(mu*2+1))*g5*Sout_inv*g5*vertex[mu]*Sin_inv)()()(); LOG(Message) << "Vamp[" << mu << "] - " << result.Vamp[mu] << std::endl; } } From decab587a0a47513d0639dd87a1293606fe473df Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sun, 20 Oct 2019 14:14:06 +0100 Subject: [PATCH 292/347] PerambFileName defaults to object name if empty --- Hadrons/Modules/MIO/LoadPerambulator.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 719a041a..558bf145 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -111,7 +111,11 @@ template void TLoadPerambulator::execute(void) { auto &perambulator = envGet(MDistil::PerambTensor, getName()); - const std::string sPerambName{par().PerambFileName + "." + std::to_string(vm().getTrajectory())}; + std::string sPerambName{ par().PerambFileName }; + if( sPerambName.empty() ) + sPerambName = getName(); + sPerambName.append( 1, '.' ); + sPerambName.append( std::to_string( vm().getTrajectory() ) ); perambulator.read(sPerambName.c_str()); } From 78bdb0ff6a0c3f74c612105f55a4b7aa343d63f8 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sun, 20 Oct 2019 14:22:45 +0100 Subject: [PATCH 293/347] Grid --- Hadrons/Distil.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 5abae500..42b083a4 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -1,6 +1,6 @@ /************************************************************************************* - grid physics library, www.github.com/paboyle/Grid + Grid physics library, www.github.com/paboyle/Grid Source file: Hadrons/Modules/MDistil/Distil.hpp From ca234325bc1d87ba435142158c72e7265e5a2dd5 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 23 Oct 2019 21:49:32 +0100 Subject: [PATCH 294/347] Fix single-precision error --- Hadrons/Modules/MNPR/Amputate.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MNPR/Amputate.hpp b/Hadrons/Modules/MNPR/Amputate.hpp index 107e95df..7cbe45ef 100644 --- a/Hadrons/Modules/MNPR/Amputate.hpp +++ b/Hadrons/Modules/MNPR/Amputate.hpp @@ -187,7 +187,7 @@ void TAmputate::execute(void) result.Vamp.resize(Gamma::nGamma/2); for( int mu=0; mu < Gamma::nGamma/2; mu++){ Gamma::Algebra gam = mu; - result.Vamp[mu] = 1/12.0*trace(adj(Gamma(mu*2+1))*g5*Sout_inv*g5*vertex[mu]*Sin_inv)()()(); + result.Vamp[mu] = static_cast( 1 / 12.0 ) * trace(adj(Gamma(mu*2+1))*g5*Sout_inv*g5*vertex[mu]*Sin_inv)()()(); LOG(Message) << "Vamp[" << mu << "] - " << result.Vamp[mu] << std::endl; } } From 2a926b3dc62bf82b118981ba2d01d4e267646bba Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 30 Oct 2019 14:52:34 +0000 Subject: [PATCH 295/347] Merged latest changes from develop, in preparation for release. --- .../iterative/ImplicitlyRestartedLanczos.h | 2 +- Grid/qcd/smearing/StoutSmearing.h | 22 +- Hadrons/Modules/MDistil/BContraction.cc | 36 --- Hadrons/Modules/MDistil/BContraction.hpp | 289 ------------------ Hadrons/Modules/MDistil/Baryon2pt.cc | 36 --- Hadrons/Modules/MDistil/Baryon2pt.hpp | 221 -------------- 6 files changed, 8 insertions(+), 598 deletions(-) delete mode 100644 Hadrons/Modules/MDistil/BContraction.cc delete mode 100644 Hadrons/Modules/MDistil/BContraction.hpp delete mode 100644 Hadrons/Modules/MDistil/Baryon2pt.cc delete mode 100644 Hadrons/Modules/MDistil/Baryon2pt.hpp diff --git a/Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h b/Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h index 75fb872f..47dcee52 100644 --- a/Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h +++ b/Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h @@ -64,7 +64,7 @@ void basisRotate(std::vector &basis,Eigen::MatrixXd& Qt,int j0, int j1, i thread_region { - std::vector < vobj > B(Nm); // Thread private + std::vector < vobj , commAllocator > B(Nm); // Thread private thread_for_in_region(ss, grid->oSites(),{ for(int j=j0; j class Smear_Stout : public Smear { private: int OrthogDim = -1; - const std::vector SmearRho{}; + const std::vector SmearRho; // Smear* ownership semantics: // Smear* passed in to constructor are owned by caller, so we don't delete them here // Smear* created within constructor need to be deleted as part of the destructor @@ -61,7 +61,6 @@ public: /*! Stout smearing with base explicitly specified */ Smear_Stout(Smear* base) : SmearBase{base} { - std::cout << GridLogDebug << "Stout smearing constructor : Smear_Stout(Smear* base)" << std::endl assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } @@ -73,32 +72,25 @@ public: } /*! Default constructor. rho is constant in all directions, optionally except for orthogonal dimension */ - Smear_Stout(double rho, int orthogdim = -1) - //: OwnedBase{(orthogdim<0 || orthogdim>=Nd) ? new Smear_APE(rho) : new Smear_APE(rho3D(rho,orthogdim))}, + Smear_Stout(double rho = 1.0, int orthogdim = -1) : OrthogDim{orthogdim}, SmearRho{ rho3D(rho,orthogdim) }, OwnedBase{ new Smear_APE(SmearRho) }, SmearBase{OwnedBase.get()} { - std::cout << GridLogDebug << "Stout smearing constructor : Smear_StoutSmear_Stout(double " << rho << ", int " << OrthogDim << " )\nrho3d=" << SmearRho << std::endl; assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); } - /* - Smear_Stout(double rho = 1.0) : SmearBase(new Smear_APE(rho)) { - assert(Nc == 3 && "Stout smearing currently implemented only for Nc==3"); - }*/ - ~Smear_Stout() {} // delete SmearBase... void smear(GaugeField& u_smr, const GaugeField& U) const { GaugeField C(U.Grid()); GaugeLinkField tmp(U.Grid()), iq_mu(U.Grid()), Umu(U.Grid()); - std::cout << GridLogDebug << "Stout smearing started" << std::endl; + std::cout << GridLogDebug << "Stout smearing started\n"; // Smear the configurations SmearBase->smear(C, U); for (int mu = 0; mu < Nd; mu++) { if( mu == OrthogDim ) - tmp = 1.0; + tmp = 1.0; // Don't smear in the orthogonal direction else { tmp = peekLorentz(C, mu); Umu = peekLorentz(U, mu); @@ -109,7 +101,7 @@ public: } pokeLorentz(u_smr, tmp * Umu, mu); // u_smr = exp(iQ_mu)*U_mu } - std::cout << GridLogDebug << "Stout smearing completed" << std::endl; + std::cout << GridLogDebug << "Stout smearing completed\n"; }; void derivative(GaugeField& SigmaTerm, const GaugeField& iLambda, @@ -169,7 +161,7 @@ public: c0max = 2.0 * pow(tmp, 1.5); theta = acos(c0 / c0max) * - one_over_three; // divide by three here, now leave as it is + one_over_three; // divide by three here, now leave as it is u = sqrt(tmp) * cos(theta); w = sqrt(c1) * sin(theta); } @@ -194,7 +186,7 @@ public: e2iu = cos(2.0 * u) + timesI(sin(2.0 * u)); h0 = e2iu * (u2 - w2) + - emiu * ((8.0 * u2 * cosw) + (2.0 * u * (3.0 * u2 + w2) * ixi0)); + emiu * ((8.0 * u2 * cosw) + (2.0 * u * (3.0 * u2 + w2) * ixi0)); h1 = e2iu * (2.0 * u) - emiu * ((2.0 * u * cosw) - (3.0 * u2 - w2) * ixi0); h2 = e2iu - emiu * (cosw + (3.0 * u) * ixi0); diff --git a/Hadrons/Modules/MDistil/BContraction.cc b/Hadrons/Modules/MDistil/BContraction.cc deleted file mode 100644 index 7fbd1880..00000000 --- a/Hadrons/Modules/MDistil/BContraction.cc +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/BContraction.cc - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 - -using namespace Grid; -using namespace Hadrons; -using namespace MDistil; - -template class Grid::Hadrons::MDistil::TBContraction; diff --git a/Hadrons/Modules/MDistil/BContraction.hpp b/Hadrons/Modules/MDistil/BContraction.hpp deleted file mode 100644 index 23400fcb..00000000 --- a/Hadrons/Modules/MDistil/BContraction.hpp +++ /dev/null @@ -1,289 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/BContraction.hpp - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 */ - -#ifndef Hadrons_MDistil_BContraction_hpp_ -#define Hadrons_MDistil_BContraction_hpp_ - -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation -#include -BEGIN_HADRONS_NAMESPACE - -/****************************************************************************** - * BContraction * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) - - // general baryon tensor set based on Eigen tensors and Grid-allocated memory - // Dimensions: - // 0 - ext - external field (momentum, EM field, ...) - // 1 - str - dirac structure - // 2 - t - timeslice - // 3 - s - free spin index - // 4 - i - left distillation mode index - // 5 - j - middle distillation mode index - // 6 - k - left distillation mode index - // template - // using BaryonTensorSet = Eigen::TensorMap>; - - -class BContractionPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(BContractionPar, - std::string, one, - std::string, two, - std::string, three, - std::string, output, - int, parity, - std::vector, mom); -}; - -template -class TBContraction: public Module -{ -public: - using W = WilsonImplR; // Debug so I can see type info for default FImpl - FERM_TYPE_ALIASES(FImpl,); -public: - // constructor - TBContraction(const std::string name); - // destructor - virtual ~TBContraction(void) {}; - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); -private: - bool hasPhase_{false}; - std::string momphName_; - std::vector gamma12_; - std::vector gamma23_; - std::vector> mom_; -protected: - GridCartesian * grid4d; - GridCartesian * grid3d; -}; - -/*class BFieldIO: Serializable{ -public: - using BaryonTensorSet = Eigen::Tensor; - GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, - BaryonTensorSet, BField - ); -};*/ - -MODULE_REGISTER_TMP(BContraction, TBContraction, MDistil); - -/****************************************************************************** - * TBContraction implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -TBContraction::TBContraction(const std::string name) -: Module(name) -{} - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TBContraction::getInput(void) -{ - std::vector in = {par().one, par().two, par().three}; - - return in; -} - -template -std::vector TBContraction::getOutput(void) -{ - std::vector out = {}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void TBContraction::setup(void) -{ - -} - -// execution /////////////////////////////////////////////////////////////////// -template -void TBContraction::execute(void) -{ - auto &one = envGet(std::vector, par().one); - auto &two = envGet(std::vector, par().two); - auto &three = envGet(std::vector, par().three); - - int N_1 = one.size(); - int N_2 = two.size(); - int N_3 = three.size(); - - int parity = par().parity; - const std::string &output{par().output}; - - LOG(Message) << "Computing distillation baryon fields" << std::endl; - LOG(Message) << "One: '" << par().one << "' Two: '" << par().two << "' Three: '" << par().three << "'" << std::endl; - LOG(Message) << "Momenta:" << std::endl; - for (auto &p: mom_) - { - LOG(Message) << " " << p << std::endl; - } - - grid4d = env().getGrid(); - grid3d = MakeLowerDimGrid(grid4d); - int Nmom=1; - int Nt=64; - // std::vector BField(Nmom*Nt*N_1*N_2*N_3); - int Bindex; - int Nc=3; //Num colours - - FermionField ftmp1(grid3d); - FermionField ftmp2(grid3d); - FermionField ftmp3(grid3d); - LatticeView tmp1{ ftmp1 }; - LatticeView tmp2{ ftmp2 }; - LatticeView tmp3{ ftmp3 }; - //std::complex * tmp33 = reinterpret_cast *>(&(tmp3[0]()(0)(0))); - -#ifdef THIS_IS_NAUGHTY_TODO_FIXME - // The reinterpret_cast gets rid of SIMD attributes - // Plus other badness - e.g. we perhaps shouldn't explicitly say SpinColourVector - // ... but rather use some correct FIMPL types - // REVIEW WITH PETER -#endif - - SpinColourVector * tmp11 = reinterpret_cast(&(tmp1[0]()(0)(0))); - SpinColourVector * tmp22 = reinterpret_cast(&(tmp2[0]()(0)(0))); - SpinColourVector * tmp33 = reinterpret_cast(&(tmp3[0]()(0)(0))); - - SpinVector tmp11s; - SpinVector tmp22s; - SpinVector tmp33s; - SpinVector tmp333; - SpinMatrix diquark; - SpinMatrix g_diquark; - SpinVector tmp222; - SpinVector tmp111; - - - assert(parity == 1 || parity == -1); - - std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - - Gamma g4(Gamma::Algebra::GammaT); - - gamma12_ = { - Gamma::Algebra::Identity, // I - Gamma::Algebra::Gamma5, // gamma_5 - Gamma::Algebra::Identity, // I - }; - gamma23_ = { // C = i gamma_2 gamma_4 - Gamma::Algebra::SigmaXZ, // C gamma_5 = -i gamma_1 gamma_3 - Gamma::Algebra::SigmaYT, // C = i gamma_2 gamma_4 - Gamma::Algebra::GammaYGamma5, // i gamma_4 C gamma_5 = i gamma_2 gamma_5 - }; - std::vector factor23{{0.,-1.},{0.,1.},{0.,1.}}; - using BaryonTensorSet = Eigen::Tensor; - int Ngamma=3; - BaryonTensorSet BField3(Nmom,Ngamma,Nt,4,N_1,N_2,N_3); - - Eigen::Tensor corr(Nmom,4,Nt); - - - Complex diquark2; - for (int i1=0 ; i1 < N_1 ; i1++){ - for (int i2=0 ; i2 < N_2 ; i2++){ - for (int i3=0 ; i3 < N_3 ; i3++){ - for (int imom=0 ; imom < Nmom ; imom++){ - for (int t=0 ; t < Nt ; t++){ - Bindex = i1 + N_1*(i2 + N_2*(i3 + N_3*(imom+Nmom*t))); - ExtractSliceLocal(ftmp1,one[i1],0,t,3); - ExtractSliceLocal(ftmp2,two[i2],0,t,3); - ExtractSliceLocal(ftmp3,three[i3],0,t,3); - accelerator_for(sU, grid3d->oSites(), grid3d->Nsimd(), - { - for (int ie=0 ; ie < 6 ; ie++){ - // Why does peekColour not work???? - for (int is=0 ; is < 4 ; is++){ - tmp11s()(is)() = tmp11[sU]()(is)(epsilon[ie][0]); - tmp22s()(is)() = tmp22[sU]()(is)(epsilon[ie][1]); - tmp33s()(is)() = tmp33[sU]()(is)(epsilon[ie][2]); - } - for (int ig=0 ; ig < Ngamma ; ig++){ - tmp333 = Gamma(gamma23_[ig])*tmp33s; - tmp111 = Gamma(gamma12_[ig])*tmp11s; - tmp222 = g4*tmp111; - tmp111 = 0.5*(double)parity*(tmp111 + tmp222); // P_\pm * ... - diquark2 = factor23[0]*innerProduct(tmp22s,tmp333); - for (int is=0 ; is < 4 ; is++){ - BField3(imom,ig,t,is,i1,i2,i3)+=static_cast(epsilon_sgn[ie])*tmp111()(is)()*diquark2; - } - } - } - } ); - } - } - } - } - } - for (int is=0 ; is < 4 ; is++){ - for (int t=0 ; t < Nt ; t++){ - std::cout << "BaryonField(is=" << is << ",t=" << t << ") = " << BField3(0,0,t,is,0,0,0) << std::endl; - } - } - - BFieldIO BField_save; -#ifdef FELIX_ISSUE - BField_save.BField = BField3; -#endif - std::string filename ="./" + output + ".h5"; - std::cout << "Writing to file " << filename << std::endl; - Hdf5Writer writer(filename); - write(writer,"BaryonField",BField_save.BField); - -} - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MDistil_BContraction_hpp_ diff --git a/Hadrons/Modules/MDistil/Baryon2pt.cc b/Hadrons/Modules/MDistil/Baryon2pt.cc deleted file mode 100644 index 21e79995..00000000 --- a/Hadrons/Modules/MDistil/Baryon2pt.cc +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/Baryon2pt.cc - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 - -using namespace Grid; -using namespace Hadrons; -using namespace MDistil; - -template class Grid::Hadrons::MDistil::TBaryon2pt; diff --git a/Hadrons/Modules/MDistil/Baryon2pt.hpp b/Hadrons/Modules/MDistil/Baryon2pt.hpp deleted file mode 100644 index 520357bf..00000000 --- a/Hadrons/Modules/MDistil/Baryon2pt.hpp +++ /dev/null @@ -1,221 +0,0 @@ -/************************************************************************************* - - Grid physics library, www.github.com/paboyle/Grid - - Source file: Hadrons/Modules/MDistil/Baryon2pt.hpp - - Copyright (C) 2019 - - Author: Felix Erben - Author: Michael Marshall - - 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 */ - -#ifndef Hadrons_MDistil_Baryon2pt_hpp_ -#define Hadrons_MDistil_Baryon2pt_hpp_ - -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation -#include -BEGIN_HADRONS_NAMESPACE - -/****************************************************************************** - * Baryon2pt * - ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) - -class Baryon2ptPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(Baryon2ptPar, - std::string, inputL, - std::string, inputR, - std::string, quarksL, - std::string, quarksR, - std::string, output - ); -}; - -template -class TBaryon2pt: public Module -{ -public: - // constructor - TBaryon2pt(const std::string name); - // destructor - virtual ~TBaryon2pt(void) {}; - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); -}; - -class C2IO: Serializable{ -public: -using C2Set = Eigen::Tensor; - GRID_SERIALIZABLE_CLASS_MEMBERS(C2IO, - C2Set, C2 - ); -}; - -MODULE_REGISTER_TMP(Baryon2pt, TBaryon2pt, MDistil); - -/****************************************************************************** - * TBaryon2pt implementation * - ******************************************************************************/ -// constructor ///////////////////////////////////////////////////////////////// -template -TBaryon2pt::TBaryon2pt(const std::string name) -: Module(name) -{} - -// dependencies/products /////////////////////////////////////////////////////// -template -std::vector TBaryon2pt::getInput(void) -{ - std::vector in; - - return in; -} - -template -std::vector TBaryon2pt::getOutput(void) -{ - std::vector out = {getName()}; - - return out; -} - -// setup /////////////////////////////////////////////////////////////////////// -template -void TBaryon2pt::setup(void) -{ - -} - -// execution /////////////////////////////////////////////////////////////////// -template -void TBaryon2pt::execute(void) -{ - - const std::string &inputL{par().inputL}; - const std::string &inputR{par().inputR}; - const std::string &quarksL{par().quarksL}; - const std::string &quarksR{par().quarksR}; - const std::string &output{par().output}; - - int Nmom=1; - int Nt=32; - int Nc=3; //Num colours - std::vector> epsilon = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - std::vector epsilon_sgn = {1,1,1,-1,-1,-1}; - int Ngamma=3; - - int N_1=12; - int N_2=12; - int N_3=12; - - // using BaryonTensorSet = Eigen::Tensor; - - BFieldIO BFieldL; - BFieldL.BField.resize(Nmom,Nt,N_1,N_2,N_3,4); - - std::string filenameL ="./" + inputL + ".h5"; - std::cout << "Reading from file " << filenameL << std::endl; - Hdf5Reader readerL(filenameL); - read(readerL,"BaryonField",BFieldL.BField); - - BFieldIO BFieldR; - BFieldR.BField.resize(Nmom,Nt,N_1,N_2,N_3,4); - - std::string filenameR ="./" + inputR + ".h5"; - std::cout << "Reading from file " << filenameR << std::endl; - Hdf5Reader readerR(filenameR); - read(readerR,"BaryonField",BFieldR.BField); - - Eigen::Tensor corr(Nmom,Nt); - - int Npairs = 0; - char left[] = "uud"; - char right[] = "uud"; - std::vector pairs(6); - for (int ie=0, i=0 ; ie < 6 ; ie++){ - if (left[0] == right[epsilon[ie][0]] && left[1] == right[epsilon[ie][1]] && left[2] == right[epsilon[ie][2]]){ - pairs[i] = ie; - i++; - Npairs++; - } - } - pairs.resize(Npairs); - std::cout << Npairs << " pairs: " << pairs << std::endl; - for (int imom=0 ; imom < Nmom ; imom++){ - for (int t=0 ; t < Nt ; t++){ - corr(imom,t) = 0.; - } - } - - int tsrc=0; - - for (int ipair=0 ; ipair < Npairs ; ipair++){ - Eigen::array, 3> product_dims = { Eigen::IndexPair(0,epsilon[pairs[ipair]][0]),Eigen::IndexPair(1,epsilon[pairs[ipair]][1]) ,Eigen::IndexPair(2,epsilon[pairs[ipair]][2]) }; - for (int imom=0 ; imom < Nmom ; imom++){ - std::cout << imom << std::endl; - Eigen::Tensor B5L = BFieldL.BField.chip(imom,0); - Eigen::Tensor B5R = BFieldR.BField.chip(imom,0); - for (int t=0 ; t < Nt ; t++){ - Eigen::Tensor B4L = B5L.chip(t,0); - Eigen::Tensor B4R = B5R.chip(tsrc,0); - for (int is=0 ; is < 4 ; is++){ - Eigen::Tensor B3L = B4L.chip(is,3); - Eigen::Tensor B3R = B4R.chip(is,3); - Eigen::Tensor C2 = B3L.contract(B3R,product_dims); - corr(imom,t) += static_cast(epsilon_sgn[pairs[ipair]])*C2(0); - } - } - } - } - for (int t=0 ; t < Nt ; t++){ - std::cout << "C2(t=" << t << ") = " << corr(0,t) << std::endl; - } - - /* C2IO C2_save; - C2_save.C2 = corr; - - std::string filename ="./" + output + ".h5"; - std::cout << "Writing to file " << filename << std::endl; - Hdf5Writer writer(filename); - write(writer,"C2",C2_save.C2); - */ -} - -END_MODULE_NAMESPACE - -END_HADRONS_NAMESPACE - -#endif // Hadrons_MDistil_Baryon2pt_hpp_ From 3b3680c64eb9a8931ef55132d00dc2c6b2d18f20 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 30 Oct 2019 15:50:04 +0000 Subject: [PATCH 296/347] Reversed Felix's interim A2Autils.h changes ... these were finished and went into develop via a separate branch --- Grid/qcd/utils/A2Autils.h | 411 +------------------------------------- 1 file changed, 2 insertions(+), 409 deletions(-) diff --git a/Grid/qcd/utils/A2Autils.h b/Grid/qcd/utils/A2Autils.h index 6aa4e888..c7c7d329 100644 --- a/Grid/qcd/utils/A2Autils.h +++ b/Grid/qcd/utils/A2Autils.h @@ -40,21 +40,12 @@ public: const std::vector &mom, int orthogdim); - - static void NucleonFieldMom(Eigen::Tensor &mat, - const std::vector &one, - const std::vector &two, - const std::vector &three, - const std::vector &mom, - int parity, - int orthogdim); - static void PionFieldXX(Eigen::Tensor &mat, const FermionField *wi, const FermionField *vj, int orthogdim, int g5); - + static void PionFieldWV(Eigen::Tensor &mat, const FermionField *wi, const FermionField *vj, @@ -127,404 +118,6 @@ private: const int Ns, const int ss); }; -template -void A2Autils::NucleonFieldMom(Eigen::Tensor &mat, - const std::vector &one, - const std::vector &two, - const std::vector &three, - const std::vector &mom, - int parity, - int orthogdim) -{ - assert(parity == 1 || parity == -1); - - typedef typename FImpl::SiteSpinor vobj; - - typedef typename vobj::scalar_object sobj; - typedef typename vobj::scalar_type scalar_type; - typedef typename vobj::vector_type vector_type; - - typedef iSpinVector SpinVector_v; - typedef iSpinVector SpinVector_s; - - int oneBlock = mat.dimension(2); - int twoBlock = mat.dimension(3); - int threeBlock = mat.dimension(4); - - GridBase *grid = one[0].Grid(); - - const int nd = grid->_ndimension; - const int Nsimd = grid->Nsimd(); - - int Nt = grid->GlobalDimensions()[orthogdim]; - int Nmom = mom.size(); - - int fd=grid->_fdimensions[orthogdim]; - int ld=grid->_ldimensions[orthogdim]; - int rd=grid->_rdimensions[orthogdim]; - - // will locally sum vectors first - // sum across these down to scalars - // splitting the SIMD - int MFrvol = rd*oneBlock*twoBlock*threeBlock*Nmom; - int MFlvol = ld*oneBlock*twoBlock*threeBlock*Nmom; - - Vector lvSum(MFrvol); - accelerator_for (r, MFrvol, Nsimd, { - lvSum[r] = 0; - } ); - - Vector lsSum(MFlvol); - accelerator_for (r, MFlvol, Nsimd, { - lsSum[r] = scalar_type(0.0); - } ); - - int e1= grid->_slice_nblock[orthogdim]; - int e2= grid->_slice_block [orthogdim]; - int stride=grid->_slice_stride[orthogdim]; - - accelerator_for(r, rd, Nsimd, { - int so=r*grid->_ostride[orthogdim]; // base offset for start of plane - - for(int n=0;n C gamma_5 = - i gamma_1 gamma_3 - //auto v2g = v2*Gamma(Gamma::Algebra::SigmaXZ); - //auto v2g=v2; - - for(int k=0;k extracted(Nsimd); - - for(int i=0;iiCoorFromIindex(icoor,idx); - - int ldx = rt+icoor[orthogdim]*rd; - - int ij_ldx = m+Nmom*i + Nmom*oneBlock * j + Nmom*oneBlock * twoBlock * k + Nmom*oneBlock * twoBlock *threeBlock * ldx; - - lsSum[ij_ldx]=lsSum[ij_ldx]+extracted[idx]; - - } - }}}} - } ); - - assert(mat.dimension(0) == Nmom); - assert(mat.dimension(1) == Nt); - - int pd = grid->_processors[orthogdim]; - int pc = grid->_processor_coor[orthogdim]; - accelerator_for(lt, ld, Nsimd, - { - for(int pt=0;ptGlobalSumVector(&mat(0,0,0,0,0,0),Nmom*Nt*oneBlock*twoBlock*threeBlock*4); -} - - - - - -/* -template -template -void A2Autils::BaryonField(TensorType &mat, - const FermionField *one, - const FermionField *two, - const FermionField *three, - std::vector gammaA, - std::vector gammaB, - const std::vector &mom, - int orthogdim, double *t_kernel, double *t_gsum) -{ - typedef typename FImpl::SiteSpinor vobj; - - typedef typename vobj::scalar_object sobj; - typedef typename vobj::scalar_type scalar_type; - typedef typename vobj::vector_type vector_type; - - typedef iSpinMatrix SpinMatrix_v; - typedef iSpinMatrix SpinMatrix_s; - - typedef iSpinColourMatrix SpinColourMatrix_v; - - int oneBlock = mat.dimension(3); - int twoBlock = mat.dimension(4); - int threeBlock = mat.dimension(5); - - GridBase *grid = one[0].Grid(); - - const int Nd = grid->_ndimension; - const int Nsimd = grid->Nsimd(); - - int Nt = grid->GlobalDimensions()[orthogdim]; - int Ngamma = gammaA.size(); - assert (Ngamma = gammaB.size()); // Only combinatin of two gammas gives correct operator! - int Nmom = mom.size(); - - int fd=grid->_fdimensions[orthogdim]; - int ld=grid->_ldimensions[orthogdim]; - int rd=grid->_rdimensions[orthogdim]; - - // will locally sum vectors first - // sum across these down to scalars - // splitting the SIMD - int MFrvol = rd*twoBlock*threeBlock*Nmom; - int MFlvol = ld*twoBlock*threeBlock*Nmom; - - Vector> lvSum(3); - for (int ic=0;ic<3;ic++){ - lvSum[ic].resize(MFrvol); - parallel_for (int r = 0; r < MFrvol; r++){ - lvSum[ic][r] = zero; - } - } - - Vector> lsSum(3); - for (int ic=0;ic<3;ic++){ - lsSum[ic].resize(MFlvol); - parallel_for (int r = 0; r < MFlvol; r++){ - lsSum[ic][r] = scalar_type(0.0); - } - } - - int e1= grid->_slice_nblock[orthogdim]; - int e2= grid->_slice_block [orthogdim]; - int stride=grid->_slice_stride[orthogdim]; - - // potentially wasting cores here if local time extent too small - if (t_kernel) *t_kernel = -usecond(); - parallel_for(int r=0;r_ostride[orthogdim]; // base offset for start of plane - - // first, the diquark two*gammaB*three - for(int n=0;n vv(3); - - for(int s1=0;s1 icoor(Nd); - std::vector extracted(Nsimd); - - for(int i=0;iiCoorFromIindex(icoor,idx); - - int ldx = rt+icoor[orthogdim]*rd; - - int ij_ldx = m+Nmom*i+Nmom*Lblock*j+Nmom*Lblock*Rblock*ldx; - - lsSum[ic][ij_ldx]=lsSum[ic][ij_ldx]+extracted[idx]; - - } - }}} - } - - - if (t_kernel) *t_kernel += usecond(); - assert(mat.dimension(0) == Nmom); - assert(mat.dimension(1) == Ngamma); - assert(mat.dimension(2) == Nt); - - TensorType diquark; // Need this instead of mat!!! - - // ld loop and local only?? - int pd = grid->_processors[orthogdim]; - int pc = grid->_processor_coor[orthogdim]; - parallel_for_nest2(int lt=0;ltGlobalSumVector(&mat(0,0,0,0,0),Nmom*Ngamma*Nt*Lblock*Rblock); - if (t_gsum) *t_gsum += usecond(); -} -*/ - template template void A2Autils::MesonField(TensorType &mat, @@ -667,7 +260,7 @@ void A2Autils::MesonField(TensorType &mat, int ij_dx = m+Nmom*i + Nmom*Lblock * j + Nmom*Lblock * Rblock * lt; for(int mu=0;mu Date: Thu, 31 Oct 2019 11:11:52 +0000 Subject: [PATCH 297/347] Cleanup of messages --- Hadrons/Modules/MDistil/Perambulator.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index b4d1a9a1..9777d01f 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -202,7 +202,7 @@ void TPerambulator::execute(void) for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { for (int ds = 0; ds < SI; ds++) { - std::cout << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; + LOG(Message) << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; dist_source = 0; tmp3d_nospin = 0; evec3d = 0; @@ -250,7 +250,7 @@ void TPerambulator::execute(void) } } } - std::cout << "perambulator done" << std::endl; + LOG(Message) << "perambulator done" << std::endl; perambulator.SliceShare( grid3d, grid4d ); if(grid4d->IsBoss()) { @@ -295,7 +295,7 @@ void TPerambulator::execute(void) const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; if( !UnsmearedSinkFileName.empty() ) { bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); - std::cout << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; + LOG(Message) << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; //Grid::Hdf5Writer writer(filename); //write(writer,"unsmeared_sink",sink); A2AVectorsIo::write(UnsmearedSinkFileName, unsmeared_sink, bMulti, vm().getTrajectory()); From 4ed937953525e69e1b9598d9dc58a4ac0626a5a3 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 31 Oct 2019 11:45:50 +0000 Subject: [PATCH 298/347] some cleanup --- Hadrons/Modules/MDistil/Perambulator.hpp | 41 +++--------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 9777d01f..9905a7f3 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -48,9 +48,9 @@ public: std::string, lapevec, std::string, solver, std::string, noise, - std::string, PerambFileName, //stem!!! - std::string, UnsmearedSinkFileName, // Filename to save unsmeared sink - std::string, UnsmearedSinkMultiFile, // One file per vector? + std::string, PerambFileName, + std::string, UnsmearedSinkFileName, + std::string, UnsmearedSinkMultiFile, int, nvec, DistilParameters, Distil); }; @@ -212,7 +212,6 @@ void TPerambulator::execute(void) for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += SI){ ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - //tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); tmp3d=0; pokeSpin(tmp3d,tmp3d_nospin,is); @@ -233,7 +232,7 @@ void TPerambulator::execute(void) mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); result = v4dtmp; } - if ((1)) // comment out if unsmeared sink is too large??? + if( !UnsmearedSinkFileName.empty() ) unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; for (int is = 0; is < Ns; is++) { result_nospin = peekSpin(result,is); @@ -259,45 +258,13 @@ void TPerambulator::execute(void) sPerambName = getName(); sPerambName.append( "." ); sPerambName.append( std::to_string(vm().getTrajectory())); - //perambulator.WriteBinary(sPerambName); perambulator.write(sPerambName.c_str()); } - // Save the unsmeared sink as well if requested - /*const int X{grid4d->GlobalDimensions()[0]}; - const int Y{grid4d->GlobalDimensions()[1]}; - const int Z{grid4d->GlobalDimensions()[2]}; - const int T{grid4d->GlobalDimensions()[3]}; - - if(grid4d->IsBoss()) { - Eigen::Tensor sink(nnoise,LI,Nt_inv,SI,X,Y,Z,T,3,4); - - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - for (int ix=0; ix < X; ix++) { - for (int iy=0; iy < Y; iy++) { - for (int iz=0; iz < Z; iz++) { - for (int it=0; it < T; it++) { - std::vector site({ix,iy,iz,it}); - for (int ic=0; ic < 3; ic++) { - for (int is=0; is < 4; is++) { - //peekSite(sink[inoise,dk,dt,ds,ix,iy,iz,it,ic,is],unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))]()(is)(ic),site); // Build fails when uncommenting - - }} - }}}} - } - } - } - }*/ - const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; if( !UnsmearedSinkFileName.empty() ) { bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); LOG(Message) << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; - //Grid::Hdf5Writer writer(filename); - //write(writer,"unsmeared_sink",sink); A2AVectorsIo::write(UnsmearedSinkFileName, unsmeared_sink, bMulti, vm().getTrajectory()); } } From 5c54f27ac16dd9c491ee4b0c9cc6ba7c843da96d Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Thu, 31 Oct 2019 11:51:05 +0000 Subject: [PATCH 299/347] some cleanup, but hard-coded src in LapEvec unclear --- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 -- Hadrons/Modules/MDistil/LapEvec.hpp | 4 ++-- Hadrons/Modules/MDistil/Noises.hpp | 4 +--- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 4 +--- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 2b549b6c..d7097a06 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -198,7 +198,6 @@ void TDistilVectors::setup(void) envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); - //envTmp(LatticeColourVector, "tmp_nospin",1,LatticeColourVector(grid4d)); envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); @@ -261,7 +260,6 @@ void TDistilVectors::execute(void) for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += SI){ ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - //tmp3d_nospin = evec3d * noise[inoise + nnoise*(t_inv + Nt*(ik+nvec*is))]; tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); tmp3d=0; pokeSpin(tmp3d,tmp3d_nospin,is); diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 54578f35..89ffd0e5 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -51,7 +51,7 @@ BEGIN_MODULE_NAMESPACE(MDistil) struct StoutParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(StoutParameters, int, steps, - double, rho) // TODO: change name of this to rho + double, rho) StoutParameters() = default; template StoutParameters(Reader& Reader){read(Reader,"StoutSmearing",*this);} }; @@ -243,7 +243,7 @@ void TLapEvec::execute(void) Chebyshev Cheb(ChebPar.alpha,ChebPar.beta,ChebPar.PolyOrder); // Construct source vector according to Test_dwf_compressed_lanczos.cc - src = 11.0; + src = 11.0; //TODO: Why hard-coded 11? RealD nn = norm2(src); nn = Grid::sqrt(nn); src = src * (1.0/nn); diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 91cb35ca..37471463 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -132,7 +132,7 @@ void TNoises::execute(void) if( UniqueIdentifier.length() == 0 ) { UniqueIdentifier = getName(); } - UniqueIdentifier.append( std::to_string( vm().getTrajectory() ) ); //maybe add more?? + UniqueIdentifier.append( std::to_string( vm().getTrajectory() ) ); GridSerialRNG sRNG; sRNG.SeedUniqueString(UniqueIdentifier); @@ -143,13 +143,11 @@ void TNoises::execute(void) for( int ivec = 0; ivec < nvec; ivec++ ) { for( int is = 0; is < Ns; is++ ) { if( exact_distillation ) - //noise[inoise + nnoise*(t + Nt*(ivec+nvec*is))] = 1.; noise(inoise, t, ivec, is) = 1.; else{ random(sRNG,rn); // We could use a greater number of complex roots of unity // ... but this seems to work well - //noise[inoise + nnoise*(t + Nt*(ivec+nvec*is))] = (rn > 0.5) ? -1 : 1; noise(inoise, t, ivec, is) = (rn > 0.5) ? -1 : 1; } } diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index ce2b91d8..fc580b55 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -126,7 +126,6 @@ void TPerambFromSolve::setup(void) const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, true) }; grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); - //std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; envCreate(PerambTensor, getName(), 1, PerambIndexNames,Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); envCreate(NoiseTensor, getName() + "_noise", 1, nnoise, Nt, nvec, Ns ); envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); @@ -173,7 +172,7 @@ void TPerambFromSolve::execute(void) for (int ivec = 0; ivec < nvec_reduced; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); - std::cout << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; + LOG(Message) << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; } } } @@ -188,7 +187,6 @@ void TPerambFromSolve::execute(void) sPerambName = getName(); sPerambName.append( "." ); sPerambName.append( std::to_string(vm().getTrajectory())); - //perambulator.WriteBinary(sPerambName); perambulator.write(sPerambName.c_str()); } } From 45d4cf0971f083a59a0429356d063b3a6ec199c5 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 1 Nov 2019 15:35:07 +0000 Subject: [PATCH 300/347] Cleanup in progress --- Hadrons/Distil.hpp | 277 +-------- Hadrons/Modules/MDistil/LapEvec.hpp | 8 +- Hadrons/Modules/MDistil/Noises.hpp | 3 +- Hadrons/Modules/MDistil/Perambulator.hpp | 304 ++++----- tests/hadrons/Test_distil.cc | 748 +---------------------- 5 files changed, 209 insertions(+), 1131 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 42b083a4..e658025c 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -38,39 +38,6 @@ #include #include -/****************************************************************************** - A consistent set of cross-platform methods for big endian <-> host byte ordering - I imagine this exists already? - This can be removed once the (deprecated) NamedTensor::ReadBinary & WriteBinary methods are deleted - ******************************************************************************/ - -#if defined(__linux__) -# include -#elif defined(__FreeBSD__) || defined(__NetBSD__) -# include -#elif defined(__OpenBSD__) -# include -# define be16toh(x) betoh16(x) -# define be32toh(x) betoh32(x) -# define be64toh(x) betoh64(x) -#elif defined(__APPLE__) -#include -#define htobe16(x) OSSwapHostToBigInt16(x) -#define htole16(x) OSSwapHostToLittleInt16(x) -#define be16toh(x) OSSwapBigToHostInt16(x) -#define le16toh(x) OSSwapLittleToHostInt16(x) - -#define htobe32(x) OSSwapHostToBigInt32(x) -#define htole32(x) OSSwapHostToLittleInt32(x) -#define be32toh(x) OSSwapBigToHostInt32(x) -#define le32toh(x) OSSwapLittleToHostInt32(x) - -#define htobe64(x) OSSwapHostToBigInt64(x) -#define htole64(x) OSSwapHostToLittleInt64(x) -#define be64toh(x) OSSwapBigToHostInt64(x) -#define le64toh(x) OSSwapLittleToHostInt64(x) -#endif - /****************************************************************************** This potentially belongs in CartesianCommunicator ******************************************************************************/ @@ -139,7 +106,7 @@ inline void SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Bu *************************************************************************************/ template -class LinOpPeardonNabla : public LinearOperatorBase, public LinearFunction { +class Laplacian3D : public LinearOperatorBase, public LinearFunction { typedef typename GaugeField::vector_type vCoeff_t; protected: // I don't really mind if _gf is messed with ... so make this public? //GaugeField & _gf; @@ -147,7 +114,7 @@ protected: // I don't really mind if _gf is messed with ... so make this public? std::vector > > U; public: // Construct this operator given a gauge field and the number of dimensions it should act on - LinOpPeardonNabla( GaugeField& gf, int dimSpatial = Tdir ) : /*_gf(gf),*/ nd{dimSpatial} { + Laplacian3D( GaugeField& gf, int dimSpatial = Tdir ) : /*_gf(gf),*/ nd{dimSpatial} { assert(dimSpatial>=1); for( int mu = 0 ; mu < nd ; mu++ ) U.push_back(PeekIndex(gf,mu)); @@ -178,12 +145,12 @@ public: }; template -class LinOpPeardonNablaHerm : public LinearFunction { +class Laplacian3DHerm : public LinearFunction { public: OperatorFunction & _poly; LinearOperatorBase &_Linop; - LinOpPeardonNablaHerm(OperatorFunction & poly,LinearOperatorBase& linop) + Laplacian3DHerm(OperatorFunction & poly,LinearOperatorBase& linop) : _poly{poly}, _Linop{linop} {} void operator()(const Field& in, Field& out) { @@ -244,12 +211,6 @@ const bool full_tdil{ TI == Nt }; \ const bool exact_distillation{ full_tdil && LI == nvec }; \ const int Nt_inv{ full_tdil ? 1 : TI } -class BFieldIO: Serializable{ -public: - using BaryonTensorSet = Eigen::Tensor; - GRID_SERIALIZABLE_CLASS_MEMBERS(BFieldIO, BaryonTensorSet, BField ); -}; - /****************************************************************************** Default for distillation file operations. For now only used by NamedTensor ******************************************************************************/ @@ -268,9 +229,6 @@ static const char * FileExtension = ".dat"; NamedTensor object This is an Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) They can be persisted to disk - Scalar_ objects are assumed to be composite objects of size Endian_Scalar_Size. - (Disable big-endian by setting Endian_Scalar_Size=1). - NB: Endian_Scalar_Size will disappear when ReadBinary & WriteBinary retired IndexNames contains one name for each index, and IndexNames are validated on load. WHAT TO SAVE / VALIDATE ON LOAD (Override to warn instead of assert on load) Ensemble string @@ -280,20 +238,18 @@ static const char * FileExtension = ".dat"; ******************************************************************************/ -template +template class NamedTensor : Serializable { public: using Scalar = Scalar_; static constexpr int NumIndices = NumIndices_; - static constexpr uint16_t Endian_Scalar_Size = Endian_Scalar_Size_; using ET = Eigen::Tensor; using Index = typename ET::Index; GRID_SERIALIZABLE_CLASS_MEMBERS(NamedTensor , ET, tensor , std::vector, IndexNames ); -public: // Named tensors are intended to be a superset of Eigen tensor inline operator ET&() { return tensor; } template @@ -351,9 +307,6 @@ public: // Read/Write in default format, i.e. HDF5 if present, else binary inline void read (const char * filename, const char * pszTag = nullptr); inline void write(const char * filename, const char * pszTag = nullptr) const; - // Original I/O implementation. This will be removed when we're sure it's no longer needed - EIGEN_DEPRECATED inline void ReadBinary (const std::string filename); // To be removed - EIGEN_DEPRECATED inline void WriteBinary(const std::string filename); // To be removed // Case insensitive compare of two strings // Pesumably this exists already? Where should this go? @@ -375,218 +328,31 @@ public: // Is this a named tensor template struct is_named_tensor : public std::false_type {}; -template struct is_named_tensor> : public std::true_type {}; -template struct is_named_tensor, T>::value>::type> : public std::true_type {}; +template struct is_named_tensor> : public std::true_type {}; +template struct is_named_tensor, T>::value>::type> : public std::true_type {}; /****************************************************************************** PerambTensor object - Endian_Scalar_Size can be removed once (deprecated) NamedTensor::ReadBinary & WriteBinary methods deleted ******************************************************************************/ -//template -using PerambTensor = NamedTensor; +using PerambTensor = NamedTensor; static const std::array PerambIndexNames{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; -/****************************************************************************** - Save NamedTensor binary format (NB: On-disk format is Big Endian) - Assumes the Scalar_ objects are contiguous (no padding) - ******************************************************************************/ - -template -void NamedTensor::WriteBinary(const std::string filename) { - LOG(Message) << "Writing NamedTensor to \"" << filename << "\"" << std::endl; - std::ofstream w(filename, std::ios::binary); - // Enforce assumption that the scalar is composed of fundamental elements of size Endian_Scalar_Size - assert((Endian_Scalar_Size == 1 || Endian_Scalar_Size == 2 || Endian_Scalar_Size == 4 || Endian_Scalar_Size == 8 ) - && "NamedTensor error: Endian_Scalar_Size should be 1, 2, 4 or 8"); - assert((sizeof(Scalar_) % Endian_Scalar_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Endian_Scalar_Size" ); - // Size of the data (in bytes) - const uint32_t Scalar_Size{sizeof(Scalar_)}; - const auto NumElements = tensor.size(); - const std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; - uint64_t u64 = htobe64(static_cast(TotalDataSize)); - w.write(reinterpret_cast(&u64), sizeof(u64)); - // Size of a Scalar_ - uint32_t u32{htobe32(Scalar_Size)}; - w.write(reinterpret_cast(&u32), sizeof(u32)); - // Endian_Scalar_Size - uint16_t u16{htobe16(Endian_Scalar_Size)}; - w.write(reinterpret_cast(&u16), sizeof(u16)); - // number of dimensions which aren't 1 - u16 = static_cast(this->NumIndices); - for( auto dim : tensor.dimensions() ) - if( dim == 1 ) - u16--; - u16 = htobe16( u16 ); - w.write(reinterpret_cast(&u16), sizeof(u16)); - // dimensions together with names - int d = 0; - for( auto dim : tensor.dimensions() ) { - if( dim != 1 ) { - // size of this dimension - u16 = htobe16( static_cast( dim ) ); - w.write(reinterpret_cast(&u16), sizeof(u16)); - // length of this dimension name - u16 = htobe16( static_cast( IndexNames[d].size() ) ); - w.write(reinterpret_cast(&u16), sizeof(u16)); - // dimension name - w.write(IndexNames[d].c_str(), IndexNames[d].size()); - } - d++; - } - // Actual data - char * const pStart{reinterpret_cast(tensor.data())}; - // Swap to network byte order in place (alternative is to copy memory - still slow) - void * const pEnd{pStart + TotalDataSize}; - if(Endian_Scalar_Size == 8) - for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = htobe64( * p ); - else if(Endian_Scalar_Size == 4) - for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = htobe32( * p ); - else if(Endian_Scalar_Size == 2) - for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = htobe16( * p ); - w.write(pStart, TotalDataSize); - // Swap back from network byte order - if(Endian_Scalar_Size == 8) - for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = be64toh( * p ); - else if(Endian_Scalar_Size == 4) - for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = be32toh( * p ); - else if(Endian_Scalar_Size == 2) - for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = be16toh( * p ); - // checksum -#ifdef USE_IPP - u32 = htobe32(GridChecksum::crc32c(tensor.data(), TotalDataSize)); -#else - u32 = htobe32(GridChecksum::crc32(tensor.data(), TotalDataSize)); -#endif - w.write(reinterpret_cast(&u32), sizeof(u32)); -} - -/****************************************************************************** - Load NamedTensor binary format (NB: On-disk format is Big Endian) - Assumes the Scalar_ objects are contiguous (no padding) - ******************************************************************************/ - -template -void NamedTensor::ReadBinary(const std::string filename) { - LOG(Message) << "Reading NamedTensor from \"" << filename << "\"" << std::endl; - std::ifstream r(filename, std::ios::binary); - // Enforce assumption that the scalar is composed of fundamental elements of size Endian_Scalar_Size - assert((Endian_Scalar_Size == 1 || Endian_Scalar_Size == 2 || Endian_Scalar_Size == 4 || Endian_Scalar_Size == 8 ) - && "NamedTensor error: Endian_Scalar_Size should be 1, 2, 4 or 8"); - assert((sizeof(Scalar_) % Endian_Scalar_Size) == 0 && "NamedTensor error: Scalar_ is not composed of Endian_Scalar_Size" ); - // Size of the data in bytes - const uint32_t Scalar_Size{sizeof(Scalar_)}; - Index NumElements{tensor.size()}; - std::streamsize TotalDataSize{static_cast(NumElements * Scalar_Size)}; - uint64_t u64; - r.read(reinterpret_cast(&u64), sizeof(u64)); - assert( TotalDataSize == 0 || TotalDataSize == be64toh( u64 ) && "NamedTensor error: Size of the data in bytes" ); - // Size of a Scalar_ - uint32_t u32; - r.read(reinterpret_cast(&u32), sizeof(u32)); - assert( Scalar_Size == be32toh( u32 ) && "NamedTensor error: sizeof(Scalar_)"); - // Endian_Scalar_Size - uint16_t u16; - r.read(reinterpret_cast(&u16), sizeof(u16)); - assert( Endian_Scalar_Size == be16toh( u16 ) && "NamedTensor error: Scalar_Unit_size"); - // number of dimensions which aren't 1 - uint16_t NumFileDimensions; - r.read(reinterpret_cast(&NumFileDimensions), sizeof(NumFileDimensions)); - NumFileDimensions = be16toh( NumFileDimensions ); - /*for( auto dim : tensor.dimensions() ) - if( dim == 1 ) - u16++;*/ - assert( ( TotalDataSize == 0 && this->NumIndices >= NumFileDimensions || this->NumIndices == NumFileDimensions ) - && "NamedTensor error: number of dimensions which aren't 1" ); - if( TotalDataSize == 0 ) { - // Read each dimension, using names to skip past dimensions == 1 - std::array NewDimensions; - for( Index &i : NewDimensions ) i = 1; - int d = 0; - for( int FileDimension = 0; FileDimension < NumFileDimensions; FileDimension++ ) { - // read dimension - uint16_t thisDim; - r.read(reinterpret_cast(&thisDim), sizeof(thisDim)); - // read dimension name - r.read(reinterpret_cast(&u16), sizeof(u16)); - size_t l = be16toh( u16 ); - std::string s( l, '?' ); - r.read(&s[0], l); - // skip forward to matching name - while( IndexNames[d].size() > 0 && !CompareCaseInsensitive( s, IndexNames[d] ) ) - assert(++d < NumIndices && "NamedTensor error: dimension name" ); - if( IndexNames[d].size() == 0 ) - IndexNames[d] = s; - NewDimensions[d++] = be16toh( thisDim ); - } - tensor.resize(NewDimensions); - NumElements = 1; - for( Index i : NewDimensions ) NumElements *= i; - TotalDataSize = NumElements * Scalar_Size; - } else { - // dimensions together with names - const auto & TensorDims = tensor.dimensions(); - for( int d = 0; d < NumIndices_; d++ ) { - // size of dimension - r.read(reinterpret_cast(&u16), sizeof(u16)); - u16 = be16toh( u16 ); - assert( TensorDims[d] == u16 && "size of dimension" ); - // length of dimension name - r.read(reinterpret_cast(&u16), sizeof(u16)); - size_t l = be16toh( u16 ); - assert( l == IndexNames[d].size() && "NamedTensor error: length of dimension name" ); - // dimension name - std::string s( l, '?' ); - r.read(&s[0], l); - assert( s == IndexNames[d] && "NamedTensor error: dimension name" ); - } - } - // Actual data - char * const pStart{reinterpret_cast(tensor.data())}; - void * const pEnd{pStart + TotalDataSize}; - r.read(pStart,TotalDataSize); - // Swap back from network byte order - if(Endian_Scalar_Size == 8) - for(uint64_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = be64toh( * p ); - else if(Endian_Scalar_Size == 4) - for(uint32_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = be32toh( * p ); - else if(Endian_Scalar_Size == 2) - for(uint16_t * p = reinterpret_cast(pStart) ; p < pEnd ; p++ ) - * p = be16toh( * p ); - // checksum - r.read(reinterpret_cast(&u32), sizeof(u32)); - u32 = be32toh( u32 ); -#ifdef USE_IPP - u32 -= GridChecksum::crc32c(tensor.data(), TotalDataSize); -#else - u32 -= GridChecksum::crc32(tensor.data(), TotalDataSize); -#endif - assert( u32 == 0 && "NamedTensor error: PerambTensor checksum invalid"); -} - /****************************************************************************** Write NamedTensor ******************************************************************************/ -template +template template -void NamedTensor::write(Writer &w, const char * pszTag)const{ +void NamedTensor::write(Writer &w, const char * pszTag)const{ if( pszTag == nullptr ) pszTag = "NamedTensor"; LOG(Message) << "Writing NamedTensor to tag " << pszTag << std::endl; write(w, pszTag, *this); } -template -void NamedTensor::write(const char * filename, const char * pszTag)const{ +template +void NamedTensor::write(const char * filename, const char * pszTag)const{ std::string sFileName{filename}; sFileName.append( MDistil::FileExtension ); LOG(Message) << "Writing NamedTensor to file " << sFileName << std::endl; @@ -598,8 +364,8 @@ void NamedTensor::write(const char * f Validate named tensor index names ******************************************************************************/ -template -bool NamedTensor::ValidateIndexNames( int iNumNames, const std::string * MatchNames ) const { +template +bool NamedTensor::ValidateIndexNames( int iNumNames, const std::string * MatchNames ) const { bool bSame{ iNumNames == NumIndices_ && IndexNames.size() == NumIndices_ }; for( int i = 0; bSame && i < NumIndices_; i++ ) bSame = CompareCaseInsensitive( MatchNames[i], IndexNames[i] ); @@ -610,9 +376,9 @@ bool NamedTensor::ValidateIndexNames( Read NamedTensor ******************************************************************************/ -template +template template -void NamedTensor::read(Reader &r, const char * pszTag) { +void NamedTensor::read(Reader &r, const char * pszTag) { if( pszTag == nullptr ) pszTag = "NamedTensor"; // Grab index names and dimensions @@ -626,8 +392,8 @@ void NamedTensor::read(Reader &r, cons assert( ValidateIndexNames( OldIndexNames.size(), &OldIndexNames[0] ) && "NamedTensor::load dimension name" ); } -template -void NamedTensor::read(const char * filename, const char * pszTag) { +template +void NamedTensor::read(const char * filename, const char * pszTag) { std::string sFileName{filename}; sFileName.append( MDistil::FileExtension ); LOG(Message) << "Reading NamedTensor from file " << sFileName << std::endl; @@ -664,19 +430,14 @@ inline void RotateEigen(std::vector & evec) Coordinate siteFirst(grid->Nd(),0); peekSite(cv0, evec[0], siteFirst); Grid::Complex cplx0 = cv0()()(0); -#ifdef GRID_NVCC if( cplx0.imag() == 0 ) -#else - if( std::imag(cplx0) == 0 ) -#endif std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; else { + const Real cplx0_mag = Grid::abs(cplx0); #ifdef GRID_NVCC - const Real cplx0_mag = thrust::abs(cplx0); const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); const Real argphase = thrust::arg(phase); #else - const Real cplx0_mag = std::abs(cplx0); const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); const Real argphase = std::arg(phase); #endif diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 89ffd0e5..101afedf 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -219,7 +219,7 @@ void TLapEvec::execute(void) } //////////////////////////////////////////////////////////////////////// - // Invert Peardon Nabla operator separately on each time-slice + // Invert nabla operator separately on each time-slice //////////////////////////////////////////////////////////////////////// auto & eig4d = envGet(LapEvecs, getName() ); @@ -237,7 +237,7 @@ void TLapEvec::execute(void) // Construct smearing operator ExtractSliceLocal(UmuNoTime,Umu_smear,0,t,Tdir); // switch to 3d/4d objects - LinOpPeardonNabla PeardonNabla(UmuNoTime); + Laplacian3D Nabla(UmuNoTime); LOG(Debug) << "Chebyshev preconditioning to order " << ChebPar.PolyOrder << " with parameters (alpha,beta) = (" << ChebPar.alpha << "," << ChebPar.beta << ")" << std::endl; Chebyshev Cheb(ChebPar.alpha,ChebPar.beta,ChebPar.PolyOrder); @@ -248,9 +248,9 @@ void TLapEvec::execute(void) nn = Grid::sqrt(nn); src = src * (1.0/nn); - LinOpPeardonNablaHerm PeardonNablaCheby(Cheb,PeardonNabla); + Laplacian3DHerm NablaCheby(Cheb,Nabla); ImplicitlyRestartedLanczos - IRL(PeardonNablaCheby,PeardonNabla,LPar.Nvec,LPar.Nk,LPar.Nk+LPar.Np,LPar.resid,LPar.MaxIt); + IRL(NablaCheby,Nabla,LPar.Nvec,LPar.Nk,LPar.Nk+LPar.Np,LPar.resid,LPar.MaxIt); int Nconv = 0; IRL.calc(eig[t].eval,eig[t].evec,src,Nconv); if( Nconv < LPar.Nvec ) { diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 37471463..a8c612cd 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -133,7 +133,8 @@ void TNoises::execute(void) UniqueIdentifier = getName(); } UniqueIdentifier.append( std::to_string( vm().getTrajectory() ) ); - + + // We use our own seeds so we can specify different noises per quark GridSerialRNG sRNG; sRNG.SeedUniqueString(UniqueIdentifier); Real rn; diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 9905a7f3..6a4deca1 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -30,7 +30,6 @@ #ifndef Hadrons_MDistil_Perambulator_hpp_ #define Hadrons_MDistil_Perambulator_hpp_ -// These are members of Distillation #include BEGIN_HADRONS_NAMESPACE @@ -47,10 +46,10 @@ public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambulatorPar, std::string, lapevec, std::string, solver, - std::string, noise, - std::string, PerambFileName, - std::string, UnsmearedSinkFileName, - std::string, UnsmearedSinkMultiFile, + std::string, noise, + std::string, PerambFileName, + std::string, UnsmearedSinkFileName, + std::string, UnsmearedSinkMultiFile, int, nvec, DistilParameters, Distil); }; @@ -59,29 +58,29 @@ template class TPerambulator: public Module { public: - FERM_TYPE_ALIASES(FImpl,); - SOLVER_TYPE_ALIASES(FImpl,); - // constructor - TPerambulator(const std::string name); - // destructor - virtual ~TPerambulator(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); + FERM_TYPE_ALIASES(FImpl,); + SOLVER_TYPE_ALIASES(FImpl,); + // constructor + TPerambulator(const std::string name); + // destructor + virtual ~TPerambulator(void); + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); protected: - virtual void Cleanup(void); + virtual void Cleanup(void); protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) - // Other members - unsigned int Ls_; - std::string sLapEvecName; - std::string sNoiseName; + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) + // Other members + unsigned int Ls_; + std::string sLapEvecName; + std::string sNoiseName; }; MODULE_REGISTER_TMP(Perambulator, TPerambulator, MDistil); @@ -99,174 +98,181 @@ TPerambulator::TPerambulator(const std::string name) template TPerambulator::~TPerambulator(void) { - Cleanup(); + Cleanup(); }; // dependencies/products /////////////////////////////////////////////////////// template std::vector TPerambulator::getInput(void) { - sLapEvecName = par().lapevec; - sNoiseName = par().noise; - if( sNoiseName.length() == 0 ) - sNoiseName = getName() + "_noise"; - return {sLapEvecName, par().solver, sNoiseName }; + sLapEvecName = par().lapevec; + sNoiseName = par().noise; + if( sNoiseName.length() == 0 ) + sNoiseName = getName() + "_noise"; + return {sLapEvecName, par().solver, sNoiseName }; } template std::vector TPerambulator::getOutput(void) { - return {getName(), getName() + "_unsmeared_sink"}; + return {getName(), getName() + "_unsmeared_sink"}; } // setup /////////////////////////////////////////////////////////////////////// template void TPerambulator::setup(void) { - Cleanup(); - grid4d = env().getGrid(); - grid3d = MakeLowerDimGrid(grid4d); - DISTIL_PARAMETERS_DEFINE( true ); - const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; - if( !UnsmearedSinkFileName.empty() ) - bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, true ) != 0 ); - - envCreate(PerambTensor, getName(), 1, PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); - envCreate(std::vector, getName() + "_unsmeared_sink", 1, - nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); - - envTmpLat(LatticeSpinColourVector, "dist_source"); - envTmpLat(LatticeSpinColourVector, "tmp2"); - envTmpLat(LatticeSpinColourVector, "result"); - envTmpLat(LatticeColourVector, "result_nospin"); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); - - Ls_ = env().getObjectLs(par().solver); - envTmpLat(FermionField, "v4dtmp"); - envTmpLat(FermionField, "v5dtmp", Ls_); - envTmpLat(FermionField, "v5dtmp_sol", Ls_); + Cleanup(); + grid4d = env().getGrid(); + grid3d = MakeLowerDimGrid(grid4d); + DISTIL_PARAMETERS_DEFINE( true ); + const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; + if( !UnsmearedSinkFileName.empty() ) + bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, true ) != 0 ); + + envCreate(PerambTensor, getName(), 1, PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); + envCreate(std::vector, getName() + "_unsmeared_sink", 1, + nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); + + envTmpLat(LatticeSpinColourVector, "dist_source"); + envTmpLat(LatticeSpinColourVector, "tmp2"); + envTmpLat(LatticeSpinColourVector, "result"); + envTmpLat(LatticeColourVector, "result_nospin"); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + + Ls_ = env().getObjectLs(par().solver); + envTmpLat(FermionField, "v4dtmp"); + envTmpLat(FermionField, "v5dtmp", Ls_); + envTmpLat(FermionField, "v5dtmp_sol", Ls_); } // clean up any temporaries created by setup (that aren't stored in the environment) template void TPerambulator::Cleanup(void) { - if( grid3d != nullptr ) { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; + if( grid3d != nullptr ) + { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; } // execution /////////////////////////////////////////////////////////////////// template void TPerambulator::execute(void) { - DISTIL_PARAMETERS_DEFINE( false ); - + DISTIL_PARAMETERS_DEFINE( false ); auto &solver=envGet(Solver, par().solver); auto &mat = solver.getFMat(); envGetTmp(FermionField, v4dtmp); envGetTmp(FermionField, v5dtmp); envGetTmp(FermionField, v5dtmp_sol); - auto &noise = envGet(NoiseTensor, sNoiseName); auto &perambulator = envGet(PerambTensor, getName()); auto &epack = envGet(LapEvecs, sLapEvecName); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); - - - // Load perambulator if it exists on disk instead of creating it - // Not sure this is how we want it - rather specify an input flag 'read' - // and assert that the file is there. - - envGetTmp(LatticeSpinColourVector, dist_source); - envGetTmp(LatticeSpinColourVector, tmp2); - envGetTmp(LatticeSpinColourVector, result); - envGetTmp(LatticeColourVector, result_nospin); - envGetTmp(LatticeSpinColourVector, tmp3d); - envGetTmp(LatticeColourVector, tmp3d_nospin); - envGetTmp(LatticeColourVector, result_3d); - envGetTmp(LatticeColourVector, evec3d); - + envGetTmp(LatticeSpinColourVector, dist_source); + envGetTmp(LatticeSpinColourVector, tmp2); + envGetTmp(LatticeSpinColourVector, result); + envGetTmp(LatticeColourVector, result_nospin); + envGetTmp(LatticeSpinColourVector, tmp3d); + envGetTmp(LatticeColourVector, tmp3d_nospin); + envGetTmp(LatticeColourVector, result_3d); + envGetTmp(LatticeColourVector, evec3d); const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - + const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; + { - - int t_inv; - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - LOG(Message) << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; - dist_source = 0; - tmp3d_nospin = 0; - evec3d = 0; - for (int it = dt; it < Nt; it += TI){ - if (full_tdil) t_inv = tsrc; else t_inv = it; - if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += SI){ - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); - tmp3d=0; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=0; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Tdir); - dist_source += tmp2; - } + int t_inv; + for (int inoise = 0; inoise < nnoise; inoise++) + { + for (int dk = 0; dk < LI; dk++) + { + for (int dt = 0; dt < Nt_inv; dt++) + { + for (int ds = 0; ds < SI; ds++) + { + LOG(Message) << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; + dist_source = 0; + tmp3d_nospin = 0; + evec3d = 0; + for (int it = dt; it < Nt; it += TI) + { + if (full_tdil) t_inv = tsrc; else t_inv = it; + if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) + { + for (int ik = dk; ik < nvec; ik += LI) + { + for (int is = ds; is < Ns; is += SI) + { + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); + tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); + tmp3d=0; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=0; + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Tdir); + dist_source += tmp2; + } + } + } + } + result=0; + v4dtmp = dist_source; + if (Ls_ == 1) + { + solver(result, v4dtmp); + } + else + { + mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); + solver(v5dtmp_sol, v5dtmp); + mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); + result = v4dtmp; + } + if( !UnsmearedSinkFileName.empty() ) + unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; + for (int is = 0; is < Ns; is++) + { + result_nospin = peekSpin(result,is); + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) + { + ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); + for (int ivec = 0; ivec < nvec; ivec++) + { + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); + pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); + } + } + } + } } - } } - result=0; - v4dtmp = dist_source; - if (Ls_ == 1){ - solver(result, v4dtmp); - } else { - mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); - solver(v5dtmp_sol, v5dtmp); - mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); - result = v4dtmp; - } - if( !UnsmearedSinkFileName.empty() ) - unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; - for (int is = 0; is < Ns; is++) { - result_nospin = peekSpin(result,is); - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); - for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); - } - } - } - } } - } } - } - LOG(Message) << "perambulator done" << std::endl; - perambulator.SliceShare( grid3d, grid4d ); - - if(grid4d->IsBoss()) { - std::string sPerambName{par().PerambFileName}; - if( sPerambName.length() == 0 ) - sPerambName = getName(); - sPerambName.append( "." ); - sPerambName.append( std::to_string(vm().getTrajectory())); - perambulator.write(sPerambName.c_str()); - } - - const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; - if( !UnsmearedSinkFileName.empty() ) { - bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); - LOG(Message) << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; - A2AVectorsIo::write(UnsmearedSinkFileName, unsmeared_sink, bMulti, vm().getTrajectory()); - } + LOG(Message) << "perambulator done" << std::endl; + perambulator.SliceShare( grid3d, grid4d ); + + if(grid4d->IsBoss()) + { + std::string sPerambName{par().PerambFileName}; + if( sPerambName.length() == 0 ) + sPerambName = getName(); + sPerambName.append( "." ); + sPerambName.append( std::to_string(vm().getTrajectory())); + perambulator.write(sPerambName.c_str()); + } + + if( !UnsmearedSinkFileName.empty() ) + { + bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); + LOG(Message) << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; + A2AVectorsIo::write(UnsmearedSinkFileName, unsmeared_sink, bMulti, vm().getTrajectory()); + } } END_MODULE_NAMESPACE diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index d60eb2b7..5e2d50f0 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -190,98 +190,22 @@ void test_Perambulators( Application &application, const char * pszSuffix = null // DistilVectors ///////////////////////////////////////////////////////////// -#define TEST_DISTIL_VECTORS_COMMON \ -std::string sModuleName{"DistilVecs"}; \ -if( pszSuffix ) \ - sModuleName.append( pszSuffix ); \ -std::string sPerambName{"Peramb"}; \ -if( pszSuffix ) \ - sPerambName.append( pszSuffix ); \ -MDistil::DistilVectors::Par DistilVecPar; \ -DistilVecPar.noise = sPerambName + "_noise"; \ -DistilVecPar.perambulator = sPerambName; \ -DistilVecPar.lapevec = "LapEvec"; \ -DistilVecPar.tsrc = 0; \ -if( pszNvec ) \ - DistilVecPar.nvec = pszNvec - -#define TEST_DISTIL_VECTORS_COMMON_END \ -application.createModule(sModuleName,DistilVecPar) - void test_DistilVectors(Application &application, const char * pszSuffix = nullptr, const char * pszNvec = nullptr ) { - TEST_DISTIL_VECTORS_COMMON; - TEST_DISTIL_VECTORS_COMMON_END; -} - -void test_DistilVectorsSS(Application &application, const char * pszSink, const char * pszSource, - const char * pszSuffix = nullptr, const char * pszNvec = nullptr ) -{ - TEST_DISTIL_VECTORS_COMMON; - if( pszSink ) - DistilVecPar.sink = pszSink; - if( pszSource ) - DistilVecPar.source = pszSource; - TEST_DISTIL_VECTORS_COMMON_END; -} - -///////////////////////////////////////////////////////////// -// Multiple Perambulators -///////////////////////////////////////////////////////////// - -void test_MultiPerambulators(Application &application) -{ - test_Perambulators( application, "5" ); - MDistil::PerambFromSolve::Par SolvePar; - SolvePar.eigenPack="LapEvec"; - SolvePar.PerambFileName="Peramb2"; - SolvePar.solve = "Peramb5_unsmeared_sink"; - SolvePar.Distil.nnoise = 1; - SolvePar.Distil.LI=5; - SolvePar.Distil.SI=4; - SolvePar.Distil.TI=8; - SolvePar.nvec=5; - SolvePar.nvec_reduced=2; - SolvePar.LI_reduced=2; - application.createModule("Peramb2",SolvePar); - SolvePar.PerambFileName="Peramb3"; - SolvePar.nvec_reduced=3; - SolvePar.LI_reduced=3; - application.createModule("Peramb3",SolvePar); - - test_DistilVectors( application, "2", "2" ); - test_DistilVectors( application, "3", "3" ); - test_DistilVectors( application, "5", "5" ); - - MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs2_rho"; - A2AMesonFieldPar.right="DistilVecs2_rho"; - A2AMesonFieldPar.output="MesonSinksRho2"; - A2AMesonFieldPar.gammas="Identity"; - A2AMesonFieldPar.mom={"0 0 0"}; - A2AMesonFieldPar.cacheBlock=2; - A2AMesonFieldPar.block=4; - application.createModule("DistilMesonFieldRho2",A2AMesonFieldPar); - A2AMesonFieldPar.left="DistilVecs2_phi"; - A2AMesonFieldPar.right="DistilVecs2_phi"; - A2AMesonFieldPar.output="MesonSinksPhi2"; - application.createModule("DistilMesonFieldPhi2",A2AMesonFieldPar); - A2AMesonFieldPar.left="DistilVecs3_rho"; - A2AMesonFieldPar.right="DistilVecs3_rho"; - A2AMesonFieldPar.output="MesonSinksRho3"; - application.createModule("DistilMesonFieldRho3",A2AMesonFieldPar); - A2AMesonFieldPar.left="DistilVecs3_phi"; - A2AMesonFieldPar.right="DistilVecs3_phi"; - A2AMesonFieldPar.output="MesonSinksPhi3"; - application.createModule("DistilMesonFieldPhi3",A2AMesonFieldPar); - A2AMesonFieldPar.left="DistilVecs5_rho"; - A2AMesonFieldPar.right="DistilVecs5_rho"; - A2AMesonFieldPar.output="MesonSinksRho5"; - application.createModule("DistilMesonFieldRho5",A2AMesonFieldPar); - A2AMesonFieldPar.left="DistilVecs5_phi"; - A2AMesonFieldPar.right="DistilVecs5_phi"; - A2AMesonFieldPar.output="MesonSinksPhi5"; - application.createModule("DistilMesonFieldPhi5",A2AMesonFieldPar); + std::string sModuleName{"DistilVecs"}; + if( pszSuffix ) + sModuleName.append( pszSuffix ); + std::string sPerambName{"Peramb"}; + if( pszSuffix ) + sPerambName.append( pszSuffix ); + MDistil::DistilVectors::Par DistilVecPar; + DistilVecPar.noise = sPerambName + "_noise"; + DistilVecPar.perambulator = sPerambName; + DistilVecPar.lapevec = "LapEvec"; + DistilVecPar.tsrc = 0; + if( pszNvec ) + DistilVecPar.nvec = pszNvec; + application.createModule(sModuleName,DistilVecPar); } ///////////////////////////////////////////////////////////// @@ -330,163 +254,6 @@ void test_MesonField(Application &application, const char * pszFileSuffix, application.createModule(sObjectName, A2AMesonFieldPar); } -///////////////////////////////////////////////////////////// -// g5*unsmeared -///////////////////////////////////////////////////////////// - -#ifdef DISTIL_PRE_RELEASE -void test_g5_sinks(Application &application) -{ - // DistilVectors parameters - MDistil::g5_multiply::Par g5_multiplyPar; - g5_multiplyPar.input="Peramb_unsmeared_sink"; - g5_multiplyPar.nnoise = 1; - g5_multiplyPar.LI=5; - g5_multiplyPar.Ns=4; - g5_multiplyPar.Nt_inv=1; - application.createModule("g5phi",g5_multiplyPar); -} - -///////////////////////////////////////////////////////////// -// BaryonFields - phiphiphi - efficient -///////////////////////////////////////////////////////////// - -void test_BaryonFieldPhi2(Application &application) -{ - // DistilVectors parameters - MDistil::BC2::Par BC2Par; - BC2Par.one="DistilVecs_phi"; - BC2Par.two="DistilVecs_phi"; - BC2Par.three="DistilVecs_phi"; - BC2Par.output="BaryonFieldPhi2"; - BC2Par.parity=1; - BC2Par.mom={"0 0 0"}; - application.createModule("BaryonFieldPhi2",BC2Par); -} -///////////////////////////////////////////////////////////// -// BaryonFields - rhorhorho - efficient -///////////////////////////////////////////////////////////// - -void test_BaryonFieldRho2(Application &application) -{ - // DistilVectors parameters - MDistil::BC2::Par BC2Par; - BC2Par.one="DistilVecs_rho"; - BC2Par.two="DistilVecs_rho"; - BC2Par.three="DistilVecs_rho"; - BC2Par.output="BaryonFieldRho2"; - BC2Par.parity=1; - BC2Par.mom={"0 0 0"}; - application.createModule("BaryonFieldRho2",BC2Par); -} -///////////////////////////////////////////////////////////// -// BaryonFields - phiphiphi -///////////////////////////////////////////////////////////// - -void test_BaryonFieldPhi(Application &application) -{ - // DistilVectors parameters - MDistil::BContraction::Par BContractionPar; - BContractionPar.one="DistilVecs_phi"; - BContractionPar.two="DistilVecs_phi"; - BContractionPar.three="DistilVecs_phi"; - BContractionPar.output="BaryonFieldPhi"; - BContractionPar.parity=1; - BContractionPar.mom={"0 0 0"}; - application.createModule("BaryonFieldPhi",BContractionPar); -} -///////////////////////////////////////////////////////////// -// BaryonFields - rhorhorho -///////////////////////////////////////////////////////////// - -void test_BaryonFieldRho(Application &application) -{ - // DistilVectors parameters - MDistil::BContraction::Par BContractionPar; - BContractionPar.one="DistilVecs_rho"; - BContractionPar.two="DistilVecs_rho"; - BContractionPar.three="DistilVecs_rho"; - BContractionPar.output="BaryonFieldRho"; - BContractionPar.parity=1; - BContractionPar.mom={"0 0 0"}; - application.createModule("BaryonFieldRho",BContractionPar); -} -///////////////////////////////////////////////////////////// -// BaryonContraction -///////////////////////////////////////////////////////////// - -void test_Baryon2pt(Application &application) -{ - // DistilVectors parameters - MDistil::Baryon2pt::Par Baryon2ptPar; - Baryon2ptPar.inputL="BaryonFieldPhi"; - Baryon2ptPar.inputR="BaryonFieldRho"; - Baryon2ptPar.quarksL="uud"; - Baryon2ptPar.quarksR="uud"; - Baryon2ptPar.output="C2_baryon"; - application.createModule("C2_b",Baryon2ptPar); -} -#endif - -///////////////////////////////////////////////////////////// -// emField -///////////////////////////////////////////////////////////// -void test_em(Application &application) -{ - MGauge::StochEm::Par StochEmPar; - StochEmPar.gauge=PhotonR::Gauge::feynman; - StochEmPar.zmScheme=PhotonR::ZmScheme::qedL; - application.createModule("Em",StochEmPar); -} - -///////////////////////////////////////////////////////////// -// MesonA2ASlash -///////////////////////////////////////////////////////////// - -void test_Aslash(Application &application) -{ - // DistilVectors parameters - MContraction::A2AAslashField::Par A2AAslashFieldPar; - A2AAslashFieldPar.left="g5phi"; - //A2AAslashFieldPar.right="DistilVecs_phi"; - A2AAslashFieldPar.right="Peramb_unsmeared_sink"; - A2AAslashFieldPar.output="unsmeared_Aslash"; - A2AAslashFieldPar.emField={"Em"}; - A2AAslashFieldPar.cacheBlock=2; - A2AAslashFieldPar.block=4; - application.createModule("Aslash_field",A2AAslashFieldPar); -} - -///////////////////////////////////////////////////////////// -// MesonA2ASlashSequential -///////////////////////////////////////////////////////////// - -void test_AslashSeq(Application &application) -{ - // DistilVectors parameters - MSolver::A2AAslashVectors::Par A2AAslashVectorsPar; - A2AAslashVectorsPar.vector="PerambS_unsmeared_sink"; - A2AAslashVectorsPar.emField="Em"; - A2AAslashVectorsPar.solver="CG_s"; - A2AAslashVectorsPar.output="AslashSeq"; - application.createModule("Aslash_seq",A2AAslashVectorsPar); -} -///////////////////////////////////////////////////////////// -// Aslash_perambulators -///////////////////////////////////////////////////////////// -void test_PerambulatorsSolve(Application &application) -{ - // Perambulator parameters - MDistil::PerambFromSolve::Par PerambFromSolvePar; - PerambFromSolvePar.eigenPack="LapEvec"; - PerambFromSolvePar.solve="Aslash_seq"; - PerambFromSolvePar.PerambFileName="perambAslashS.bin"; - PerambFromSolvePar.Distil.tsrc = 0; - PerambFromSolvePar.Distil.nnoise = 1; - PerambFromSolvePar.nvec=5; - application.createModule("PerambAslashS",PerambFromSolvePar); -} - bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) { if( bGobbleWhiteSpace ) @@ -515,398 +282,8 @@ bool bNumber( int &ri, const char * & pstr, bool bGobbleWhiteSpace = true ) return true; } -#ifdef DEBUG - -typedef Grid::Hadrons::MDistil::NamedTensor MyTensor; - -template -typename std::enable_if::value && !Grid::Hadrons::MDistil::is_named_tensor::value>::type -DebugShowTensor(T &x, const char * n, std::string * pIndexNames=nullptr) -{ - const MyTensor::Index s{x.size()}; - std::cout << n << ".size() = " << s << std::endl; - std::cout << n << ".NumDimensions = " << x.NumDimensions << " (TensorBase)" << std::endl; - std::cout << n << ".NumIndices = " << x.NumIndices << std::endl; - const auto d{x.dimensions()}; - //std::cout << n << ".dimensions().size() = " << d.size() << std::endl; - std::cout << "Dimensions are "; - for(auto i = 0; i < x.NumDimensions ; i++) - std::cout << "[" << d[i] << "]"; - std::cout << std::endl; - MyTensor::Index SizeCalculated{1}; - std::cout << "Dimensions again"; - for(int i=0 ; i < x.NumDimensions ; i++ ) { - std::cout << " : [" << i; - if( pIndexNames ) - std::cout << ", " << pIndexNames[i]; - std::cout << "]=" << x.dimension(i); - SizeCalculated *= d[i]; - } - std::cout << std::endl; - std::cout << "SizeCalculated = " << SizeCalculated << std::endl;\ - assert( SizeCalculated == s ); - // Initialise - assert( x.NumDimensions == 3 ); - for( int i = 0 ; i < d[0] ; i++ ) - for( int j = 0 ; j < d[1] ; j++ ) - for( int k = 0 ; k < d[2] ; k++ ) { - x(i,j,k) = std::complex(SizeCalculated, -SizeCalculated); - SizeCalculated--; - } - // Show raw data - std::cout << "Data follow : " << std::endl; - typename T::Scalar * p = x.data(); - for( auto i = 0 ; i < s ; i++ ) { - if( i ) std::cout << ", "; - std::cout << n << ".data()[" << i << "]=" << * p++; - } - std::cout << std::endl; -} - -template -typename std::enable_if::value>::type -DebugShowTensor(T &x, const char * n) -{ - DebugShowTensor( x.tensor, n, &x.IndexNames[0] ); -} - -// Test whether typedef and underlying types are the same - -void DebugTestTypeEqualities(void) -{ - Real r1; - RealD r2; - double r3; - const std::type_info &tr1{typeid(r1)}; - const std::type_info &tr2{typeid(r2)}; - const std::type_info &tr3{typeid(r3)}; - if( tr1 == tr2 && tr2 == tr3 ) - std::cout << "r1, r2 and r3 are the same type" << std::endl; - else - std::cout << "r1, r2 and r3 are different types" << std::endl; - std::cout << "r1 is a " << tr1.name() << std::endl; - std::cout << "r2 is a " << tr2.name() << std::endl; - std::cout << "r3 is a " << tr3.name() << std::endl; - - // These are the same - Complex c1; - std::complex c2; - const std::type_info &tc1{typeid(c1)}; - const std::type_info &tc2{typeid(c2)}; - const std::type_info &tc3{typeid(SpinVector::scalar_type)}; - if( tc1 == tc2 && tc2 == tc3) - std::cout << "c1, c2 and SpinVector::scalar_type are the same type" << std::endl; - else - std::cout << "c1, c2 and SpinVector::scalar_type are different types" << std::endl; - std::cout << "c1 is a " << tc1.name() << std::endl; - std::cout << "c2 is a " << tc2.name() << std::endl; - std::cout << "SpinVector::scalar_type is a " << tc3.name() << std::endl; - - // These are the same - SpinVector s1; - iSpinVector s2; - iScalar, Ns> > s3; - const std::type_info &ts1{typeid(s1)}; - const std::type_info &ts2{typeid(s2)}; - const std::type_info &ts3{typeid(s3)}; - if( ts1 == ts2 && ts2 == ts3 ) - std::cout << "s1, s2 and s3 are the same type" << std::endl; - else - std::cout << "s1, s2 and s3 are different types" << std::endl; - std::cout << "s1 is a " << ts1.name() << std::endl; - std::cout << "s2 is a " << ts2.name() << std::endl; - std::cout << "s3 is a " << ts3.name() << std::endl; - - // These are the same - SpinColourVector sc1; - iSpinColourVector sc2; - const std::type_info &tsc1{typeid(sc1)}; - const std::type_info &tsc2{typeid(sc2)}; - if( tsc1 == tsc2 ) - std::cout << "sc1 and sc2 are the same type" << std::endl; - else - std::cout << "sc1 and sc2 are different types" << std::endl; - std::cout << "sc1 is a " << tsc1.name() << std::endl; - std::cout << "sc2 is a " << tsc2.name() << std::endl; -} - -bool DebugEigenTest() -{ - { - Eigen::TensorFixedSize,Eigen::Sizes<3,4,5>> x; - DebugShowTensor(x, "fixed"); - } - const char pszTestFileName[] = "test_tensor.bin"; - std::array as={"Alpha", "Beta", "Gamma"}; - MyTensor x(as, 2,1,4); - DebugShowTensor(x, "x"); - x.write(pszTestFileName); - // Test initialisation of an array of strings - for( auto a : as ) - std::cout << a << std::endl; - Grid::Hadrons::MDistil::NamedTensor p{as,2,7,2}; - DebugShowTensor(p, "p"); - std::cout << "p.IndexNames follow" << std::endl; - for( auto a : p.IndexNames ) - std::cout << a << std::endl; - - // Now see whether we can read a tensor back - std::array Names2={"Alpha", "Gamma", "Delta"}; - MyTensor y(Names2, 2,4,1); - y.read(pszTestFileName); - DebugShowTensor(y, "y"); - - // Now see whether we can read a tensor back from an hdf5 file - const char * pszFileName = "test"; - y.write(pszFileName); - { - MyTensor z; - const char * pszName = "z1"; - DebugShowTensor(z, pszName); - z.read(pszFileName); - DebugShowTensor(z, pszName); - } - { - MyTensor z(Names2,2,0,0); - const char * pszName = "z2"; - DebugShowTensor(z, pszName); - z.read(pszFileName); - DebugShowTensor(z, pszName); - } - { - // Now see whether we can read a tensor back from an xml file - const char * pszXmlName = "test.xml"; - { - XmlWriter w(pszXmlName); - y.write(w); - } - MyTensor z; - const char * pszName = "xml1"; - DebugShowTensor(z, pszName); - XmlReader r(pszXmlName); - z.read(r); - DebugShowTensor(z, pszName); - } - if((0)) // The following tests would fail - { - MyTensor z(Names2,2,0,78); - //std::array NamesBad={"Alpha", "Gamma", "Kilo"}; - //MyTensor z(NamesBad); - const char * pszName = "zFail"; - DebugShowTensor(z, pszName); - z.read(pszFileName); - DebugShowTensor(z, pszName); - } - - // Testing whether typedef produces the same type - yes it does - - DebugTestTypeEqualities(); - std::cout << std::endl; - - // How to access members of SpinColourVector - SpinColourVector sc; - for( int s = 0 ; s < Ns ; s++ ) { - auto cv{sc()(s)}; - iVector c2{sc()(s)}; - std::cout << " cv is a " << typeid(cv).name() << std::endl; - std::cout << " c2 is a " << typeid(c2).name() << std::endl; - for( int c = 0 ; c < Nc ; c++ ) { - Complex & z{cv(c)}; - std::cout << " sc[spin=" << s << ", colour=" << c << "] = " << z << std::endl; - } - } - // We could have removed the Lorentz index independently, but much easier to do as we do above - iVector,Ns> sc2{sc()}; - std::cout << "sc() is a " << typeid(sc()).name() << std::endl; - std::cout << "sc2 is a " << typeid(sc2 ).name() << std::endl; - - // Or you can access elements directly - std::complex z = sc()(0)(0); - std::cout << "z = " << z << std::endl; - sc()(3)(2) = std::complex{3.141,-3.141}; - std::cout << "sc()(3)(2) = " << sc()(3)(2) << std::endl; - - return true; -} - -template -void DebugGridTensorTest_print( int i ) -{ - // std::cout << i << " : " << EigenIO::is_tensor::value - // << ", Rank " << EigenIO::Traits::Rank - // << ", count " << EigenIO::Traits::count - // << std::endl; -} - -// begin() and end() are the minimum necessary to support range-for loops -// should really turn this into an iterator ... -template -class TestObject { -public: - using value_type = T; -private: - value_type * m_p; -public: - TestObject() { - m_p = reinterpret_cast(std::malloc(N * sizeof(value_type))); - } - ~TestObject() { std::free(m_p); } - inline value_type * begin(void) { return m_p; } - inline value_type * end(void) { return m_p + N; } -}; - -template typename std::enable_if::value>::type -dump_tensor(const ET & et, const char * psz = nullptr) { - if( psz ) - std::cout << psz << ": "; - else - std::cout << "Unnamed tensor: "; - Serializable::WriteMember( std::cout, et ); -} - -template -void EigenSliceExample() -{ - std::cout << "Eigen example, Options = " << Options << std::endl; - using T2 = Eigen::Tensor; - T2 a(4, 3); - a.setValues({{0, 100, 200}, {300, 400, 500}, - {600, 700, 800}, {900, 1000, 1100}}); - std::cout << "a\n" << a << std::endl; - dump_tensor( a, "a" ); - Eigen::array offsets = {0, 1}; - Eigen::array extents = {4, 2}; - T2 slice = a.slice(offsets, extents); - std::cout << "slice\n" << slice << std::endl; - dump_tensor( slice, "slice" ); - std::cout << "\n========================================" << std::endl; -} - -template -void EigenSliceExample2() -{ - using TestScalar = std::complex; - using T3 = Eigen::Tensor; - using T2 = Eigen::Tensor; - T3 a(2,3,4); - - std::cout << "Initialising a:"; - TestScalar f{ 0 }; - const TestScalar Inc{ 1, -1 }; - for( auto &c : a ) { - c = f; - f += Inc; - } - std::cout << std::endl; - std::cout << "Validating a (Eigen::" << ( ( Options & Eigen::RowMajor ) ? "Row" : "Col" ) << "Major):" << std::endl; - f = 0; - for( int i = 0 ; i < a.dimension(0) ; i++ ) - for( int j = 0 ; j < a.dimension(1) ; j++ ) - for( int k = 0 ; k < a.dimension(2) ; k++ ) { - std::cout << " a(" << i << "," << j << "," << k << ")=" << a(i,j,k) << std::endl; - assert( ( Options & Eigen::RowMajor ) == 0 || a(i,j,k) == f ); - f += Inc; - } - //std::cout << std::endl; - //std::cout << "a initialised to:\n" << a << std::endl; - dump_tensor( a, "a" ); - std::cout << std::endl; - Eigen::array offsets = {0,1,1}; - Eigen::array extents = {1,2,2}; - T3 b; - b = a.slice( offsets, extents );//.reshape(NewExtents); - std::cout << "b = a.slice( offsets, extents ):\n" << b << std::endl; - dump_tensor( b, "b" ); - T2 c(3,4); - c = a.chip(0,1); - std::cout << "c = a.chip(0,0):\n" << c << std::endl; - dump_tensor( c, "c" ); - //T2 d = b.reshape(extents); - //std::cout << "b.reshape(extents) is:\n" << d << std::endl; - std::cout << "\n========================================" << std::endl; -} - -void DebugFelixTensorTest( void ) -{ - unsigned int Nmom = 2; - unsigned int Nt = 2; - unsigned int N_1 = 2; - unsigned int N_2 = 2; - unsigned int N_3 = 2; - using BaryonTensorSet = Eigen::Tensor; - BaryonTensorSet BField3(Nmom,4,Nt,N_1,N_2,N_3); - std::vector Memory(Nmom * Nt * N_1 * N_2 * N_3 * 2); - using BaryonTensorMap = Eigen::TensorMap; - BaryonTensorMap BField4 (&Memory[0], Nmom,4,Nt,N_1,N_2,N_3); - - EigenSliceExample(); - EigenSliceExample<0>(); - EigenSliceExample2(); - EigenSliceExample2<0>(); -} - -bool DebugGridTensorTest( void ) -{ - DebugFelixTensorTest(); - typedef Complex t1; - typedef iScalar t2; - typedef iVector t3; - typedef iMatrix t4; - typedef iVector,4> t5; - typedef iScalar t6; - typedef iMatrix t7; - typedef iMatrix,4>,2> t8; - int i = 1; - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - DebugGridTensorTest_print( i++ ); - - //using TOC7 = TestObject, 7>; - using TOC7 = t7; - TOC7 toc7; - constexpr std::complex Inc{1,-1}; - std::complex Start{Inc}; - for( auto &x : toc7 ) { - x = Start; - Start += Inc; - } - i = 0; - std::cout << "toc7:"; - for( auto x : toc7 ) std::cout << " [" << i++ << "]=" << x; - std::cout << std::endl; - - //t2 o2; - //auto a2 = TensorRemove(o2); - //t3 o3; - //t4 o4; - //auto a3 = TensorRemove(o3); - //auto a4 = TensorRemove(o4); - - return true; -} - -bool ConvertPeramb(const char * pszSource, const char * pszDest) { - Grid::Hadrons::MDistil::PerambTensor p(Hadrons::MDistil::PerambIndexNames); - p.ReadBinary( pszSource ); - p.write(pszDest); - return true; -} -#endif - int main(int argc, char *argv[]) { -#ifdef DEBUG - // Debug only - test of Eigen::Tensor - //if( DebugEigenTest() ) return 0; - //if(DebugGridTensorTest()) return 0; - //if(ConvertPeramb("PerambL_100_tsrc0.3000","PerambL_100_tsrc0.3000")) return 0; -#endif - // Decode command-line parameters. 1st one is which test to run int iTestNum = -1; @@ -948,31 +325,10 @@ int main(int argc, char *argv[]) // For now perform free propagator test - replace this with distillation test(s) LOG(Message) << "====== Creating xml for test " << iTestNum << " ======" << std::endl; - //const unsigned int nt = GridDefaultLatt()[Tp]; switch(iTestNum) { - case 0: - test_Global( application ); - test_LapEvec( application ); - break; - case 1: - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application ); - break; - default: // 2 - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application ); - test_DistilVectors( application ); - break; - case 3: - test_Global( application ); - test_LapEvec( application ); - test_LoadPerambulators( application ); - test_DistilVectors( application ); - break; - case 4: + default: // 0 + LOG(Message) << "Computing Meson 2pt-function" << std::endl; test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); @@ -980,7 +336,17 @@ int main(int argc, char *argv[]) test_MesonField( application, "Phi", "_phi" ); test_MesonField( application, "Rho", "_rho" ); break; - case 5: + case 1: + LOG(Message) << "Computing Meson 2pt-function by loading perambulators" << std::endl; + test_Global( application ); + test_LapEvec( application ); + test_LoadPerambulators( application ); + test_DistilVectors( application ); + test_MesonField( application, "Phi", "_phi" ); + test_MesonField( application, "Rho", "_rho" ); + break; + case 2: + LOG(Message) << "Computing Meson 2pt-function for two quark flavours" << std::endl; test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); @@ -990,69 +356,13 @@ int main(int argc, char *argv[]) test_MesonField( application, "SPhi", "S_phi" ); test_MesonField( application, "SRho", "S_rho" ); break; -#ifdef DISTIL_PRE_RELEASE - case 6: // 3 + case 3: + LOG(Message) << "Computing Meson 2pt-function with current insertion" << std::endl; test_Global( application ); test_LapEvec( application ); test_Perambulators( application ); - test_g5_sinks( application ); test_MesonSink( application ); break; - case 7: // 3 - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application ); - test_DistilVectors( application ); - test_BaryonFieldPhi( application ); - test_BaryonFieldRho( application ); - break; -#endif - case 8: // 3 - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application ); - test_DistilVectors( application ); - test_MesonField( application, "Phi", "_phi" ); - test_MesonField( application, "Rho", "_rho" ); - break; -#ifdef DISTIL_PRE_RELEASE - case 9: // 3 - test_Global( application ); - test_Solver( application ); - test_Baryon2pt( application ); - break; - case 10: // 3 - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application ); - test_g5_sinks( application ); - test_em( application ); - test_Aslash( application ); - break; - case 11: // 3 - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application ); - test_DistilVectors( application ); - test_BaryonFieldPhi2( application ); - test_BaryonFieldRho2( application ); - break; -#endif - case 12: // 3 - test_Global( application ); - test_LapEvec( application ); - test_Perambulators( application, "S" ); - test_em( application ); - test_AslashSeq( application ); - test_PerambulatorsSolve( application ); - test_DistilVectorsSS( application, "AslashSeq", nullptr, "S" ); - test_MesonField( application, "AslashSeq" ); - break; - case 13: - test_Global( application ); - test_LapEvec( application ); - test_MultiPerambulators( application ); - break; } // execution static const char XmlFileName[] = "test_distil.xml"; From ada0a7a83b528f2db142997608a38e7c20aa7c67 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 1 Nov 2019 16:05:08 +0000 Subject: [PATCH 301/347] C++11 case comparison of named tensor index names --- Hadrons/Distil.hpp | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index e658025c..3d27b9f1 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -299,7 +299,7 @@ public: Grid::SliceShare( gridLowDim, gridHighDim, tensor.data(), (int) (tensor.size() * sizeof(Scalar_))); } - bool ValidateIndexNames( int iNumNames, const std::string * MatchNames ) const; + bool ValidateIndexNames( std::size_t NumNames, const std::string * MatchNames ) const; // Read/Write in any format template inline void read (Reader &r, const char * pszTag = nullptr); @@ -307,23 +307,6 @@ public: // Read/Write in default format, i.e. HDF5 if present, else binary inline void read (const char * filename, const char * pszTag = nullptr); inline void write(const char * filename, const char * pszTag = nullptr) const; - - // Case insensitive compare of two strings - // Pesumably this exists already? Where should this go? - static inline bool CompareCaseInsensitive( const std::string &s1, const std::string &s2 ) { - auto Len = s1.size(); - bool bSame{ Len == s2.size() }; - for( int j = 0; bSame && j < Len; j++ ) { - wchar_t c1 = s1[j]; - if( c1 >= 'a' && c1 <= 'z' ) - c1 -= 'a' - 'A'; - wchar_t c2 = s2[j]; - if( c2 >= 'a' && c1 <= 'z' ) - c2 -= 'a' - 'A'; - bSame = ( c1 == c2 ); - } - return bSame; - } }; // Is this a named tensor @@ -365,10 +348,14 @@ void NamedTensor::write(const char * filename, const char ******************************************************************************/ template -bool NamedTensor::ValidateIndexNames( int iNumNames, const std::string * MatchNames ) const { - bool bSame{ iNumNames == NumIndices_ && IndexNames.size() == NumIndices_ }; - for( int i = 0; bSame && i < NumIndices_; i++ ) - bSame = CompareCaseInsensitive( MatchNames[i], IndexNames[i] ); +bool NamedTensor::ValidateIndexNames( std::size_t NumNames, const std::string * MatchNames ) const { + bool bSame{ NumNames == NumIndices_ && IndexNames.size() == NumIndices_ }; + for( std::size_t i = 0; bSame && i < NumIndices_; i++ ) + { + bSame = MatchNames[i].size() == IndexNames[i].size() + && std::equal( MatchNames[i].begin(), MatchNames[i].end(), IndexNames[i].begin(), + [](const char & c1, const char & c2){ return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); + } return bSame; } From 52d8d576d05c738a9030dab26cddb50f6f155bc8 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 1 Nov 2019 20:10:51 +0000 Subject: [PATCH 302/347] Removed SliceShare as a reusable routine --- Hadrons/Distil.hpp | 154 ++--------------------- Hadrons/Modules/MDistil/LapEvec.hpp | 61 +++++++++ Hadrons/Modules/MDistil/Perambulator.hpp | 45 +++++-- 3 files changed, 107 insertions(+), 153 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 3d27b9f1..9d4423ad 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -38,136 +38,13 @@ #include #include -/****************************************************************************** - This potentially belongs in CartesianCommunicator - ******************************************************************************/ - -BEGIN_MODULE_NAMESPACE(Grid) -inline void SliceShare( GridBase * gridLowDim, GridBase * gridHighDim, void * Buffer, int BufferSize ) -{ - // Work out which dimension is the spread-out dimension - assert(gridLowDim); - assert(gridHighDim); - const int iNumDims{(const int)gridHighDim->_gdimensions.size()}; - assert(iNumDims == gridLowDim->_gdimensions.size()); - int dimSpreadOut = -1; - Coordinate coor(iNumDims); - for( int i = 0 ; i < iNumDims ; i++ ) { - coor[i] = gridHighDim->_processor_coor[i]; - if( gridLowDim->_gdimensions[i] != gridHighDim->_gdimensions[i] ) { - assert( dimSpreadOut == -1 ); - assert( gridLowDim->_processors[i] == 1 ); // easiest assumption to make for now - dimSpreadOut = i; - } - } - if( dimSpreadOut != -1 && gridHighDim->_processors[dimSpreadOut] != gridLowDim->_processors[dimSpreadOut] ) { - // Make sure the same number of data elements exist on each slice - const int NumSlices{gridHighDim->_processors[dimSpreadOut] / gridLowDim->_processors[dimSpreadOut]}; - assert(gridHighDim->_processors[dimSpreadOut] == gridLowDim->_processors[dimSpreadOut] * NumSlices); - const int SliceSize{BufferSize/NumSlices}; - //CCC_DEBUG_DUMP(Buffer, NumSlices, SliceSize); - assert(BufferSize == SliceSize * NumSlices); -//#ifndef USE_LOCAL_SLICES -// assert(0); // Can't do this without MPI (should really test whether MPI is defined) -//#else - const auto MyRank = gridHighDim->ThisRank(); - std::vector reqs(0); - int MySlice{coor[dimSpreadOut]}; - char * const _buffer{(char *)Buffer}; - char * const MyData{_buffer + MySlice * SliceSize}; - for(int i = 1; i < NumSlices ; i++ ){ - int SendSlice = ( MySlice + i ) % NumSlices; - int RecvSlice = ( MySlice - i + NumSlices ) % NumSlices; - char * const RecvData{_buffer + RecvSlice * SliceSize}; - coor[dimSpreadOut] = SendSlice; - const auto SendRank = gridHighDim->RankFromProcessorCoor(coor); - coor[dimSpreadOut] = RecvSlice; - const auto RecvRank = gridHighDim->RankFromProcessorCoor(coor); - std::cout << GridLogMessage << "Send slice " << MySlice << " (" << MyRank << ") to " << SendSlice << " (" << SendRank - << "), receive slice from " << RecvSlice << " (" << RecvRank << ")" << std::endl; - gridHighDim->SendToRecvFromBegin(reqs,MyData,SendRank,RecvData,RecvRank,SliceSize); - //memcpy(RecvData,MyData,SliceSize); // Debug - } - gridHighDim->SendToRecvFromComplete(reqs); - std::cout << GridLogMessage << "Slice data shared." << std::endl; - //CCC_DEBUG_DUMP(Buffer, NumSlices, SliceSize); -//#endif - } -} - -/************************************************************************************* - - Not sure where the right home for this is? But presumably in Grid - - -Grad^2 (Peardon, 2009, pg 2, equation 3, https://arxiv.org/abs/0905.2160) - Field Type of field the operator will be applied to - GaugeField Gauge field the operator will smear using - - *************************************************************************************/ - -template -class Laplacian3D : public LinearOperatorBase, public LinearFunction { - typedef typename GaugeField::vector_type vCoeff_t; -protected: // I don't really mind if _gf is messed with ... so make this public? - //GaugeField & _gf; - int nd; // number of spatial dimensions - std::vector > > U; -public: - // Construct this operator given a gauge field and the number of dimensions it should act on - Laplacian3D( GaugeField& gf, int dimSpatial = Tdir ) : /*_gf(gf),*/ nd{dimSpatial} { - assert(dimSpatial>=1); - for( int mu = 0 ; mu < nd ; mu++ ) - U.push_back(PeekIndex(gf,mu)); - } - - // Apply this operator to "in", return result in "out" - void operator()(const Field& in, Field& out) { - assert( nd <= in.Grid()->Nd() ); - conformable( in, out ); - out = ( ( Real ) ( 2 * nd ) ) * in; - Field _tmp(in.Grid()); - typedef typename GaugeField::vector_type vCoeff_t; - //Lattice > U(in.Grid()); - for( int mu = 0 ; mu < nd ; mu++ ) { - //U = PeekIndex(_gf,mu); - out -= U[mu] * Cshift( in, mu, 1); - _tmp = adj( U[mu] ) * in; - out -= Cshift(_tmp,mu,-1); - } - } - - void OpDiag (const Field &in, Field &out) { assert(0); }; - void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }; - void Op (const Field &in, Field &out) { assert(0); }; - void AdjOp (const Field &in, Field &out) { assert(0); }; - void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2) { assert(0); }; - void HermOp(const Field &in, Field &out) { operator()(in,out); }; -}; - -template -class Laplacian3DHerm : public LinearFunction { -public: - OperatorFunction & _poly; - LinearOperatorBase &_Linop; - - Laplacian3DHerm(OperatorFunction & poly,LinearOperatorBase& linop) - : _poly{poly}, _Linop{linop} {} - - void operator()(const Field& in, Field& out) { - _poly(_Linop,in,out); - } -}; - -END_MODULE_NAMESPACE // Grid +BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** Common elements for distillation ******************************************************************************/ -BEGIN_HADRONS_NAMESPACE - -BEGIN_MODULE_NAMESPACE(MDistil) - using LapEvecs = Grid::Hadrons::EigenPack; // Noise vector index order: nnoise, nt, nvec, ns @@ -182,7 +59,7 @@ struct DistilParameters: Serializable { std::string, SI ) DistilParameters() = default; template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} - + // Numeric parameter is allowed to be empty (in which case it = Default), // but assert during setup() if specified but not numeric @@ -213,7 +90,7 @@ const int Nt_inv{ full_tdil ? 1 : TI } /****************************************************************************** Default for distillation file operations. For now only used by NamedTensor -******************************************************************************/ + ******************************************************************************/ #ifdef HAVE_HDF5 using Default_Reader = Grid::Hdf5Reader; @@ -235,7 +112,7 @@ static const char * FileExtension = ".dat"; Configuration number Noise unique string Distillation parameters - + ******************************************************************************/ template @@ -271,7 +148,7 @@ public: assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); return tensor.operator()(std::array{{firstDimension, otherDimensions...}}); } - + // Construct a named tensor explicitly specifying size of each dimension template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) @@ -281,8 +158,8 @@ public: assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); for( int i = 0; i < NumIndices_; i++ ) IndexNames[i] = IndexNames_[i]; - } - + } + // Default constructor (assumes tensor will be loaded from file) EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor() : IndexNames{NumIndices_} {} @@ -292,15 +169,10 @@ public: { for( int i = 0; i < NumIndices_; i++ ) IndexNames[i] = IndexNames_[i]; - } + } - // Share data for timeslices we calculated with other nodes - inline void SliceShare( GridCartesian * gridLowDim, GridCartesian * gridHighDim ) { - Grid::SliceShare( gridLowDim, gridHighDim, tensor.data(), (int) (tensor.size() * sizeof(Scalar_))); - } - bool ValidateIndexNames( std::size_t NumNames, const std::string * MatchNames ) const; - + // Read/Write in any format template inline void read (Reader &r, const char * pszTag = nullptr); template inline void write(Writer &w, const char * pszTag = nullptr) const; @@ -330,7 +202,7 @@ template void NamedTensor::write(Writer &w, const char * pszTag)const{ if( pszTag == nullptr ) pszTag = "NamedTensor"; - LOG(Message) << "Writing NamedTensor to tag " << pszTag << std::endl; + LOG(Message) << "Writing NamedTensor to tag " << pszTag << std::endl; write(w, pszTag, *this); } @@ -353,8 +225,8 @@ bool NamedTensor::ValidateIndexNames( std::size_t NumNames for( std::size_t i = 0; bSame && i < NumIndices_; i++ ) { bSame = MatchNames[i].size() == IndexNames[i].size() - && std::equal( MatchNames[i].begin(), MatchNames[i].end(), IndexNames[i].begin(), - [](const char & c1, const char & c2){ return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); + && std::equal( MatchNames[i].begin(), MatchNames[i].end(), IndexNames[i].begin(), + [](const char & c1, const char & c2){ return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); } return bSame; } diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 101afedf..5ac8e258 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -186,6 +186,67 @@ void TLapEvec::Cleanup(void) gridHD = nullptr; } +/************************************************************************************* + + -Grad^2 (Peardon, 2009, pg 2, equation 3, https://arxiv.org/abs/0905.2160) + Field Type of field the operator will be applied to + GaugeField Gauge field the operator will smear using + + *************************************************************************************/ + +template +class Laplacian3D : public LinearOperatorBase, public LinearFunction { + typedef typename GaugeField::vector_type vCoeff_t; +protected: // I don't really mind if _gf is messed with ... so make this public? + //GaugeField & _gf; + int nd; // number of spatial dimensions + std::vector > > U; +public: + // Construct this operator given a gauge field and the number of dimensions it should act on + Laplacian3D( GaugeField& gf, int dimSpatial = Tdir ) : /*_gf(gf),*/ nd{dimSpatial} { + assert(dimSpatial>=1); + for( int mu = 0 ; mu < nd ; mu++ ) + U.push_back(PeekIndex(gf,mu)); + } + + // Apply this operator to "in", return result in "out" + void operator()(const Field& in, Field& out) { + assert( nd <= in.Grid()->Nd() ); + conformable( in, out ); + out = ( ( Real ) ( 2 * nd ) ) * in; + Field _tmp(in.Grid()); + typedef typename GaugeField::vector_type vCoeff_t; + //Lattice > U(in.Grid()); + for( int mu = 0 ; mu < nd ; mu++ ) { + //U = PeekIndex(_gf,mu); + out -= U[mu] * Cshift( in, mu, 1); + _tmp = adj( U[mu] ) * in; + out -= Cshift(_tmp,mu,-1); + } + } + + void OpDiag (const Field &in, Field &out) { assert(0); }; + void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }; + void Op (const Field &in, Field &out) { assert(0); }; + void AdjOp (const Field &in, Field &out) { assert(0); }; + void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2) { assert(0); }; + void HermOp(const Field &in, Field &out) { operator()(in,out); }; +}; + +template +class Laplacian3DHerm : public LinearFunction { +public: + OperatorFunction & _poly; + LinearOperatorBase &_Linop; + + Laplacian3DHerm(OperatorFunction & poly,LinearOperatorBase& linop) + : _poly{poly}, _Linop{linop} {} + + void operator()(const Field& in, Field& out) { + _poly(_Linop,in,out); + } +}; + /****************************************************************************** Calculate low-mode eigenvalues of the Laplacian ******************************************************************************/ diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 6a4deca1..86740283 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -33,12 +33,11 @@ #include BEGIN_HADRONS_NAMESPACE - +BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** * Perambulator * ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) class PerambulatorPar: Serializable { @@ -254,20 +253,43 @@ void TPerambulator::execute(void) } } } - LOG(Message) << "perambulator done" << std::endl; - perambulator.SliceShare( grid3d, grid4d ); - - if(grid4d->IsBoss()) + // Now share my timeslice data with other members of the grid + const int NumSlices{grid4d->_processors[Tdir] / grid3d->_processors[Tdir]}; + if (NumSlices > 1) { - std::string sPerambName{par().PerambFileName}; - if( sPerambName.length() == 0 ) + LOG(Debug) << "Sharing perambulator data with other nodes" << std::endl; + const int MySlice {grid4d->_processor_coor[Tdir]}; + const int SliceCount {static_cast(perambulator.tensor.size()/NumSlices)}; + PerambTensor::Scalar * const MyData {perambulator.tensor.data()+MySlice*SliceCount}; + Coordinate coor(Nd); + for (int i = 0 ; i < Tdir ; i++) coor[i] = grid4d->_processor_coor[i]; + std::vector reqs(0); + for (int i = 1; i < NumSlices ; i++) + { + coor[Tdir] = (MySlice+i)%NumSlices; + const int SendRank { grid4d->RankFromProcessorCoor(coor) }; + const int RecvSlice { ( MySlice - i + NumSlices ) % NumSlices }; + coor[Tdir] = RecvSlice; + const auto RecvRank = grid4d->RankFromProcessorCoor(coor); + grid4d->SendToRecvFromBegin(reqs,MyData,SendRank, perambulator.tensor.data() + + RecvSlice*SliceCount,RecvRank,SliceCount*sizeof(PerambTensor::Scalar)); + } + grid4d->SendToRecvFromComplete(reqs); + } + + // Save the perambulator to disk from the boss node + if (grid4d->IsBoss()) + { + std::string sPerambName {par().PerambFileName}; + if (sPerambName.empty()) sPerambName = getName(); - sPerambName.append( "." ); - sPerambName.append( std::to_string(vm().getTrajectory())); + sPerambName.append("."); + sPerambName.append(std::to_string(vm().getTrajectory())); perambulator.write(sPerambName.c_str()); } - if( !UnsmearedSinkFileName.empty() ) + //Save the unsmeared sinks if filename specified + if (!UnsmearedSinkFileName.empty()) { bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); LOG(Message) << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; @@ -276,7 +298,6 @@ void TPerambulator::execute(void) } END_MODULE_NAMESPACE - END_HADRONS_NAMESPACE #endif // Hadrons_MDistil_Perambulator_hpp_ From 1c10933db1e8200edcc9996ec49a6c21ab993ea3 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 2 Nov 2019 14:58:32 +0000 Subject: [PATCH 303/347] Rationalisation of NamedTensor (Perambulator) --- Hadrons/Distil.hpp | 393 ++++++-------------- Hadrons/Modules/MDistil/DistilCommon.hpp | 143 +++++++ Hadrons/Modules/MDistil/DistilVectors.hpp | 17 +- Hadrons/Modules/MDistil/LapEvec.hpp | 9 +- Hadrons/Modules/MDistil/Noises.hpp | 13 +- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 19 +- Hadrons/Modules/MDistil/Perambulator.cc | 11 + Hadrons/Modules/MDistil/Perambulator.hpp | 6 +- Hadrons/Modules/MIO/LoadPerambulator.hpp | 10 +- tests/IO/Test_serialisation.cc | 37 ++ 10 files changed, 324 insertions(+), 334 deletions(-) create mode 100644 Hadrons/Modules/MDistil/DistilCommon.hpp diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index 9d4423ad..ef2b3cbf 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -2,13 +2,13 @@ Grid physics library, www.github.com/paboyle/Grid - Source file: Hadrons/Modules/MDistil/Distil.hpp - + Source file: Hadrons/Distil.hpp + Copyright (C) 2015-2019 Author: Felix Erben Author: Michael Marshall - + 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 @@ -27,295 +27,132 @@ *************************************************************************************/ /* END LEGAL */ -#ifndef Hadrons_MDistil_Distil_hpp_ -#define Hadrons_MDistil_Distil_hpp_ +#ifndef Hadrons_Distil_hpp_ +#define Hadrons_Distil_hpp_ #include -#include -#include -#include #include -#include -#include BEGIN_HADRONS_NAMESPACE -BEGIN_MODULE_NAMESPACE(MDistil) + +/****************************************************************************** + NamedTensor + This is an Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) + They can be persisted to disk in tag Name_, and IndexNames are validated on load. + TODO: WHAT TO SAVE / VALIDATE ON LOAD (Override to warn instead of assert on load) + Ensemble string + Configuration number + Noise unique string + Distillation parameters + ******************************************************************************/ + +extern const std::string NamedTensorFileExtension; + +template &IndexNames_> +class NamedTensor : Serializable +{ +public: + using Scalar = Scalar_; + static constexpr int NumIndices = NumIndices_; + using ET = Eigen::Tensor; + using Index = typename ET::Index; + GRID_SERIALIZABLE_CLASS_MEMBERS(NamedTensor, + ET, tensor, + std::vector, IndexNames ); + + // Get the default index names as std::vector + std::vector DefaultIndexNames() + { + std::vector names{NumIndices_}; + for (std::size_t i = 0; i < NumIndices_; i++) + names[i] = IndexNames_[i]; + return names; + } + + // Default constructor (assumes tensor will be loaded from file) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor() : IndexNames{DefaultIndexNames()} {} + + // Construct a named tensor explicitly specifying size of each dimension + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(Eigen::Index firstDimension, IndexTypes... otherDimensions) + : tensor(firstDimension, otherDimensions...), IndexNames{DefaultIndexNames()} + { + assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); + } + + // Do my index names match the default for my type? + bool ValidateIndexNames() const + { + bool bSame{ IndexNames.size() == NumIndices_ }; + for( std::size_t i = 0; bSame && i < NumIndices_; i++ ) + { + bSame = IndexNames[i].size() == IndexNames_[i].size() + && std::equal( IndexNames[i].begin(), IndexNames[i].end(), IndexNames_[i].begin(), + [](const char & c1, const char & c2){ return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); + } + return bSame; + } + +#ifdef HAVE_HDF5 + using Default_Reader = Grid::Hdf5Reader; + using Default_Writer = Grid::Hdf5Writer; +#else + using Default_Reader = Grid::BinaryReader; + using Default_Writer = Grid::BinaryWriter; +#endif + + template void write(Writer &w, const std::string &Tag = Name_) const + { write(w, Tag, *this); } + + inline void write(const std::string &filename, const std::string &Tag = Name_) const + { + std::string sFileName{filename}; + sFileName.append( NamedTensorFileExtension ); + LOG(Message) << "Writing " << Name_ << " to file " << sFileName << " tag " << Tag << std::endl; + Default_Writer w( sFileName ); + write( w, Tag ); + } + + // Read and validate index names + template void read(Reader &r, bool bValidate = true, const std::string &Tag = Name_) + { + // Grab index names and dimensions + std::vector OldIndexNames{std::move(IndexNames)}; + typename ET::Dimensions OldDimensions{tensor.dimensions()}; + read(r, Tag, *this); + const typename ET::Dimensions & NewDimensions{tensor.dimensions()}; + for (int i = 0; i < NumIndices_; i++) + assert(OldDimensions[i] == 0 || OldDimensions[i] == NewDimensions[i] && "NamedTensor::read dimension size"); + if (bValidate) + assert(ValidateIndexNames() && "NamedTensor::read dimension name"); + } + + inline void read (const std::string &filename, bool bValidate = true, const std::string &Tag = Name_) + { + std::string sFileName{filename}; + sFileName.append( NamedTensorFileExtension ); + LOG(Message) << "Reading " << Name_ << " from file " << sFileName << " tag " << Tag << std::endl; + Default_Reader r(sFileName); + read(r, bValidate, Tag); + } +}; /****************************************************************************** Common elements for distillation ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + +//Eigenvectors of the Laplacian using LapEvecs = Grid::Hadrons::EigenPack; -// Noise vector index order: nnoise, nt, nvec, ns +// Noise vector (index order: nnoise, nt, nvec, ns) using NoiseTensor = Eigen::Tensor; -struct DistilParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, - int, nnoise, - int, tsrc, - std::string, TI, - std::string, LI, - std::string, SI ) - DistilParameters() = default; - template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} - - // Numeric parameter is allowed to be empty (in which case it = Default), - // but assert during setup() if specified but not numeric - - static int ParameterDefault( const std::string & s, int Default, bool bCalledFromSetup ) - { - int i = Default; - if( s.length() > 0 ) { - std::istringstream ss( s ); - ss >> i; - if( bCalledFromSetup ) - assert( !ss.fail() && "Parameter should either be empty or integer" ); - } - return i; - } -}; - -#define DISTIL_PARAMETERS_DEFINE( inSetup ) \ -const int Nt{env().getDim(Tdir)}; \ -const int nvec{par().nvec}; \ -const int nnoise{par().Distil.nnoise}; \ -const int tsrc{par().Distil.tsrc}; \ -const int TI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.TI, Nt, inSetup)}; \ -const int LI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.LI, nvec, inSetup)}; \ -const int SI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.SI, Ns, inSetup)}; \ -const bool full_tdil{ TI == Nt }; \ -const bool exact_distillation{ full_tdil && LI == nvec }; \ -const int Nt_inv{ full_tdil ? 1 : TI } - -/****************************************************************************** - Default for distillation file operations. For now only used by NamedTensor - ******************************************************************************/ - -#ifdef HAVE_HDF5 -using Default_Reader = Grid::Hdf5Reader; -using Default_Writer = Grid::Hdf5Writer; -static const char * FileExtension = ".h5"; -#else -using Default_Reader = Grid::BinaryReader; -using Default_Writer = Grid::BinaryWriter; -static const char * FileExtension = ".dat"; -#endif - -/****************************************************************************** - NamedTensor object - This is an Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) - They can be persisted to disk - IndexNames contains one name for each index, and IndexNames are validated on load. - WHAT TO SAVE / VALIDATE ON LOAD (Override to warn instead of assert on load) - Ensemble string - Configuration number - Noise unique string - Distillation parameters - - ******************************************************************************/ - -template -class NamedTensor : Serializable -{ -public: - using Scalar = Scalar_; - static constexpr int NumIndices = NumIndices_; - using ET = Eigen::Tensor; - using Index = typename ET::Index; - GRID_SERIALIZABLE_CLASS_MEMBERS(NamedTensor - , ET, tensor - , std::vector, IndexNames - ); - // Named tensors are intended to be a superset of Eigen tensor - inline operator ET&() { return tensor; } - template - inline const Scalar_& operator()(const std::array &Indices) const - { return tensor.operator()(Indices); } - inline Scalar_& operator()(const std::array &Indices) - { return tensor.operator()(Indices); } - template - inline const Scalar_& operator()(Eigen::Index firstDimension, IndexTypes... otherDimensions) const - { - // The number of indices used to access a tensor coefficient must be equal to the rank of the tensor. - assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); - return tensor.operator()(std::array{{firstDimension, otherDimensions...}}); - } - template - inline Scalar_& operator()(Eigen::Index firstDimension, IndexTypes... otherDimensions) - { - // The number of indices used to access a tensor coefficient must be equal to the rank of the tensor. - assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); - return tensor.operator()(std::array{{firstDimension, otherDimensions...}}); - } - - // Construct a named tensor explicitly specifying size of each dimension - template - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::array &IndexNames_, Eigen::Index firstDimension, IndexTypes... otherDimensions) - : tensor(firstDimension, otherDimensions...), IndexNames{NumIndices} - { - // The number of dimensions used to construct a tensor must be equal to the rank of the tensor. - assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); - for( int i = 0; i < NumIndices_; i++ ) - IndexNames[i] = IndexNames_[i]; - } - - // Default constructor (assumes tensor will be loaded from file) - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor() : IndexNames{NumIndices_} {} - - // Construct a named tensor without specifying size of each dimension (because it will be loaded from file) - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::array &IndexNames_) - : IndexNames{NumIndices_} - { - for( int i = 0; i < NumIndices_; i++ ) - IndexNames[i] = IndexNames_[i]; - } - - bool ValidateIndexNames( std::size_t NumNames, const std::string * MatchNames ) const; - - // Read/Write in any format - template inline void read (Reader &r, const char * pszTag = nullptr); - template inline void write(Writer &w, const char * pszTag = nullptr) const; - // Read/Write in default format, i.e. HDF5 if present, else binary - inline void read (const char * filename, const char * pszTag = nullptr); - inline void write(const char * filename, const char * pszTag = nullptr) const; -}; - -// Is this a named tensor -template struct is_named_tensor : public std::false_type {}; -template struct is_named_tensor> : public std::true_type {}; -template struct is_named_tensor, T>::value>::type> : public std::true_type {}; - -/****************************************************************************** - PerambTensor object - ******************************************************************************/ - -using PerambTensor = NamedTensor; -static const std::array PerambIndexNames{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; - -/****************************************************************************** - Write NamedTensor - ******************************************************************************/ - -template -template -void NamedTensor::write(Writer &w, const char * pszTag)const{ - if( pszTag == nullptr ) - pszTag = "NamedTensor"; - LOG(Message) << "Writing NamedTensor to tag " << pszTag << std::endl; - write(w, pszTag, *this); -} - -template -void NamedTensor::write(const char * filename, const char * pszTag)const{ - std::string sFileName{filename}; - sFileName.append( MDistil::FileExtension ); - LOG(Message) << "Writing NamedTensor to file " << sFileName << std::endl; - MDistil::Default_Writer w( sFileName ); - write( w, pszTag ); -} - -/****************************************************************************** - Validate named tensor index names - ******************************************************************************/ - -template -bool NamedTensor::ValidateIndexNames( std::size_t NumNames, const std::string * MatchNames ) const { - bool bSame{ NumNames == NumIndices_ && IndexNames.size() == NumIndices_ }; - for( std::size_t i = 0; bSame && i < NumIndices_; i++ ) - { - bSame = MatchNames[i].size() == IndexNames[i].size() - && std::equal( MatchNames[i].begin(), MatchNames[i].end(), IndexNames[i].begin(), - [](const char & c1, const char & c2){ return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); - } - return bSame; -} - -/****************************************************************************** - Read NamedTensor - ******************************************************************************/ - -template -template -void NamedTensor::read(Reader &r, const char * pszTag) { - if( pszTag == nullptr ) - pszTag = "NamedTensor"; - // Grab index names and dimensions - std::vector OldIndexNames{std::move(IndexNames)}; - typename ET::Dimensions OldDimensions{tensor.dimensions()}; - LOG(Message) << "Reading NamedTensor from tag " << pszTag << std::endl; - read(r, pszTag, *this); - const typename ET::Dimensions & NewDimensions{tensor.dimensions()}; - for( int i = 0; i < NumIndices_; i++ ) - assert(OldDimensions[i] == 0 || OldDimensions[i] == NewDimensions[i] && "NamedTensor::load dimension size"); - assert( ValidateIndexNames( OldIndexNames.size(), &OldIndexNames[0] ) && "NamedTensor::load dimension name" ); -} - -template -void NamedTensor::read(const char * filename, const char * pszTag) { - std::string sFileName{filename}; - sFileName.append( MDistil::FileExtension ); - LOG(Message) << "Reading NamedTensor from file " << sFileName << std::endl; - MDistil::Default_Reader r( sFileName ); - read( r, pszTag ); -} - -/****************************************************************************** - Make a lower dimensional grid in preparation for local slice operations - ******************************************************************************/ - -inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) -{ - int nd{static_cast(gridHD->_ndimension)}; - Coordinate latt_size = gridHD->_gdimensions; - latt_size[nd-1] = 1; - Coordinate simd_layout = GridDefaultSimd(nd-1, vComplex::Nsimd()); - simd_layout.push_back( 1 ); - Coordinate mpi_layout = gridHD->_processors; - mpi_layout[nd-1] = 1; - GridCartesian * gridLD = new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); - return gridLD; -} - -/************************************************************************************* - Rotate eigenvectors into our phase convention - First component of first eigenvector is real and positive - *************************************************************************************/ - -inline void RotateEigen(std::vector & evec) -{ - ColourVector cv0; - auto grid = evec[0].Grid(); - Coordinate siteFirst(grid->Nd(),0); - peekSite(cv0, evec[0], siteFirst); - Grid::Complex cplx0 = cv0()()(0); - if( cplx0.imag() == 0 ) - std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; - else { - const Real cplx0_mag = Grid::abs(cplx0); -#ifdef GRID_NVCC - const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); - const Real argphase = thrust::arg(phase); -#else - const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); - const Real argphase = std::arg(phase); -#endif - std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / 3.14159265) << " pi" << std::endl; - { - // TODO: Only really needed on the master slice - for( int k = 0 ; k < evec.size() ; k++ ) - evec[k] *= phase; - if(grid->IsBoss()){ - for( int c = 0 ; c < Nc ; c++ ) - cv0()()(c) *= phase; - cplx0.imag(0); // This assumes phase convention is real, positive (so I get rid of rounding error) - //pokeSite(cv0, evec[0], siteFirst); - pokeLocalSite(cv0, evec[0], siteFirst); - } - } - } -} +extern const std::string PerambTensorName; +extern const std::array PerambIndexNames; +using PerambTensor = NamedTensor; END_MODULE_NAMESPACE END_HADRONS_NAMESPACE -#endif // Hadrons_MDistil_Distil_hpp_ +#endif // Hadrons_Distil_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilCommon.hpp b/Hadrons/Modules/MDistil/DistilCommon.hpp new file mode 100644 index 00000000..bb224447 --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilCommon.hpp @@ -0,0 +1,143 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/DistilCommon.hpp + + Copyright (C) 2015-2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + +#ifndef Hadrons_MDistil_DistilCommon_hpp_ +#define Hadrons_MDistil_DistilCommon_hpp_ + +#include +#include +#include +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MDistil) + +/****************************************************************************** + Common elements for distillation + ******************************************************************************/ + +struct DistilParameters: Serializable { + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, nnoise, + int, tsrc, + std::string, TI, + std::string, LI, + std::string, SI ) + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} + + // Numeric parameter is allowed to be empty (in which case it = Default), + // but assert during setup() if specified but not numeric + + static int ParameterDefault( const std::string & s, int Default, bool bCalledFromSetup ) + { + int i = Default; + if( s.length() > 0 ) { + std::istringstream ss( s ); + ss >> i; + if( bCalledFromSetup ) + assert( !ss.fail() && "Parameter should either be empty or integer" ); + } + return i; + } +}; + +#define DISTIL_PARAMETERS_DEFINE( inSetup ) \ +const int Nt{env().getDim(Tdir)}; \ +const int nvec{par().nvec}; \ +const int nnoise{par().Distil.nnoise}; \ +const int tsrc{par().Distil.tsrc}; \ +const int TI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.TI, Nt, inSetup)}; \ +const int LI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.LI, nvec, inSetup)}; \ +const int SI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.SI, Ns, inSetup)}; \ +const bool full_tdil{ TI == Nt }; \ +const bool exact_distillation{ full_tdil && LI == nvec }; \ +const int Nt_inv{ full_tdil ? 1 : TI } + +/****************************************************************************** + Make a lower dimensional grid in preparation for local slice operations + ******************************************************************************/ + +inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) +{ + int nd{static_cast(gridHD->_ndimension)}; + Coordinate latt_size = gridHD->_gdimensions; + latt_size[nd-1] = 1; + Coordinate simd_layout = GridDefaultSimd(nd-1, vComplex::Nsimd()); + simd_layout.push_back( 1 ); + Coordinate mpi_layout = gridHD->_processors; + mpi_layout[nd-1] = 1; + GridCartesian * gridLD = new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); + return gridLD; +} + +/************************************************************************************* + Rotate eigenvectors into our phase convention + First component of first eigenvector is real and positive + *************************************************************************************/ + +inline void RotateEigen(std::vector & evec) +{ + ColourVector cv0; + auto grid = evec[0].Grid(); + Coordinate siteFirst(grid->Nd(),0); + peekSite(cv0, evec[0], siteFirst); + Grid::Complex cplx0 = cv0()()(0); + if( cplx0.imag() == 0 ) + std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; + else { + const Real cplx0_mag = Grid::abs(cplx0); +#ifdef GRID_NVCC + const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); + const Real argphase = thrust::arg(phase); +#else + const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); + const Real argphase = std::arg(phase); +#endif + std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / 3.14159265) << " pi" << std::endl; + { + // TODO: Only really needed on the master slice + for( int k = 0 ; k < evec.size() ; k++ ) + evec[k] *= phase; + if(grid->IsBoss()){ + for( int c = 0 ; c < Nc ; c++ ) + cv0()()(c) *= phase; + cplx0.imag(0); // This assumes phase convention is real, positive (so I get rid of rounding error) + //pokeSite(cv0, evec[0], siteFirst); + pokeLocalSite(cv0, evec[0], siteFirst); + } + } + } +} + +END_MODULE_NAMESPACE +END_HADRONS_NAMESPACE +#endif // Hadrons_MDistil_DistilCommon_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index d7097a06..9ac3750d 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -30,24 +30,15 @@ #ifndef Hadrons_MDistil_DistilVectors_hpp_ #define Hadrons_MDistil_DistilVectors_hpp_ -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** * DistilVectors * * (Create rho and/or phi vectors) * ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) class DistilVectorsPar: Serializable { @@ -166,7 +157,7 @@ void TDistilVectors::setup(void) auto &perambulator = envGet(PerambTensor, PerambulatorName); // We expect the perambulator to have been created with these indices - assert( perambulator.ValidateIndexNames( PerambIndexNames.size(), &PerambIndexNames[0] ) && "Perambulator index names bad" ); + assert( perambulator.ValidateIndexNames() && "Perambulator index names bad" ); const int Nt{ env().getDim(Tdir) }; assert( Nt == static_cast( perambulator.tensor.dimension(0) ) && "PerambTensor time dimensionality bad" ); @@ -287,7 +278,7 @@ void TDistilVectors::execute(void) sink_tslice=0; for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - sink_tslice += evec3d * perambulator(t, ivec, dk, inoise,dt,ds); + sink_tslice += evec3d * perambulator.tensor(t, ivec, dk, inoise,dt,ds); } InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Tdir); } diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 5ac8e258..022e88d1 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -30,16 +30,9 @@ #ifndef Hadrons_MDistil_LapEvec_hpp_ #define Hadrons_MDistil_LapEvec_hpp_ -#include -#include -#include -#include - -// These are members of Distillation -#include +#include BEGIN_HADRONS_NAMESPACE - BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index a8c612cd..58c658dc 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -30,23 +30,14 @@ #ifndef Hadrons_MDistil_Noises_hpp_ #define Hadrons_MDistil_Noises_hpp_ -#include -#include -#include -#include -#include -#include -#include - -// These are members of Distillation - #include +#include BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** * Noises * ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) class NoisesPar: Serializable { diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index fc580b55..3562c4d2 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -30,23 +30,14 @@ #ifndef Hadrons_MDistil_PerambFromSolve_hpp_ #define Hadrons_MDistil_PerambFromSolve_hpp_ -#include -#include -#include -#include -#include -#include -#include +#include -// These are members of Distillation -#include - BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** * PerambFromSolve * ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) class PerambFromSolvePar: Serializable { @@ -126,7 +117,7 @@ void TPerambFromSolve::setup(void) const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, true) }; grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); - envCreate(PerambTensor, getName(), 1, PerambIndexNames,Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); + envCreate(PerambTensor, getName(), 1, Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); envCreate(NoiseTensor, getName() + "_noise", 1, nnoise, Nt, nvec, Ns ); envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); @@ -171,8 +162,8 @@ void TPerambFromSolve::execute(void) ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); for (int ivec = 0; ivec < nvec_reduced; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); - LOG(Message) << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; + pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); + LOG(Message) << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator.tensor(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; } } } diff --git a/Hadrons/Modules/MDistil/Perambulator.cc b/Hadrons/Modules/MDistil/Perambulator.cc index 055b8814..b141eb95 100644 --- a/Hadrons/Modules/MDistil/Perambulator.cc +++ b/Hadrons/Modules/MDistil/Perambulator.cc @@ -34,3 +34,14 @@ using namespace Hadrons; using namespace MDistil; template class Grid::Hadrons::MDistil::TPerambulator; + +// Global constants for distillation + +const std::string Grid::Hadrons::MDistil::PerambTensorName{ "Perambulator" }; +const std::array Grid::Hadrons::MDistil::PerambIndexNames{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; + +#ifdef HAVE_HDF5 +extern const std::string Grid::Hadrons::NamedTensorFileExtension{".h5"}; +#else +extern const std::string Grid::Hadrons::NamedTensorFileExtension{".dat"}; +#endif diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 86740283..52ec7501 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -30,7 +30,7 @@ #ifndef Hadrons_MDistil_Perambulator_hpp_ #define Hadrons_MDistil_Perambulator_hpp_ -#include +#include BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) @@ -129,7 +129,7 @@ void TPerambulator::setup(void) if( !UnsmearedSinkFileName.empty() ) bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, true ) != 0 ); - envCreate(PerambTensor, getName(), 1, PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); + envCreate(PerambTensor, getName(), 1, Nt,nvec,LI,nnoise,Nt_inv,SI); envCreate(std::vector, getName() + "_unsmeared_sink", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); @@ -244,7 +244,7 @@ void TPerambulator::execute(void) for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); + pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); } } } diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 558bf145..9d30f9fa 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -30,18 +30,14 @@ #ifndef Hadrons_MIO_LoadPerambulator_hpp_ #define Hadrons_MIO_LoadPerambulator_hpp_ -#include -#include -#include -#include -#include +#include BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MIO) /****************************************************************************** * LoadPerambulator * ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MIO) class LoadPerambulatorPar: Serializable { @@ -103,7 +99,7 @@ void TLoadPerambulator::setup(void) { DISTIL_PARAMETERS_DEFINE( true ); //std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; - envCreate(MDistil::PerambTensor, getName(), 1, MDistil::PerambIndexNames,Nt,nvec,LI,nnoise,Nt_inv,SI); + envCreate(MDistil::PerambTensor, getName(), 1, Nt,nvec,LI,nnoise,Nt_inv,SI); } // execution /////////////////////////////////////////////////////////////////// diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index 7f3e5bca..fd0b8b52 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -30,6 +30,7 @@ Author: Michael Marshall /* END LEGAL */ #include #include +#include using namespace Grid; @@ -252,6 +253,14 @@ void tensorConvTestFn(GridSerialRNG &rng, const std::string label) } #define tensorConvTest(rng, type) tensorConvTestFn(rng, #type) +static const std::string TheyCallMeLurve{"That's not my name"}; +static const std::string Coco{"Coco the dancing seal"}; +static const std::array GreatestShowOnEarth{ "Burnum", "Burnum" }; + +using MyPerambTensor = Grid::Hadrons::NamedTensor; +static const std::string MPTName{ "Perambulator" }; +static const std::array MPTIndexNames{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; +using MyPerambTensor2 = Grid::Hadrons::NamedTensor; int main(int argc,char **argv) { @@ -383,5 +392,33 @@ int main(int argc,char **argv) XmlWriter HMCwr("HMCparameters.xml"); write(HMCwr,"HMCparameters",HMCparams); } + + using Tst1=Hadrons::NamedTensor; + using Tst2=Hadrons::NamedTensor; + using Tst3=Hadrons::NamedTensor; + std::cout << "typeid(Tst1)=" << typeid(Tst1).name() << std::endl; + std::cout << "typeid(Tst2)=" << typeid(Tst2).name() << std::endl; + std::cout << "typeid(Tst3)=" << typeid(Tst3).name() << std::endl; + std::cout << "Tst1 is " << ( typeid(Tst1) == typeid(Tst2) ? "" : "not " ) + << "the same as Tst2" << std::endl; + std::cout << "Tst3 is " << ( typeid(Tst3) == typeid(Tst2) ? "" : "not " ) + << "the same as Tst2" << std::endl; + std::cout << "typeid(PerambTensor)=" << typeid(Grid::Hadrons::MDistil::PerambTensor).name() << std::endl; + std::cout << "typeid(MyPerambTensor)=" << typeid(MyPerambTensor).name() << std::endl; + std::cout << "Grid::Hadrons::MDistil::PerambTensor is " + << ( typeid(Grid::Hadrons::MDistil::PerambTensor) == typeid(MyPerambTensor) ? "" : "not " ) + << "the same as MyPerambTensor" << std::endl; + std::cout << "Grid::Hadrons::MDistil::PerambTensor is " + << ( typeid(Grid::Hadrons::MDistil::PerambTensor) == typeid(MyPerambTensor2) ? "" : "not " ) + << "the same as MyPerambTensor2" << std::endl; + + const std::string FileOriginal{"Peramb.3000"}; + const std::string FileCopy{"Peramb_deleteme.3000"}; + MyPerambTensor p; + p.read(FileOriginal,true,std::string("NamedTensor")); + //p.IndexNames[3] = "Lemons " + p.IndexNames[3]; + p.write(FileCopy); + p.read(FileCopy,false); + p.read(FileCopy); Grid_finalize(); } From 4bcdb4ff9505f6b38e0c8b554b29d5d43bae4db9 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 2 Nov 2019 15:24:12 +0000 Subject: [PATCH 304/347] Remove accidental check-in of local debugging --- tests/IO/Test_serialisation.cc | 36 ---------------------------------- 1 file changed, 36 deletions(-) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index fd0b8b52..a0d4720a 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -253,14 +253,6 @@ void tensorConvTestFn(GridSerialRNG &rng, const std::string label) } #define tensorConvTest(rng, type) tensorConvTestFn(rng, #type) -static const std::string TheyCallMeLurve{"That's not my name"}; -static const std::string Coco{"Coco the dancing seal"}; -static const std::array GreatestShowOnEarth{ "Burnum", "Burnum" }; - -using MyPerambTensor = Grid::Hadrons::NamedTensor; -static const std::string MPTName{ "Perambulator" }; -static const std::array MPTIndexNames{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; -using MyPerambTensor2 = Grid::Hadrons::NamedTensor; int main(int argc,char **argv) { @@ -392,33 +384,5 @@ int main(int argc,char **argv) XmlWriter HMCwr("HMCparameters.xml"); write(HMCwr,"HMCparameters",HMCparams); } - - using Tst1=Hadrons::NamedTensor; - using Tst2=Hadrons::NamedTensor; - using Tst3=Hadrons::NamedTensor; - std::cout << "typeid(Tst1)=" << typeid(Tst1).name() << std::endl; - std::cout << "typeid(Tst2)=" << typeid(Tst2).name() << std::endl; - std::cout << "typeid(Tst3)=" << typeid(Tst3).name() << std::endl; - std::cout << "Tst1 is " << ( typeid(Tst1) == typeid(Tst2) ? "" : "not " ) - << "the same as Tst2" << std::endl; - std::cout << "Tst3 is " << ( typeid(Tst3) == typeid(Tst2) ? "" : "not " ) - << "the same as Tst2" << std::endl; - std::cout << "typeid(PerambTensor)=" << typeid(Grid::Hadrons::MDistil::PerambTensor).name() << std::endl; - std::cout << "typeid(MyPerambTensor)=" << typeid(MyPerambTensor).name() << std::endl; - std::cout << "Grid::Hadrons::MDistil::PerambTensor is " - << ( typeid(Grid::Hadrons::MDistil::PerambTensor) == typeid(MyPerambTensor) ? "" : "not " ) - << "the same as MyPerambTensor" << std::endl; - std::cout << "Grid::Hadrons::MDistil::PerambTensor is " - << ( typeid(Grid::Hadrons::MDistil::PerambTensor) == typeid(MyPerambTensor2) ? "" : "not " ) - << "the same as MyPerambTensor2" << std::endl; - - const std::string FileOriginal{"Peramb.3000"}; - const std::string FileCopy{"Peramb_deleteme.3000"}; - MyPerambTensor p; - p.read(FileOriginal,true,std::string("NamedTensor")); - //p.IndexNames[3] = "Lemons " + p.IndexNames[3]; - p.write(FileCopy); - p.read(FileCopy,false); - p.read(FileCopy); Grid_finalize(); } From fcd90705bc0a97b34c51e543dab52c22df681267 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 2 Nov 2019 16:15:48 +0000 Subject: [PATCH 305/347] Beautification --- Hadrons/Modules/MDistil/DistilCommon.hpp | 121 +++--- Hadrons/Modules/MDistil/DistilVectors.hpp | 371 ++++++++++--------- Hadrons/Modules/MDistil/LapEvec.hpp | 389 ++++++++++---------- Hadrons/Modules/MDistil/Noises.hpp | 81 ++-- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 128 ++++--- Hadrons/Modules/MDistil/Perambulator.hpp | 95 +++-- 6 files changed, 588 insertions(+), 597 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilCommon.hpp b/Hadrons/Modules/MDistil/DistilCommon.hpp index bb224447..96621367 100644 --- a/Hadrons/Modules/MDistil/DistilCommon.hpp +++ b/Hadrons/Modules/MDistil/DistilCommon.hpp @@ -3,12 +3,12 @@ Grid physics library, www.github.com/paboyle/Grid Source file: Hadrons/Modules/MDistil/DistilCommon.hpp - + Copyright (C) 2015-2019 Author: Felix Erben Author: Michael Marshall - + 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 @@ -41,33 +41,33 @@ BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** - Common elements for distillation + Distillation code that is common across modules ******************************************************************************/ struct DistilParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, - int, nnoise, - int, tsrc, - std::string, TI, - std::string, LI, - std::string, SI ) - DistilParameters() = default; - template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} - - // Numeric parameter is allowed to be empty (in which case it = Default), - // but assert during setup() if specified but not numeric - - static int ParameterDefault( const std::string & s, int Default, bool bCalledFromSetup ) - { - int i = Default; - if( s.length() > 0 ) { - std::istringstream ss( s ); - ss >> i; - if( bCalledFromSetup ) - assert( !ss.fail() && "Parameter should either be empty or integer" ); + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, nnoise, + int, tsrc, + std::string, TI, + std::string, LI, + std::string, SI ) + DistilParameters() = default; + template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} + + // Numeric parameter is allowed to be empty (in which case it = Default), + // but assert during setup() if specified but not numeric + + static int ParameterDefault( const std::string & s, int Default, bool bCalledFromSetup ) + { + int i = Default; + if( s.length() > 0 ) { + std::istringstream ss( s ); + ss >> i; + if( bCalledFromSetup ) + assert( !ss.fail() && "Parameter should either be empty or integer" ); + } + return i; } - return i; - } }; #define DISTIL_PARAMETERS_DEFINE( inSetup ) \ @@ -88,54 +88,55 @@ const int Nt_inv{ full_tdil ? 1 : TI } inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) { - int nd{static_cast(gridHD->_ndimension)}; - Coordinate latt_size = gridHD->_gdimensions; - latt_size[nd-1] = 1; - Coordinate simd_layout = GridDefaultSimd(nd-1, vComplex::Nsimd()); - simd_layout.push_back( 1 ); - Coordinate mpi_layout = gridHD->_processors; - mpi_layout[nd-1] = 1; - GridCartesian * gridLD = new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); - return gridLD; + int nd{static_cast(gridHD->_ndimension)}; + Coordinate latt_size = gridHD->_gdimensions; + latt_size[nd-1] = 1; + Coordinate simd_layout = GridDefaultSimd(nd-1, vComplex::Nsimd()); + simd_layout.push_back( 1 ); + Coordinate mpi_layout = gridHD->_processors; + mpi_layout[nd-1] = 1; + GridCartesian * gridLD = new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); + return gridLD; } /************************************************************************************* Rotate eigenvectors into our phase convention First component of first eigenvector is real and positive + TODO: Should this be in Distil.hpp? *************************************************************************************/ inline void RotateEigen(std::vector & evec) { - ColourVector cv0; - auto grid = evec[0].Grid(); - Coordinate siteFirst(grid->Nd(),0); - peekSite(cv0, evec[0], siteFirst); - Grid::Complex cplx0 = cv0()()(0); - if( cplx0.imag() == 0 ) - std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; - else { - const Real cplx0_mag = Grid::abs(cplx0); + ColourVector cv0; + auto grid = evec[0].Grid(); + Coordinate siteFirst(grid->Nd(),0); + peekSite(cv0, evec[0], siteFirst); + Grid::Complex cplx0 = cv0()()(0); + if( cplx0.imag() == 0 ) + std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; + else { + const Real cplx0_mag = Grid::abs(cplx0); #ifdef GRID_NVCC - const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); - const Real argphase = thrust::arg(phase); + const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); + const Real argphase = thrust::arg(phase); #else - const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); - const Real argphase = std::arg(phase); + const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); + const Real argphase = std::arg(phase); #endif - std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / 3.14159265) << " pi" << std::endl; - { - // TODO: Only really needed on the master slice - for( int k = 0 ; k < evec.size() ; k++ ) - evec[k] *= phase; - if(grid->IsBoss()){ - for( int c = 0 ; c < Nc ; c++ ) - cv0()()(c) *= phase; - cplx0.imag(0); // This assumes phase convention is real, positive (so I get rid of rounding error) - //pokeSite(cv0, evec[0], siteFirst); - pokeLocalSite(cv0, evec[0], siteFirst); - } + std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / 3.14159265) << " pi" << std::endl; + { + // TODO: Only really needed on the master slice + for( int k = 0 ; k < evec.size() ; k++ ) + evec[k] *= phase; + if(grid->IsBoss()){ + for( int c = 0 ; c < Nc ; c++ ) + cv0()()(c) *= phase; + cplx0.imag(0); // This assumes phase convention is real, positive (so I get rid of rounding error) + //pokeSite(cv0, evec[0], siteFirst); + pokeLocalSite(cv0, evec[0], siteFirst); + } + } } - } } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 9ac3750d..0b549913 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -43,47 +43,47 @@ BEGIN_MODULE_NAMESPACE(MDistil) class DistilVectorsPar: Serializable { public: - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilVectorsPar, - std::string, noise, - std::string, perambulator, - std::string, lapevec, - std::string, source, - std::string, sink, - int, tsrc, - std::string, nvec, - std::string, TI) + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilVectorsPar, + std::string, noise, + std::string, perambulator, + std::string, lapevec, + std::string, source, + std::string, sink, + int, tsrc, + std::string, nvec, + std::string, TI) }; template class TDistilVectors: public Module { public: - FERM_TYPE_ALIASES(FImpl,); - // constructor - TDistilVectors(const std::string name); - // destructor - virtual ~TDistilVectors(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); + FERM_TYPE_ALIASES(FImpl,); + // constructor + TDistilVectors(const std::string name); + // destructor + virtual ~TDistilVectors(void); + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) - virtual void Cleanup(void); + // These variables are created in setup() and freed in Cleanup() + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; // Owned by environment (so I won't delete it) + virtual void Cleanup(void); public: - // These variables contain parameters - std::string PerambulatorName; - std::string NoiseVectorName; - std::string LapEvecName; - bool bMakeSource; - bool bMakeSink; - std::string SourceName; - std::string SinkName; + // These variables contain parameters + std::string PerambulatorName; + std::string NoiseVectorName; + std::string LapEvecName; + bool bMakeSource; + bool bMakeSink; + std::string SourceName; + std::string SinkName; }; MODULE_REGISTER_TMP(DistilVectors, TDistilVectors, MDistil); @@ -100,193 +100,196 @@ TDistilVectors::TDistilVectors(const std::string name) template TDistilVectors::~TDistilVectors(void) { - Cleanup(); + Cleanup(); }; // dependencies/products /////////////////////////////////////////////////////// template std::vector TDistilVectors::getInput(void) { - PerambulatorName = par().perambulator; - if( PerambulatorName.size() == 0 ) { - PerambulatorName = getName(); - PerambulatorName.append( "_peramb" ); - } - NoiseVectorName = par().noise; - if( NoiseVectorName.size() == 0 ) { - NoiseVectorName = PerambulatorName; - NoiseVectorName.append( "_noise" ); - } - LapEvecName = par().lapevec; - if( LapEvecName.size() == 0 ) { - LapEvecName = PerambulatorName; - LapEvecName.append( "_lapevec" ); - } - return { PerambulatorName, NoiseVectorName, LapEvecName }; + PerambulatorName = par().perambulator; + if (PerambulatorName.empty()) + { + PerambulatorName = getName(); + PerambulatorName.append("_peramb"); + } + NoiseVectorName = par().noise; + if (NoiseVectorName.empty()) + { + NoiseVectorName = PerambulatorName; + NoiseVectorName.append("_noise"); + } + LapEvecName = par().lapevec; + if (LapEvecName.empty()) + { + LapEvecName = PerambulatorName; + LapEvecName.append("_lapevec"); + } + return { PerambulatorName, NoiseVectorName, LapEvecName }; } template std::vector TDistilVectors::getOutput(void) { - SourceName = par().source; - SinkName = par().sink; - bMakeSource = ( SourceName.size() > 0 ); - bMakeSink = ( SinkName.size() > 0 ); - if( !bMakeSource && !bMakeSink ) { - SourceName = getName(); - SinkName = SourceName; - SourceName.append( "_rho" ); - SinkName.append( "_phi" ); - bMakeSource = true; - bMakeSink = true; - } - std::vector out; - if( bMakeSource ) - out.push_back( SourceName ); - if( bMakeSink ) - out.push_back( SinkName ); - return out; + SourceName = par().source; + SinkName = par().sink; + bMakeSource = ( SourceName.size() > 0 ); + bMakeSink = ( SinkName.size() > 0 ); + if (!bMakeSource && !bMakeSink) + { + SourceName = getName(); + SinkName = SourceName; + SourceName.append("_rho"); + SinkName.append("_phi"); + bMakeSource = true; + bMakeSink = true; + } + std::vector out; + if (bMakeSource) + out.push_back(SourceName); + if (bMakeSink) + out.push_back(SinkName); + return out; } // setup /////////////////////////////////////////////////////////////////////// template void TDistilVectors::setup(void) { - Cleanup(); - auto &noise = envGet(NoiseTensor, NoiseVectorName); - auto &perambulator = envGet(PerambTensor, PerambulatorName); - - // We expect the perambulator to have been created with these indices - assert( perambulator.ValidateIndexNames() && "Perambulator index names bad" ); - - const int Nt{ env().getDim(Tdir) }; - assert( Nt == static_cast( perambulator.tensor.dimension(0) ) && "PerambTensor time dimensionality bad" ); - const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, true) }; - const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; - const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; - const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; - const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; - assert( nnoise >= static_cast( noise.dimension(0) ) && "Not enough noise vectors for perambulator" ); - // Nvec defaults to what's in the perambulator unless overriden - const int nvec_per{ static_cast( perambulator.tensor.dimension(1) ) }; - const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, nvec_per, true) }; - assert( nvec <= nvec_per && "Not enough distillation sub-space vectors" ); - - if( bMakeSource ) - envCreate(std::vector, SourceName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - if( bMakeSink ) - envCreate(std::vector, SinkName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - - grid4d = env().getGrid(); - Coordinate latt_size = GridDefaultLatt(); - Coordinate simd_layout = GridDefaultSimd(Nd, vComplex::Nsimd()); - Coordinate mpi_layout = GridDefaultMpi(); - Coordinate simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); - latt_size[Nd-1] = 1; - simd_layout_3.push_back( 1 ); - mpi_layout[Nd-1] = 1; - grid3d = MakeLowerDimGrid(grid4d); - - - envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + Cleanup(); + auto &noise = envGet(NoiseTensor, NoiseVectorName); + auto &perambulator = envGet(PerambTensor, PerambulatorName); + + // We expect the perambulator to have been created with these indices + assert( perambulator.ValidateIndexNames() && "Perambulator index names bad" ); + + const int Nt{ env().getDim(Tdir) }; + assert( Nt == static_cast( perambulator.tensor.dimension(0) ) && "PerambTensor time dimensionality bad" ); + const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; + const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; + const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; + const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; + assert( nnoise >= static_cast( noise.dimension(0) ) && "Not enough noise vectors for perambulator" ); + // Nvec defaults to what's in the perambulator unless overriden + const int nvec_per{ static_cast( perambulator.tensor.dimension(1) ) }; + const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, nvec_per, true) }; + assert( nvec <= nvec_per && "Not enough distillation sub-space vectors" ); + + if (bMakeSource) + envCreate(std::vector, SourceName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); + if (bMakeSink) + envCreate(std::vector, SinkName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); + + grid4d = env().getGrid(); + Coordinate latt_size = GridDefaultLatt(); + Coordinate mpi_layout = GridDefaultMpi(); + Coordinate simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); + latt_size[Nd-1] = 1; + simd_layout_3.push_back( 1 ); + mpi_layout[Nd-1] = 1; + grid3d = MakeLowerDimGrid(grid4d); + + envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); } // clean up any temporaries created by setup (that aren't stored in the environment) template void TDistilVectors::Cleanup(void) { - if( grid3d != nullptr ) { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; + if ( grid3d != nullptr) + { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; } // execution /////////////////////////////////////////////////////////////////// template void TDistilVectors::execute(void) { - auto &noise = envGet(NoiseTensor, NoiseVectorName); - auto &perambulator = envGet(PerambTensor, PerambulatorName); - auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); - - envGetTmp(LatticeSpinColourVector, tmp2); - envGetTmp(LatticeSpinColourVector, tmp3d); - envGetTmp(LatticeColourVector, tmp3d_nospin); - envGetTmp(LatticeSpinColourVector, sink_tslice); - envGetTmp(LatticeColourVector, evec3d); - - const int Ntlocal{ grid4d->LocalDimensions()[3] }; - const int Ntfirst{ grid4d->LocalStarts()[3] }; - - const int Nt{ env().getDim(Tdir) }; - const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false ) }; - const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; - const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; - const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; - const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; - // Nvec defaults to what's in the perambulator unless overriden - const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, static_cast( perambulator.tensor.dimension(1) ), false)}; - const int tsrc{ par().tsrc }; - const bool full_tdil{ TI==Nt }; - - int vecindex; - int t_inv; - if( bMakeSource ) { - auto &rho = envGet(std::vector, SourceName); - for( int inoise = 0; inoise < nnoise; inoise++ ) { - for( int dk = 0; dk < LI; dk++ ) { - for( int dt = 0; dt < Nt_inv; dt++ ) { - for( int ds = 0; ds < SI; ds++ ) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; - rho[vecindex] = 0; - tmp3d_nospin = 0; - for (int it = dt; it < Nt; it += TI){ - if (full_tdil) t_inv = tsrc; else t_inv = it; - if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += SI){ - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); - tmp3d=0; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=0; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Tdir); - rho[vecindex] += tmp2; - } + auto &noise = envGet(NoiseTensor, NoiseVectorName); + auto &perambulator = envGet(PerambTensor, PerambulatorName); + auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); + + envGetTmp(LatticeSpinColourVector, tmp2); + envGetTmp(LatticeSpinColourVector, tmp3d); + envGetTmp(LatticeColourVector, tmp3d_nospin); + envGetTmp(LatticeSpinColourVector, sink_tslice); + envGetTmp(LatticeColourVector, evec3d); + + const int Ntlocal{ grid4d->LocalDimensions()[3] }; + const int Ntfirst{ grid4d->LocalStarts()[3] }; + + const int Nt{ env().getDim(Tdir) }; + const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false ) }; + const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; + const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; + const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; + const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; + // Nvec defaults to what's in the perambulator unless overriden + const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, static_cast( perambulator.tensor.dimension(1) ), false)}; + const int tsrc{ par().tsrc }; + const bool full_tdil{ TI==Nt }; + + int vecindex; + int t_inv; + if (bMakeSource) + { + auto &rho = envGet(std::vector, SourceName); + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; + rho[vecindex] = 0; + tmp3d_nospin = 0; + for (int it = dt; it < Nt; it += TI){ + if (full_tdil) t_inv = tsrc; else t_inv = it; + if (t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal) { + for (int ik = dk; ik < nvec; ik += LI){ + for (int is = ds; is < Ns; is += SI){ + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); + tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); + tmp3d=0; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=0; + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Tdir); + rho[vecindex] += tmp2; + } + } + } + } + } } - } } - } } - } } - } - if( bMakeSink ) { - auto &phi = envGet(std::vector, SinkName); - for( int inoise = 0; inoise < nnoise; inoise++ ) { - for( int dk = 0; dk < LI; dk++ ) { - for( int dt = 0; dt < Nt_inv; dt++ ) { - for( int ds = 0; ds < SI; ds++ ) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; - phi[vecindex] = 0; - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - sink_tslice=0; - for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - sink_tslice += evec3d * perambulator.tensor(t, ivec, dk, inoise,dt,ds); - } - InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Tdir); + if (bMakeSink) { + auto &phi = envGet(std::vector, SinkName); + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; + phi[vecindex] = 0; + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + sink_tslice=0; + for (int ivec = 0; ivec < nvec; ivec++) { + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); + sink_tslice += evec3d * perambulator.tensor(t, ivec, dk, inoise,dt,ds); + } + InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Tdir); + } + } + } } - } } - } } - } } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 022e88d1..428585b0 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -3,12 +3,12 @@ Grid physics library, www.github.com/paboyle/Grid Source file: Hadrons/Modules/MDistil/LapEvec.hpp - + Copyright (C) 2019 Author: Felix Erben Author: Michael Marshall - + 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 @@ -36,48 +36,48 @@ BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** - + Laplacian eigenvectors - parameters - + ******************************************************************************/ struct StoutParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(StoutParameters, - int, steps, - double, rho) - StoutParameters() = default; - template StoutParameters(Reader& Reader){read(Reader,"StoutSmearing",*this);} + GRID_SERIALIZABLE_CLASS_MEMBERS(StoutParameters, + int, steps, + double, rho) + StoutParameters() = default; + template StoutParameters(Reader& Reader){read(Reader,"StoutSmearing",*this);} }; struct ChebyshevParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(ChebyshevParameters, - int, PolyOrder, - double, alpha, - double, beta) - ChebyshevParameters() = default; - template ChebyshevParameters(Reader& Reader){read(Reader,"Chebyshev",*this);} + GRID_SERIALIZABLE_CLASS_MEMBERS(ChebyshevParameters, + int, PolyOrder, + double, alpha, + double, beta) + ChebyshevParameters() = default; + template ChebyshevParameters(Reader& Reader){read(Reader,"Chebyshev",*this);} }; struct LanczosParameters: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters, - int, Nvec, - int, Nk, - int, Np, - int, MaxIt, - double, resid, - int, IRLLog) - LanczosParameters() = default; - template LanczosParameters(Reader& Reader){read(Reader,"Lanczos",*this);} + GRID_SERIALIZABLE_CLASS_MEMBERS(LanczosParameters, + int, Nvec, + int, Nk, + int, Np, + int, MaxIt, + double, resid, + int, IRLLog) + LanczosParameters() = default; + template LanczosParameters(Reader& Reader){read(Reader,"Lanczos",*this);} }; // These are the actual parameters passed to the module during construction struct LapEvecPar: Serializable { - GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar - ,std::string, gauge - ,StoutParameters, Stout - ,ChebyshevParameters, Cheby - ,LanczosParameters, Lanczos) + GRID_SERIALIZABLE_CLASS_MEMBERS(LapEvecPar + ,std::string, gauge + ,StoutParameters, Stout + ,ChebyshevParameters, Cheby + ,LanczosParameters, Lanczos) }; /****************************************************************************** @@ -90,25 +90,25 @@ template class TLapEvec: public Module { public: - GAUGE_TYPE_ALIASES(GImpl,); - // constructor - TLapEvec(const std::string name); - // destructor - virtual ~TLapEvec(void); - // dependency relation - virtual std::vector getInput(void); - virtual std::vector getOutput(void); - // setup - virtual void setup(void); - // execution - virtual void execute(void); + GAUGE_TYPE_ALIASES(GImpl,); + // constructor + TLapEvec(const std::string name); + // destructor + virtual ~TLapEvec(void); + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * gridLD; // Owned by me, so I must delete it - GridCartesian * gridHD; // Owned by environment (so I won't delete it) - std::string sGaugeName; + // These variables are created in setup() and freed in Cleanup() + GridCartesian * gridLD; // Owned by me, so I must delete it + GridCartesian * gridHD; // Owned by environment (so I won't delete it) + std::string sGaugeName; protected: - virtual void Cleanup(void); + virtual void Cleanup(void); }; MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); @@ -127,19 +127,20 @@ TLapEvec::TLapEvec(const std::string name) : gridLD{nullptr}, Module TLapEvec::~TLapEvec() { - Cleanup(); + Cleanup(); } // dependencies/products /////////////////////////////////////////////////////// template std::vector TLapEvec::getInput(void) { - sGaugeName = par().gauge; - if( sGaugeName.size() == 0 ) { - sGaugeName = getName(); - sGaugeName.append( "_gauge" ); - } - return std::vector{ sGaugeName }; + sGaugeName = par().gauge; + if (sGaugeName.empty()) + { + sGaugeName = getName(); + sGaugeName.append( "_gauge" ); + } + return std::vector{ sGaugeName }; } template @@ -153,34 +154,35 @@ std::vector TLapEvec::getOutput(void) template void TLapEvec::setup(void) { - Cleanup(); - Environment & e{env()}; - gridHD = e.getGrid(); - gridLD = MakeLowerDimGrid( gridHD ); - const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; - // Temporaries - envTmpLat(GaugeField, "Umu_stout"); - envTmpLat(GaugeField, "Umu_smear"); - envTmp(LatticeGaugeField, "UmuNoTime",1,LatticeGaugeField(gridLD)); - envTmp(LatticeColourVector, "src",1,LatticeColourVector(gridLD)); - envTmp(std::vector, "eig",1,std::vector(Ntlocal)); - // Output objects - envCreate(LapEvecs, getName(), 1, par().Lanczos.Nvec, gridHD ); + Cleanup(); + Environment & e{env()}; + gridHD = e.getGrid(); + gridLD = MakeLowerDimGrid( gridHD ); + const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; + // Temporaries + envTmpLat(GaugeField, "Umu_stout"); + envTmpLat(GaugeField, "Umu_smear"); + envTmp(LatticeGaugeField, "UmuNoTime",1,LatticeGaugeField(gridLD)); + envTmp(LatticeColourVector, "src",1,LatticeColourVector(gridLD)); + envTmp(std::vector, "eig",1,std::vector(Ntlocal)); + // Output objects + envCreate(LapEvecs, getName(), 1, par().Lanczos.Nvec, gridHD ); } // clean up any temporaries created by setup (that aren't stored in the environment) template void TLapEvec::Cleanup(void) { - if( gridLD != nullptr ) { - delete gridLD; - gridLD = nullptr; - } - gridHD = nullptr; + if (gridLD != nullptr) + { + delete gridLD; + gridLD = nullptr; + } + gridHD = nullptr; } /************************************************************************************* - + -Grad^2 (Peardon, 2009, pg 2, equation 3, https://arxiv.org/abs/0905.2160) Field Type of field the operator will be applied to GaugeField Gauge field the operator will smear using @@ -189,55 +191,52 @@ void TLapEvec::Cleanup(void) template class Laplacian3D : public LinearOperatorBase, public LinearFunction { - typedef typename GaugeField::vector_type vCoeff_t; -protected: // I don't really mind if _gf is messed with ... so make this public? - //GaugeField & _gf; - int nd; // number of spatial dimensions - std::vector > > U; -public: - // Construct this operator given a gauge field and the number of dimensions it should act on - Laplacian3D( GaugeField& gf, int dimSpatial = Tdir ) : /*_gf(gf),*/ nd{dimSpatial} { - assert(dimSpatial>=1); - for( int mu = 0 ; mu < nd ; mu++ ) - U.push_back(PeekIndex(gf,mu)); - } - - // Apply this operator to "in", return result in "out" - void operator()(const Field& in, Field& out) { - assert( nd <= in.Grid()->Nd() ); - conformable( in, out ); - out = ( ( Real ) ( 2 * nd ) ) * in; - Field _tmp(in.Grid()); typedef typename GaugeField::vector_type vCoeff_t; - //Lattice > U(in.Grid()); - for( int mu = 0 ; mu < nd ; mu++ ) { - //U = PeekIndex(_gf,mu); - out -= U[mu] * Cshift( in, mu, 1); - _tmp = adj( U[mu] ) * in; - out -= Cshift(_tmp,mu,-1); +public: + int nd; // number of spatial dimensions + std::vector > > U; + // Construct this operator given a gauge field and the number of dimensions it should act on + Laplacian3D( GaugeField& gf, int dimSpatial = Tdir ) : nd{dimSpatial} + { + assert(dimSpatial>=1); + for (int mu = 0 ; mu < nd ; mu++) + U.push_back(PeekIndex(gf,mu)); } - } - - void OpDiag (const Field &in, Field &out) { assert(0); }; - void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }; - void Op (const Field &in, Field &out) { assert(0); }; - void AdjOp (const Field &in, Field &out) { assert(0); }; - void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2) { assert(0); }; - void HermOp(const Field &in, Field &out) { operator()(in,out); }; + + // Apply this operator to "in", return result in "out" + void operator()(const Field& in, Field& out) { + assert( nd <= in.Grid()->Nd() ); + conformable( in, out ); + out = ( ( Real ) ( 2 * nd ) ) * in; + Field _tmp(in.Grid()); + typedef typename GaugeField::vector_type vCoeff_t; + for (int mu = 0 ; mu < nd ; mu++) + { + out -= U[mu] * Cshift( in, mu, 1); + _tmp = adj( U[mu] ) * in; + out -= Cshift(_tmp,mu,-1); + } + } + + void OpDiag (const Field &in, Field &out) { assert(0); }; + void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }; + void Op (const Field &in, Field &out) { assert(0); }; + void AdjOp (const Field &in, Field &out) { assert(0); }; + void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2) { assert(0); }; + void HermOp(const Field &in, Field &out) { operator()(in,out); }; }; template class Laplacian3DHerm : public LinearFunction { public: - OperatorFunction & _poly; - LinearOperatorBase &_Linop; - - Laplacian3DHerm(OperatorFunction & poly,LinearOperatorBase& linop) - : _poly{poly}, _Linop{linop} {} - - void operator()(const Field& in, Field& out) { - _poly(_Linop,in,out); - } + OperatorFunction & _poly; + LinearOperatorBase &_Linop; + Laplacian3DHerm(OperatorFunction & poly,LinearOperatorBase& linop) + : _poly{poly}, _Linop{linop} {} + void operator()(const Field& in, Field& out) + { + _poly(_Linop,in,out); + } }; /****************************************************************************** @@ -248,91 +247,93 @@ public: template void TLapEvec::execute(void) { - const ChebyshevParameters &ChebPar{par().Cheby}; - const LanczosParameters &LPar{par().Lanczos}; - - // Disable IRL logging if requested - LOG(Message) << "IRLLog=" << LPar.IRLLog << std::endl; - const int PreviousIRLLogState{GridLogIRL.isActive()}; - GridLogIRL.Active( LPar.IRLLog == 0 ? 0 : 1 ); - - // Stout smearing - envGetTmp(GaugeField, Umu_smear); - Umu_smear = envGet(GaugeField, sGaugeName); // The smeared field starts off as the Gauge field - LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; - const StoutParameters &Stout{par().Stout}; - if( Stout.steps ) - { - envGetTmp(GaugeField, Umu_stout); - Smear_Stout LS(Stout.rho, Tdir); // spatial smearing only - for (int i = 0; i < Stout.steps; i++) { - LS.smear(Umu_stout, Umu_smear); - Umu_smear = Umu_stout; - } - LOG(Message) << "Smeared plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; - } - - //////////////////////////////////////////////////////////////////////// - // Invert nabla operator separately on each time-slice - //////////////////////////////////////////////////////////////////////// - - auto & eig4d = envGet(LapEvecs, getName() ); - envGetTmp(std::vector, eig); // Eigenpack for each timeslice - envGetTmp(LatticeGaugeField, UmuNoTime); // Gauge field without time dimension - envGetTmp(LatticeColourVector, src); - const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; - const int Ntfirst{gridHD->LocalStarts()[Tdir]}; - uint32_t ConvergenceErrors{0}; - for(int t = 0; t < Ntlocal; t++ ) { - LOG(Message) << "------------------------------------------------------------" << std::endl; - LOG(Message) << " Compute eigenpack, local timeslice = " << t << " / " << Ntlocal << std::endl; - LOG(Message) << "------------------------------------------------------------" << std::endl; - eig[t].resize(LPar.Nk+LPar.Np,gridLD); + const ChebyshevParameters &ChebPar{par().Cheby}; + const LanczosParameters &LPar{par().Lanczos}; - // Construct smearing operator - ExtractSliceLocal(UmuNoTime,Umu_smear,0,t,Tdir); // switch to 3d/4d objects - Laplacian3D Nabla(UmuNoTime); - LOG(Debug) << "Chebyshev preconditioning to order " << ChebPar.PolyOrder - << " with parameters (alpha,beta) = (" << ChebPar.alpha << "," << ChebPar.beta << ")" << std::endl; - Chebyshev Cheb(ChebPar.alpha,ChebPar.beta,ChebPar.PolyOrder); + // Disable IRL logging if requested + LOG(Message) << "IRLLog=" << LPar.IRLLog << std::endl; + const int PreviousIRLLogState{GridLogIRL.isActive()}; + GridLogIRL.Active( LPar.IRLLog == 0 ? 0 : 1 ); - // Construct source vector according to Test_dwf_compressed_lanczos.cc - src = 11.0; //TODO: Why hard-coded 11? - RealD nn = norm2(src); - nn = Grid::sqrt(nn); - src = src * (1.0/nn); - - Laplacian3DHerm NablaCheby(Cheb,Nabla); - ImplicitlyRestartedLanczos - IRL(NablaCheby,Nabla,LPar.Nvec,LPar.Nk,LPar.Nk+LPar.Np,LPar.resid,LPar.MaxIt); - int Nconv = 0; - IRL.calc(eig[t].eval,eig[t].evec,src,Nconv); - if( Nconv < LPar.Nvec ) { - // NB: Can't assert here since we are processing local slices - i.e. not all nodes would assert - ConvergenceErrors = 1; - LOG(Error) << "MDistil::LapEvec : Not enough eigenvectors converged. If this occurs in practice, we should modify the eigensolver to iterate once more to ensure the second convergence test does not take us below the requested number of eigenvectors" << std::endl; + // Stout smearing + envGetTmp(GaugeField, Umu_smear); + Umu_smear = envGet(GaugeField, sGaugeName); // The smeared field starts off as the Gauge field + LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; + const StoutParameters &Stout{par().Stout}; + if( Stout.steps ) + { + envGetTmp(GaugeField, Umu_stout); + Smear_Stout LS(Stout.rho, Tdir); // spatial smearing only + for (int i = 0; i < Stout.steps; i++) { + LS.smear(Umu_stout, Umu_smear); + Umu_smear = Umu_stout; + } + LOG(Message) << "Smeared plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; } - if( Nconv != LPar.Nvec ) - eig[t].resize( LPar.Nvec, gridLD ); - RotateEigen( eig[t].evec ); // Rotate the eigenvectors into our phase convention - - for (int i=0;i, eig); // Eigenpack for each timeslice + envGetTmp(LatticeGaugeField, UmuNoTime); // Gauge field without time dimension + envGetTmp(LatticeColourVector, src); + const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; + const int Ntfirst{gridHD->LocalStarts()[Tdir]}; + uint32_t ConvergenceErrors{0}; + for (int t = 0; t < Ntlocal; t++ ) + { + LOG(Message) << "------------------------------------------------------------" << std::endl; + LOG(Message) << " Compute eigenpack, local timeslice = " << t << " / " << Ntlocal << std::endl; + LOG(Message) << "------------------------------------------------------------" << std::endl; + eig[t].resize(LPar.Nk+LPar.Np,gridLD); + + // Construct smearing operator + ExtractSliceLocal(UmuNoTime,Umu_smear,0,t,Tdir); // switch to 3d/4d objects + Laplacian3D Nabla(UmuNoTime); + LOG(Message) << "Chebyshev preconditioning to order " << ChebPar.PolyOrder + << " with parameters (alpha,beta) = (" << ChebPar.alpha << "," << ChebPar.beta << ")" << std::endl; + Chebyshev Cheb(ChebPar.alpha,ChebPar.beta,ChebPar.PolyOrder); + + // Construct source vector according to Test_dwf_compressed_lanczos.cc + src = 11.0; // NB: This is a dummy parameter and just needs to be non-zero + RealD nn = norm2(src); + nn = Grid::sqrt(nn); + src = src * (1.0/nn); + + Laplacian3DHerm NablaCheby(Cheb,Nabla); + ImplicitlyRestartedLanczos + IRL(NablaCheby,Nabla,LPar.Nvec,LPar.Nk,LPar.Nk+LPar.Np,LPar.resid,LPar.MaxIt); + int Nconv = 0; + IRL.calc(eig[t].eval,eig[t].evec,src,Nconv); + if (Nconv < LPar.Nvec) + { + // NB: Can't assert here since we are processing local slices - i.e. not all nodes would assert + ConvergenceErrors = 1; + LOG(Error) << "MDistil::LapEvec : Not enough eigenvectors converged. If this occurs in practice, we should modify the eigensolver to iterate once more to ensure the second convergence test does not take us below the requested number of eigenvectors" << std::endl; + } + if( Nconv != LPar.Nvec ) + eig[t].resize( LPar.Nvec, gridLD ); + RotateEigen( eig[t].evec ); // Rotate the eigenvectors into our phase convention + + for (int i=0;iGlobalSum(ConvergenceErrors); - assert(ConvergenceErrors==0 && "The eingensolver failed to find enough eigenvectors on at least one node"); + GridLogIRL.Active( PreviousIRLLogState ); + gridHD->GlobalSum(ConvergenceErrors); + assert(ConvergenceErrors==0 && "The eingensolver failed to find enough eigenvectors on at least one node"); #if DEBUG - // Now write out the 4d eigenvectors - eig4d.record.operatorXml = "Distillation"; - eig4d.record.solverXml = "CG"; - std::string sEigenPackName(getName()); - sEigenPackName.append("."); - sEigenPackName.append(std::to_string(vm().getTrajectory())); - eig4d.write(sEigenPackName,false); + // Now write out the 4d eigenvectors + eig4d.record.operatorXml = "Distillation"; + eig4d.record.solverXml = "CG"; + std::string sEigenPackName(getName()); + sEigenPackName.append("."); + sEigenPackName.append(std::to_string(vm().getTrajectory())); + eig4d.write(sEigenPackName,false); #endif } diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 58c658dc..463ec635 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -82,17 +82,13 @@ TNoises::TNoises(const std::string name) template std::vector TNoises::getInput(void) { - std::vector in; - - return in; + return {}; } template std::vector TNoises::getOutput(void) { - std::vector out = {getName()}; - - return out; + return {getName()}; } // setup /////////////////////////////////////////////////////////////////////// @@ -100,52 +96,49 @@ std::vector TNoises::getOutput(void) template void TNoises::setup(void) { - const int Nt{env().getDim(Tdir)}; - const int nnoise{par().nnoise}; - const int nvec{par().nvec}; - const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, true) }; - const int LI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI, nvec, true) }; - envCreate(NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); + const int Nt{env().getDim(Tdir)}; + const int nnoise{par().nnoise}; + const int nvec{par().nvec}; + envCreate(NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); } // execution /////////////////////////////////////////////////////////////////// template void TNoises::execute(void) { - const int Nt{env().getDim(Tdir)}; - const int nnoise{par().nnoise}; - const int nvec{par().nvec}; - const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false) }; - const int LI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI, nvec, false) }; - const bool full_tdil{ TI == Nt }; \ - const bool exact_distillation{ full_tdil && LI == nvec }; \ - std::string UniqueIdentifier{par().UniqueIdentifier}; - if( UniqueIdentifier.length() == 0 ) { - UniqueIdentifier = getName(); - } - UniqueIdentifier.append( std::to_string( vm().getTrajectory() ) ); - - // We use our own seeds so we can specify different noises per quark - GridSerialRNG sRNG; - sRNG.SeedUniqueString(UniqueIdentifier); - Real rn; - auto &noise = envGet(NoiseTensor, getName()); - for( int inoise = 0; inoise < nnoise; inoise++ ) { - for( int t = 0; t < Nt; t++ ) { - for( int ivec = 0; ivec < nvec; ivec++ ) { - for( int is = 0; is < Ns; is++ ) { - if( exact_distillation ) - noise(inoise, t, ivec, is) = 1.; - else{ - random(sRNG,rn); - // We could use a greater number of complex roots of unity - // ... but this seems to work well - noise(inoise, t, ivec, is) = (rn > 0.5) ? -1 : 1; - } + const int Nt{env().getDim(Tdir)}; + const int nnoise{par().nnoise}; + const int nvec{par().nvec}; + const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false) }; + const int LI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI, nvec, false) }; + const bool full_tdil{ TI == Nt }; \ + const bool exact_distillation{ full_tdil && LI == nvec }; \ + std::string UniqueIdentifier{par().UniqueIdentifier}; + if (UniqueIdentifier.empty()) + UniqueIdentifier = getName(); + UniqueIdentifier.append( std::to_string( vm().getTrajectory() ) ); + + // We use our own seeds so we can specify different noises per quark + GridSerialRNG sRNG; + sRNG.SeedUniqueString(UniqueIdentifier); + Real rn; + auto &noise = envGet(NoiseTensor, getName()); + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int t = 0; t < Nt; t++) { + for (int ivec = 0; ivec < nvec; ivec++) { + for (int is = 0; is < Ns; is++) { + if (exact_distillation) + noise(inoise, t, ivec, is) = 1.; + else{ + random(sRNG,rn); + // We could use a greater number of complex roots of unity + // ... but this seems to work well + noise(inoise, t, ivec, is) = (rn > 0.5) ? -1 : 1; + } + } + } } - } } - } } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 3562c4d2..130fa669 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -69,11 +69,11 @@ public: // execution virtual void execute(void); protected: - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; + GridCartesian * grid3d; // Owned by me, so I must delete it + GridCartesian * grid4d; protected: - virtual void Cleanup(void); - + virtual void Cleanup(void); + }; MODULE_REGISTER_TMP(PerambFromSolve, TPerambFromSolve, MDistil); @@ -90,100 +90,98 @@ TPerambFromSolve::TPerambFromSolve(const std::string name) template TPerambFromSolve::~TPerambFromSolve(void) { - Cleanup(); + Cleanup(); }; - // dependencies/products /////////////////////////////////////////////////////// template std::vector TPerambFromSolve::getInput(void) { - return std::vector{ par().solve, par().eigenPack }; + return std::vector{ par().solve, par().eigenPack }; } template std::vector TPerambFromSolve::getOutput(void) { - return std::vector{ getName() }; + return std::vector{ getName() }; } // setup /////////////////////////////////////////////////////////////////////// template void TPerambFromSolve::setup(void) { - Cleanup(); - DISTIL_PARAMETERS_DEFINE( true ); - const int nvec_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().nvec_reduced, nvec, true) }; - const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, true) }; - grid4d = env().getGrid(); - grid3d = MakeLowerDimGrid(grid4d); - envCreate(PerambTensor, getName(), 1, Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); - envCreate(NoiseTensor, getName() + "_noise", 1, nnoise, Nt, nvec, Ns ); - envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); - envTmpLat(LatticeColourVector, "result_nospin"); + Cleanup(); + DISTIL_PARAMETERS_DEFINE( true ); + const int nvec_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().nvec_reduced, nvec, true) }; + const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, true) }; + grid4d = env().getGrid(); + grid3d = MakeLowerDimGrid(grid4d); + envCreate(PerambTensor, getName(), 1, Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); + envCreate(NoiseTensor, getName() + "_noise", 1, nnoise, Nt, nvec, Ns ); + envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + envTmpLat(LatticeColourVector, "result_nospin"); } template void TPerambFromSolve::Cleanup(void) { - if( grid3d != nullptr ) { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; + if (grid3d != nullptr) + { + delete grid3d; + grid3d = nullptr; + } + grid4d = nullptr; } // execution /////////////////////////////////////////////////////////////////// template void TPerambFromSolve::execute(void) { - GridCartesian * grid4d = env().getGrid(); - const int Ntlocal{grid4d->LocalDimensions()[3]}; - const int Ntfirst{grid4d->LocalStarts()[3]}; - DISTIL_PARAMETERS_DEFINE( false ); - const int nvec_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().nvec_reduced, nvec, false) }; - const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, false) }; - auto &perambulator = envGet(PerambTensor, getName()); - auto &solve = envGet(std::vector, par().solve); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - - envGetTmp(LatticeColourVector, result_nospin); - envGetTmp(LatticeColourVector, result_3d); - envGetTmp(LatticeColourVector, evec3d); - - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI_reduced; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - for (int is = 0; is < Ns; is++) { - result_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); - for (int ivec = 0; ivec < nvec_reduced; ivec++) { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); - LOG(Message) << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator.tensor(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; - } + GridCartesian * grid4d = env().getGrid(); + const int Ntlocal{grid4d->LocalDimensions()[3]}; + const int Ntfirst{grid4d->LocalStarts()[3]}; + DISTIL_PARAMETERS_DEFINE( false ); + const int nvec_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().nvec_reduced, nvec, false) }; + const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, false) }; + auto &perambulator = envGet(PerambTensor, getName()); + auto &solve = envGet(std::vector, par().solve); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + + envGetTmp(LatticeColourVector, result_nospin); + envGetTmp(LatticeColourVector, result_3d); + envGetTmp(LatticeColourVector, evec3d); + + for (int inoise = 0; inoise < nnoise; inoise++) { + for (int dk = 0; dk < LI_reduced; dk++) { + for (int dt = 0; dt < Nt_inv; dt++) { + for (int ds = 0; ds < SI; ds++) { + for (int is = 0; is < Ns; is++) { + result_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); + for (int ivec = 0; ivec < nvec_reduced; ivec++) { + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); + pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); + LOG(Message) << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator.tensor(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; + } + } + } + } } - } } - } } - } - - if(grid4d->IsBoss()) { - std::string sPerambName{par().PerambFileName}; - if( sPerambName.length() == 0 ) - sPerambName = getName(); - sPerambName.append( "." ); - sPerambName.append( std::to_string(vm().getTrajectory())); - perambulator.write(sPerambName.c_str()); - } + if(grid4d->IsBoss()) + { + std::string sPerambName{par().PerambFileName}; + if (sPerambName.empty()) + sPerambName = getName(); + sPerambName.append( "." ); + sPerambName.append( std::to_string(vm().getTrajectory())); + perambulator.write(sPerambName.c_str()); + } } END_MODULE_NAMESPACE - END_HADRONS_NAMESPACE - #endif // Hadrons_MDistil_PerambFromSolve_hpp_ diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 52ec7501..e2ab7e0d 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -126,7 +126,7 @@ void TPerambulator::setup(void) grid3d = MakeLowerDimGrid(grid4d); DISTIL_PARAMETERS_DEFINE( true ); const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; - if( !UnsmearedSinkFileName.empty() ) + if (!UnsmearedSinkFileName.empty()) bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, true ) != 0 ); envCreate(PerambTensor, getName(), 1, Nt,nvec,LI,nnoise,Nt_inv,SI); @@ -152,7 +152,7 @@ void TPerambulator::setup(void) template void TPerambulator::Cleanup(void) { - if( grid3d != nullptr ) + if (grid3d != nullptr) { delete grid3d; grid3d = nullptr; @@ -185,67 +185,62 @@ void TPerambulator::execute(void) const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; - + + for (int inoise = 0; inoise < nnoise; inoise++) { - int t_inv; - for (int inoise = 0; inoise < nnoise; inoise++) + for (int dk = 0; dk < LI; dk++) { - for (int dk = 0; dk < LI; dk++) + for (int dt = 0; dt < Nt_inv; dt++) { - for (int dt = 0; dt < Nt_inv; dt++) + for (int ds = 0; ds < SI; ds++) { - for (int ds = 0; ds < SI; ds++) + LOG(Message) << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; + dist_source = 0; + tmp3d_nospin = 0; + evec3d = 0; + for (int it = dt; it < Nt; it += TI) { - LOG(Message) << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; - dist_source = 0; - tmp3d_nospin = 0; - evec3d = 0; - for (int it = dt; it < Nt; it += TI) + const int t_inv{full_tdil ? tsrc : it}; + if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { - if (full_tdil) t_inv = tsrc; else t_inv = it; - if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) + for (int ik = dk; ik < nvec; ik += LI) { - for (int ik = dk; ik < nvec; ik += LI) + for (int is = ds; is < Ns; is += SI) { - for (int is = ds; is < Ns; is += SI) - { - ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); - tmp3d=0; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=0; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Tdir); - dist_source += tmp2; - } + ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); + tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); + tmp3d=0; + pokeSpin(tmp3d,tmp3d_nospin,is); + tmp2=0; + InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Tdir); + dist_source += tmp2; } } } - result=0; - v4dtmp = dist_source; - if (Ls_ == 1) + } + result=0; + v4dtmp = dist_source; + if (Ls_ == 1) + solver(result, v4dtmp); + else + { + mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); + solver(v5dtmp_sol, v5dtmp); + mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); + result = v4dtmp; + } + if (!UnsmearedSinkFileName.empty()) + unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; + for (int is = 0; is < Ns; is++) + { + result_nospin = peekSpin(result,is); + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - solver(result, v4dtmp); - } - else - { - mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); - solver(v5dtmp_sol, v5dtmp); - mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); - result = v4dtmp; - } - if( !UnsmearedSinkFileName.empty() ) - unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; - for (int is = 0; is < Ns; is++) - { - result_nospin = peekSpin(result,is); - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) + ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); + for (int ivec = 0; ivec < nvec; ivec++) { - ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); - for (int ivec = 0; ivec < nvec; ivec++) - { - ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); - } + ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); + pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); } } } From 4f9a7c5d76c19e7120be96b2c19aa28b9aaa2319 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Sat, 2 Nov 2019 16:50:29 +0000 Subject: [PATCH 306/347] Back out unnecessary change --- tests/IO/Test_serialisation.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/IO/Test_serialisation.cc b/tests/IO/Test_serialisation.cc index a0d4720a..7f3e5bca 100644 --- a/tests/IO/Test_serialisation.cc +++ b/tests/IO/Test_serialisation.cc @@ -30,7 +30,6 @@ Author: Michael Marshall /* END LEGAL */ #include #include -#include using namespace Grid; From 6f0439c0e4157e330701909fbadf68debc4365b0 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 4 Nov 2019 15:50:14 +0000 Subject: [PATCH 307/347] Remove unnecessary cast --- Hadrons/Modules/MDistil/Perambulator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index e2ab7e0d..b4339656 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -240,7 +240,7 @@ void TPerambulator::execute(void) for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); + pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); } } } From 22c654182af30273d55eb3df55fa52faecb00c07 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 4 Nov 2019 17:24:34 +0000 Subject: [PATCH 308/347] Fixes for GPU compile --- Grid/qcd/utils/BaryonUtils.h | 16 ++++++++-------- Hadrons/Modules/MDistil/DistilCommon.hpp | 3 ++- Hadrons/Modules/MDistil/Perambulator.hpp | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index 73d41422..ad3236da 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -43,7 +43,7 @@ public: typedef typename ComplexField::vector_object vobj; static constexpr int epsilon[6][3] = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - static constexpr Complex epsilon_sgn[6]= {1,1,1,-1,-1,-1}; + static constexpr int epsilon_sgn[6]= {1,1,1,-1,-1,-1}; private: template @@ -86,7 +86,7 @@ public: template constexpr int BaryonUtils::epsilon[6][3]; template -constexpr Complex BaryonUtils::epsilon_sgn[6]; +constexpr int BaryonUtils::epsilon_sgn[6]; template template @@ -123,7 +123,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1()(gamma_left,gamma_left)(c_right,c_left)*D2g()(alpha_right,beta_left)(a_right,a_left)*gD3()(alpha_right,beta_left)(b_right,b_left); }}} } //This is the \delta_{456}^{231} part @@ -132,7 +132,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1g()(gamma_left,beta_left)(c_right,a_left)*D2()(alpha_right,beta_left)(a_right,b_left)*gD3()(alpha_right,gamma_left)(b_right,c_left); }}} } //This is the \delta_{456}^{312} part @@ -141,7 +141,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1()(gamma_left,beta_left)(c_right,b_left)*D2()(alpha_right,gamma_left)(a_right,c_left)*gD3g()(alpha_right,beta_left)(b_right,a_left); }}} } //This is the \delta_{456}^{132} part @@ -150,7 +150,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1()(gamma_left,gamma_left)(c_right,c_left)*D2()(alpha_right,beta_left)(a_right,b_left)*gD3g()(alpha_right,beta_left)(b_right,a_left); }}} } //This is the \delta_{456}^{321} part @@ -159,7 +159,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1()(gamma_left,beta_left)(c_right,b_left)*D2g()(alpha_right,beta_left)(a_right,a_left)*gD3()(alpha_right,gamma_left)(b_right,c_left); }}} } //This is the \delta_{456}^{213} part @@ -168,7 +168,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1g()(gamma_left,beta_left)(c_right,a_left)*D2()(alpha_right,gamma_left)(a_right,c_left)*gD3()(alpha_right,beta_left)(b_right,b_left); }}} } } diff --git a/Hadrons/Modules/MDistil/DistilCommon.hpp b/Hadrons/Modules/MDistil/DistilCommon.hpp index 96621367..16b87bf1 100644 --- a/Hadrons/Modules/MDistil/DistilCommon.hpp +++ b/Hadrons/Modules/MDistil/DistilCommon.hpp @@ -115,11 +115,12 @@ inline void RotateEigen(std::vector & evec) if( cplx0.imag() == 0 ) std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; else { - const Real cplx0_mag = Grid::abs(cplx0); #ifdef GRID_NVCC + const Real cplx0_mag = thrust::abs(cplx0); const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); const Real argphase = thrust::arg(phase); #else + const Real cplx0_mag = std::abs(cplx0); const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); const Real argphase = std::arg(phase); #endif diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index b4339656..e2ab7e0d 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -240,7 +240,7 @@ void TPerambulator::execute(void) for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),innerProduct(evec3d, result_3d),is); + pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); } } } From 5c23abe507a757a5ffb7bcaa9e8837dacf805971 Mon Sep 17 00:00:00 2001 From: ferben Date: Thu, 7 Nov 2019 11:57:40 +0000 Subject: [PATCH 309/347] commented on Notation --- Hadrons/Modules/MDistil/DistilCommon.hpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Hadrons/Modules/MDistil/DistilCommon.hpp b/Hadrons/Modules/MDistil/DistilCommon.hpp index 16b87bf1..da46b918 100644 --- a/Hadrons/Modules/MDistil/DistilCommon.hpp +++ b/Hadrons/Modules/MDistil/DistilCommon.hpp @@ -42,6 +42,23 @@ BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** Distillation code that is common across modules + + Documentation on how t use this code available at + + * https://aportelli.github.io/Hadrons-doc/#/mdistil * + + Notation for (stochastic) DistilParameters taken from 1104.3870: + + TI is interlaced dilution in time (corresponding to Nt = time-dimension of the lattice) + LI is interlaced dilution in laplacian-eigenvector space (corresponding to nvec) + SI is interlaced dilution in spin (corresponding to Ns, taken from Grid, usually Ns=4) + + This code automatically computes perambulators using exact distillation if + * (TI,LI,SI) = (Nt,nvec,Ns) * + In this case, nnoise=1 and Noises is set to an array of values =1 as well. + tsrc then specifies the only timeslice on which the sources are supported. + (( for stochastic distillation, the vaue of tsrc has no meaning in this code )) + ******************************************************************************/ struct DistilParameters: Serializable { From a8f3a111a5d8c604e53ba0f509a1bec97f77281f Mon Sep 17 00:00:00 2001 From: ferben Date: Thu, 7 Nov 2019 13:45:38 +0000 Subject: [PATCH 310/347] added Serial RNG - code compiles but not tested! --- Hadrons/Environment.cc | 10 ++++++++++ Hadrons/Environment.hpp | 3 +++ Hadrons/Module.cc | 15 +++++++++++++++ Hadrons/Module.hpp | 2 +- 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/Hadrons/Environment.cc b/Hadrons/Environment.cc index 2287a7fd..449646e4 100644 --- a/Hadrons/Environment.cc +++ b/Hadrons/Environment.cc @@ -84,6 +84,16 @@ GridParallelRNG * Environment::get4dRng(void) return rng4d_.get(); } +GridSerialRNG * Environment::getSerialRng(void) +{ + if (rngSerial_ == nullptr) + { + rngSerial_.reset(new GridSerialRNG()); + } + + return rngSerial_.get(); +} + // general memory management /////////////////////////////////////////////////// void Environment::addObject(const std::string name, const int moduleAddress) { diff --git a/Hadrons/Environment.hpp b/Hadrons/Environment.hpp index 24f6b31e..8066a0e7 100644 --- a/Hadrons/Environment.hpp +++ b/Hadrons/Environment.hpp @@ -74,6 +74,7 @@ public: typedef std::unique_ptr GridPt; typedef std::unique_ptr GridRbPt; typedef std::unique_ptr RngPt; + typedef std::unique_ptr SerialRngPt; enum class Storage {object, cache, temporary}; private: struct ObjInfo @@ -114,6 +115,7 @@ public: double getVolume(void) const; // random number generator GridParallelRNG * get4dRng(void); + GridSerialRNG * getSerialRng(void); // general memory management void addObject(const std::string name, const int moduleAddress = -1); @@ -183,6 +185,7 @@ private: unsigned int nd_; // random number generator RngPt rng4d_{nullptr}; + SerialRngPt rngSerial_{nullptr}; // object store std::vector object_; std::map objectAddress_; diff --git a/Hadrons/Module.cc b/Hadrons/Module.cc index 8e332d5a..17017513 100644 --- a/Hadrons/Module.cc +++ b/Hadrons/Module.cc @@ -93,3 +93,18 @@ GridParallelRNG & ModuleBase::rng4d(void) return r; } + +GridSerialRNG & ModuleBase::rngSerial(void) +{ + auto &r = *env().getSerialRng(); + + if (makeSeedString() != seed_) + { + seed_ = makeSeedString(); + LOG(Message) << "Seeding Serial RNG " << &r << " with string '" + << seed_ << "'" << std::endl; + r.SeedUniqueString(seed_); + } + + return r; +} diff --git a/Hadrons/Module.hpp b/Hadrons/Module.hpp index 95db9511..3bea7ff2 100644 --- a/Hadrons/Module.hpp +++ b/Hadrons/Module.hpp @@ -1,7 +1,6 @@ /************************************************************************************* Grid physics library, www.github.com/paboyle/Grid - Source file: Hadrons/Module.hpp Copyright (C) 2015-2019 @@ -196,6 +195,7 @@ protected: DEFINE_VM_ALIAS; // RNG seeded from module string GridParallelRNG &rng4d(void); + GridSerialRNG &rngSerial(void); private: std::string makeSeedString(void); private: From 293bfe17d1162d2dc8ec7e2cbe24ff0954c95f37 Mon Sep 17 00:00:00 2001 From: ferben Date: Thu, 7 Nov 2019 14:00:40 +0000 Subject: [PATCH 311/347] added code to the noise module... --- Hadrons/Modules/MDistil/Noises.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 463ec635..8b7d1f45 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -119,8 +119,6 @@ void TNoises::execute(void) UniqueIdentifier.append( std::to_string( vm().getTrajectory() ) ); // We use our own seeds so we can specify different noises per quark - GridSerialRNG sRNG; - sRNG.SeedUniqueString(UniqueIdentifier); Real rn; auto &noise = envGet(NoiseTensor, getName()); for (int inoise = 0; inoise < nnoise; inoise++) { @@ -130,7 +128,7 @@ void TNoises::execute(void) if (exact_distillation) noise(inoise, t, ivec, is) = 1.; else{ - random(sRNG,rn); + random(rngSerial(),rn); // We could use a greater number of complex roots of unity // ... but this seems to work well noise(inoise, t, ivec, is) = (rn > 0.5) ? -1 : 1; From 65aa54804e00b9a24318d426f7c69c69ab746778 Mon Sep 17 00:00:00 2001 From: Felix Erben Date: Fri, 8 Nov 2019 11:15:51 +0000 Subject: [PATCH 312/347] added comments --- Hadrons/Modules/MDistil/LapEvec.hpp | 8 ++++++++ Hadrons/Modules/MDistil/PerambFromSolve.hpp | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 428585b0..7cf3a517 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -38,6 +38,14 @@ BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** Laplacian eigenvectors - parameters + + Computes the eigenvectors of the 3D-Laplacian, built from stout-smeared + gauge links with the specified number of steps and smearing parameter rho. + The smearing is only applied to the spatial components of the gauge field, + i.e. rho_{4i} = rho_{i4} = rho_{44} = 0. + + Chebyshev-preconditioning is needed for convergence of the nvec lowest + eigenvectors. ******************************************************************************/ diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 130fa669..14340408 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -36,7 +36,12 @@ BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** - * PerambFromSolve * + * PerambFromSolve + + This module computes a perambulator from an already completed solve. + Optionally, the number of eigenvectors used in the perambulator and the + parameter LI can be chosen to be lower than the ones in the solve, allowing + for a study of the signal with different values of nvec. ******************************************************************************/ class PerambFromSolvePar: Serializable From f8e1941327bec125ad0fb6b72c65a9e39fab9c0d Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 8 Nov 2019 11:55:00 +0000 Subject: [PATCH 313/347] Implemented specialisations of NamedTensor as derived classes, however this suffers a number of problems: 1) virtual functions not available in base class constructor where I'd like to use them - e.g. IndexNames 2) Must define new constructors in derived classes ... so the specialisations are fatter than I'd like. Would prefer to revert to specifying tensor name and index name defaults in template --- Hadrons/Distil.hpp | 123 +++++++++++++--------- Hadrons/Modules/MDistil/DistilVectors.hpp | 4 +- Hadrons/Modules/MDistil/Noises.hpp | 19 ++-- Hadrons/Modules/MDistil/Perambulator.cc | 20 +++- Hadrons/Modules/MDistil/Perambulator.hpp | 4 +- 5 files changed, 102 insertions(+), 68 deletions(-) diff --git a/Hadrons/Distil.hpp b/Hadrons/Distil.hpp index ef2b3cbf..a5f87c98 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/Distil.hpp @@ -37,18 +37,18 @@ BEGIN_HADRONS_NAMESPACE /****************************************************************************** NamedTensor - This is an Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) - They can be persisted to disk in tag Name_, and IndexNames are validated on load. - TODO: WHAT TO SAVE / VALIDATE ON LOAD (Override to warn instead of assert on load) - Ensemble string - Configuration number - Noise unique string - Distillation parameters + Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order), together with a name for each index. + Index names are mutable, but tensor dimensionality is not (size of each dimension is mutable). + They can be persisted to / restored from disk, by default using tag Name. + During restore from disk, these validations are performed: + 1) Tensor dimensionality must match + 2) IndexNames are validated against current values + 3) If the tensor has non-zero size, the tensor being loaded must have same extent in each dimension ******************************************************************************/ extern const std::string NamedTensorFileExtension; -template &IndexNames_> +template class NamedTensor : Serializable { public: @@ -59,40 +59,40 @@ public: GRID_SERIALIZABLE_CLASS_MEMBERS(NamedTensor, ET, tensor, std::vector, IndexNames ); - - // Get the default index names as std::vector - std::vector DefaultIndexNames() - { - std::vector names{NumIndices_}; - for (std::size_t i = 0; i < NumIndices_; i++) - names[i] = IndexNames_[i]; - return names; - } - + + // Name of the object and Index names as set in the constructor + const std::string &Name; + const std::vector &DefaultIndexNames; + + virtual ~NamedTensor(){}; // Default constructor (assumes tensor will be loaded from file) - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor() : IndexNames{DefaultIndexNames()} {} + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name_, const std::vector &IndexNames_) + : IndexNames{IndexNames_}, Name{Name_}, DefaultIndexNames{IndexNames_} {} // Construct a named tensor explicitly specifying size of each dimension template - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(Eigen::Index firstDimension, IndexTypes... otherDimensions) - : tensor(firstDimension, otherDimensions...), IndexNames{DefaultIndexNames()} + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name_, const std::vector &IndexNames_, + Eigen::Index firstDimension, IndexTypes... otherDimensions) + : tensor(firstDimension, otherDimensions...), IndexNames{IndexNames_}, Name{Name_}, DefaultIndexNames{IndexNames_} { assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); } - + // Do my index names match the default for my type? - bool ValidateIndexNames() const + bool ValidateIndexNames( const std::vector &CheckNames ) const { + assert( CheckNames.size() == NumIndices_ && "Bug: CheckNames don't match NumIndices_" ); bool bSame{ IndexNames.size() == NumIndices_ }; for( std::size_t i = 0; bSame && i < NumIndices_; i++ ) { - bSame = IndexNames[i].size() == IndexNames_[i].size() - && std::equal( IndexNames[i].begin(), IndexNames[i].end(), IndexNames_[i].begin(), + bSame = IndexNames[i].size() == CheckNames[i].size() + && std::equal( IndexNames[i].begin(), IndexNames[i].end(), CheckNames[i].begin(), [](const char & c1, const char & c2){ return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); } return bSame; } - + bool ValidateIndexNames() const { return ValidateIndexNames(DefaultIndexNames); } + #ifdef HAVE_HDF5 using Default_Reader = Grid::Hdf5Reader; using Default_Writer = Grid::Hdf5Writer; @@ -101,40 +101,40 @@ public: using Default_Writer = Grid::BinaryWriter; #endif - template void write(Writer &w, const std::string &Tag = Name_) const - { write(w, Tag, *this); } - - inline void write(const std::string &filename, const std::string &Tag = Name_) const + void write(const std::string &FileName, const std::string &Tag) const { - std::string sFileName{filename}; - sFileName.append( NamedTensorFileExtension ); - LOG(Message) << "Writing " << Name_ << " to file " << sFileName << " tag " << Tag << std::endl; - Default_Writer w( sFileName ); - write( w, Tag ); + std::string FileName_{FileName}; + FileName_.append( NamedTensorFileExtension ); + LOG(Message) << "Writing " << Name << " to file " << FileName_ << " tag " << Tag << std::endl; + Default_Writer w( FileName_ ); + write( w, Tag, *this ); } - - // Read and validate index names - template void read(Reader &r, bool bValidate = true, const std::string &Tag = Name_) + void write(const std::string &FileName) const { return write(FileName, Name); } + + // Read tensor. + // Validate: + // 1) index names (if requested) + // 2) index dimensions (if they are non-zero when called) + template void read(Reader &r, bool bValidate, const std::string &Tag) { // Grab index names and dimensions std::vector OldIndexNames{std::move(IndexNames)}; - typename ET::Dimensions OldDimensions{tensor.dimensions()}; + const typename ET::Dimensions OldDimensions{tensor.dimensions()}; read(r, Tag, *this); const typename ET::Dimensions & NewDimensions{tensor.dimensions()}; for (int i = 0; i < NumIndices_; i++) assert(OldDimensions[i] == 0 || OldDimensions[i] == NewDimensions[i] && "NamedTensor::read dimension size"); if (bValidate) - assert(ValidateIndexNames() && "NamedTensor::read dimension name"); + assert(ValidateIndexNames(OldIndexNames) && "NamedTensor::read dimension name"); } - - inline void read (const std::string &filename, bool bValidate = true, const std::string &Tag = Name_) + template void read(Reader &r, bool bValidate = true) { read(r, bValidate, Name); } + + inline void read (const std::string &FileName, bool bValidate, const std::string &Tag) { - std::string sFileName{filename}; - sFileName.append( NamedTensorFileExtension ); - LOG(Message) << "Reading " << Name_ << " from file " << sFileName << " tag " << Tag << std::endl; - Default_Reader r(sFileName); + Default_Reader r(FileName + NamedTensorFileExtension); read(r, bValidate, Tag); } + inline void read (const std::string &FileName, bool bValidate= true) { return read(FileName, bValidate, Name); } }; /****************************************************************************** @@ -147,11 +147,34 @@ BEGIN_MODULE_NAMESPACE(MDistil) using LapEvecs = Grid::Hadrons::EigenPack; // Noise vector (index order: nnoise, nt, nvec, ns) -using NoiseTensor = Eigen::Tensor; -extern const std::string PerambTensorName; -extern const std::array PerambIndexNames; -using PerambTensor = NamedTensor; +class NoiseTensor : public NamedTensor +{ + static const std::string Name_; + static const std::vector DefaultIndexNames_; + public: + // Default constructor (assumes tensor will be loaded from file) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NoiseTensor() : NamedTensor{Name_, DefaultIndexNames_} {} + + // Construct a named tensor explicitly specifying size of each dimension + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NoiseTensor(Eigen::Index nNoise, Eigen::Index nT, Eigen::Index nVec, Eigen::Index nS) + : NamedTensor{Name_, DefaultIndexNames_, nNoise, nT, nVec, nS} {} +}; + +class PerambTensor : public NamedTensor +{ + static const std::string Name_; + static const std::vector DefaultIndexNames_; + public: + // Default constructor (assumes tensor will be loaded from file) + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PerambTensor() : NamedTensor{Name_, DefaultIndexNames_} {} + + // Construct a named tensor explicitly specifying size of each dimension + template + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PerambTensor(Eigen::Index nT, Eigen::Index nVec, Eigen::Index LI, Eigen::Index nNoise, Eigen::Index nT_inv, Eigen::Index SI) + : NamedTensor{Name_, DefaultIndexNames_, nT, nVec, LI, nNoise, nT_inv, SI} {} +}; END_MODULE_NAMESPACE END_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 0b549913..643e7d63 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -169,7 +169,7 @@ void TDistilVectors::setup(void) const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; - assert( nnoise >= static_cast( noise.dimension(0) ) && "Not enough noise vectors for perambulator" ); + assert( nnoise >= static_cast( noise.tensor.dimension(0) ) && "Not enough noise vectors for perambulator" ); // Nvec defaults to what's in the perambulator unless overriden const int nvec_per{ static_cast( perambulator.tensor.dimension(1) ) }; const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, nvec_per, true) }; @@ -254,7 +254,7 @@ void TDistilVectors::execute(void) for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += SI){ ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); + tmp3d_nospin = evec3d * noise.tensor(inoise, t_inv, ik, is); tmp3d=0; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=0; diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 8b7d1f45..c17a29fa 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -45,9 +45,9 @@ public: GRID_SERIALIZABLE_CLASS_MEMBERS(NoisesPar, int, nnoise, int, nvec, - std::string, UniqueIdentifier, std::string, TI, - std::string, LI) + std::string, LI, + std::string, NoiseFileName) }; template @@ -113,10 +113,6 @@ void TNoises::execute(void) const int LI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI, nvec, false) }; const bool full_tdil{ TI == Nt }; \ const bool exact_distillation{ full_tdil && LI == nvec }; \ - std::string UniqueIdentifier{par().UniqueIdentifier}; - if (UniqueIdentifier.empty()) - UniqueIdentifier = getName(); - UniqueIdentifier.append( std::to_string( vm().getTrajectory() ) ); // We use our own seeds so we can specify different noises per quark Real rn; @@ -126,17 +122,24 @@ void TNoises::execute(void) for (int ivec = 0; ivec < nvec; ivec++) { for (int is = 0; is < Ns; is++) { if (exact_distillation) - noise(inoise, t, ivec, is) = 1.; + noise.tensor(inoise, t, ivec, is) = 1.; else{ random(rngSerial(),rn); // We could use a greater number of complex roots of unity // ... but this seems to work well - noise(inoise, t, ivec, is) = (rn > 0.5) ? -1 : 1; + noise.tensor(inoise, t, ivec, is) = (rn > 0.5) ? -1 : 1; } } } } } + if (env().getGrid()->IsBoss()) + { + std::string sName {par().NoiseFileName}; + sName.append("."); + sName.append(std::to_string(vm().getTrajectory())); + noise.write(sName.c_str()); + } } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Perambulator.cc b/Hadrons/Modules/MDistil/Perambulator.cc index b141eb95..7b1d68d9 100644 --- a/Hadrons/Modules/MDistil/Perambulator.cc +++ b/Hadrons/Modules/MDistil/Perambulator.cc @@ -35,13 +35,23 @@ using namespace MDistil; template class Grid::Hadrons::MDistil::TPerambulator; +BEGIN_HADRONS_NAMESPACE + // Global constants for distillation -const std::string Grid::Hadrons::MDistil::PerambTensorName{ "Perambulator" }; -const std::array Grid::Hadrons::MDistil::PerambIndexNames{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; - #ifdef HAVE_HDF5 -extern const std::string Grid::Hadrons::NamedTensorFileExtension{".h5"}; +extern const std::string NamedTensorFileExtension{".h5"}; #else -extern const std::string Grid::Hadrons::NamedTensorFileExtension{".dat"}; +extern const std::string NamedTensorFileExtension{".dat"}; #endif + +BEGIN_MODULE_NAMESPACE(MDistil) + +const std::string NoiseTensor::Name_{"Noises"}; +const std::vector NoiseTensor::DefaultIndexNames_{"nNoise", "nT", "nVec", "nS"}; + +const std::string PerambTensor::Name_{"Perambulator"}; +const std::vector PerambTensor::DefaultIndexNames_{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; + +END_MODULE_NAMESPACE +END_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index e2ab7e0d..17ea41a2 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -208,7 +208,7 @@ void TPerambulator::execute(void) for (int is = ds; is < Ns; is += SI) { ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - tmp3d_nospin = evec3d * noise(inoise, t_inv, ik, is); + tmp3d_nospin = evec3d * noise.tensor(inoise, t_inv, ik, is); tmp3d=0; pokeSpin(tmp3d,tmp3d_nospin,is); tmp2=0; @@ -276,8 +276,6 @@ void TPerambulator::execute(void) if (grid4d->IsBoss()) { std::string sPerambName {par().PerambFileName}; - if (sPerambName.empty()) - sPerambName = getName(); sPerambName.append("."); sPerambName.append(std::to_string(vm().getTrajectory())); perambulator.write(sPerambName.c_str()); From e7d7ea4f8f66b7764a73d378c538069f07ac66a9 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 11 Nov 2019 12:55:45 +0000 Subject: [PATCH 314/347] added LoadNoise module --- Hadrons/Modules.hpp | 141 +++++++++++----------- Hadrons/modules.inc | 280 ++++++++++++++++++++++---------------------- 2 files changed, 215 insertions(+), 206 deletions(-) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index 8de2f85d..c3c0cf86 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -1,79 +1,82 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include +#include #include #include #include -#include -#include -#include -#include +#include +#include +#include #include #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 91bce4ad..f3e43d28 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -1,158 +1,164 @@ modules_cc =\ - Modules/MFermion/FreeProp.cc \ - Modules/MFermion/GaugeProp.cc \ - Modules/MFermion/EMLepton.cc \ - Modules/MIO/LoadA2AVectors.cc \ - Modules/MIO/LoadEigenPack.cc \ - Modules/MIO/LoadCosmHol.cc \ - Modules/MIO/LoadBinary.cc \ - Modules/MIO/LoadA2AMatrixDiskVector.cc \ - Modules/MIO/LoadCoarseEigenPack.cc \ - Modules/MIO/LoadNersc.cc \ - Modules/MAction/ZMobiusDWF.cc \ - Modules/MAction/ScaledDWF.cc \ - Modules/MAction/Wilson.cc \ - Modules/MAction/DWF.cc \ - Modules/MAction/WilsonClover.cc \ - Modules/MAction/MobiusDWF.cc \ Modules/MUtilities/RandomVectors.cc \ - Modules/MIO/LoadPerambulator.cc \ Modules/MUtilities/PrecisionCast.cc \ - Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ - Modules/MContraction/Gamma3pt.cc \ - Modules/MContraction/A2AFourQuarkContraction.cc \ - Modules/MContraction/A2AAslashField.cc \ - Modules/MContraction/A2ALoop.cc \ - Modules/MContraction/WeakEye3pt.cc \ - Modules/MContraction/WeakNonEye3pt.cc \ - Modules/MContraction/A2AMesonField.cc \ - Modules/MContraction/DiscLoop.cc \ - Modules/MContraction/Baryon.cc \ - Modules/MContraction/WeakMesonDecayKl2.cc \ - Modules/MContraction/Meson.cc \ - Modules/MDistil/DistilVectors.cc \ - Modules/MDistil/LapEvec.cc \ - Modules/MDistil/Noises.cc \ - Modules/MDistil/PerambFromSolve.cc \ - Modules/MDistil/Perambulator.cc \ - Modules/MSink/Point.cc \ - Modules/MSink/Smear.cc \ - Modules/MScalarSUN/TrPhi.cc \ - Modules/MScalarSUN/TrMag.cc \ - Modules/MScalarSUN/TrKinetic.cc \ - Modules/MScalarSUN/TwoPoint.cc \ - Modules/MScalarSUN/Grad.cc \ - Modules/MScalarSUN/TwoPointNPR.cc \ - Modules/MScalarSUN/StochFreeField.cc \ - Modules/MScalarSUN/TransProj.cc \ - Modules/MScalarSUN/EMT.cc \ - Modules/MScalarSUN/Div.cc \ - Modules/MNPR/Amputate.cc \ - Modules/MNPR/Bilinear.cc \ - Modules/MNPR/FourQuark.cc \ - Modules/MSource/SeqGamma.cc \ + Modules/MGauge/FundtoHirep.cc \ + Modules/MGauge/UnitEm.cc \ + Modules/MGauge/StoutSmearing.cc \ + Modules/MGauge/Electrify.cc \ + Modules/MGauge/Unit.cc \ + Modules/MGauge/StochEm.cc \ + Modules/MGauge/GaugeFix.cc \ + Modules/MGauge/Random.cc \ + Modules/MSolver/MixedPrecisionRBPrecCG.cc \ + Modules/MSolver/RBPrecCG.cc \ + Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSolver/A2AAslashVectors.cc \ + Modules/MSolver/A2AVectors.cc \ Modules/MSource/Z2.cc \ - Modules/MSource/Convolution.cc \ - Modules/MSource/Momentum.cc \ Modules/MSource/Wall.cc \ Modules/MSource/Point.cc \ - Modules/MSource/SeqAslash.cc \ + Modules/MSource/MomentumPhase.cc \ Modules/MSource/Gauss.cc \ + Modules/MSource/Convolution.cc \ + Modules/MSource/SeqGamma.cc \ + Modules/MSource/Momentum.cc \ Modules/MSource/SeqConserved.cc \ - Modules/MGauge/StoutSmearing.cc \ - Modules/MGauge/GaugeFix.cc \ - Modules/MGauge/Electrify.cc \ - Modules/MGauge/Random.cc \ - Modules/MGauge/Unit.cc \ - Modules/MGauge/UnitEm.cc \ - Modules/MGauge/FundtoHirep.cc \ - Modules/MGauge/StochEm.cc \ - Modules/MSolver/RBPrecCG.cc \ - Modules/MSolver/A2AAslashVectors.cc \ - Modules/MSolver/MixedPrecisionRBPrecCG.cc \ - Modules/MSolver/A2AVectors.cc \ - Modules/MSolver/LocalCoherenceLanczos.cc \ + Modules/MSource/SeqAslash.cc \ + Modules/MNPR/Bilinear.cc \ + Modules/MNPR/FourQuark.cc \ + Modules/MNPR/Amputate.cc \ + Modules/MContraction/Baryon.cc \ + Modules/MContraction/A2ALoop.cc \ + Modules/MContraction/A2AFourQuarkContraction.cc \ + Modules/MContraction/DiscLoop.cc \ + Modules/MContraction/WeakEye3pt.cc \ + Modules/MContraction/A2AAslashField.cc \ + Modules/MContraction/Meson.cc \ + Modules/MContraction/WeakMesonDecayKl2.cc \ + Modules/MContraction/WeakNonEye3pt.cc \ + Modules/MContraction/Gamma3pt.cc \ + Modules/MContraction/A2AMesonField.cc \ + Modules/MScalar/FreeProp.cc \ Modules/MScalar/ChargedProp.cc \ - Modules/MScalar/FreeProp.cc + Modules/MSink/Point.cc \ + Modules/MSink/Smear.cc \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.cc \ + Modules/MNoise/FullVolumeSpinColorDiagonal.cc \ + Modules/MIO/LoadA2AVectors.cc \ + Modules/MIO/LoadBinary.cc \ + Modules/MIO/LoadPerambulator.cc \ + Modules/MIO/LoadA2AMatrixDiskVector.cc \ + Modules/MIO/LoadDistilNoise.cc \ + Modules/MIO/LoadNersc.cc \ + Modules/MIO/LoadCoarseEigenPack.cc \ + Modules/MIO/LoadCosmHol.cc \ + Modules/MIO/LoadEigenPack.cc \ + Modules/MScalarSUN/Div.cc \ + Modules/MScalarSUN/TwoPointNPR.cc \ + Modules/MScalarSUN/TrPhi.cc \ + Modules/MScalarSUN/TransProj.cc \ + Modules/MScalarSUN/TwoPoint.cc \ + Modules/MScalarSUN/Grad.cc \ + Modules/MScalarSUN/EMT.cc \ + Modules/MScalarSUN/TrKinetic.cc \ + Modules/MScalarSUN/StochFreeField.cc \ + Modules/MScalarSUN/TrMag.cc \ + Modules/MDistil/PerambFromSolve.cc \ + Modules/MDistil/DistilVectors.cc \ + Modules/MDistil/LapEvec.cc \ + Modules/MDistil/Perambulator.cc \ + Modules/MDistil/Noises.cc \ + Modules/MAction/MobiusDWF.cc \ + Modules/MAction/DWF.cc \ + Modules/MAction/ScaledDWF.cc \ + Modules/MAction/Wilson.cc \ + Modules/MAction/ZMobiusDWF.cc \ + Modules/MAction/WilsonClover.cc \ + Modules/MFermion/EMLepton.cc \ + Modules/MFermion/FreeProp.cc \ + Modules/MFermion/GaugeProp.cc modules_hpp =\ - Modules/MFermion/GaugeProp.hpp \ - Modules/MFermion/EMLepton.hpp \ - Modules/MFermion/FreeProp.hpp \ - Modules/MIO/LoadCosmHol.hpp \ - Modules/MIO/LoadEigenPack.hpp \ - Modules/MIO/LoadA2AVectors.hpp \ - Modules/MIO/LoadA2AMatrixDiskVector.hpp \ - Modules/MIO/LoadCoarseEigenPack.hpp \ - Modules/MIO/LoadNersc.hpp \ - Modules/MIO/LoadBinary.hpp \ - Modules/MIO/LoadPerambulator.hpp \ - Modules/MAction/ZMobiusDWF.hpp \ - Modules/MAction/MobiusDWF.hpp \ - Modules/MAction/Wilson.hpp \ - Modules/MAction/DWF.hpp \ - Modules/MAction/WilsonClover.hpp \ - Modules/MAction/ScaledDWF.hpp \ - Modules/MUtilities/RandomVectors.hpp \ Modules/MUtilities/PrecisionCast.hpp \ - Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ - Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ - Modules/MContraction/DiscLoop.hpp \ - Modules/MContraction/Meson.hpp \ - Modules/MContraction/WeakMesonDecayKl2.hpp \ - Modules/MContraction/A2AMesonField.hpp \ - Modules/MContraction/WeakNonEye3pt.hpp \ - Modules/MContraction/Gamma3pt.hpp \ - Modules/MContraction/A2AAslashField.hpp \ - Modules/MContraction/A2AFourQuarkContraction.hpp \ - Modules/MContraction/Baryon.hpp \ - Modules/MDistil/DistilVectors.hpp \ - Modules/MDistil/LapEvec.hpp \ - Modules/MDistil/Noises.hpp \ - Modules/MDistil/PerambFromSolve.hpp \ - Modules/MDistil/Perambulator.hpp \ - Modules/MContraction/WeakEye3pt.hpp \ - Modules/MContraction/A2ALoop.hpp \ - Modules/MSink/Point.hpp \ - Modules/MSink/Smear.hpp \ - Modules/MScalarSUN/StochFreeField.hpp \ - Modules/MScalarSUN/EMT.hpp \ - Modules/MScalarSUN/Utils.hpp \ - Modules/MScalarSUN/TwoPoint.hpp \ - Modules/MScalarSUN/TransProj.hpp \ - Modules/MScalarSUN/TwoPointNPR.hpp \ - Modules/MScalarSUN/TrPhi.hpp \ - Modules/MScalarSUN/Grad.hpp \ - Modules/MScalarSUN/TrMag.hpp \ - Modules/MScalarSUN/Div.hpp \ - Modules/MScalarSUN/TrKinetic.hpp \ - Modules/MNPR/Amputate.hpp \ - Modules/MNPR/FourQuark.hpp \ - Modules/MNPR/Bilinear.hpp \ - Modules/MSource/SeqAslash.hpp \ - Modules/MSource/Momentum.hpp \ - Modules/MSource/Z2.hpp \ - Modules/MSource/Point.hpp \ - Modules/MSource/Gauss.hpp \ - Modules/MSource/SeqConserved.hpp \ - Modules/MSource/Wall.hpp \ - Modules/MSource/SeqGamma.hpp \ - Modules/MSource/Convolution.hpp \ - Modules/MGauge/Random.hpp \ + Modules/MUtilities/RandomVectors.hpp \ Modules/MGauge/FundtoHirep.hpp \ - Modules/MGauge/StochEm.hpp \ - Modules/MGauge/UnitEm.hpp \ + Modules/MGauge/Electrify.hpp \ Modules/MGauge/GaugeFix.hpp \ Modules/MGauge/StoutSmearing.hpp \ Modules/MGauge/Unit.hpp \ - Modules/MGauge/Electrify.hpp \ - Modules/MSolver/A2AVectors.hpp \ - Modules/MSolver/RBPrecCG.hpp \ - Modules/MSolver/LocalCoherenceLanczos.hpp \ + Modules/MGauge/UnitEm.hpp \ + Modules/MGauge/Random.hpp \ + Modules/MGauge/StochEm.hpp \ Modules/MSolver/Guesser.hpp \ Modules/MSolver/MixedPrecisionRBPrecCG.hpp \ + Modules/MSolver/A2AVectors.hpp \ + Modules/MSolver/LocalCoherenceLanczos.hpp \ Modules/MSolver/A2AAslashVectors.hpp \ + Modules/MSolver/RBPrecCG.hpp \ + Modules/MSource/Convolution.hpp \ + Modules/MSource/Point.hpp \ + Modules/MSource/Gauss.hpp \ + Modules/MSource/MomentumPhase.hpp \ + Modules/MSource/Z2.hpp \ + Modules/MSource/SeqAslash.hpp \ + Modules/MSource/SeqConserved.hpp \ + Modules/MSource/SeqGamma.hpp \ + Modules/MSource/Momentum.hpp \ + Modules/MSource/Wall.hpp \ + Modules/MNPR/Amputate.hpp \ + Modules/MNPR/Bilinear.hpp \ + Modules/MNPR/FourQuark.hpp \ + Modules/MContraction/Meson.hpp \ + Modules/MContraction/WeakMesonDecayKl2.hpp \ + Modules/MContraction/WeakNonEye3pt.hpp \ + Modules/MContraction/A2AFourQuarkContraction.hpp \ + Modules/MContraction/DiscLoop.hpp \ + Modules/MContraction/WeakEye3pt.hpp \ + Modules/MContraction/A2AAslashField.hpp \ + Modules/MContraction/Gamma3pt.hpp \ + Modules/MContraction/A2AMesonField.hpp \ + Modules/MContraction/A2ALoop.hpp \ + Modules/MContraction/Baryon.hpp \ + Modules/MScalar/FreeProp.hpp \ Modules/MScalar/ChargedProp.hpp \ Modules/MScalar/Scalar.hpp \ - Modules/MScalar/FreeProp.hpp + Modules/MSink/Point.hpp \ + Modules/MSink/Smear.hpp \ + Modules/MNoise/FullVolumeSpinColorDiagonal.hpp \ + Modules/MNoise/TimeDilutedSpinColorDiagonal.hpp \ + Modules/MIO/LoadBinary.hpp \ + Modules/MIO/LoadNersc.hpp \ + Modules/MIO/LoadEigenPack.hpp \ + Modules/MIO/LoadDistilNoise.hpp \ + Modules/MIO/LoadA2AMatrixDiskVector.hpp \ + Modules/MIO/LoadCoarseEigenPack.hpp \ + Modules/MIO/LoadPerambulator.hpp \ + Modules/MIO/LoadA2AVectors.hpp \ + Modules/MIO/LoadCosmHol.hpp \ + Modules/MScalarSUN/TwoPointNPR.hpp \ + Modules/MScalarSUN/TransProj.hpp \ + Modules/MScalarSUN/TwoPoint.hpp \ + Modules/MScalarSUN/TrPhi.hpp \ + Modules/MScalarSUN/Utils.hpp \ + Modules/MScalarSUN/TrMag.hpp \ + Modules/MScalarSUN/EMT.hpp \ + Modules/MScalarSUN/Grad.hpp \ + Modules/MScalarSUN/StochFreeField.hpp \ + Modules/MScalarSUN/Div.hpp \ + Modules/MScalarSUN/TrKinetic.hpp \ + Modules/MDistil/PerambFromSolve.hpp \ + Modules/MDistil/DistilVectors.hpp \ + Modules/MDistil/Noises.hpp \ + Modules/MDistil/LapEvec.hpp \ + Modules/MDistil/Perambulator.hpp \ + Modules/MDistil/DistilCommon.hpp \ + Modules/MAction/ZMobiusDWF.hpp \ + Modules/MAction/ScaledDWF.hpp \ + Modules/MAction/WilsonClover.hpp \ + Modules/MAction/Wilson.hpp \ + Modules/MAction/MobiusDWF.hpp \ + Modules/MAction/DWF.hpp \ + Modules/MFermion/FreeProp.hpp \ + Modules/MFermion/EMLepton.hpp \ + Modules/MFermion/GaugeProp.hpp + From 7a446d5b7f05773c251a72a86aa2cf3c27774efa Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 11 Nov 2019 14:36:45 +0000 Subject: [PATCH 315/347] removed default filenames --- Hadrons/Modules/MIO/LoadDistilNoise.cc | 7 ++ Hadrons/Modules/MIO/LoadDistilNoise.hpp | 119 +++++++++++++++++++++++ Hadrons/Modules/MIO/LoadPerambulator.hpp | 2 - 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 Hadrons/Modules/MIO/LoadDistilNoise.cc create mode 100644 Hadrons/Modules/MIO/LoadDistilNoise.hpp diff --git a/Hadrons/Modules/MIO/LoadDistilNoise.cc b/Hadrons/Modules/MIO/LoadDistilNoise.cc new file mode 100644 index 00000000..da532830 --- /dev/null +++ b/Hadrons/Modules/MIO/LoadDistilNoise.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MIO; + +template class Grid::Hadrons::MIO::TLoadDistilNoise; diff --git a/Hadrons/Modules/MIO/LoadDistilNoise.hpp b/Hadrons/Modules/MIO/LoadDistilNoise.hpp new file mode 100644 index 00000000..27b7e76a --- /dev/null +++ b/Hadrons/Modules/MIO/LoadDistilNoise.hpp @@ -0,0 +1,119 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/LoadDistilNoise.hpp + + Copyright (C) 2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + +#ifndef Hadrons_MIO_LoadDistilNoise_hpp_ +#define Hadrons_MIO_LoadDistilNoise_hpp_ + +#include + +BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MIO) + +/****************************************************************************** + * LoadDistilNoise * + ******************************************************************************/ + +class LoadDistilNoisePar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(LoadDistilNoisePar, + std::string, NoiseFileName, + int, nvec, + MDistil::DistilParameters, Distil); +}; + +template +class TLoadDistilNoise: public Module +{ +public: + // constructor + TLoadDistilNoise(const std::string name); + // destructor + virtual ~TLoadDistilNoise(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(LoadDistilNoise, TLoadDistilNoise, MIO); + +/****************************************************************************** + * TLoadDistilNoise implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TLoadDistilNoise::TLoadDistilNoise(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TLoadDistilNoise::getInput(void) +{ + std::vector in; + + return in; +} + +template +std::vector TLoadDistilNoise::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TLoadDistilNoise::setup(void) +{ + DISTIL_PARAMETERS_DEFINE( true ); + envCreate(MDistil::NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TLoadDistilNoise::execute(void) +{ + auto &noises = envGet(MDistil::NoiseTensor, getName()); + std::string sNoiseName{ par().NoiseFileName }; + sNoiseName.append( 1, '.' ); + sNoiseName.append( std::to_string( vm().getTrajectory() ) ); + noises.read(sNoiseName.c_str()); +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MIO_LoadDistilNoise_hpp_ diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 9d30f9fa..23244512 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -108,8 +108,6 @@ void TLoadPerambulator::execute(void) { auto &perambulator = envGet(MDistil::PerambTensor, getName()); std::string sPerambName{ par().PerambFileName }; - if( sPerambName.empty() ) - sPerambName = getName(); sPerambName.append( 1, '.' ); sPerambName.append( std::to_string( vm().getTrajectory() ) ); perambulator.read(sPerambName.c_str()); From df586a142d4a10ddffaad9bbb74caaa9378e9938 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 11 Nov 2019 17:29:55 +0000 Subject: [PATCH 316/347] added DistilPar-module and cleaned up some code --- Hadrons/Modules.hpp | 1 + Hadrons/Modules/MDistil/DistilCommon.hpp | 36 +---- Hadrons/Modules/MDistil/DistilPar.cc | 7 + Hadrons/Modules/MDistil/DistilPar.hpp | 96 ++++++++++++++ Hadrons/Modules/MDistil/DistilVectors.hpp | 138 ++++++++++---------- Hadrons/Modules/MDistil/Noises.hpp | 23 ++-- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 72 ++++++---- Hadrons/Modules/MDistil/Perambulator.hpp | 90 +++++++------ Hadrons/Modules/MIO/LoadDistilNoise.hpp | 9 +- Hadrons/Modules/MIO/LoadPerambulator.hpp | 17 ++- Hadrons/modules.inc | 3 + 11 files changed, 302 insertions(+), 190 deletions(-) create mode 100644 Hadrons/Modules/MDistil/DistilPar.cc create mode 100644 Hadrons/Modules/MDistil/DistilPar.hpp diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index c3c0cf86..e0e8832e 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include diff --git a/Hadrons/Modules/MDistil/DistilCommon.hpp b/Hadrons/Modules/MDistil/DistilCommon.hpp index da46b918..72ee7449 100644 --- a/Hadrons/Modules/MDistil/DistilCommon.hpp +++ b/Hadrons/Modules/MDistil/DistilCommon.hpp @@ -63,42 +63,14 @@ BEGIN_MODULE_NAMESPACE(MDistil) struct DistilParameters: Serializable { GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParameters, + int, nvec, int, nnoise, int, tsrc, - std::string, TI, - std::string, LI, - std::string, SI ) - DistilParameters() = default; - template DistilParameters(Reader& Reader){read(Reader,"Distil",*this);} - - // Numeric parameter is allowed to be empty (in which case it = Default), - // but assert during setup() if specified but not numeric - - static int ParameterDefault( const std::string & s, int Default, bool bCalledFromSetup ) - { - int i = Default; - if( s.length() > 0 ) { - std::istringstream ss( s ); - ss >> i; - if( bCalledFromSetup ) - assert( !ss.fail() && "Parameter should either be empty or integer" ); - } - return i; - } + int, TI, + int, LI, + int, SI ) }; -#define DISTIL_PARAMETERS_DEFINE( inSetup ) \ -const int Nt{env().getDim(Tdir)}; \ -const int nvec{par().nvec}; \ -const int nnoise{par().Distil.nnoise}; \ -const int tsrc{par().Distil.tsrc}; \ -const int TI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.TI, Nt, inSetup)}; \ -const int LI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.LI, nvec, inSetup)}; \ -const int SI{Hadrons::MDistil::DistilParameters::ParameterDefault(par().Distil.SI, Ns, inSetup)}; \ -const bool full_tdil{ TI == Nt }; \ -const bool exact_distillation{ full_tdil && LI == nvec }; \ -const int Nt_inv{ full_tdil ? 1 : TI } - /****************************************************************************** Make a lower dimensional grid in preparation for local slice operations ******************************************************************************/ diff --git a/Hadrons/Modules/MDistil/DistilPar.cc b/Hadrons/Modules/MDistil/DistilPar.cc new file mode 100644 index 00000000..f0c03ec9 --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilPar.cc @@ -0,0 +1,7 @@ +#include + +using namespace Grid; +using namespace Hadrons; +using namespace MDistil; + +template class Grid::Hadrons::MDistil::TDistilPar; diff --git a/Hadrons/Modules/MDistil/DistilPar.hpp b/Hadrons/Modules/MDistil/DistilPar.hpp new file mode 100644 index 00000000..328280bc --- /dev/null +++ b/Hadrons/Modules/MDistil/DistilPar.hpp @@ -0,0 +1,96 @@ +#ifndef Hadrons_MDistil_DistilPar_hpp_ +#define Hadrons_MDistil_DistilPar_hpp_ + +#include +#include +#include +#include + +BEGIN_HADRONS_NAMESPACE + +/****************************************************************************** + * DistilPar * + ******************************************************************************/ +BEGIN_MODULE_NAMESPACE(MDistil) + + +class DistilParPar: Serializable +{ +public: + GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParPar, + int, nnoise, + int, tsrc, + int, TI, + int, LI, + int, SI ); +}; + +template +class TDistilPar: public Module +{ +public: + // constructor + TDistilPar(const std::string name); + // destructor + virtual ~TDistilPar(void) {}; + // dependency relation + virtual std::vector getInput(void); + virtual std::vector getOutput(void); + // setup + virtual void setup(void); + // execution + virtual void execute(void); +}; + +MODULE_REGISTER_TMP(DistilPar, TDistilPar, MDistil); + +/****************************************************************************** + * TDistilPar implementation * + ******************************************************************************/ +// constructor ///////////////////////////////////////////////////////////////// +template +TDistilPar::TDistilPar(const std::string name) +: Module(name) +{} + +// dependencies/products /////////////////////////////////////////////////////// +template +std::vector TDistilPar::getInput(void) +{ + std::vector in; + + return in; +} + +template +std::vector TDistilPar::getOutput(void) +{ + std::vector out = {getName()}; + + return out; +} + +// setup /////////////////////////////////////////////////////////////////////// +template +void TDistilPar::setup(void) +{ + +} + +// execution /////////////////////////////////////////////////////////////////// +template +void TDistilPar::execute(void) +{ + Hadrons::MDistil::DistilParameters &out = envGet(Hadrons::MDistil::DistilParameters, getName()); + out.nnoise=par().nnoise; + out.tsrc=par().tsrc; + out.TI=par().TI; + out.LI=par().LI; + out.SI=par().SI; +} + +END_MODULE_NAMESPACE + +END_HADRONS_NAMESPACE + +#endif // Hadrons_MDistil_DistilPar_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 643e7d63..3f9909b7 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -47,11 +47,9 @@ public: std::string, noise, std::string, perambulator, std::string, lapevec, - std::string, source, - std::string, sink, - int, tsrc, - std::string, nvec, - std::string, TI) + std::string, rho, + std::string, phi, + MDistil::DistilParameters, DistilPar); }; template @@ -80,10 +78,10 @@ public: std::string PerambulatorName; std::string NoiseVectorName; std::string LapEvecName; - bool bMakeSource; - bool bMakeSink; - std::string SourceName; - std::string SinkName; + bool bMakeRho; + bool bMakePhi; + std::string RhoName; + std::string PhiName; }; MODULE_REGISTER_TMP(DistilVectors, TDistilVectors, MDistil); @@ -131,24 +129,24 @@ std::vector TDistilVectors::getInput(void) template std::vector TDistilVectors::getOutput(void) { - SourceName = par().source; - SinkName = par().sink; - bMakeSource = ( SourceName.size() > 0 ); - bMakeSink = ( SinkName.size() > 0 ); - if (!bMakeSource && !bMakeSink) + RhoName = par().rho; + PhiName = par().phi; + bMakeRho = ( RhoName.size() > 0 ); + bMakePhi = ( PhiName.size() > 0 ); + if (!bMakeRho && !bMakePhi) { - SourceName = getName(); - SinkName = SourceName; - SourceName.append("_rho"); - SinkName.append("_phi"); - bMakeSource = true; - bMakeSink = true; + RhoName = getName(); + PhiName = RhoName; + RhoName.append("_rho"); + PhiName.append("_phi"); + bMakeRho = true; + bMakePhi = true; } std::vector out; - if (bMakeSource) - out.push_back(SourceName); - if (bMakeSink) - out.push_back(SinkName); + if (bMakeRho) + out.push_back(RhoName); + if (bMakePhi) + out.push_back(PhiName); return out; } @@ -157,28 +155,25 @@ template void TDistilVectors::setup(void) { Cleanup(); - auto &noise = envGet(NoiseTensor, NoiseVectorName); + auto &noise = envGet(NoiseTensor, NoiseVectorName); auto &perambulator = envGet(PerambTensor, PerambulatorName); // We expect the perambulator to have been created with these indices assert( perambulator.ValidateIndexNames() && "Perambulator index names bad" ); - const int Nt{ env().getDim(Tdir) }; - assert( Nt == static_cast( perambulator.tensor.dimension(0) ) && "PerambTensor time dimensionality bad" ); - const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; - const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; - const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; - const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; - assert( nnoise >= static_cast( noise.tensor.dimension(0) ) && "Not enough noise vectors for perambulator" ); - // Nvec defaults to what's in the perambulator unless overriden - const int nvec_per{ static_cast( perambulator.tensor.dimension(1) ) }; - const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, nvec_per, true) }; - assert( nvec <= nvec_per && "Not enough distillation sub-space vectors" ); + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; + const int TI{par().DistilPar.TI}; + const int LI{par().DistilPar.LI}; + const int SI{par().DistilPar.SI}; + const bool full_tdil{ TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : TI }; - if (bMakeSource) - envCreate(std::vector, SourceName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - if (bMakeSink) - envCreate(std::vector, SinkName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); + if (bMakeRho) + envCreate(std::vector, RhoName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); + if (bMakePhi) + envCreate(std::vector, PhiName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); grid4d = env().getGrid(); Coordinate latt_size = GridDefaultLatt(); @@ -189,10 +184,10 @@ void TDistilVectors::setup(void) mpi_layout[Nd-1] = 1; grid3d = MakeLowerDimGrid(grid4d); - envTmp(LatticeSpinColourVector, "tmp2",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeSpinColourVector, "sink_tslice",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeSpinColourVector, "source4d",1,LatticeSpinColourVector(grid4d)); + envTmp(LatticeSpinColourVector, "source3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "source3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeSpinColourVector, "sink3d",1,LatticeSpinColourVector(grid3d)); envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); } @@ -216,50 +211,49 @@ void TDistilVectors::execute(void) auto &perambulator = envGet(PerambTensor, PerambulatorName); auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); - envGetTmp(LatticeSpinColourVector, tmp2); - envGetTmp(LatticeSpinColourVector, tmp3d); - envGetTmp(LatticeColourVector, tmp3d_nospin); - envGetTmp(LatticeSpinColourVector, sink_tslice); + envGetTmp(LatticeSpinColourVector, source4d); + envGetTmp(LatticeSpinColourVector, source3d); + envGetTmp(LatticeColourVector, source3d_nospin); + envGetTmp(LatticeSpinColourVector, sink3d); envGetTmp(LatticeColourVector, evec3d); const int Ntlocal{ grid4d->LocalDimensions()[3] }; const int Ntfirst{ grid4d->LocalStarts()[3] }; - const int Nt{ env().getDim(Tdir) }; - const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false ) }; - const int LI{ static_cast( perambulator.tensor.dimension(2) ) }; - const int SI{ static_cast( perambulator.tensor.dimension(5) ) }; - const int Nt_inv{ static_cast( perambulator.tensor.dimension(4) ) }; - const int nnoise{ static_cast( perambulator.tensor.dimension(3) ) }; - // Nvec defaults to what's in the perambulator unless overriden - const int nvec{Hadrons::MDistil::DistilParameters::ParameterDefault(par().nvec, static_cast( perambulator.tensor.dimension(1) ), false)}; - const int tsrc{ par().tsrc }; - const bool full_tdil{ TI==Nt }; + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; + const int tsrc{par().DistilPar.tsrc}; + const int TI{par().DistilPar.TI}; + const int LI{par().DistilPar.LI}; + const int SI{par().DistilPar.SI}; + const bool full_tdil{ TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : TI }; int vecindex; int t_inv; - if (bMakeSource) + if (bMakeRho) { - auto &rho = envGet(std::vector, SourceName); + auto &rho = envGet(std::vector, RhoName); for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { for (int ds = 0; ds < SI; ds++) { vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; rho[vecindex] = 0; - tmp3d_nospin = 0; + source3d_nospin = 0; for (int it = dt; it < Nt; it += TI){ if (full_tdil) t_inv = tsrc; else t_inv = it; if (t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal) { for (int ik = dk; ik < nvec; ik += LI){ for (int is = ds; is < Ns; is += SI){ ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - tmp3d_nospin = evec3d * noise.tensor(inoise, t_inv, ik, is); - tmp3d=0; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=0; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Tdir); - rho[vecindex] += tmp2; + source3d_nospin = evec3d * noise.tensor(inoise, t_inv, ik, is); + source3d=0; + pokeSpin(source3d,source3d_nospin,is); + source4d=0; + InsertSliceLocal(source3d,source4d,0,t_inv-Ntfirst,Tdir); + rho[vecindex] += source4d; } } } @@ -269,8 +263,8 @@ void TDistilVectors::execute(void) } } } - if (bMakeSink) { - auto &phi = envGet(std::vector, SinkName); + if (bMakePhi) { + auto &phi = envGet(std::vector, PhiName); for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { @@ -278,12 +272,12 @@ void TDistilVectors::execute(void) vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; phi[vecindex] = 0; for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - sink_tslice=0; + sink3d=0; for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - sink_tslice += evec3d * perambulator.tensor(t, ivec, dk, inoise,dt,ds); + sink3d += evec3d * perambulator.tensor(t, ivec, dk, inoise,dt,ds); } - InsertSliceLocal(sink_tslice,phi[vecindex],0,t-Ntfirst,Tdir); + InsertSliceLocal(sink3d,phi[vecindex],0,t-Ntfirst,Tdir); } } } diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index c17a29fa..1af023e4 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -43,10 +43,7 @@ class NoisesPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(NoisesPar, - int, nnoise, - int, nvec, - std::string, TI, - std::string, LI, + MDistil::DistilParameters, DistilPar, std::string, NoiseFileName) }; @@ -96,9 +93,9 @@ std::vector TNoises::getOutput(void) template void TNoises::setup(void) { - const int Nt{env().getDim(Tdir)}; - const int nnoise{par().nnoise}; - const int nvec{par().nvec}; + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; envCreate(NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); } @@ -107,12 +104,12 @@ template void TNoises::execute(void) { const int Nt{env().getDim(Tdir)}; - const int nnoise{par().nnoise}; - const int nvec{par().nvec}; - const int TI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().TI, Nt, false) }; - const int LI{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI, nvec, false) }; - const bool full_tdil{ TI == Nt }; \ - const bool exact_distillation{ full_tdil && LI == nvec }; \ + const int nnoise{par().DistilPar.nnoise}; + const int nvec{par().DistilPar.nvec}; + const int TI{par().DistilPar.TI}; + const int LI{par().DistilPar.LI}; + const bool full_tdil{ TI == Nt }; + const bool exact_distillation{ full_tdil && LI == nvec }; // We use our own seeds so we can specify different noises per quark Real rn; diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 14340408..662055a0 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -51,10 +51,9 @@ public: std::string, eigenPack, std::string, PerambFileName, std::string, solve, - int, nvec, std::string, nvec_reduced, std::string, LI_reduced, - DistilParameters, Distil); + MDistil::DistilParameters, DistilPar); }; template @@ -116,16 +115,23 @@ template void TPerambFromSolve::setup(void) { Cleanup(); - DISTIL_PARAMETERS_DEFINE( true ); - const int nvec_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().nvec_reduced, nvec, true) }; - const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, true) }; + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; + const int LI{par().DistilPar.LI}; + const int TI{par().DistilPar.TI}; + const int SI{par().DistilPar.SI}; + const bool full_tdil{ TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : TI }; + const int nvec_reduced{ par().nvec_reduced.empty() ? nvec:std::stoi(par().nvec_reduced)}; + const int LI_reduced{ par().LI_reduced.empty() ? LI:std::stoi(par().LI_reduced)}; grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); envCreate(PerambTensor, getName(), 1, Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); envCreate(NoiseTensor, getName() + "_noise", 1, nnoise, Nt, nvec, Ns ); - envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "result3d_nospin",1,LatticeColourVector(grid3d)); envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); - envTmpLat(LatticeColourVector, "result_nospin"); + envTmpLat(LatticeColourVector, "result4d_nospin"); } template @@ -146,28 +152,42 @@ void TPerambFromSolve::execute(void) GridCartesian * grid4d = env().getGrid(); const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - DISTIL_PARAMETERS_DEFINE( false ); - const int nvec_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().nvec_reduced, nvec, false) }; - const int LI_reduced{ Hadrons::MDistil::DistilParameters::ParameterDefault( par().LI_reduced, LI, false) }; - auto &perambulator = envGet(PerambTensor, getName()); - auto &solve = envGet(std::vector, par().solve); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; + const int TI{par().DistilPar.TI}; + const int LI{par().DistilPar.LI}; + const int SI{par().DistilPar.SI}; + const bool full_tdil{ TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : TI }; + const int nvec_reduced{ par().nvec_reduced.empty() ? nvec:std::stoi(par().nvec_reduced)}; + const int LI_reduced{ par().LI_reduced.empty() ? LI:std::stoi(par().LI_reduced)}; + auto &perambulator = envGet(PerambTensor, getName()); + auto &solve = envGet(std::vector, par().solve); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); - envGetTmp(LatticeColourVector, result_nospin); - envGetTmp(LatticeColourVector, result_3d); + envGetTmp(LatticeColourVector, result4d_nospin); + envGetTmp(LatticeColourVector, result3d_nospin); envGetTmp(LatticeColourVector, evec3d); - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI_reduced; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - for (int is = 0; is < Ns; is++) { - result_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); - for (int ivec = 0; ivec < nvec_reduced; ivec++) { + for (int inoise = 0; inoise < nnoise; inoise++) + { + for (int dk = 0; dk < LI_reduced; dk++) + { + for (int dt = 0; dt < Nt_inv; dt++) + { + for (int ds = 0; ds < SI; ds++) + { + for (int is = 0; is < Ns; is++) + { + result4d_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) + { + ExtractSliceLocal(result3d_nospin,result4d_nospin,0,t-Ntfirst,Tdir); + for (int ivec = 0; ivec < nvec_reduced; ivec++) + { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); + pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result3d_nospin)),is); LOG(Message) << "perambulator(t, ivec, dk, inoise,dt,ds)(is) = (" << t << "," << ivec << "," << dk << "," << inoise << "," << dt << "," << ds << ")(" << is << ") = " << perambulator.tensor(t, ivec, dk, inoise,dt,ds)()(is)() << std::endl; } } @@ -179,8 +199,6 @@ void TPerambFromSolve::execute(void) if(grid4d->IsBoss()) { std::string sPerambName{par().PerambFileName}; - if (sPerambName.empty()) - sPerambName = getName(); sPerambName.append( "." ); sPerambName.append( std::to_string(vm().getTrajectory())); perambulator.write(sPerambName.c_str()); diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 17ea41a2..3cc444e3 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -49,8 +49,7 @@ public: std::string, PerambFileName, std::string, UnsmearedSinkFileName, std::string, UnsmearedSinkMultiFile, - int, nvec, - DistilParameters, Distil); + MDistil::DistilParameters, DistilPar); }; template @@ -106,8 +105,6 @@ std::vector TPerambulator::getInput(void) { sLapEvecName = par().lapevec; sNoiseName = par().noise; - if( sNoiseName.length() == 0 ) - sNoiseName = getName() + "_noise"; return {sLapEvecName, par().solver, sNoiseName }; } @@ -124,23 +121,32 @@ void TPerambulator::setup(void) Cleanup(); grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); - DISTIL_PARAMETERS_DEFINE( true ); + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; + const int tsrc{par().DistilPar.tsrc}; + const int TI{par().DistilPar.TI}; + const int LI{par().DistilPar.LI}; + const int SI{par().DistilPar.SI}; + const bool full_tdil{ TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : TI }; const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; if (!UnsmearedSinkFileName.empty()) - bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, true ) != 0 ); + //bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, true ) != 0 ); + bool bMulti = 0; envCreate(PerambTensor, getName(), 1, Nt,nvec,LI,nnoise,Nt_inv,SI); envCreate(std::vector, getName() + "_unsmeared_sink", 1, nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); - envTmpLat(LatticeSpinColourVector, "dist_source"); - envTmpLat(LatticeSpinColourVector, "tmp2"); - envTmpLat(LatticeSpinColourVector, "result"); - envTmpLat(LatticeColourVector, "result_nospin"); - envTmp(LatticeSpinColourVector, "tmp3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "tmp3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "result_3d",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + envTmpLat(LatticeSpinColourVector, "dist_source"); + envTmpLat(LatticeSpinColourVector, "source4d"); + envTmp(LatticeSpinColourVector, "source3d",1,LatticeSpinColourVector(grid3d)); + envTmp(LatticeColourVector, "source3d_nospin",1,LatticeColourVector(grid3d)); + envTmpLat(LatticeSpinColourVector, "result4d"); + envTmpLat(LatticeColourVector, "result4d_nospin"); + envTmp(LatticeColourVector, "result3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); Ls_ = env().getObjectLs(par().solver); envTmpLat(FermionField, "v4dtmp"); @@ -164,7 +170,16 @@ void TPerambulator::Cleanup(void) template void TPerambulator::execute(void) { - DISTIL_PARAMETERS_DEFINE( false ); + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; + const int tsrc{par().DistilPar.tsrc}; + const int TI{par().DistilPar.TI}; + const int LI{par().DistilPar.LI}; + const int SI{par().DistilPar.SI}; + const bool full_tdil{ TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : TI }; + auto &solver=envGet(Solver, par().solver); auto &mat = solver.getFMat(); envGetTmp(FermionField, v4dtmp); @@ -175,12 +190,12 @@ void TPerambulator::execute(void) auto &epack = envGet(LapEvecs, sLapEvecName); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); envGetTmp(LatticeSpinColourVector, dist_source); - envGetTmp(LatticeSpinColourVector, tmp2); - envGetTmp(LatticeSpinColourVector, result); - envGetTmp(LatticeColourVector, result_nospin); - envGetTmp(LatticeSpinColourVector, tmp3d); - envGetTmp(LatticeColourVector, tmp3d_nospin); - envGetTmp(LatticeColourVector, result_3d); + envGetTmp(LatticeSpinColourVector, source4d); + envGetTmp(LatticeSpinColourVector, source3d); + envGetTmp(LatticeColourVector, source3d_nospin); + envGetTmp(LatticeSpinColourVector, result4d); + envGetTmp(LatticeColourVector, result4d_nospin); + envGetTmp(LatticeColourVector, result3d_nospin); envGetTmp(LatticeColourVector, evec3d); const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; @@ -196,7 +211,7 @@ void TPerambulator::execute(void) { LOG(Message) << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; dist_source = 0; - tmp3d_nospin = 0; + source3d_nospin = 0; evec3d = 0; for (int it = dt; it < Nt; it += TI) { @@ -208,39 +223,39 @@ void TPerambulator::execute(void) for (int is = ds; is < Ns; is += SI) { ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); - tmp3d_nospin = evec3d * noise.tensor(inoise, t_inv, ik, is); - tmp3d=0; - pokeSpin(tmp3d,tmp3d_nospin,is); - tmp2=0; - InsertSliceLocal(tmp3d,tmp2,0,t_inv-Ntfirst,Tdir); - dist_source += tmp2; + source3d_nospin = evec3d * noise.tensor(inoise, t_inv, ik, is); + source3d=0; + pokeSpin(source3d,source3d_nospin,is); + source4d=0; + InsertSliceLocal(source3d,source4d,0,t_inv-Ntfirst,Tdir); + dist_source += source4d; } } } } - result=0; + result4d=0; v4dtmp = dist_source; if (Ls_ == 1) - solver(result, v4dtmp); + solver(result4d, v4dtmp); else { mat.ImportPhysicalFermionSource(v4dtmp, v5dtmp); solver(v5dtmp_sol, v5dtmp); mat.ExportPhysicalFermionSolution(v5dtmp_sol, v4dtmp); - result = v4dtmp; + result4d = v4dtmp; } if (!UnsmearedSinkFileName.empty()) - unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result; + unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result4d; for (int is = 0; is < Ns; is++) { - result_nospin = peekSpin(result,is); + result4d_nospin = peekSpin(result4d,is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { - ExtractSliceLocal(result_3d,result_nospin,0,t-Ntfirst,Tdir); - for (int ivec = 0; ivec < nvec; ivec++) + ExtractSliceLocal(result3d_nospin,result4d_nospin,0,t-Ntfirst,Tdir); + for (int ivec = 0; ivec < nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); - pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result_3d)),is); + pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result3d_nospin)),is); } } } @@ -284,7 +299,8 @@ void TPerambulator::execute(void) //Save the unsmeared sinks if filename specified if (!UnsmearedSinkFileName.empty()) { - bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); + // bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); + bool bMulti =0; LOG(Message) << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; A2AVectorsIo::write(UnsmearedSinkFileName, unsmeared_sink, bMulti, vm().getTrajectory()); } diff --git a/Hadrons/Modules/MIO/LoadDistilNoise.hpp b/Hadrons/Modules/MIO/LoadDistilNoise.hpp index 27b7e76a..a1a20ca1 100644 --- a/Hadrons/Modules/MIO/LoadDistilNoise.hpp +++ b/Hadrons/Modules/MIO/LoadDistilNoise.hpp @@ -44,8 +44,7 @@ class LoadDistilNoisePar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(LoadDistilNoisePar, std::string, NoiseFileName, - int, nvec, - MDistil::DistilParameters, Distil); + MDistil::DistilParameters, DistilPar); }; template @@ -97,8 +96,10 @@ std::vector TLoadDistilNoise::getOutput(void) template void TLoadDistilNoise::setup(void) { - DISTIL_PARAMETERS_DEFINE( true ); - envCreate(MDistil::NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; + envCreate(MDistil::NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); } // execution /////////////////////////////////////////////////////////////////// diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 23244512..45f31b2d 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -43,9 +43,8 @@ class LoadPerambulatorPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(LoadPerambulatorPar, - std::string, PerambFileName, //stem!!! - int, nvec, - MDistil::DistilParameters, Distil); + std::string, PerambFileName, + MDistil::DistilParameters, DistilPar); }; template @@ -97,8 +96,16 @@ std::vector TLoadPerambulator::getOutput(void) template void TLoadPerambulator::setup(void) { - DISTIL_PARAMETERS_DEFINE( true ); - //std::array sIndexNames{"Nt", "nvec", "LI", "nnoise", "Nt_inv", "SI"}; + const int Nt{env().getDim(Tdir)}; + const int nvec{par().DistilPar.nvec}; + const int nnoise{par().DistilPar.nnoise}; + const int tsrc{par().DistilPar.tsrc}; + const int TI{par().DistilPar.TI}; + const int LI{par().DistilPar.LI}; + const int SI{par().DistilPar.SI}; + const bool full_tdil{ TI == Nt }; + const bool exact_distillation{ full_tdil && LI == nvec }; + const int Nt_inv{ full_tdil ? 1 : TI }; envCreate(MDistil::PerambTensor, getName(), 1, Nt,nvec,LI,nnoise,Nt_inv,SI); } diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index f3e43d28..30a7cc6d 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -65,7 +65,9 @@ modules_cc =\ Modules/MScalarSUN/TrMag.cc \ Modules/MDistil/PerambFromSolve.cc \ Modules/MDistil/DistilVectors.cc \ + Modules/MDistil/DistilPar.cc \ Modules/MDistil/LapEvec.cc \ + Modules/MDistil/DistilParameters.cc \ Modules/MDistil/Perambulator.cc \ Modules/MDistil/Noises.cc \ Modules/MAction/MobiusDWF.cc \ @@ -151,6 +153,7 @@ modules_hpp =\ Modules/MDistil/Noises.hpp \ Modules/MDistil/LapEvec.hpp \ Modules/MDistil/Perambulator.hpp \ + Modules/MDistil/DistilPar.hpp \ Modules/MDistil/DistilCommon.hpp \ Modules/MAction/ZMobiusDWF.hpp \ Modules/MAction/ScaledDWF.hpp \ From b8f0878981c7e4a8cacb6216b784af582ceaeacf Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 11 Nov 2019 17:49:38 +0000 Subject: [PATCH 317/347] removed most default behaviour --- Hadrons/Modules/MDistil/DistilVectors.hpp | 15 --------------- Hadrons/Modules/MDistil/LapEvec.hpp | 5 ----- 2 files changed, 20 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 3f9909b7..ec8c8d59 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -106,23 +106,8 @@ template std::vector TDistilVectors::getInput(void) { PerambulatorName = par().perambulator; - if (PerambulatorName.empty()) - { - PerambulatorName = getName(); - PerambulatorName.append("_peramb"); - } NoiseVectorName = par().noise; - if (NoiseVectorName.empty()) - { - NoiseVectorName = PerambulatorName; - NoiseVectorName.append("_noise"); - } LapEvecName = par().lapevec; - if (LapEvecName.empty()) - { - LapEvecName = PerambulatorName; - LapEvecName.append("_lapevec"); - } return { PerambulatorName, NoiseVectorName, LapEvecName }; } diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 7cf3a517..40f859c8 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -143,11 +143,6 @@ template std::vector TLapEvec::getInput(void) { sGaugeName = par().gauge; - if (sGaugeName.empty()) - { - sGaugeName = getName(); - sGaugeName.append( "_gauge" ); - } return std::vector{ sGaugeName }; } From db952993fae1a8d5acebfb60dbd141489985fa4f Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 12 Nov 2019 12:23:34 +0000 Subject: [PATCH 318/347] envCreate problem.. --- Hadrons/Modules/MDistil/DistilPar.hpp | 6 ++- Hadrons/Modules/MDistil/DistilVectors.hpp | 30 ++++++----- Hadrons/Modules/MDistil/Noises.hpp | 21 +++++--- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 30 ++++++----- Hadrons/Modules/MDistil/Perambulator.hpp | 32 +++++++----- Hadrons/Modules/MIO/LoadDistilNoise.hpp | 13 +++-- Hadrons/Modules/MIO/LoadPerambulator.hpp | 22 ++++---- tests/hadrons/Test_distil.cc | 58 ++++++++++++++------- 8 files changed, 128 insertions(+), 84 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilPar.hpp b/Hadrons/Modules/MDistil/DistilPar.hpp index 328280bc..d1f333c5 100644 --- a/Hadrons/Modules/MDistil/DistilPar.hpp +++ b/Hadrons/Modules/MDistil/DistilPar.hpp @@ -18,6 +18,7 @@ class DistilParPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParPar, + int, nvec, int, nnoise, int, tsrc, int, TI, @@ -74,7 +75,7 @@ std::vector TDistilPar::getOutput(void) template void TDistilPar::setup(void) { - + // envCreate(Hadrons::MDistil::DistilParameters, getName(), 1); //DOES NOT WORK } // execution /////////////////////////////////////////////////////////////////// @@ -82,11 +83,12 @@ template void TDistilPar::execute(void) { Hadrons::MDistil::DistilParameters &out = envGet(Hadrons::MDistil::DistilParameters, getName()); + /* out.nvec=par().nvec; out.nnoise=par().nnoise; out.tsrc=par().tsrc; out.TI=par().TI; out.LI=par().LI; - out.SI=par().SI; + out.SI=par().SI; */ } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index ec8c8d59..919c6d91 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -49,7 +49,7 @@ public: std::string, lapevec, std::string, rho, std::string, phi, - MDistil::DistilParameters, DistilPar); + std::string, DistilPar); }; template @@ -78,6 +78,7 @@ public: std::string PerambulatorName; std::string NoiseVectorName; std::string LapEvecName; + std::string DParName; bool bMakeRho; bool bMakePhi; std::string RhoName; @@ -108,7 +109,8 @@ std::vector TDistilVectors::getInput(void) PerambulatorName = par().perambulator; NoiseVectorName = par().noise; LapEvecName = par().lapevec; - return { PerambulatorName, NoiseVectorName, LapEvecName }; + DParName = par().DistilPar; + return { PerambulatorName, NoiseVectorName, LapEvecName, DParName }; } template @@ -142,16 +144,17 @@ void TDistilVectors::setup(void) Cleanup(); auto &noise = envGet(NoiseTensor, NoiseVectorName); auto &perambulator = envGet(PerambTensor, PerambulatorName); + auto &DPar = envGet(MDistil::DistilParameters, DParName); // We expect the perambulator to have been created with these indices assert( perambulator.ValidateIndexNames() && "Perambulator index names bad" ); const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; - const int TI{par().DistilPar.TI}; - const int LI{par().DistilPar.LI}; - const int SI{par().DistilPar.SI}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; + const int TI{DPar.TI}; + const int LI{DPar.LI}; + const int SI{DPar.SI}; const bool full_tdil{ TI == Nt }; const int Nt_inv{ full_tdil ? 1 : TI }; @@ -195,6 +198,7 @@ void TDistilVectors::execute(void) auto &noise = envGet(NoiseTensor, NoiseVectorName); auto &perambulator = envGet(PerambTensor, PerambulatorName); auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); + auto &DPar = envGet(MDistil::DistilParameters, DParName); envGetTmp(LatticeSpinColourVector, source4d); envGetTmp(LatticeSpinColourVector, source3d); @@ -206,12 +210,12 @@ void TDistilVectors::execute(void) const int Ntfirst{ grid4d->LocalStarts()[3] }; const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; - const int tsrc{par().DistilPar.tsrc}; - const int TI{par().DistilPar.TI}; - const int LI{par().DistilPar.LI}; - const int SI{par().DistilPar.SI}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; + const int tsrc{DPar.tsrc}; + const int TI{DPar.TI}; + const int LI{DPar.LI}; + const int SI{DPar.SI}; const bool full_tdil{ TI == Nt }; const int Nt_inv{ full_tdil ? 1 : TI }; diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 1af023e4..10596260 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -43,7 +43,7 @@ class NoisesPar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(NoisesPar, - MDistil::DistilParameters, DistilPar, + std::string, DistilPar, std::string, NoiseFileName) }; @@ -62,6 +62,8 @@ public: virtual void setup(void); // execution virtual void execute(void); +protected: + std::string DParName; }; MODULE_REGISTER_TMP(Noises, TNoises, MDistil); @@ -79,7 +81,8 @@ TNoises::TNoises(const std::string name) template std::vector TNoises::getInput(void) { - return {}; + DParName = par().DistilPar; + return { DParName }; } template @@ -93,9 +96,10 @@ std::vector TNoises::getOutput(void) template void TNoises::setup(void) { + auto &DPar = envGet(MDistil::DistilParameters, DParName); const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; envCreate(NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); } @@ -103,11 +107,12 @@ void TNoises::setup(void) template void TNoises::execute(void) { + auto &DPar = envGet(MDistil::DistilParameters, DParName); const int Nt{env().getDim(Tdir)}; - const int nnoise{par().DistilPar.nnoise}; - const int nvec{par().DistilPar.nvec}; - const int TI{par().DistilPar.TI}; - const int LI{par().DistilPar.LI}; + const int nnoise{DPar.nnoise}; + const int nvec{DPar.nvec}; + const int TI{DPar.TI}; + const int LI{DPar.LI}; const bool full_tdil{ TI == Nt }; const bool exact_distillation{ full_tdil && LI == nvec }; diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 662055a0..dec76404 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -53,7 +53,7 @@ public: std::string, solve, std::string, nvec_reduced, std::string, LI_reduced, - MDistil::DistilParameters, DistilPar); + std::string, DistilPar); }; template @@ -75,6 +75,8 @@ public: protected: GridCartesian * grid3d; // Owned by me, so I must delete it GridCartesian * grid4d; + //other members + std::string DParName; protected: virtual void Cleanup(void); @@ -101,7 +103,9 @@ TPerambFromSolve::~TPerambFromSolve(void) template std::vector TPerambFromSolve::getInput(void) { - return std::vector{ par().solve, par().eigenPack }; + //return std::vector{ par().solve, par().eigenPack }; + DParName = par().DistilPar; + return {par().solve, par().eigenPack, DParName }; } template @@ -115,12 +119,13 @@ template void TPerambFromSolve::setup(void) { Cleanup(); + auto &DPar = envGet(MDistil::DistilParameters, DParName); const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; - const int LI{par().DistilPar.LI}; - const int TI{par().DistilPar.TI}; - const int SI{par().DistilPar.SI}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; + const int LI{DPar.LI}; + const int TI{DPar.TI}; + const int SI{DPar.SI}; const bool full_tdil{ TI == Nt }; const int Nt_inv{ full_tdil ? 1 : TI }; const int nvec_reduced{ par().nvec_reduced.empty() ? nvec:std::stoi(par().nvec_reduced)}; @@ -152,12 +157,13 @@ void TPerambFromSolve::execute(void) GridCartesian * grid4d = env().getGrid(); const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; + auto &DPar = envGet(MDistil::DistilParameters, DParName); const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; - const int TI{par().DistilPar.TI}; - const int LI{par().DistilPar.LI}; - const int SI{par().DistilPar.SI}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; + const int TI{DPar.TI}; + const int LI{DPar.LI}; + const int SI{DPar.SI}; const bool full_tdil{ TI == Nt }; const int Nt_inv{ full_tdil ? 1 : TI }; const int nvec_reduced{ par().nvec_reduced.empty() ? nvec:std::stoi(par().nvec_reduced)}; diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 3cc444e3..beaeb6f0 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -49,7 +49,7 @@ public: std::string, PerambFileName, std::string, UnsmearedSinkFileName, std::string, UnsmearedSinkMultiFile, - MDistil::DistilParameters, DistilPar); + std::string, DistilPar); }; template @@ -79,6 +79,7 @@ protected: unsigned int Ls_; std::string sLapEvecName; std::string sNoiseName; + std::string DParName; }; MODULE_REGISTER_TMP(Perambulator, TPerambulator, MDistil); @@ -105,7 +106,8 @@ std::vector TPerambulator::getInput(void) { sLapEvecName = par().lapevec; sNoiseName = par().noise; - return {sLapEvecName, par().solver, sNoiseName }; + DParName = par().DistilPar; + return {sLapEvecName, par().solver, sNoiseName, DParName }; } template @@ -121,13 +123,14 @@ void TPerambulator::setup(void) Cleanup(); grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); + auto &DPar = envGet(MDistil::DistilParameters, DParName); const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; - const int tsrc{par().DistilPar.tsrc}; - const int TI{par().DistilPar.TI}; - const int LI{par().DistilPar.LI}; - const int SI{par().DistilPar.SI}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; + const int tsrc{DPar.tsrc}; + const int TI{DPar.TI}; + const int LI{DPar.LI}; + const int SI{DPar.SI}; const bool full_tdil{ TI == Nt }; const int Nt_inv{ full_tdil ? 1 : TI }; const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; @@ -170,13 +173,14 @@ void TPerambulator::Cleanup(void) template void TPerambulator::execute(void) { + auto &DPar = envGet(MDistil::DistilParameters, DParName); const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; - const int tsrc{par().DistilPar.tsrc}; - const int TI{par().DistilPar.TI}; - const int LI{par().DistilPar.LI}; - const int SI{par().DistilPar.SI}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; + const int tsrc{DPar.tsrc}; + const int TI{DPar.TI}; + const int LI{DPar.LI}; + const int SI{DPar.SI}; const bool full_tdil{ TI == Nt }; const int Nt_inv{ full_tdil ? 1 : TI }; diff --git a/Hadrons/Modules/MIO/LoadDistilNoise.hpp b/Hadrons/Modules/MIO/LoadDistilNoise.hpp index a1a20ca1..7b5ce782 100644 --- a/Hadrons/Modules/MIO/LoadDistilNoise.hpp +++ b/Hadrons/Modules/MIO/LoadDistilNoise.hpp @@ -44,7 +44,7 @@ class LoadDistilNoisePar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(LoadDistilNoisePar, std::string, NoiseFileName, - MDistil::DistilParameters, DistilPar); + std::string, DistilPar); }; template @@ -62,6 +62,8 @@ public: virtual void setup(void); // execution virtual void execute(void); +protected: + std::string DParName; }; MODULE_REGISTER_TMP(LoadDistilNoise, TLoadDistilNoise, MIO); @@ -79,9 +81,9 @@ TLoadDistilNoise::TLoadDistilNoise(const std::string name) template std::vector TLoadDistilNoise::getInput(void) { - std::vector in; + DParName = par().DistilPar; + return { par().NoiseFileName, DParName }; - return in; } template @@ -96,9 +98,10 @@ std::vector TLoadDistilNoise::getOutput(void) template void TLoadDistilNoise::setup(void) { + auto &DPar = envGet(MDistil::DistilParameters, DParName); const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; envCreate(MDistil::NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); } diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 45f31b2d..8314c2a9 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -44,7 +44,7 @@ class LoadPerambulatorPar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(LoadPerambulatorPar, std::string, PerambFileName, - MDistil::DistilParameters, DistilPar); + std::string, DistilPar); }; template @@ -62,6 +62,8 @@ public: virtual void setup(void); // execution virtual void execute(void); +protected: + std::string DParName; }; MODULE_REGISTER_TMP(LoadPerambulator, TLoadPerambulator, MIO); @@ -79,9 +81,8 @@ TLoadPerambulator::TLoadPerambulator(const std::string name) template std::vector TLoadPerambulator::getInput(void) { - std::vector in; - - return in; + DParName = par().DistilPar; + return { par().PerambFileName, DParName }; } template @@ -96,13 +97,14 @@ std::vector TLoadPerambulator::getOutput(void) template void TLoadPerambulator::setup(void) { + auto &DPar = envGet(MDistil::DistilParameters, DParName); const int Nt{env().getDim(Tdir)}; - const int nvec{par().DistilPar.nvec}; - const int nnoise{par().DistilPar.nnoise}; - const int tsrc{par().DistilPar.tsrc}; - const int TI{par().DistilPar.TI}; - const int LI{par().DistilPar.LI}; - const int SI{par().DistilPar.SI}; + const int nvec{DPar.nvec}; + const int nnoise{DPar.nnoise}; + const int tsrc{DPar.tsrc}; + const int TI{DPar.TI}; + const int LI{DPar.LI}; + const int SI{DPar.SI}; const bool full_tdil{ TI == Nt }; const bool exact_distillation{ full_tdil && LI == nvec }; const int Nt_inv{ full_tdil ? 1 : TI }; diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index 5e2d50f0..6e79f3f8 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -58,6 +58,10 @@ void test_Global(Application &application) globalPar.trajCounter.end = 1120; globalPar.trajCounter.step = 20; globalPar.runId = "test"; + globalPar.graphFile = ""; + globalPar.scheduleFile = ""; + globalPar.saveSchedule = "false"; + globalPar.parallelWriteMaxRetry = -1; application.setPar(globalPar); } @@ -65,10 +69,9 @@ void test_Global(Application &application) // Create a random gauge with the correct name ///////////////////////////////////////////////////////////// -std::string test_Gauge(Application &application, const char * pszBaseName ) +std::string test_Gauge(Application &application ) { - std::string sGaugeName{ pszBaseName }; - sGaugeName.append( "_gauge" ); + std::string sGaugeName{ "gauge" }; application.createModule( sGaugeName ); return sGaugeName; } @@ -80,8 +83,9 @@ std::string test_Gauge(Application &application, const char * pszBaseName ) void test_LapEvec(Application &application) { const char szModuleName[] = "LapEvec"; - test_Gauge( application, szModuleName ); + test_Gauge( application ); MDistil::LapEvecPar p; + p.gauge = "gauge"; p.Stout.steps = 3; p.Stout.rho = 0.2; p.Cheby.PolyOrder = 11; @@ -117,7 +121,7 @@ std::string test_Solver(Application &application, const char * pSuffix = nullptr sActionName.append( pSuffix ); } MAction::DWF::Par actionPar; - actionPar.gauge = "LapEvec_gauge"; + actionPar.gauge = "gauge"; actionPar.Ls = 16; actionPar.M5 = 1.8; actionPar.mass = 0.005; @@ -133,6 +137,23 @@ std::string test_Solver(Application &application, const char * pSuffix = nullptr return sSolverName; } +///////////////////////////////////////////////////////////// +// DistilParameters +///////////////////////////////////////////////////////////// + +std::string test_DPar(Application &application) { + // DistilVectors parameters + MDistil::DistilParPar DistilPar; + DistilPar.nvec = 5; + DistilPar.nnoise = 1; + DistilPar.tsrc = 0; + DistilPar.LI = 5; + DistilPar.TI = 8; + DistilPar.SI = 4; + std::string sDParName{"DPar_l"}; + application.createModule(sDParName,DistilPar); + return sDParName; +} ///////////////////////////////////////////////////////////// // Noises ///////////////////////////////////////////////////////////// @@ -140,8 +161,7 @@ std::string test_Solver(Application &application, const char * pSuffix = nullptr std::string test_Noises(Application &application, const std::string &sNoiseBaseName ) { // DistilVectors parameters MDistil::NoisesPar NoisePar; - NoisePar.nnoise = 1; - NoisePar.nvec = 5; + NoisePar.DistilPar = "DPar_l"; std::string sNoiseName{sNoiseBaseName + "_noise"}; application.createModule(sNoiseName,NoisePar); return sNoiseName; @@ -164,9 +184,7 @@ void test_LoadPerambulators( Application &application, const char * pszSuffix = std::string sModuleName{ PerambulatorName( pszSuffix ) }; MIO::LoadPerambulator::Par PerambPar; PerambPar.PerambFileName = sModuleName; - PerambPar.Distil.tsrc = 0; - PerambPar.Distil.nnoise = 1; - PerambPar.nvec = 5; + PerambPar.DistilPar = "DPar_l"; test_Noises(application, sModuleName); // I want these written after solver stuff application.createModule( sModuleName, PerambPar ); } @@ -179,9 +197,7 @@ void test_Perambulators( Application &application, const char * pszSuffix = null PerambPar.lapevec = "LapEvec"; PerambPar.PerambFileName = sModuleName; PerambPar.solver = test_Solver( application, pszSuffix ); - PerambPar.Distil.tsrc = 0; - PerambPar.Distil.nnoise = 1; - PerambPar.nvec = 5; + PerambPar.DistilPar = "DPar_l"; test_Noises(application, sModuleName); // I want these written after solver stuff application.createModule( sModuleName, PerambPar ); } @@ -202,9 +218,7 @@ void test_DistilVectors(Application &application, const char * pszSuffix = nullp DistilVecPar.noise = sPerambName + "_noise"; DistilVecPar.perambulator = sPerambName; DistilVecPar.lapevec = "LapEvec"; - DistilVecPar.tsrc = 0; - if( pszNvec ) - DistilVecPar.nvec = pszNvec; + DistilVecPar.DistilPar = "DPar_l"; application.createModule(sModuleName,DistilVecPar); } @@ -331,15 +345,17 @@ int main(int argc, char *argv[]) LOG(Message) << "Computing Meson 2pt-function" << std::endl; test_Global( application ); test_LapEvec( application ); - test_Perambulators( application ); - test_DistilVectors( application ); - test_MesonField( application, "Phi", "_phi" ); - test_MesonField( application, "Rho", "_rho" ); + test_DPar( application ); + //test_Perambulators( application ); + //test_DistilVectors( application ); + //test_MesonField( application, "Phi", "_phi" ); + //test_MesonField( application, "Rho", "_rho" ); break; case 1: LOG(Message) << "Computing Meson 2pt-function by loading perambulators" << std::endl; test_Global( application ); test_LapEvec( application ); + test_DPar( application ); test_LoadPerambulators( application ); test_DistilVectors( application ); test_MesonField( application, "Phi", "_phi" ); @@ -349,6 +365,7 @@ int main(int argc, char *argv[]) LOG(Message) << "Computing Meson 2pt-function for two quark flavours" << std::endl; test_Global( application ); test_LapEvec( application ); + test_DPar( application ); test_Perambulators( application ); test_DistilVectors( application ); test_Perambulators( application, "S" ); @@ -360,6 +377,7 @@ int main(int argc, char *argv[]) LOG(Message) << "Computing Meson 2pt-function with current insertion" << std::endl; test_Global( application ); test_LapEvec( application ); + test_DPar( application ); test_Perambulators( application ); test_MesonSink( application ); break; From 62dd0bfe58c4cfd2ab7e2d0bd4d2297a108d02b5 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 12 Nov 2019 13:59:53 +0000 Subject: [PATCH 319/347] New parameter module compiles. Untested. --- Hadrons/Modules/MDistil/DistilCommon.hpp | 2 ++ Hadrons/Modules/MDistil/DistilPar.hpp | 35 ++++---------------- Hadrons/Modules/MDistil/Perambulator.hpp | 41 +++++++----------------- 3 files changed, 20 insertions(+), 58 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilCommon.hpp b/Hadrons/Modules/MDistil/DistilCommon.hpp index 72ee7449..b1f0c768 100644 --- a/Hadrons/Modules/MDistil/DistilCommon.hpp +++ b/Hadrons/Modules/MDistil/DistilCommon.hpp @@ -69,6 +69,8 @@ struct DistilParameters: Serializable { int, TI, int, LI, int, SI ) + DistilParameters() = default; + DistilParameters( const DistilParameters &p ) = default; // member-wise copy }; /****************************************************************************** diff --git a/Hadrons/Modules/MDistil/DistilPar.hpp b/Hadrons/Modules/MDistil/DistilPar.hpp index d1f333c5..c5b88784 100644 --- a/Hadrons/Modules/MDistil/DistilPar.hpp +++ b/Hadrons/Modules/MDistil/DistilPar.hpp @@ -13,21 +13,8 @@ BEGIN_HADRONS_NAMESPACE ******************************************************************************/ BEGIN_MODULE_NAMESPACE(MDistil) - -class DistilParPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(DistilParPar, - int, nvec, - int, nnoise, - int, tsrc, - int, TI, - int, LI, - int, SI ); -}; - template -class TDistilPar: public Module +class TDistilPar: public Module { public: // constructor @@ -51,44 +38,34 @@ MODULE_REGISTER_TMP(DistilPar, TDistilPar, MDistil); // constructor ///////////////////////////////////////////////////////////////// template TDistilPar::TDistilPar(const std::string name) -: Module(name) +: Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template std::vector TDistilPar::getInput(void) { - std::vector in; - - return in; + return {}; } template std::vector TDistilPar::getOutput(void) { - std::vector out = {getName()}; - - return out; + return {getName()}; } // setup /////////////////////////////////////////////////////////////////////// template void TDistilPar::setup(void) { - // envCreate(Hadrons::MDistil::DistilParameters, getName(), 1); //DOES NOT WORK + envCreate(DistilParameters, getName(), 1, par() ); } // execution /////////////////////////////////////////////////////////////////// template void TDistilPar::execute(void) { - Hadrons::MDistil::DistilParameters &out = envGet(Hadrons::MDistil::DistilParameters, getName()); - /* out.nvec=par().nvec; - out.nnoise=par().nnoise; - out.tsrc=par().tsrc; - out.TI=par().TI; - out.LI=par().LI; - out.SI=par().SI; */ + // Nothing to do. setup() created and initialised the output object } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index beaeb6f0..ab71b60a 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -48,7 +48,6 @@ public: std::string, noise, std::string, PerambFileName, std::string, UnsmearedSinkFileName, - std::string, UnsmearedSinkMultiFile, std::string, DistilPar); }; @@ -77,9 +76,6 @@ protected: GridCartesian * grid4d; // Owned by environment (so I won't delete it) // Other members unsigned int Ls_; - std::string sLapEvecName; - std::string sNoiseName; - std::string DParName; }; MODULE_REGISTER_TMP(Perambulator, TPerambulator, MDistil); @@ -104,10 +100,7 @@ TPerambulator::~TPerambulator(void) template std::vector TPerambulator::getInput(void) { - sLapEvecName = par().lapevec; - sNoiseName = par().noise; - DParName = par().DistilPar; - return {sLapEvecName, par().solver, sNoiseName, DParName }; + return {par().lapevec, par().solver, par().noise, par().DistilPar}; } template @@ -123,24 +116,14 @@ void TPerambulator::setup(void) Cleanup(); grid4d = env().getGrid(); grid3d = MakeLowerDimGrid(grid4d); - auto &DPar = envGet(MDistil::DistilParameters, DParName); - const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - const int tsrc{DPar.tsrc}; - const int TI{DPar.TI}; - const int LI{DPar.LI}; - const int SI{DPar.SI}; - const bool full_tdil{ TI == Nt }; - const int Nt_inv{ full_tdil ? 1 : TI }; - const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; - if (!UnsmearedSinkFileName.empty()) - //bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, true ) != 0 ); - bool bMulti = 0; - - envCreate(PerambTensor, getName(), 1, Nt,nvec,LI,nnoise,Nt_inv,SI); + const DistilParameters &dp = envGet(DistilParameters, par().DistilPar); + const int Nt{env().getDim(Tdir)}; + const bool full_tdil{ dp.TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : dp.TI }; + + envCreate(PerambTensor, getName(), 1, Nt, dp.nvec, dp.LI, dp.nnoise, Nt_inv, dp.SI); envCreate(std::vector, getName() + "_unsmeared_sink", 1, - nnoise*LI*Ns*Nt_inv, envGetGrid(FermionField)); + dp.nnoise*dp.LI*Ns*Nt_inv, envGetGrid(FermionField)); envTmpLat(LatticeSpinColourVector, "dist_source"); envTmpLat(LatticeSpinColourVector, "source4d"); @@ -173,8 +156,8 @@ void TPerambulator::Cleanup(void) template void TPerambulator::execute(void) { - auto &DPar = envGet(MDistil::DistilParameters, DParName); - const int Nt{env().getDim(Tdir)}; + const DistilParameters &DPar{ envGet(DistilParameters, par().DistilPar) }; + const int Nt{env().getDim(Tdir)}; const int nvec{DPar.nvec}; const int nnoise{DPar.nnoise}; const int tsrc{DPar.tsrc}; @@ -189,9 +172,9 @@ void TPerambulator::execute(void) envGetTmp(FermionField, v4dtmp); envGetTmp(FermionField, v5dtmp); envGetTmp(FermionField, v5dtmp_sol); - auto &noise = envGet(NoiseTensor, sNoiseName); + auto &noise = envGet(NoiseTensor, par().noise); auto &perambulator = envGet(PerambTensor, getName()); - auto &epack = envGet(LapEvecs, sLapEvecName); + auto &epack = envGet(LapEvecs, par().lapevec); auto &unsmeared_sink = envGet(std::vector, getName() + "_unsmeared_sink"); envGetTmp(LatticeSpinColourVector, dist_source); envGetTmp(LatticeSpinColourVector, source4d); From 78f75b0e9fa6e64aafeb1af841ad6816a33d095b Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 12 Nov 2019 14:00:46 +0000 Subject: [PATCH 320/347] Better than graffiti --- Hadrons/Modules/MDistil/DistilPar.hpp | 31 ++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/DistilPar.hpp b/Hadrons/Modules/MDistil/DistilPar.hpp index c5b88784..fbca36a4 100644 --- a/Hadrons/Modules/MDistil/DistilPar.hpp +++ b/Hadrons/Modules/MDistil/DistilPar.hpp @@ -1,4 +1,33 @@ -#ifndef Hadrons_MDistil_DistilPar_hpp_ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: Hadrons/Modules/MDistil/DistilPar.hpp + + Copyright (C) 2019 + + Author: Felix Erben + Author: Michael Marshall + + 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 */ + +##ifndef Hadrons_MDistil_DistilPar_hpp_ #define Hadrons_MDistil_DistilPar_hpp_ #include From fb2834bf822aebc17941e0567182842ea39cd4c8 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 12 Nov 2019 14:01:20 +0000 Subject: [PATCH 321/347] Oops --- Hadrons/Modules/MDistil/DistilPar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/DistilPar.hpp b/Hadrons/Modules/MDistil/DistilPar.hpp index fbca36a4..63982926 100644 --- a/Hadrons/Modules/MDistil/DistilPar.hpp +++ b/Hadrons/Modules/MDistil/DistilPar.hpp @@ -27,7 +27,7 @@ *************************************************************************************/ /* END LEGAL */ -##ifndef Hadrons_MDistil_DistilPar_hpp_ +#ifndef Hadrons_MDistil_DistilPar_hpp_ #define Hadrons_MDistil_DistilPar_hpp_ #include From b0f24ec3024d5dc2783848fb7247ae30e5c5e345 Mon Sep 17 00:00:00 2001 From: ferben Date: Tue, 12 Nov 2019 15:14:13 +0000 Subject: [PATCH 322/347] Test works now --- tests/hadrons/Test_distil.cc | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index 6e79f3f8..d137e5e7 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -143,7 +143,8 @@ std::string test_Solver(Application &application, const char * pSuffix = nullptr std::string test_DPar(Application &application) { // DistilVectors parameters - MDistil::DistilParPar DistilPar; + //MDistil::DistilParPar DistilPar; + MDistil::DistilParameters DistilPar; DistilPar.nvec = 5; DistilPar.nnoise = 1; DistilPar.tsrc = 0; @@ -162,7 +163,8 @@ std::string test_Noises(Application &application, const std::string &sNoiseBaseN // DistilVectors parameters MDistil::NoisesPar NoisePar; NoisePar.DistilPar = "DPar_l"; - std::string sNoiseName{sNoiseBaseName + "_noise"}; + NoisePar.NoiseFileName = "noise"; + std::string sNoiseName{"noise"}; application.createModule(sNoiseName,NoisePar); return sNoiseName; } @@ -198,6 +200,7 @@ void test_Perambulators( Application &application, const char * pszSuffix = null PerambPar.PerambFileName = sModuleName; PerambPar.solver = test_Solver( application, pszSuffix ); PerambPar.DistilPar = "DPar_l"; + PerambPar.noise = "noise"; test_Noises(application, sModuleName); // I want these written after solver stuff application.createModule( sModuleName, PerambPar ); } @@ -215,7 +218,9 @@ void test_DistilVectors(Application &application, const char * pszSuffix = nullp if( pszSuffix ) sPerambName.append( pszSuffix ); MDistil::DistilVectors::Par DistilVecPar; - DistilVecPar.noise = sPerambName + "_noise"; + DistilVecPar.noise = "noise"; + DistilVecPar.rho = "rho"; + DistilVecPar.phi = "phi"; DistilVecPar.perambulator = sPerambName; DistilVecPar.lapevec = "LapEvec"; DistilVecPar.DistilPar = "DPar_l"; @@ -253,7 +258,7 @@ void test_MesonField(Application &application, const char * pszFileSuffix, if( pszObjectRight == nullptr ) pszObjectRight = pszObjectLeft; MContraction::A2AMesonField::Par A2AMesonFieldPar; - A2AMesonFieldPar.left="DistilVecs"; + A2AMesonFieldPar.left=""; A2AMesonFieldPar.right=A2AMesonFieldPar.left; A2AMesonFieldPar.left.append( pszObjectLeft ); A2AMesonFieldPar.right.append( pszObjectRight ); @@ -346,10 +351,10 @@ int main(int argc, char *argv[]) test_Global( application ); test_LapEvec( application ); test_DPar( application ); - //test_Perambulators( application ); - //test_DistilVectors( application ); - //test_MesonField( application, "Phi", "_phi" ); - //test_MesonField( application, "Rho", "_rho" ); + test_Perambulators( application ); + test_DistilVectors( application ); + test_MesonField( application, "Phi", "phi" ); + test_MesonField( application, "Rho", "rho" ); break; case 1: LOG(Message) << "Computing Meson 2pt-function by loading perambulators" << std::endl; @@ -358,8 +363,8 @@ int main(int argc, char *argv[]) test_DPar( application ); test_LoadPerambulators( application ); test_DistilVectors( application ); - test_MesonField( application, "Phi", "_phi" ); - test_MesonField( application, "Rho", "_rho" ); + test_MesonField( application, "Phi", "phi" ); + test_MesonField( application, "Rho", "rho" ); break; case 2: LOG(Message) << "Computing Meson 2pt-function for two quark flavours" << std::endl; From 6d7043e0c293ec57df8d43bf7bfaa2f479c592de Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 12 Nov 2019 17:31:42 +0000 Subject: [PATCH 323/347] NamedTensor changes done --- Hadrons/Modules.hpp | 11 ++- .../MDistil/{DistilCommon.hpp => Distil.hpp} | 24 ++--- Hadrons/Modules/MDistil/DistilPar.hpp | 5 +- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/Noises.hpp | 2 +- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 2 +- Hadrons/Modules/MDistil/Perambulator.cc | 8 +- Hadrons/Modules/MDistil/Perambulator.hpp | 2 +- Hadrons/Modules/MIO/LoadDistilNoise.hpp | 2 +- Hadrons/Modules/MIO/LoadPerambulator.hpp | 2 +- Hadrons/{Distil.hpp => NamedTensor.hpp} | 89 ++++++++++--------- 12 files changed, 77 insertions(+), 74 deletions(-) rename Hadrons/Modules/MDistil/{DistilCommon.hpp => Distil.hpp} (88%) rename Hadrons/{Distil.hpp => NamedTensor.hpp} (66%) diff --git a/Hadrons/Modules.hpp b/Hadrons/Modules.hpp index e0e8832e..0b1235d6 100644 --- a/Hadrons/Modules.hpp +++ b/Hadrons/Modules.hpp @@ -65,13 +65,12 @@ #include #include #include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include #include #include #include diff --git a/Hadrons/Modules/MDistil/DistilCommon.hpp b/Hadrons/Modules/MDistil/Distil.hpp similarity index 88% rename from Hadrons/Modules/MDistil/DistilCommon.hpp rename to Hadrons/Modules/MDistil/Distil.hpp index b1f0c768..03029acb 100644 --- a/Hadrons/Modules/MDistil/DistilCommon.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -2,7 +2,7 @@ Grid physics library, www.github.com/paboyle/Grid - Source file: Hadrons/Modules/MDistil/DistilCommon.hpp + Source file: Hadrons/Modules/MDistil/Distil.hpp Copyright (C) 2015-2019 @@ -27,10 +27,10 @@ *************************************************************************************/ /* END LEGAL */ -#ifndef Hadrons_MDistil_DistilCommon_hpp_ -#define Hadrons_MDistil_DistilCommon_hpp_ +#ifndef Hadrons_MDistil_Distil_hpp_ +#define Hadrons_MDistil_Distil_hpp_ -#include +#include #include #include #include @@ -105,14 +105,18 @@ inline void RotateEigen(std::vector & evec) Grid::Complex cplx0 = cv0()()(0); if( cplx0.imag() == 0 ) std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; - else { + else + { + const Real cplx0_mag = Grid::sqrt(cplx0.real()*cplx0.real()+cplx0.imag()*cplx0.imag()); + Grid::Complex phase{cplx0 / Grid::Complex(cplx0_mag, 0) }; + phase.imag(-phase.imag()); #ifdef GRID_NVCC - const Real cplx0_mag = thrust::abs(cplx0); - const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); + //const Real cplx0_mag = thrust::abs(cplx0); + //const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); const Real argphase = thrust::arg(phase); #else - const Real cplx0_mag = std::abs(cplx0); - const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); + //const Real cplx0_mag = std::abs(cplx0); + //const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); const Real argphase = std::arg(phase); #endif std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / 3.14159265) << " pi" << std::endl; @@ -133,4 +137,4 @@ inline void RotateEigen(std::vector & evec) END_MODULE_NAMESPACE END_HADRONS_NAMESPACE -#endif // Hadrons_MDistil_DistilCommon_hpp_ +#endif // Hadrons_MDistil_Distil_hpp_ diff --git a/Hadrons/Modules/MDistil/DistilPar.hpp b/Hadrons/Modules/MDistil/DistilPar.hpp index 63982926..14b0174c 100644 --- a/Hadrons/Modules/MDistil/DistilPar.hpp +++ b/Hadrons/Modules/MDistil/DistilPar.hpp @@ -30,10 +30,7 @@ #ifndef Hadrons_MDistil_DistilPar_hpp_ #define Hadrons_MDistil_DistilPar_hpp_ -#include -#include -#include -#include +#include BEGIN_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 919c6d91..d248d553 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -30,7 +30,7 @@ #ifndef Hadrons_MDistil_DistilVectors_hpp_ #define Hadrons_MDistil_DistilVectors_hpp_ -#include +#include BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 40f859c8..3ea5c152 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -30,7 +30,7 @@ #ifndef Hadrons_MDistil_LapEvec_hpp_ #define Hadrons_MDistil_LapEvec_hpp_ -#include +#include BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 10596260..5e3aac44 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -30,7 +30,7 @@ #ifndef Hadrons_MDistil_Noises_hpp_ #define Hadrons_MDistil_Noises_hpp_ -#include +#include BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index dec76404..a0516efa 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -30,7 +30,7 @@ #ifndef Hadrons_MDistil_PerambFromSolve_hpp_ #define Hadrons_MDistil_PerambFromSolve_hpp_ -#include +#include BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) diff --git a/Hadrons/Modules/MDistil/Perambulator.cc b/Hadrons/Modules/MDistil/Perambulator.cc index 7b1d68d9..e710698e 100644 --- a/Hadrons/Modules/MDistil/Perambulator.cc +++ b/Hadrons/Modules/MDistil/Perambulator.cc @@ -47,11 +47,11 @@ extern const std::string NamedTensorFileExtension{".dat"}; BEGIN_MODULE_NAMESPACE(MDistil) -const std::string NoiseTensor::Name_{"Noises"}; -const std::vector NoiseTensor::DefaultIndexNames_{"nNoise", "nT", "nVec", "nS"}; +const std::string NoiseTensor::Name__{"Noises"}; +const std::array NoiseTensor::DefaultIndexNames__{"nNoise", "nT", "nVec", "nS"}; -const std::string PerambTensor::Name_{"Perambulator"}; -const std::vector PerambTensor::DefaultIndexNames_{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; +const std::string PerambTensor::Name__{"Perambulator"}; +const std::array PerambTensor::DefaultIndexNames__{"nT", "nVec", "LI", "nNoise", "nT_inv", "SI"}; END_MODULE_NAMESPACE END_HADRONS_NAMESPACE diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index ab71b60a..e3525f17 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -30,7 +30,7 @@ #ifndef Hadrons_MDistil_Perambulator_hpp_ #define Hadrons_MDistil_Perambulator_hpp_ -#include +#include BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MDistil) diff --git a/Hadrons/Modules/MIO/LoadDistilNoise.hpp b/Hadrons/Modules/MIO/LoadDistilNoise.hpp index 7b5ce782..ace88887 100644 --- a/Hadrons/Modules/MIO/LoadDistilNoise.hpp +++ b/Hadrons/Modules/MIO/LoadDistilNoise.hpp @@ -30,7 +30,7 @@ #ifndef Hadrons_MIO_LoadDistilNoise_hpp_ #define Hadrons_MIO_LoadDistilNoise_hpp_ -#include +#include BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MIO) diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 8314c2a9..46c0c74d 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -30,7 +30,7 @@ #ifndef Hadrons_MIO_LoadPerambulator_hpp_ #define Hadrons_MIO_LoadPerambulator_hpp_ -#include +#include BEGIN_HADRONS_NAMESPACE BEGIN_MODULE_NAMESPACE(MIO) diff --git a/Hadrons/Distil.hpp b/Hadrons/NamedTensor.hpp similarity index 66% rename from Hadrons/Distil.hpp rename to Hadrons/NamedTensor.hpp index a5f87c98..a8c98127 100644 --- a/Hadrons/Distil.hpp +++ b/Hadrons/NamedTensor.hpp @@ -2,7 +2,7 @@ Grid physics library, www.github.com/paboyle/Grid - Source file: Hadrons/Distil.hpp + Source file: Hadrons/NamedTensor.hpp Copyright (C) 2015-2019 @@ -27,8 +27,8 @@ *************************************************************************************/ /* END LEGAL */ -#ifndef Hadrons_Distil_hpp_ -#define Hadrons_Distil_hpp_ +#ifndef Hadrons_NamedTensor_hpp_ +#define Hadrons_NamedTensor_hpp_ #include #include @@ -36,11 +36,11 @@ BEGIN_HADRONS_NAMESPACE /****************************************************************************** - NamedTensor - Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order), together with a name for each index. - Index names are mutable, but tensor dimensionality is not (size of each dimension is mutable). - They can be persisted to / restored from disk, by default using tag Name. - During restore from disk, these validations are performed: + NamedTensor contains: + 1) Name of the tensor. By default, this is the tag name used for save / load + 2) Eigen::Tensor of type Scalar_ and rank NumIndices_ (row-major order) + 3) Name for each index + They can be persisted to / restored from disk. During restore, these validations are performed: 1) Tensor dimensionality must match 2) IndexNames are validated against current values 3) If the tensor has non-zero size, the tensor being loaded must have same extent in each dimension @@ -61,37 +61,39 @@ public: std::vector, IndexNames ); // Name of the object and Index names as set in the constructor - const std::string &Name; - const std::vector &DefaultIndexNames; + const std::string &Name_; + const std::array &DefaultIndexNames_; - virtual ~NamedTensor(){}; // Default constructor (assumes tensor will be loaded from file) - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name_, const std::vector &IndexNames_) - : IndexNames{IndexNames_}, Name{Name_}, DefaultIndexNames{IndexNames_} {} + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name, + const std::array &indexNames) + : IndexNames{indexNames.begin(), indexNames.end()}, Name_{Name}, DefaultIndexNames_{indexNames} {} // Construct a named tensor explicitly specifying size of each dimension template - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name_, const std::vector &IndexNames_, + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name, + const std::array &indexNames, Eigen::Index firstDimension, IndexTypes... otherDimensions) - : tensor(firstDimension, otherDimensions...), IndexNames{IndexNames_}, Name{Name_}, DefaultIndexNames{IndexNames_} + : tensor(firstDimension, otherDimensions...), + IndexNames{indexNames.begin(), indexNames.end()}, Name_{Name}, DefaultIndexNames_{indexNames} { - assert(sizeof...(otherDimensions) + 1 == NumIndices_ && "NamedTensor: dimensions != tensor rank"); + if(sizeof...(otherDimensions) + 1 != NumIndices) + HADRONS_ERROR(Argument, "NamedTensor: dimensions != tensor rank"); } // Do my index names match the default for my type? - bool ValidateIndexNames( const std::vector &CheckNames ) const + template + bool ValidateIndexNames( const array_or_vector_of_string &CheckNames ) const { - assert( CheckNames.size() == NumIndices_ && "Bug: CheckNames don't match NumIndices_" ); - bool bSame{ IndexNames.size() == NumIndices_ }; - for( std::size_t i = 0; bSame && i < NumIndices_; i++ ) - { - bSame = IndexNames[i].size() == CheckNames[i].size() - && std::equal( IndexNames[i].begin(), IndexNames[i].end(), CheckNames[i].begin(), - [](const char & c1, const char & c2){ return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); - } - return bSame; + return IndexNames.size() == CheckNames.size() && std::equal( IndexNames.begin(), IndexNames.end(), CheckNames.begin(), + [](const std::string &s1, const std::string &s2) + { + return s1.size() == s2.size() && std::equal( s1.begin(), s1.end(), s2.begin(), + [](const char & c1, const char & c2) + { return c1 == c2 || std::toupper(c1) == std::toupper(c2); }); // case insensitive + }); } - bool ValidateIndexNames() const { return ValidateIndexNames(DefaultIndexNames); } + bool ValidateIndexNames() const { return ValidateIndexNames(DefaultIndexNames_); } #ifdef HAVE_HDF5 using Default_Reader = Grid::Hdf5Reader; @@ -105,11 +107,11 @@ public: { std::string FileName_{FileName}; FileName_.append( NamedTensorFileExtension ); - LOG(Message) << "Writing " << Name << " to file " << FileName_ << " tag " << Tag << std::endl; + LOG(Message) << "Writing " << Name_ << " to file " << FileName_ << " tag " << Tag << std::endl; Default_Writer w( FileName_ ); write( w, Tag, *this ); } - void write(const std::string &FileName) const { return write(FileName, Name); } + void write(const std::string &FileName) const { return write(FileName, Name_); } // Read tensor. // Validate: @@ -123,18 +125,19 @@ public: read(r, Tag, *this); const typename ET::Dimensions & NewDimensions{tensor.dimensions()}; for (int i = 0; i < NumIndices_; i++) - assert(OldDimensions[i] == 0 || OldDimensions[i] == NewDimensions[i] && "NamedTensor::read dimension size"); - if (bValidate) - assert(ValidateIndexNames(OldIndexNames) && "NamedTensor::read dimension name"); + if(OldDimensions[i] && OldDimensions[i] != NewDimensions[i]) + HADRONS_ERROR(Size,"NamedTensor::read dimension size"); + if (bValidate && !ValidateIndexNames(OldIndexNames)) + HADRONS_ERROR(Definition,"NamedTensor::read dimension name"); } - template void read(Reader &r, bool bValidate = true) { read(r, bValidate, Name); } + template void read(Reader &r, bool bValidate = true) { read(r, bValidate, Name_); } inline void read (const std::string &FileName, bool bValidate, const std::string &Tag) { Default_Reader r(FileName + NamedTensorFileExtension); read(r, bValidate, Tag); } - inline void read (const std::string &FileName, bool bValidate= true) { return read(FileName, bValidate, Name); } + inline void read (const std::string &FileName, bool bValidate= true) { return read(FileName, bValidate, Name_); } }; /****************************************************************************** @@ -150,32 +153,32 @@ using LapEvecs = Grid::Hadrons::EigenPack; class NoiseTensor : public NamedTensor { - static const std::string Name_; - static const std::vector DefaultIndexNames_; + static const std::string Name__; + static const std::array DefaultIndexNames__; public: // Default constructor (assumes tensor will be loaded from file) - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NoiseTensor() : NamedTensor{Name_, DefaultIndexNames_} {} + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NoiseTensor() : NamedTensor{Name__, DefaultIndexNames__} {} // Construct a named tensor explicitly specifying size of each dimension template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NoiseTensor(Eigen::Index nNoise, Eigen::Index nT, Eigen::Index nVec, Eigen::Index nS) - : NamedTensor{Name_, DefaultIndexNames_, nNoise, nT, nVec, nS} {} + : NamedTensor{Name__, DefaultIndexNames__, nNoise, nT, nVec, nS} {} }; class PerambTensor : public NamedTensor { - static const std::string Name_; - static const std::vector DefaultIndexNames_; + static const std::string Name__; + static const std::array DefaultIndexNames__; public: // Default constructor (assumes tensor will be loaded from file) - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PerambTensor() : NamedTensor{Name_, DefaultIndexNames_} {} + EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PerambTensor() : NamedTensor{Name__, DefaultIndexNames__} {} // Construct a named tensor explicitly specifying size of each dimension template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PerambTensor(Eigen::Index nT, Eigen::Index nVec, Eigen::Index LI, Eigen::Index nNoise, Eigen::Index nT_inv, Eigen::Index SI) - : NamedTensor{Name_, DefaultIndexNames_, nT, nVec, LI, nNoise, nT_inv, SI} {} + : NamedTensor{Name__, DefaultIndexNames__, nT, nVec, LI, nNoise, nT_inv, SI} {} }; END_MODULE_NAMESPACE END_HADRONS_NAMESPACE -#endif // Hadrons_Distil_hpp_ +#endif // Hadrons_NamedTensor_hpp_ From 3f00b8f6c76bc8a62d588c4031f30998b4a7e239 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 12 Nov 2019 21:53:09 +0000 Subject: [PATCH 324/347] Switch to std::unique_ptr grid3d; Remove hand-coded reference to pi - switch to definition --- Hadrons/Modules/MDistil/Distil.hpp | 9 +- Hadrons/Modules/MDistil/DistilPar.hpp | 10 +- Hadrons/Modules/MDistil/DistilVectors.hpp | 111 ++++++-------------- Hadrons/Modules/MDistil/LapEvec.hpp | 55 +++------- Hadrons/Modules/MDistil/Noises.hpp | 33 ++---- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 91 +++++----------- Hadrons/Modules/MDistil/Perambulator.hpp | 55 +++------- 7 files changed, 105 insertions(+), 259 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 03029acb..0cfd31b4 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -30,6 +30,9 @@ #ifndef Hadrons_MDistil_Distil_hpp_ #define Hadrons_MDistil_Distil_hpp_ +#define _USE_MATH_DEFINES +#include + #include #include #include @@ -43,7 +46,7 @@ BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** Distillation code that is common across modules - Documentation on how t use this code available at + Documentation on how to use this code available at * https://aportelli.github.io/Hadrons-doc/#/mdistil * @@ -119,7 +122,7 @@ inline void RotateEigen(std::vector & evec) //const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); const Real argphase = std::arg(phase); #endif - std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / 3.14159265) << " pi" << std::endl; + std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / M_PI) << " pi" << std::endl; { // TODO: Only really needed on the master slice for( int k = 0 ; k < evec.size() ; k++ ) @@ -137,4 +140,4 @@ inline void RotateEigen(std::vector & evec) END_MODULE_NAMESPACE END_HADRONS_NAMESPACE -#endif // Hadrons_MDistil_Distil_hpp_ +#endif diff --git a/Hadrons/Modules/MDistil/DistilPar.hpp b/Hadrons/Modules/MDistil/DistilPar.hpp index 14b0174c..f38e78d1 100644 --- a/Hadrons/Modules/MDistil/DistilPar.hpp +++ b/Hadrons/Modules/MDistil/DistilPar.hpp @@ -33,11 +33,11 @@ #include BEGIN_HADRONS_NAMESPACE +BEGIN_MODULE_NAMESPACE(MDistil) /****************************************************************************** * DistilPar * ******************************************************************************/ -BEGIN_MODULE_NAMESPACE(MDistil) template class TDistilPar: public Module @@ -63,9 +63,7 @@ MODULE_REGISTER_TMP(DistilPar, TDistilPar, MDistil); ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TDistilPar::TDistilPar(const std::string name) -: Module(name) -{} +TDistilPar::TDistilPar(const std::string name) : Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template @@ -95,7 +93,5 @@ void TDistilPar::execute(void) } END_MODULE_NAMESPACE - END_HADRONS_NAMESPACE - -#endif // Hadrons_MDistil_DistilPar_hpp_ +#endif diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index d248d553..1152960a 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -49,7 +49,7 @@ public: std::string, lapevec, std::string, rho, std::string, phi, - std::string, DistilPar); + std::string, DistilParams); }; template @@ -69,18 +69,9 @@ public: // execution virtual void execute(void); protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) - virtual void Cleanup(void); + std::unique_ptr grid3d; // Owned by me, so I must delete it public: // These variables contain parameters - std::string PerambulatorName; - std::string NoiseVectorName; - std::string LapEvecName; - std::string DParName; - bool bMakeRho; - bool bMakePhi; std::string RhoName; std::string PhiName; }; @@ -92,47 +83,31 @@ MODULE_REGISTER_TMP(DistilVectors, TDistilVectors, MDistil); ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TDistilVectors::TDistilVectors(const std::string name) -: grid3d{nullptr}, grid4d{nullptr}, Module(name) -{} -// destructor -template -TDistilVectors::~TDistilVectors(void) -{ - Cleanup(); -}; +TDistilVectors::TDistilVectors(const std::string name) : Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template std::vector TDistilVectors::getInput(void) { - PerambulatorName = par().perambulator; - NoiseVectorName = par().noise; - LapEvecName = par().lapevec; - DParName = par().DistilPar; - return { PerambulatorName, NoiseVectorName, LapEvecName, DParName }; + return {par().noise,par().perambulator,par().lapevec,par().DistilParams}; } template std::vector TDistilVectors::getOutput(void) { - RhoName = par().rho; - PhiName = par().phi; - bMakeRho = ( RhoName.size() > 0 ); - bMakePhi = ( PhiName.size() > 0 ); - if (!bMakeRho && !bMakePhi) + RhoName = par().rho; + PhiName = par().phi; + if (RhoName.empty() && PhiName.empty()) { RhoName = getName(); PhiName = RhoName; RhoName.append("_rho"); PhiName.append("_phi"); - bMakeRho = true; - bMakePhi = true; } std::vector out; - if (bMakeRho) + if (!RhoName.empty()) out.push_back(RhoName); - if (bMakePhi) + if (!PhiName.empty()) out.push_back(PhiName); return out; } @@ -141,64 +116,45 @@ std::vector TDistilVectors::getOutput(void) template void TDistilVectors::setup(void) { - Cleanup(); - auto &noise = envGet(NoiseTensor, NoiseVectorName); - auto &perambulator = envGet(PerambTensor, PerambulatorName); - auto &DPar = envGet(MDistil::DistilParameters, DParName); - // We expect the perambulator to have been created with these indices - assert( perambulator.ValidateIndexNames() && "Perambulator index names bad" ); + auto &perambulator = envGet(PerambTensor, par().perambulator); + if (!perambulator.ValidateIndexNames()) + HADRONS_ERROR(Range,"Perambulator index names bad"); + + const DistilParameters &dp{envGet(DistilParameters, par().DistilParams)}; + const int Nt{env().getDim(Tdir)}; + const bool full_tdil{ dp.TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : dp.TI }; - const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - const int TI{DPar.TI}; - const int LI{DPar.LI}; - const int SI{DPar.SI}; - const bool full_tdil{ TI == Nt }; - const int Nt_inv{ full_tdil ? 1 : TI }; + if (!RhoName.empty()) + envCreate(std::vector, RhoName, 1, dp.nnoise*dp.LI*dp.SI*Nt_inv, envGetGrid(FermionField)); + if (!PhiName.empty()) + envCreate(std::vector, PhiName, 1, dp.nnoise*dp.LI*dp.SI*Nt_inv, envGetGrid(FermionField)); - if (bMakeRho) - envCreate(std::vector, RhoName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - if (bMakePhi) - envCreate(std::vector, PhiName, 1, nnoise*LI*SI*Nt_inv, envGetGrid(FermionField)); - - grid4d = env().getGrid(); Coordinate latt_size = GridDefaultLatt(); Coordinate mpi_layout = GridDefaultMpi(); Coordinate simd_layout_3 = GridDefaultSimd(Nd-1, vComplex::Nsimd()); latt_size[Nd-1] = 1; simd_layout_3.push_back( 1 ); mpi_layout[Nd-1] = 1; - grid3d = MakeLowerDimGrid(grid4d); + GridCartesian * const grid4d{env().getGrid()}; + grid3d.reset(MakeLowerDimGrid(grid4d)); envTmp(LatticeSpinColourVector, "source4d",1,LatticeSpinColourVector(grid4d)); - envTmp(LatticeSpinColourVector, "source3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "source3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeSpinColourVector, "sink3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); -} - -// clean up any temporaries created by setup (that aren't stored in the environment) -template -void TDistilVectors::Cleanup(void) -{ - if ( grid3d != nullptr) - { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; + envTmp(LatticeSpinColourVector, "source3d",1,LatticeSpinColourVector(grid3d.get())); + envTmp(LatticeColourVector, "source3d_nospin",1,LatticeColourVector(grid3d.get())); + envTmp(LatticeSpinColourVector, "sink3d",1,LatticeSpinColourVector(grid3d.get())); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d.get())); } // execution /////////////////////////////////////////////////////////////////// template void TDistilVectors::execute(void) { - auto &noise = envGet(NoiseTensor, NoiseVectorName); - auto &perambulator = envGet(PerambTensor, PerambulatorName); - auto &epack = envGet(Grid::Hadrons::EigenPack, LapEvecName); - auto &DPar = envGet(MDistil::DistilParameters, DParName); + auto &noise = envGet(NoiseTensor, par().noise); + auto &perambulator = envGet(PerambTensor, par().perambulator); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().lapevec); + const DistilParameters &DPar{envGet(DistilParameters, par().DistilParams)}; envGetTmp(LatticeSpinColourVector, source4d); envGetTmp(LatticeSpinColourVector, source3d); @@ -206,6 +162,7 @@ void TDistilVectors::execute(void) envGetTmp(LatticeSpinColourVector, sink3d); envGetTmp(LatticeColourVector, evec3d); + GridCartesian * const grid4d{env().getGrid()}; const int Ntlocal{ grid4d->LocalDimensions()[3] }; const int Ntfirst{ grid4d->LocalStarts()[3] }; @@ -221,7 +178,7 @@ void TDistilVectors::execute(void) int vecindex; int t_inv; - if (bMakeRho) + if (!RhoName.empty()) { auto &rho = envGet(std::vector, RhoName); for (int inoise = 0; inoise < nnoise; inoise++) { @@ -252,7 +209,7 @@ void TDistilVectors::execute(void) } } } - if (bMakePhi) { + if (!PhiName.empty()) { auto &phi = envGet(std::vector, PhiName); for (int inoise = 0; inoise < nnoise; inoise++) { for (int dk = 0; dk < LI; dk++) { diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 3ea5c152..3ae29d42 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -111,12 +111,7 @@ public: // execution virtual void execute(void); protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * gridLD; // Owned by me, so I must delete it - GridCartesian * gridHD; // Owned by environment (so I won't delete it) - std::string sGaugeName; -protected: - virtual void Cleanup(void); + std::unique_ptr gridLD; // Owned by me, so I must delete it }; MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); @@ -127,61 +122,36 @@ MODULE_REGISTER_TMP(LapEvec, TLapEvec, MDistil); // constructor ///////////////////////////////////////////////////////////////// template -TLapEvec::TLapEvec(const std::string name) : gridLD{nullptr}, Module(name) -{ -} - -// destructor ///////////////////////////////////////////////////////////////// -template -TLapEvec::~TLapEvec() -{ - Cleanup(); -} +TLapEvec::TLapEvec(const std::string name) : Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template std::vector TLapEvec::getInput(void) { - sGaugeName = par().gauge; - return std::vector{ sGaugeName }; + return std::vector{par().gauge}; } template std::vector TLapEvec::getOutput(void) { - std::vector out = {getName()}; // This is the higher dimensional eigenpack - return out; + return {getName()}; // This is the higher dimensional eigenpack } // setup /////////////////////////////////////////////////////////////////////// template void TLapEvec::setup(void) { - Cleanup(); - Environment & e{env()}; - gridHD = e.getGrid(); - gridLD = MakeLowerDimGrid( gridHD ); + GridCartesian * gridHD = env().getGrid(); + gridLD.reset(MakeLowerDimGrid(gridHD)); const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; // Temporaries envTmpLat(GaugeField, "Umu_stout"); envTmpLat(GaugeField, "Umu_smear"); - envTmp(LatticeGaugeField, "UmuNoTime",1,LatticeGaugeField(gridLD)); - envTmp(LatticeColourVector, "src",1,LatticeColourVector(gridLD)); + envTmp(LatticeGaugeField, "UmuNoTime",1,LatticeGaugeField(gridLD.get())); + envTmp(LatticeColourVector, "src",1,LatticeColourVector(gridLD.get())); envTmp(std::vector, "eig",1,std::vector(Ntlocal)); // Output objects - envCreate(LapEvecs, getName(), 1, par().Lanczos.Nvec, gridHD ); -} - -// clean up any temporaries created by setup (that aren't stored in the environment) -template -void TLapEvec::Cleanup(void) -{ - if (gridLD != nullptr) - { - delete gridLD; - gridLD = nullptr; - } - gridHD = nullptr; + envCreate(LapEvecs, getName(), 1, par().Lanczos.Nvec, gridHD); } /************************************************************************************* @@ -260,7 +230,7 @@ void TLapEvec::execute(void) // Stout smearing envGetTmp(GaugeField, Umu_smear); - Umu_smear = envGet(GaugeField, sGaugeName); // The smeared field starts off as the Gauge field + Umu_smear = envGet(GaugeField, par().gauge); // The smeared field starts off as the Gauge field LOG(Message) << "Initial plaquette: " << WilsonLoops::avgPlaquette(Umu_smear) << std::endl; const StoutParameters &Stout{par().Stout}; if( Stout.steps ) @@ -282,6 +252,7 @@ void TLapEvec::execute(void) envGetTmp(std::vector, eig); // Eigenpack for each timeslice envGetTmp(LatticeGaugeField, UmuNoTime); // Gauge field without time dimension envGetTmp(LatticeColourVector, src); + GridCartesian * gridHD = env().getGrid(); const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; const int Ntfirst{gridHD->LocalStarts()[Tdir]}; uint32_t ConvergenceErrors{0}; @@ -290,7 +261,7 @@ void TLapEvec::execute(void) LOG(Message) << "------------------------------------------------------------" << std::endl; LOG(Message) << " Compute eigenpack, local timeslice = " << t << " / " << Ntlocal << std::endl; LOG(Message) << "------------------------------------------------------------" << std::endl; - eig[t].resize(LPar.Nk+LPar.Np,gridLD); + eig[t].resize(LPar.Nk+LPar.Np,gridLD.get()); // Construct smearing operator ExtractSliceLocal(UmuNoTime,Umu_smear,0,t,Tdir); // switch to 3d/4d objects @@ -317,7 +288,7 @@ void TLapEvec::execute(void) LOG(Error) << "MDistil::LapEvec : Not enough eigenvectors converged. If this occurs in practice, we should modify the eigensolver to iterate once more to ensure the second convergence test does not take us below the requested number of eigenvectors" << std::endl; } if( Nconv != LPar.Nvec ) - eig[t].resize( LPar.Nvec, gridLD ); + eig[t].resize(LPar.Nvec, gridLD.get()); RotateEigen( eig[t].evec ); // Rotate the eigenvectors into our phase convention for (int i=0;i, MDistil); @@ -73,16 +71,13 @@ MODULE_REGISTER_TMP(Noises, TNoises, MDistil); ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TNoises::TNoises(const std::string name) -: Module(name) -{} +TNoises::TNoises(const std::string name) : Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template std::vector TNoises::getInput(void) { - DParName = par().DistilPar; - return { DParName }; + return {par().DistilParams}; } template @@ -96,32 +91,26 @@ std::vector TNoises::getOutput(void) template void TNoises::setup(void) { - auto &DPar = envGet(MDistil::DistilParameters, DParName); + const DistilParameters &dp{envGet(DistilParameters, par().DistilParams)}; const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - envCreate(NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); + envCreate(NoiseTensor, getName(), 1, dp.nnoise, Nt, dp.nvec, Ns); } // execution /////////////////////////////////////////////////////////////////// template void TNoises::execute(void) { - auto &DPar = envGet(MDistil::DistilParameters, DParName); + const DistilParameters &dp{envGet(DistilParameters, par().DistilParams)}; const int Nt{env().getDim(Tdir)}; - const int nnoise{DPar.nnoise}; - const int nvec{DPar.nvec}; - const int TI{DPar.TI}; - const int LI{DPar.LI}; - const bool full_tdil{ TI == Nt }; - const bool exact_distillation{ full_tdil && LI == nvec }; + const bool full_tdil{ dp.TI == Nt }; + const bool exact_distillation{ full_tdil && dp.LI == dp.nvec }; // We use our own seeds so we can specify different noises per quark Real rn; auto &noise = envGet(NoiseTensor, getName()); - for (int inoise = 0; inoise < nnoise; inoise++) { + for (int inoise = 0; inoise < dp.nnoise; inoise++) { for (int t = 0; t < Nt; t++) { - for (int ivec = 0; ivec < nvec; ivec++) { + for (int ivec = 0; ivec < dp.nvec; ivec++) { for (int is = 0; is < Ns; is++) { if (exact_distillation) noise.tensor(inoise, t, ivec, is) = 1.; @@ -146,4 +135,4 @@ void TNoises::execute(void) END_MODULE_NAMESPACE END_HADRONS_NAMESPACE -#endif // Hadrons_MDistil_Noises_hpp_ +#endif diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index a0516efa..c63f5c2b 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -51,9 +51,9 @@ public: std::string, eigenPack, std::string, PerambFileName, std::string, solve, - std::string, nvec_reduced, - std::string, LI_reduced, - std::string, DistilPar); + int, nvec_reduced, + int, LI_reduced, + std::string, DistilParams); }; template @@ -73,13 +73,7 @@ public: // execution virtual void execute(void); protected: - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; - //other members - std::string DParName; -protected: - virtual void Cleanup(void); - + std::unique_ptr grid3d; // Owned by me, so I must delete it }; MODULE_REGISTER_TMP(PerambFromSolve, TPerambFromSolve, MDistil); @@ -89,67 +83,37 @@ MODULE_REGISTER_TMP(PerambFromSolve, TPerambFromSolve, MDistil); ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TPerambFromSolve::TPerambFromSolve(const std::string name) -:grid3d{nullptr}, grid4d{nullptr}, Module(name) -{} -//destructor -template -TPerambFromSolve::~TPerambFromSolve(void) -{ - Cleanup(); -}; +TPerambFromSolve::TPerambFromSolve(const std::string name) : Module(name){} // dependencies/products /////////////////////////////////////////////////////// template std::vector TPerambFromSolve::getInput(void) { - //return std::vector{ par().solve, par().eigenPack }; - DParName = par().DistilPar; - return {par().solve, par().eigenPack, DParName }; + return {par().solve, par().eigenPack, par().DistilParams}; } template std::vector TPerambFromSolve::getOutput(void) { - return std::vector{ getName() }; + return std::vector{getName()}; } // setup /////////////////////////////////////////////////////////////////////// template void TPerambFromSolve::setup(void) { - Cleanup(); - auto &DPar = envGet(MDistil::DistilParameters, DParName); - const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - const int LI{DPar.LI}; - const int TI{DPar.TI}; - const int SI{DPar.SI}; - const bool full_tdil{ TI == Nt }; - const int Nt_inv{ full_tdil ? 1 : TI }; - const int nvec_reduced{ par().nvec_reduced.empty() ? nvec:std::stoi(par().nvec_reduced)}; - const int LI_reduced{ par().LI_reduced.empty() ? LI:std::stoi(par().LI_reduced)}; - grid4d = env().getGrid(); - grid3d = MakeLowerDimGrid(grid4d); - envCreate(PerambTensor, getName(), 1, Nt,nvec_reduced,LI_reduced,nnoise,Nt_inv,SI); - envCreate(NoiseTensor, getName() + "_noise", 1, nnoise, Nt, nvec, Ns ); - envTmp(LatticeColourVector, "result3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + const DistilParameters & dp{envGet(MDistil::DistilParameters, par().DistilParams)}; + const int Nt{env().getDim(Tdir)}; + const bool full_tdil{ dp.TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : dp.TI }; + grid3d.reset( MakeLowerDimGrid( env().getGrid() ) ); + envCreate(PerambTensor, getName(), 1, Nt,par().nvec_reduced,par().LI_reduced,dp.nnoise,Nt_inv,dp.SI); + envCreate(NoiseTensor, getName() + "_noise", 1, dp.nnoise, Nt, dp.nvec, Ns ); + envTmp(LatticeColourVector, "result3d_nospin",1,LatticeColourVector(grid3d.get())); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d.get())); envTmpLat(LatticeColourVector, "result4d_nospin"); } -template -void TPerambFromSolve::Cleanup(void) -{ - if (grid3d != nullptr) - { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; -} - // execution /////////////////////////////////////////////////////////////////// template void TPerambFromSolve::execute(void) @@ -157,17 +121,12 @@ void TPerambFromSolve::execute(void) GridCartesian * grid4d = env().getGrid(); const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; - auto &DPar = envGet(MDistil::DistilParameters, DParName); - const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - const int TI{DPar.TI}; - const int LI{DPar.LI}; - const int SI{DPar.SI}; - const bool full_tdil{ TI == Nt }; - const int Nt_inv{ full_tdil ? 1 : TI }; - const int nvec_reduced{ par().nvec_reduced.empty() ? nvec:std::stoi(par().nvec_reduced)}; - const int LI_reduced{ par().LI_reduced.empty() ? LI:std::stoi(par().LI_reduced)}; + const DistilParameters &dp{envGet(DistilParameters, par().DistilParams)}; + const int Nt{env().getDim(Tdir)}; + const bool full_tdil{ dp.TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : dp.TI }; + const int nvec_reduced{par().nvec_reduced}; + const int LI_reduced{par().LI_reduced}; auto &perambulator = envGet(PerambTensor, getName()); auto &solve = envGet(std::vector, par().solve); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); @@ -176,17 +135,17 @@ void TPerambFromSolve::execute(void) envGetTmp(LatticeColourVector, result3d_nospin); envGetTmp(LatticeColourVector, evec3d); - for (int inoise = 0; inoise < nnoise; inoise++) + for (int inoise = 0; inoise < dp.nnoise; inoise++) { for (int dk = 0; dk < LI_reduced; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) + for (int ds = 0; ds < dp.SI; ds++) { for (int is = 0; is < Ns; is++) { - result4d_nospin = peekSpin(solve[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))],is); + result4d_nospin = peekSpin(solve[inoise+dp.nnoise*(dk+dp.LI*(dt+Nt_inv*ds))],is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { ExtractSliceLocal(result3d_nospin,result4d_nospin,0,t-Ntfirst,Tdir); diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index e3525f17..3a8bf610 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -48,7 +48,7 @@ public: std::string, noise, std::string, PerambFileName, std::string, UnsmearedSinkFileName, - std::string, DistilPar); + std::string, DistilParams); }; template @@ -69,12 +69,7 @@ public: // execution virtual void execute(void); protected: - virtual void Cleanup(void); -protected: - // These variables are created in setup() and freed in Cleanup() - GridCartesian * grid3d; // Owned by me, so I must delete it - GridCartesian * grid4d; // Owned by environment (so I won't delete it) - // Other members + std::unique_ptr grid3d; // Owned by me, so I must delete it unsigned int Ls_; }; @@ -85,22 +80,13 @@ MODULE_REGISTER_TMP(Perambulator, TPerambulator, MDistil); ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TPerambulator::TPerambulator(const std::string name) -: grid3d{nullptr}, grid4d{nullptr}, Module(name) -{} - -// destructor -template -TPerambulator::~TPerambulator(void) -{ - Cleanup(); -}; +TPerambulator::TPerambulator(const std::string name) : Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template std::vector TPerambulator::getInput(void) { - return {par().lapevec, par().solver, par().noise, par().DistilPar}; + return {par().lapevec, par().solver, par().noise, par().DistilParams}; } template @@ -113,10 +99,8 @@ std::vector TPerambulator::getOutput(void) template void TPerambulator::setup(void) { - Cleanup(); - grid4d = env().getGrid(); - grid3d = MakeLowerDimGrid(grid4d); - const DistilParameters &dp = envGet(DistilParameters, par().DistilPar); + grid3d.reset( MakeLowerDimGrid( env().getGrid() ) ); + const DistilParameters &dp = envGet(DistilParameters, par().DistilParams); const int Nt{env().getDim(Tdir)}; const bool full_tdil{ dp.TI == Nt }; const int Nt_inv{ full_tdil ? 1 : dp.TI }; @@ -127,12 +111,12 @@ void TPerambulator::setup(void) envTmpLat(LatticeSpinColourVector, "dist_source"); envTmpLat(LatticeSpinColourVector, "source4d"); - envTmp(LatticeSpinColourVector, "source3d",1,LatticeSpinColourVector(grid3d)); - envTmp(LatticeColourVector, "source3d_nospin",1,LatticeColourVector(grid3d)); + envTmp(LatticeSpinColourVector, "source3d",1,LatticeSpinColourVector(grid3d.get())); + envTmp(LatticeColourVector, "source3d_nospin",1,LatticeColourVector(grid3d.get())); envTmpLat(LatticeSpinColourVector, "result4d"); envTmpLat(LatticeColourVector, "result4d_nospin"); - envTmp(LatticeColourVector, "result3d_nospin",1,LatticeColourVector(grid3d)); - envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d)); + envTmp(LatticeColourVector, "result3d_nospin",1,LatticeColourVector(grid3d.get())); + envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d.get())); Ls_ = env().getObjectLs(par().solver); envTmpLat(FermionField, "v4dtmp"); @@ -140,23 +124,11 @@ void TPerambulator::setup(void) envTmpLat(FermionField, "v5dtmp_sol", Ls_); } -// clean up any temporaries created by setup (that aren't stored in the environment) -template -void TPerambulator::Cleanup(void) -{ - if (grid3d != nullptr) - { - delete grid3d; - grid3d = nullptr; - } - grid4d = nullptr; -} - // execution /////////////////////////////////////////////////////////////////// template void TPerambulator::execute(void) { - const DistilParameters &DPar{ envGet(DistilParameters, par().DistilPar) }; + const DistilParameters &DPar{ envGet(DistilParameters, par().DistilParams) }; const int Nt{env().getDim(Tdir)}; const int nvec{DPar.nvec}; const int nnoise{DPar.nnoise}; @@ -184,6 +156,7 @@ void TPerambulator::execute(void) envGetTmp(LatticeColourVector, result4d_nospin); envGetTmp(LatticeColourVector, result3d_nospin); envGetTmp(LatticeColourVector, evec3d); + GridCartesian * const grid4d{ env().getGrid() }; // Owned by environment (so I won't delete it) const int Ntlocal{grid4d->LocalDimensions()[3]}; const int Ntfirst{grid4d->LocalStarts()[3]}; const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; @@ -286,10 +259,8 @@ void TPerambulator::execute(void) //Save the unsmeared sinks if filename specified if (!UnsmearedSinkFileName.empty()) { - // bool bMulti = ( Hadrons::MDistil::DistilParameters::ParameterDefault( par().UnsmearedSinkMultiFile, 1, false ) != 0 ); - bool bMulti =0; LOG(Message) << "Writing unsmeared sink to " << UnsmearedSinkFileName << std::endl; - A2AVectorsIo::write(UnsmearedSinkFileName, unsmeared_sink, bMulti, vm().getTrajectory()); + A2AVectorsIo::write(UnsmearedSinkFileName, unsmeared_sink, false, vm().getTrajectory()); } } From 7a4c5dbbd5f12fb036929a9e92c1e5e82c5f3e60 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 12 Nov 2019 22:12:35 +0000 Subject: [PATCH 325/347] Restoring previous version for _reduced variables --- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index c63f5c2b..25420094 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -51,8 +51,8 @@ public: std::string, eigenPack, std::string, PerambFileName, std::string, solve, - int, nvec_reduced, - int, LI_reduced, + std::string, nvec_reduced, + std::string, LI_reduced, std::string, DistilParams); }; @@ -107,7 +107,9 @@ void TPerambFromSolve::setup(void) const bool full_tdil{ dp.TI == Nt }; const int Nt_inv{ full_tdil ? 1 : dp.TI }; grid3d.reset( MakeLowerDimGrid( env().getGrid() ) ); - envCreate(PerambTensor, getName(), 1, Nt,par().nvec_reduced,par().LI_reduced,dp.nnoise,Nt_inv,dp.SI); + const int nvec_reduced{par().nvec_reduced.empty() ? dp.nvec : std::stoi(par().nvec_reduced)}; + const int LI_reduced{ par().LI_reduced.empty() ? dp.LI : std::stoi(par().LI_reduced)}; + envCreate(PerambTensor, getName(), 1, Nt,nvec_reduced,LI_reduced,dp.nnoise,Nt_inv,dp.SI); envCreate(NoiseTensor, getName() + "_noise", 1, dp.nnoise, Nt, dp.nvec, Ns ); envTmp(LatticeColourVector, "result3d_nospin",1,LatticeColourVector(grid3d.get())); envTmp(LatticeColourVector, "evec3d",1,LatticeColourVector(grid3d.get())); @@ -125,8 +127,8 @@ void TPerambFromSolve::execute(void) const int Nt{env().getDim(Tdir)}; const bool full_tdil{ dp.TI == Nt }; const int Nt_inv{ full_tdil ? 1 : dp.TI }; - const int nvec_reduced{par().nvec_reduced}; - const int LI_reduced{par().LI_reduced}; + const int nvec_reduced{par().nvec_reduced.empty() ? dp.nvec : std::stoi(par().nvec_reduced)}; + const int LI_reduced{ par().LI_reduced.empty() ? dp.LI : std::stoi(par().LI_reduced)}; auto &perambulator = envGet(PerambTensor, getName()); auto &solve = envGet(std::vector, par().solve); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); From e2ab0d671e435dcde72eb19c287fd63ac399b8d3 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 12 Nov 2019 23:18:37 +0000 Subject: [PATCH 326/347] Implement destructors --- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- Hadrons/Modules/MDistil/LapEvec.hpp | 2 +- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 2 +- Hadrons/Modules/MDistil/Perambulator.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 1152960a..0cac9dfc 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -60,7 +60,7 @@ public: // constructor TDistilVectors(const std::string name); // destructor - virtual ~TDistilVectors(void); + virtual ~TDistilVectors(void) {}; // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 3ae29d42..1942bea9 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -102,7 +102,7 @@ public: // constructor TLapEvec(const std::string name); // destructor - virtual ~TLapEvec(void); + virtual ~TLapEvec(void) {}; // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 25420094..ae8431dc 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -64,7 +64,7 @@ public: // constructor TPerambFromSolve(const std::string name); // destructor - virtual ~TPerambFromSolve(void); + virtual ~TPerambFromSolve(void) {}; // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 3a8bf610..a006abd5 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -60,7 +60,7 @@ public: // constructor TPerambulator(const std::string name); // destructor - virtual ~TPerambulator(void); + virtual ~TPerambulator(void) {}; // dependency relation virtual std::vector getInput(void); virtual std::vector getOutput(void); From 55e743aad68c02cb480e1f238a6dd8f9beebf374 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Tue, 12 Nov 2019 23:57:28 +0000 Subject: [PATCH 327/347] Streamline --- Hadrons/Modules/MIO/LoadDistilNoise.hpp | 28 ++++++------------- Hadrons/Modules/MIO/LoadPerambulator.hpp | 34 +++++++----------------- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/Hadrons/Modules/MIO/LoadDistilNoise.hpp b/Hadrons/Modules/MIO/LoadDistilNoise.hpp index ace88887..cf3513fe 100644 --- a/Hadrons/Modules/MIO/LoadDistilNoise.hpp +++ b/Hadrons/Modules/MIO/LoadDistilNoise.hpp @@ -44,7 +44,7 @@ class LoadDistilNoisePar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(LoadDistilNoisePar, std::string, NoiseFileName, - std::string, DistilPar); + std::string, DistilParams); }; template @@ -62,8 +62,6 @@ public: virtual void setup(void); // execution virtual void execute(void); -protected: - std::string DParName; }; MODULE_REGISTER_TMP(LoadDistilNoise, TLoadDistilNoise, MIO); @@ -73,36 +71,28 @@ MODULE_REGISTER_TMP(LoadDistilNoise, TLoadDistilNoise, MIO); ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TLoadDistilNoise::TLoadDistilNoise(const std::string name) -: Module(name) -{} +TLoadDistilNoise::TLoadDistilNoise(const std::string name) : Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template std::vector TLoadDistilNoise::getInput(void) { - DParName = par().DistilPar; - return { par().NoiseFileName, DParName }; - + return {par().NoiseFileName, par().DistilParams}; } template std::vector TLoadDistilNoise::getOutput(void) { - std::vector out = {getName()}; - - return out; + return {getName()}; } // setup /////////////////////////////////////////////////////////////////////// template void TLoadDistilNoise::setup(void) { - auto &DPar = envGet(MDistil::DistilParameters, DParName); - const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - envCreate(MDistil::NoiseTensor, getName(), 1, nnoise, Nt, nvec, Ns); + const MDistil::DistilParameters &dp{envGet(MDistil::DistilParameters, par().DistilParams)}; + const int Nt{env().getDim(Tdir)}; + envCreate(MDistil::NoiseTensor, getName(), 1, dp.nnoise, Nt, dp.nvec, Ns); } // execution /////////////////////////////////////////////////////////////////// @@ -117,7 +107,5 @@ void TLoadDistilNoise::execute(void) } END_MODULE_NAMESPACE - END_HADRONS_NAMESPACE - -#endif // Hadrons_MIO_LoadDistilNoise_hpp_ +#endif diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 46c0c74d..5b7d361b 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -44,7 +44,7 @@ class LoadPerambulatorPar: Serializable public: GRID_SERIALIZABLE_CLASS_MEMBERS(LoadPerambulatorPar, std::string, PerambFileName, - std::string, DistilPar); + std::string, DistilParams); }; template @@ -62,8 +62,6 @@ public: virtual void setup(void); // execution virtual void execute(void); -protected: - std::string DParName; }; MODULE_REGISTER_TMP(LoadPerambulator, TLoadPerambulator, MIO); @@ -73,42 +71,30 @@ MODULE_REGISTER_TMP(LoadPerambulator, TLoadPerambulator, MIO); ******************************************************************************/ // constructor ///////////////////////////////////////////////////////////////// template -TLoadPerambulator::TLoadPerambulator(const std::string name) -: Module(name) -{} +TLoadPerambulator::TLoadPerambulator(const std::string name) : Module(name) {} // dependencies/products /////////////////////////////////////////////////////// template std::vector TLoadPerambulator::getInput(void) { - DParName = par().DistilPar; - return { par().PerambFileName, DParName }; + return {par().PerambFileName, par().DistilParams}; } template std::vector TLoadPerambulator::getOutput(void) { - std::vector out = {getName()}; - - return out; + return {getName()}; } // setup /////////////////////////////////////////////////////////////////////// template void TLoadPerambulator::setup(void) { - auto &DPar = envGet(MDistil::DistilParameters, DParName); + const MDistil::DistilParameters &dp{envGet(MDistil::DistilParameters, par().DistilParams)}; const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - const int tsrc{DPar.tsrc}; - const int TI{DPar.TI}; - const int LI{DPar.LI}; - const int SI{DPar.SI}; - const bool full_tdil{ TI == Nt }; - const bool exact_distillation{ full_tdil && LI == nvec }; - const int Nt_inv{ full_tdil ? 1 : TI }; - envCreate(MDistil::PerambTensor, getName(), 1, Nt,nvec,LI,nnoise,Nt_inv,SI); + const bool full_tdil{ dp.TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : dp.TI }; + envCreate(MDistil::PerambTensor, getName(), 1, Nt,dp.nvec,dp.LI,dp.nnoise,Nt_inv,dp.SI); } // execution /////////////////////////////////////////////////////////////////// @@ -123,7 +109,5 @@ void TLoadPerambulator::execute(void) } END_MODULE_NAMESPACE - END_HADRONS_NAMESPACE - -#endif // Hadrons_MIO_LoadPerambulator_hpp_ +#endif From 66e08113173fa2a0c13efe009564d73c28e0c5dd Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 13 Nov 2019 00:02:51 +0000 Subject: [PATCH 328/347] Attempt to fix cuda build --- Hadrons/NamedTensor.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Hadrons/NamedTensor.hpp b/Hadrons/NamedTensor.hpp index a8c98127..710064fd 100644 --- a/Hadrons/NamedTensor.hpp +++ b/Hadrons/NamedTensor.hpp @@ -72,12 +72,12 @@ public: // Construct a named tensor explicitly specifying size of each dimension template EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name, - const std::array &indexNames, + const std::array &indexNames, Eigen::Index firstDimension, IndexTypes... otherDimensions) : tensor(firstDimension, otherDimensions...), IndexNames{indexNames.begin(), indexNames.end()}, Name_{Name}, DefaultIndexNames_{indexNames} { - if(sizeof...(otherDimensions) + 1 != NumIndices) + if(sizeof...(otherDimensions) + 1 != NumIndices_) HADRONS_ERROR(Argument, "NamedTensor: dimensions != tensor rank"); } From 12e415330fa10903146236591431e37c3bebbb2c Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Nov 2019 11:21:08 +0000 Subject: [PATCH 329/347] made notation DPar->dp consistent over modules --- Hadrons/Modules/MDistil/DistilVectors.hpp | 14 +++++++------- Hadrons/Modules/MDistil/Perambulator.hpp | 14 +++++++------- Hadrons/modules.inc | 2 -- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 0cac9dfc..f34a883d 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -154,7 +154,7 @@ void TDistilVectors::execute(void) auto &noise = envGet(NoiseTensor, par().noise); auto &perambulator = envGet(PerambTensor, par().perambulator); auto &epack = envGet(Grid::Hadrons::EigenPack, par().lapevec); - const DistilParameters &DPar{envGet(DistilParameters, par().DistilParams)}; + const DistilParameters &dp{envGet(DistilParameters, par().DistilParams)}; envGetTmp(LatticeSpinColourVector, source4d); envGetTmp(LatticeSpinColourVector, source3d); @@ -167,12 +167,12 @@ void TDistilVectors::execute(void) const int Ntfirst{ grid4d->LocalStarts()[3] }; const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - const int tsrc{DPar.tsrc}; - const int TI{DPar.TI}; - const int LI{DPar.LI}; - const int SI{DPar.SI}; + const int nvec{dp.nvec}; + const int nnoise{dp.nnoise}; + const int tsrc{dp.tsrc}; + const int TI{dp.TI}; + const int LI{dp.LI}; + const int SI{dp.SI}; const bool full_tdil{ TI == Nt }; const int Nt_inv{ full_tdil ? 1 : TI }; diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index a006abd5..40738865 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -128,14 +128,14 @@ void TPerambulator::setup(void) template void TPerambulator::execute(void) { - const DistilParameters &DPar{ envGet(DistilParameters, par().DistilParams) }; + const DistilParameters &dp{ envGet(DistilParameters, par().DistilParams) }; const int Nt{env().getDim(Tdir)}; - const int nvec{DPar.nvec}; - const int nnoise{DPar.nnoise}; - const int tsrc{DPar.tsrc}; - const int TI{DPar.TI}; - const int LI{DPar.LI}; - const int SI{DPar.SI}; + const int nvec{dp.nvec}; + const int nnoise{dp.nnoise}; + const int tsrc{dp.tsrc}; + const int TI{dp.TI}; + const int LI{dp.LI}; + const int SI{dp.SI}; const bool full_tdil{ TI == Nt }; const int Nt_inv{ full_tdil ? 1 : TI }; diff --git a/Hadrons/modules.inc b/Hadrons/modules.inc index 30a7cc6d..1b35ec69 100644 --- a/Hadrons/modules.inc +++ b/Hadrons/modules.inc @@ -67,7 +67,6 @@ modules_cc =\ Modules/MDistil/DistilVectors.cc \ Modules/MDistil/DistilPar.cc \ Modules/MDistil/LapEvec.cc \ - Modules/MDistil/DistilParameters.cc \ Modules/MDistil/Perambulator.cc \ Modules/MDistil/Noises.cc \ Modules/MAction/MobiusDWF.cc \ @@ -154,7 +153,6 @@ modules_hpp =\ Modules/MDistil/LapEvec.hpp \ Modules/MDistil/Perambulator.hpp \ Modules/MDistil/DistilPar.hpp \ - Modules/MDistil/DistilCommon.hpp \ Modules/MAction/ZMobiusDWF.hpp \ Modules/MAction/ScaledDWF.hpp \ Modules/MAction/WilsonClover.hpp \ From fcc412a1c2b9bb546e05ec30c2bb1913cd6a97f5 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Wed, 13 Nov 2019 11:32:23 +0000 Subject: [PATCH 330/347] Remove conditional compilation to support GPU build --- Hadrons/Modules/MDistil/Distil.hpp | 33 +++++++++++------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index 0cfd31b4..ca5b3de8 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -105,33 +105,24 @@ inline void RotateEigen(std::vector & evec) auto grid = evec[0].Grid(); Coordinate siteFirst(grid->Nd(),0); peekSite(cv0, evec[0], siteFirst); - Grid::Complex cplx0 = cv0()()(0); + const std::complex cplx0{cv0()()(0).real(), cv0()()(0).imag()}; if( cplx0.imag() == 0 ) - std::cout << GridLogMessage << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; + LOG(Message) << "RotateEigen() : Site 0 : " << cplx0 << " => already meets phase convention" << std::endl; else { - const Real cplx0_mag = Grid::sqrt(cplx0.real()*cplx0.real()+cplx0.imag()*cplx0.imag()); - Grid::Complex phase{cplx0 / Grid::Complex(cplx0_mag, 0) }; - phase.imag(-phase.imag()); -#ifdef GRID_NVCC - //const Real cplx0_mag = thrust::abs(cplx0); - //const Grid::Complex phase = thrust::conj(cplx0 / cplx0_mag); - const Real argphase = thrust::arg(phase); -#else - //const Real cplx0_mag = std::abs(cplx0); - //const Grid::Complex phase = std::conj(cplx0 / cplx0_mag); - const Real argphase = std::arg(phase); -#endif - std::cout << GridLogMessage << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag << " => phase=" << (argphase / M_PI) << " pi" << std::endl; + const Real cplx0_mag{ std::abs(cplx0) }; + const std::complex std_phase{std::conj(cplx0/cplx0_mag)}; + LOG(Message) << "RotateEigen() : Site 0 : |" << cplx0 << "|=" << cplx0_mag + << " => phase=" << (std::arg(std_phase) / M_PI) << " pi" << std::endl; { - // TODO: Only really needed on the master slice + const Grid::Complex phase{std_phase.real(),std_phase.imag()}; for( int k = 0 ; k < evec.size() ; k++ ) evec[k] *= phase; - if(grid->IsBoss()){ - for( int c = 0 ; c < Nc ; c++ ) - cv0()()(c) *= phase; - cplx0.imag(0); // This assumes phase convention is real, positive (so I get rid of rounding error) - //pokeSite(cv0, evec[0], siteFirst); + // Get rid of the rounding error in imaginary phase on the very first site + if(grid->IsBoss()) + { + peekSite(cv0, evec[0], siteFirst); + cv0()()(0).imag(0); // this should be zero after the phase multiply - force it to be so pokeLocalSite(cv0, evec[0], siteFirst); } } From 5238808ccdf73fb4aad891d2f2c8cfa3ca3f9fd7 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Nov 2019 11:50:55 +0000 Subject: [PATCH 331/347] No DistilVectors specified in xml no throws an error --- Hadrons/Modules/MDistil/DistilVectors.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index f34a883d..cad53a27 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -99,10 +99,7 @@ std::vector TDistilVectors::getOutput(void) PhiName = par().phi; if (RhoName.empty() && PhiName.empty()) { - RhoName = getName(); - PhiName = RhoName; - RhoName.append("_rho"); - PhiName.append("_phi"); + HADRONS_ERROR(Range,"No output specified"); } std::vector out; if (!RhoName.empty()) From 667ffb70db65a963fc485e695ef125b3d763df77 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Nov 2019 12:16:56 +0000 Subject: [PATCH 332/347] changed error type --- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index cad53a27..9af5158f 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -99,7 +99,7 @@ std::vector TDistilVectors::getOutput(void) PhiName = par().phi; if (RhoName.empty() && PhiName.empty()) { - HADRONS_ERROR(Range,"No output specified"); + HADRONS_ERROR(Argument,"No output specified"); } std::vector out; if (!RhoName.empty()) From a977d9901b1064d2fce1180375654ccbad1f0aac Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Nov 2019 14:52:06 +0000 Subject: [PATCH 333/347] cleanup --- Hadrons/Modules/MDistil/DistilVectors.hpp | 62 +++++++++++++---------- Hadrons/Modules/MDistil/Perambulator.hpp | 29 ++++------- tests/hadrons/Test_distil.cc | 27 +++++----- 3 files changed, 58 insertions(+), 60 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 9af5158f..be387606 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -164,32 +164,33 @@ void TDistilVectors::execute(void) const int Ntfirst{ grid4d->LocalStarts()[3] }; const int Nt{env().getDim(Tdir)}; - const int nvec{dp.nvec}; - const int nnoise{dp.nnoise}; - const int tsrc{dp.tsrc}; - const int TI{dp.TI}; - const int LI{dp.LI}; - const int SI{dp.SI}; - const bool full_tdil{ TI == Nt }; - const int Nt_inv{ full_tdil ? 1 : TI }; + const bool full_tdil{ dp.TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : dp.TI }; int vecindex; int t_inv; if (!RhoName.empty()) { auto &rho = envGet(std::vector, RhoName); - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; + for (int inoise = 0; inoise < dp.nnoise; inoise++) + { + for (int dk = 0; dk < dp.LI; dk++) + { + for (int dt = 0; dt < Nt_inv; dt++) + { + for (int ds = 0; ds < dp.SI; ds++) + { + vecindex = inoise + dp.nnoise * dk + dp.nnoise * dp.LI * ds + dp.nnoise *dp.LI * dp.SI*dt; rho[vecindex] = 0; - source3d_nospin = 0; - for (int it = dt; it < Nt; it += TI){ - if (full_tdil) t_inv = tsrc; else t_inv = it; - if (t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal) { - for (int ik = dk; ik < nvec; ik += LI){ - for (int is = ds; is < Ns; is += SI){ + for (int it = dt; it < Nt; it += dp.TI) + { + const int t_inv{full_tdil ? dp.tsrc : it}; + if (t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal) + { + for (int ik = dk; ik < dp.nvec; ik += dp.LI) + { + for (int is = ds; is < Ns; is += dp.SI) + { ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); source3d_nospin = evec3d * noise.tensor(inoise, t_inv, ik, is); source3d=0; @@ -206,17 +207,24 @@ void TDistilVectors::execute(void) } } } - if (!PhiName.empty()) { + if (!PhiName.empty()) + { auto &phi = envGet(std::vector, PhiName); - for (int inoise = 0; inoise < nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) { - for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) { - vecindex = inoise + nnoise * dk + nnoise * LI * ds + nnoise *LI * SI*dt; + for (int inoise = 0; inoise < dp.nnoise; inoise++) + { + for (int dk = 0; dk < dp.LI; dk++) + { + for (int dt = 0; dt < Nt_inv; dt++) + { + for (int ds = 0; ds < dp.SI; ds++) + { + vecindex = inoise + dp.nnoise * dk + dp.nnoise * dp.LI * ds + dp.nnoise *dp.LI *dp. SI*dt; phi[vecindex] = 0; - for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { + for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) + { sink3d=0; - for (int ivec = 0; ivec < nvec; ivec++) { + for (int ivec = 0; ivec < dp.nvec; ivec++) + { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); sink3d += evec3d * perambulator.tensor(t, ivec, dk, inoise,dt,ds); } diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 40738865..2ffaf6fc 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -130,14 +130,8 @@ void TPerambulator::execute(void) { const DistilParameters &dp{ envGet(DistilParameters, par().DistilParams) }; const int Nt{env().getDim(Tdir)}; - const int nvec{dp.nvec}; - const int nnoise{dp.nnoise}; - const int tsrc{dp.tsrc}; - const int TI{dp.TI}; - const int LI{dp.LI}; - const int SI{dp.SI}; - const bool full_tdil{ TI == Nt }; - const int Nt_inv{ full_tdil ? 1 : TI }; + const bool full_tdil{ dp.TI == Nt }; + const int Nt_inv{ full_tdil ? 1 : dp.TI }; auto &solver=envGet(Solver, par().solver); auto &mat = solver.getFMat(); @@ -161,26 +155,25 @@ void TPerambulator::execute(void) const int Ntfirst{grid4d->LocalStarts()[3]}; const std::string UnsmearedSinkFileName{ par().UnsmearedSinkFileName }; - for (int inoise = 0; inoise < nnoise; inoise++) + for (int inoise = 0; inoise < dp.nnoise; inoise++) { - for (int dk = 0; dk < LI; dk++) + for (int dk = 0; dk < dp.LI; dk++) { for (int dt = 0; dt < Nt_inv; dt++) { - for (int ds = 0; ds < SI; ds++) + for (int ds = 0; ds < dp.SI; ds++) { LOG(Message) << "LapH source vector from noise " << inoise << " and dilution component (d_k,d_t,d_alpha) : (" << dk << ","<< dt << "," << ds << ")" << std::endl; dist_source = 0; - source3d_nospin = 0; evec3d = 0; - for (int it = dt; it < Nt; it += TI) + for (int it = dt; it < Nt; it += dp.TI) { - const int t_inv{full_tdil ? tsrc : it}; + const int t_inv{full_tdil ? dp.tsrc : it}; if( t_inv >= Ntfirst && t_inv < Ntfirst + Ntlocal ) { - for (int ik = dk; ik < nvec; ik += LI) + for (int ik = dk; ik < dp.nvec; ik += dp.LI) { - for (int is = ds; is < Ns; is += SI) + for (int is = ds; is < Ns; is += dp.SI) { ExtractSliceLocal(evec3d,epack.evec[ik],0,t_inv-Ntfirst,Tdir); source3d_nospin = evec3d * noise.tensor(inoise, t_inv, ik, is); @@ -205,14 +198,14 @@ void TPerambulator::execute(void) result4d = v4dtmp; } if (!UnsmearedSinkFileName.empty()) - unsmeared_sink[inoise+nnoise*(dk+LI*(dt+Nt_inv*ds))] = result4d; + unsmeared_sink[inoise+dp.nnoise*(dk+dp.LI*(dt+Nt_inv*ds))] = result4d; for (int is = 0; is < Ns; is++) { result4d_nospin = peekSpin(result4d,is); for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { ExtractSliceLocal(result3d_nospin,result4d_nospin,0,t-Ntfirst,Tdir); - for (int ivec = 0; ivec < nvec; ivec++) + for (int ivec = 0; ivec < dp.nvec; ivec++) { ExtractSliceLocal(evec3d,epack.evec[ivec],0,t-Ntfirst,Tdir); pokeSpin(perambulator.tensor(t, ivec, dk, inoise,dt,ds),static_cast(innerProduct(evec3d, result3d_nospin)),is); diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index d137e5e7..8519fd28 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -142,17 +142,15 @@ std::string test_Solver(Application &application, const char * pSuffix = nullptr ///////////////////////////////////////////////////////////// std::string test_DPar(Application &application) { - // DistilVectors parameters - //MDistil::DistilParPar DistilPar; - MDistil::DistilParameters DistilPar; - DistilPar.nvec = 5; - DistilPar.nnoise = 1; - DistilPar.tsrc = 0; - DistilPar.LI = 5; - DistilPar.TI = 8; - DistilPar.SI = 4; + MDistil::DistilPar DPar; + DPar.nvec = 5; + DPar.nnoise = 1; + DPar.tsrc = 0; + DPar.LI = 5; + DPar.TI = 8; + DPar.SI = 4; std::string sDParName{"DPar_l"}; - application.createModule(sDParName,DistilPar); + application.createModule(sDParName,DPar); return sDParName; } ///////////////////////////////////////////////////////////// @@ -160,9 +158,8 @@ std::string test_DPar(Application &application) { ///////////////////////////////////////////////////////////// std::string test_Noises(Application &application, const std::string &sNoiseBaseName ) { - // DistilVectors parameters MDistil::NoisesPar NoisePar; - NoisePar.DistilPar = "DPar_l"; + NoisePar.DistilParams = "DPar_l"; NoisePar.NoiseFileName = "noise"; std::string sNoiseName{"noise"}; application.createModule(sNoiseName,NoisePar); @@ -186,7 +183,7 @@ void test_LoadPerambulators( Application &application, const char * pszSuffix = std::string sModuleName{ PerambulatorName( pszSuffix ) }; MIO::LoadPerambulator::Par PerambPar; PerambPar.PerambFileName = sModuleName; - PerambPar.DistilPar = "DPar_l"; + PerambPar.DistilParams = "DPar_l"; test_Noises(application, sModuleName); // I want these written after solver stuff application.createModule( sModuleName, PerambPar ); } @@ -199,7 +196,7 @@ void test_Perambulators( Application &application, const char * pszSuffix = null PerambPar.lapevec = "LapEvec"; PerambPar.PerambFileName = sModuleName; PerambPar.solver = test_Solver( application, pszSuffix ); - PerambPar.DistilPar = "DPar_l"; + PerambPar.DistilParams = "DPar_l"; PerambPar.noise = "noise"; test_Noises(application, sModuleName); // I want these written after solver stuff application.createModule( sModuleName, PerambPar ); @@ -223,7 +220,7 @@ void test_DistilVectors(Application &application, const char * pszSuffix = nullp DistilVecPar.phi = "phi"; DistilVecPar.perambulator = sPerambName; DistilVecPar.lapevec = "LapEvec"; - DistilVecPar.DistilPar = "DPar_l"; + DistilVecPar.DistilParams = "DPar_l"; application.createModule(sModuleName,DistilVecPar); } From ee9dd226432cd769eb0e6cf7aa6fe28e079fadce Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Nov 2019 14:59:44 +0000 Subject: [PATCH 334/347] worked on test_distil --- tests/hadrons/Test_distil.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index 8519fd28..79b1fae6 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -142,7 +142,7 @@ std::string test_Solver(Application &application, const char * pSuffix = nullptr ///////////////////////////////////////////////////////////// std::string test_DPar(Application &application) { - MDistil::DistilPar DPar; + MDistil::DistilParameters DPar; DPar.nvec = 5; DPar.nnoise = 1; DPar.tsrc = 0; From 500ef17143f3ea2abce6fd5a30c15e30564e81f6 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Nov 2019 15:14:51 +0000 Subject: [PATCH 335/347] beauty --- Hadrons/Modules/MDistil/Noises.hpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index 4b41f6fc..e5ba2441 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -108,13 +108,20 @@ void TNoises::execute(void) // We use our own seeds so we can specify different noises per quark Real rn; auto &noise = envGet(NoiseTensor, getName()); - for (int inoise = 0; inoise < dp.nnoise; inoise++) { - for (int t = 0; t < Nt; t++) { - for (int ivec = 0; ivec < dp.nvec; ivec++) { - for (int is = 0; is < Ns; is++) { + for (int inoise = 0; inoise < dp.nnoise; inoise++) + { + for (int t = 0; t < Nt; t++) + { + for (int ivec = 0; ivec < dp.nvec; ivec++) + { + for (int is = 0; is < Ns; is++) + { if (exact_distillation) + { noise.tensor(inoise, t, ivec, is) = 1.; - else{ + } + else + { random(rngSerial(),rn); // We could use a greater number of complex roots of unity // ... but this seems to work well From 25d2521d77a8adbd47b19c4630f5ec4862d00bf3 Mon Sep 17 00:00:00 2001 From: ferben Date: Wed, 13 Nov 2019 16:34:09 +0000 Subject: [PATCH 336/347] small stuff --- Hadrons/Modules/MDistil/DistilVectors.hpp | 4 ++-- Hadrons/Modules/MDistil/Noises.hpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index be387606..822a27fa 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -180,7 +180,7 @@ void TDistilVectors::execute(void) { for (int ds = 0; ds < dp.SI; ds++) { - vecindex = inoise + dp.nnoise * dk + dp.nnoise * dp.LI * ds + dp.nnoise *dp.LI * dp.SI*dt; + vecindex = inoise + dp.nnoise * (dk + dp.LI * (ds + dp.SI * dt)); rho[vecindex] = 0; for (int it = dt; it < Nt; it += dp.TI) { @@ -218,7 +218,7 @@ void TDistilVectors::execute(void) { for (int ds = 0; ds < dp.SI; ds++) { - vecindex = inoise + dp.nnoise * dk + dp.nnoise * dp.LI * ds + dp.nnoise *dp.LI *dp. SI*dt; + vecindex = inoise + dp.nnoise * (dk + dp.LI * (ds + dp.SI * dt)); phi[vecindex] = 0; for (int t = Ntfirst; t < Ntfirst + Ntlocal; t++) { diff --git a/Hadrons/Modules/MDistil/Noises.hpp b/Hadrons/Modules/MDistil/Noises.hpp index e5ba2441..bd1a43ff 100644 --- a/Hadrons/Modules/MDistil/Noises.hpp +++ b/Hadrons/Modules/MDistil/Noises.hpp @@ -92,7 +92,8 @@ template void TNoises::setup(void) { const DistilParameters &dp{envGet(DistilParameters, par().DistilParams)}; - const int Nt{env().getDim(Tdir)}; + const int Nt{env().getDim(Tdir)}; + std::cout << dp.nnoise << dp.nvec << Nt << Ns << std::endl; envCreate(NoiseTensor, getName(), 1, dp.nnoise, Nt, dp.nvec, Ns); } From b1e8b5b5ce0de2211f5bc85ec4d126f9d92e37c6 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 15 Nov 2019 11:00:25 +0000 Subject: [PATCH 337/347] changed default behaviour as discussed with antonin --- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index ae8431dc..98866180 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -41,7 +41,13 @@ BEGIN_MODULE_NAMESPACE(MDistil) This module computes a perambulator from an already completed solve. Optionally, the number of eigenvectors used in the perambulator and the parameter LI can be chosen to be lower than the ones in the solve, allowing - for a study of the signal with different values of nvec. + for a study of the signal with different values of nvec. + + LI_reduced : value of LI actually used in the computation + nvec_reduced: value of nvec actually used in the computation + LI : value of LI used to compute the 'solve' + nvec : value of nvec used to compute the 'solve' + ******************************************************************************/ class PerambFromSolvePar: Serializable @@ -107,8 +113,8 @@ void TPerambFromSolve::setup(void) const bool full_tdil{ dp.TI == Nt }; const int Nt_inv{ full_tdil ? 1 : dp.TI }; grid3d.reset( MakeLowerDimGrid( env().getGrid() ) ); - const int nvec_reduced{par().nvec_reduced.empty() ? dp.nvec : std::stoi(par().nvec_reduced)}; - const int LI_reduced{ par().LI_reduced.empty() ? dp.LI : std::stoi(par().LI_reduced)}; + const int nvec_reduced{par().nvec_reduced}; + const int LI_reduced{ par().LI_reduced}; envCreate(PerambTensor, getName(), 1, Nt,nvec_reduced,LI_reduced,dp.nnoise,Nt_inv,dp.SI); envCreate(NoiseTensor, getName() + "_noise", 1, dp.nnoise, Nt, dp.nvec, Ns ); envTmp(LatticeColourVector, "result3d_nospin",1,LatticeColourVector(grid3d.get())); From 271a02230e8628a89c63962917b7803b7f8a4b4b Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 15 Nov 2019 11:11:50 +0000 Subject: [PATCH 338/347] assert -> ERROR --- Hadrons/Modules/MDistil/LapEvec.hpp | 5 ++++- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 1942bea9..acc42664 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -299,7 +299,10 @@ void TLapEvec::execute(void) } GridLogIRL.Active( PreviousIRLLogState ); gridHD->GlobalSum(ConvergenceErrors); - assert(ConvergenceErrors==0 && "The eingensolver failed to find enough eigenvectors on at least one node"); + if(!ConvergenceErrors==0) + { + HADRONS_ERROR(Program,"The eingensolver failed to find enough eigenvectors on at least one node"); + } #if DEBUG // Now write out the 4d eigenvectors eig4d.record.operatorXml = "Distillation"; diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 98866180..29745751 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -57,8 +57,8 @@ public: std::string, eigenPack, std::string, PerambFileName, std::string, solve, - std::string, nvec_reduced, - std::string, LI_reduced, + int, nvec_reduced, + int, LI_reduced, std::string, DistilParams); }; @@ -133,8 +133,8 @@ void TPerambFromSolve::execute(void) const int Nt{env().getDim(Tdir)}; const bool full_tdil{ dp.TI == Nt }; const int Nt_inv{ full_tdil ? 1 : dp.TI }; - const int nvec_reduced{par().nvec_reduced.empty() ? dp.nvec : std::stoi(par().nvec_reduced)}; - const int LI_reduced{ par().LI_reduced.empty() ? dp.LI : std::stoi(par().LI_reduced)}; + const int nvec_reduced{par().nvec_reduced}; + const int LI_reduced{ par().LI_reduced}; auto &perambulator = envGet(PerambTensor, getName()); auto &solve = envGet(std::vector, par().solve); auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); From 9f75065205926b88809bd80fdecec2252691df4f Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 15 Nov 2019 13:22:20 +0000 Subject: [PATCH 339/347] eigen_strong_inline gone --- Hadrons/NamedTensor.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Hadrons/NamedTensor.hpp b/Hadrons/NamedTensor.hpp index 710064fd..a7c11790 100644 --- a/Hadrons/NamedTensor.hpp +++ b/Hadrons/NamedTensor.hpp @@ -65,13 +65,13 @@ public: const std::array &DefaultIndexNames_; // Default constructor (assumes tensor will be loaded from file) - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name, + NamedTensor(const std::string &Name, const std::array &indexNames) : IndexNames{indexNames.begin(), indexNames.end()}, Name_{Name}, DefaultIndexNames_{indexNames} {} // Construct a named tensor explicitly specifying size of each dimension template - EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE NamedTensor(const std::string &Name, + NamedTensor(const std::string &Name, const std::array &indexNames, Eigen::Index firstDimension, IndexTypes... otherDimensions) : tensor(firstDimension, otherDimensions...), From 7f06c40107b3e3ea19b3ec3bda493c1a7f9e7110 Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 15 Nov 2019 13:26:24 +0000 Subject: [PATCH 340/347] _var -> var_ --- Hadrons/Modules/MDistil/LapEvec.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index acc42664..dfd49eaa 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -181,13 +181,13 @@ public: assert( nd <= in.Grid()->Nd() ); conformable( in, out ); out = ( ( Real ) ( 2 * nd ) ) * in; - Field _tmp(in.Grid()); + Field tmp_(in.Grid()); typedef typename GaugeField::vector_type vCoeff_t; for (int mu = 0 ; mu < nd ; mu++) { out -= U[mu] * Cshift( in, mu, 1); - _tmp = adj( U[mu] ) * in; - out -= Cshift(_tmp,mu,-1); + tmp_ = adj( U[mu] ) * in; + out -= Cshift(tmp_,mu,-1); } } @@ -202,13 +202,13 @@ public: template class Laplacian3DHerm : public LinearFunction { public: - OperatorFunction & _poly; - LinearOperatorBase &_Linop; + OperatorFunction & poly_; + LinearOperatorBase &Linop_; Laplacian3DHerm(OperatorFunction & poly,LinearOperatorBase& linop) - : _poly{poly}, _Linop{linop} {} + : poly_{poly}, Linop_{linop} {} void operator()(const Field& in, Field& out) { - _poly(_Linop,in,out); + poly_(Linop_,in,out); } }; From 2d6f4e0c0977561c795d08ef69b7330bd822b2ba Mon Sep 17 00:00:00 2001 From: ferben Date: Fri, 15 Nov 2019 13:46:47 +0000 Subject: [PATCH 341/347] fixed issue with HADRONS_ERROR, no idea why this works --- Hadrons/Modules/MDistil/DistilVectors.hpp | 2 ++ Hadrons/NamedTensor.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 822a27fa..8866b875 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -116,7 +116,9 @@ void TDistilVectors::setup(void) // We expect the perambulator to have been created with these indices auto &perambulator = envGet(PerambTensor, par().perambulator); if (!perambulator.ValidateIndexNames()) + { HADRONS_ERROR(Range,"Perambulator index names bad"); + } const DistilParameters &dp{envGet(DistilParameters, par().DistilParams)}; const int Nt{env().getDim(Tdir)}; diff --git a/Hadrons/NamedTensor.hpp b/Hadrons/NamedTensor.hpp index a7c11790..998f6c53 100644 --- a/Hadrons/NamedTensor.hpp +++ b/Hadrons/NamedTensor.hpp @@ -78,7 +78,9 @@ public: IndexNames{indexNames.begin(), indexNames.end()}, Name_{Name}, DefaultIndexNames_{indexNames} { if(sizeof...(otherDimensions) + 1 != NumIndices_) + { HADRONS_ERROR(Argument, "NamedTensor: dimensions != tensor rank"); + } } // Do my index names match the default for my type? From 7bf42b9c0e9e63c45cd47bad318f27ea3e746a33 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 18 Nov 2019 10:27:35 +0000 Subject: [PATCH 342/347] HADRONS_ERROR --- Hadrons/Modules/MDistil/LapEvec.hpp | 20 +++++++++++++------- Hadrons/NamedTensor.hpp | 4 ++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index dfd49eaa..83e44c76 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -171,14 +171,20 @@ public: // Construct this operator given a gauge field and the number of dimensions it should act on Laplacian3D( GaugeField& gf, int dimSpatial = Tdir ) : nd{dimSpatial} { - assert(dimSpatial>=1); + if (dimSpatial<1) + { + HADRONS_ERROR(Range,"Must be at least one spatial dimension"); + } for (int mu = 0 ; mu < nd ; mu++) U.push_back(PeekIndex(gf,mu)); } // Apply this operator to "in", return result in "out" void operator()(const Field& in, Field& out) { - assert( nd <= in.Grid()->Nd() ); + if (nd > in.Grid()->Nd()) + { + HADRONS_ERROR(Range,"nd too large"); + } conformable( in, out ); out = ( ( Real ) ( 2 * nd ) ) * in; Field tmp_(in.Grid()); @@ -191,11 +197,11 @@ public: } } - void OpDiag (const Field &in, Field &out) { assert(0); }; - void OpDir (const Field &in, Field &out,int dir,int disp) { assert(0); }; - void Op (const Field &in, Field &out) { assert(0); }; - void AdjOp (const Field &in, Field &out) { assert(0); }; - void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2) { assert(0); }; + void OpDiag (const Field &in, Field &out) { HADRONS_ERROR(Definition, "OpDiag() undefined"); }; + void OpDir (const Field &in, Field &out,int dir,int disp) { HADRONS_ERROR(Definition, "OpDir() undefined"); }; + void Op (const Field &in, Field &out) { HADRONS_ERROR(Definition, "Op() undefined"); }; + void AdjOp (const Field &in, Field &out) { HADRONS_ERROR(Definition, "AdjOp() undefined"); }; + void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2) { HADRONS_ERROR(Definition, "HermOpAndNorm() undefined"); }; void HermOp(const Field &in, Field &out) { operator()(in,out); }; }; diff --git a/Hadrons/NamedTensor.hpp b/Hadrons/NamedTensor.hpp index 998f6c53..954de2d8 100644 --- a/Hadrons/NamedTensor.hpp +++ b/Hadrons/NamedTensor.hpp @@ -128,9 +128,13 @@ public: const typename ET::Dimensions & NewDimensions{tensor.dimensions()}; for (int i = 0; i < NumIndices_; i++) if(OldDimensions[i] && OldDimensions[i] != NewDimensions[i]) + { HADRONS_ERROR(Size,"NamedTensor::read dimension size"); + } if (bValidate && !ValidateIndexNames(OldIndexNames)) + { HADRONS_ERROR(Definition,"NamedTensor::read dimension name"); + } } template void read(Reader &r, bool bValidate = true) { read(r, bValidate, Name_); } From 18177d9709a9e46558f783b9c82ee63f2431d86b Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 18 Nov 2019 11:59:13 +0000 Subject: [PATCH 343/347] Review changes --- Hadrons/Modules/MDistil/Distil.hpp | 10 ++-------- Hadrons/Modules/MDistil/DistilVectors.hpp | 3 +-- Hadrons/Modules/MDistil/LapEvec.hpp | 4 ++-- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 2 +- Hadrons/Modules/MDistil/Perambulator.hpp | 2 +- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Hadrons/Modules/MDistil/Distil.hpp b/Hadrons/Modules/MDistil/Distil.hpp index ca5b3de8..3b889aa4 100644 --- a/Hadrons/Modules/MDistil/Distil.hpp +++ b/Hadrons/Modules/MDistil/Distil.hpp @@ -30,9 +30,6 @@ #ifndef Hadrons_MDistil_Distil_hpp_ #define Hadrons_MDistil_Distil_hpp_ -#define _USE_MATH_DEFINES -#include - #include #include #include @@ -72,15 +69,13 @@ struct DistilParameters: Serializable { int, TI, int, LI, int, SI ) - DistilParameters() = default; - DistilParameters( const DistilParameters &p ) = default; // member-wise copy }; /****************************************************************************** Make a lower dimensional grid in preparation for local slice operations ******************************************************************************/ -inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) +inline void MakeLowerDimGrid( std::unique_ptr &up, GridCartesian * gridHD ) { int nd{static_cast(gridHD->_ndimension)}; Coordinate latt_size = gridHD->_gdimensions; @@ -89,8 +84,7 @@ inline GridCartesian * MakeLowerDimGrid( GridCartesian * gridHD ) simd_layout.push_back( 1 ); Coordinate mpi_layout = gridHD->_processors; mpi_layout[nd-1] = 1; - GridCartesian * gridLD = new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD); - return gridLD; + up.reset( new GridCartesian(latt_size,simd_layout,mpi_layout,*gridHD) ); } /************************************************************************************* diff --git a/Hadrons/Modules/MDistil/DistilVectors.hpp b/Hadrons/Modules/MDistil/DistilVectors.hpp index 8866b875..de871a64 100644 --- a/Hadrons/Modules/MDistil/DistilVectors.hpp +++ b/Hadrons/Modules/MDistil/DistilVectors.hpp @@ -137,7 +137,7 @@ void TDistilVectors::setup(void) simd_layout_3.push_back( 1 ); mpi_layout[Nd-1] = 1; GridCartesian * const grid4d{env().getGrid()}; - grid3d.reset(MakeLowerDimGrid(grid4d)); + MakeLowerDimGrid(grid3d, grid4d); envTmp(LatticeSpinColourVector, "source4d",1,LatticeSpinColourVector(grid4d)); envTmp(LatticeSpinColourVector, "source3d",1,LatticeSpinColourVector(grid3d.get())); @@ -170,7 +170,6 @@ void TDistilVectors::execute(void) const int Nt_inv{ full_tdil ? 1 : dp.TI }; int vecindex; - int t_inv; if (!RhoName.empty()) { auto &rho = envGet(std::vector, RhoName); diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 83e44c76..80ac25df 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -142,7 +142,7 @@ template void TLapEvec::setup(void) { GridCartesian * gridHD = env().getGrid(); - gridLD.reset(MakeLowerDimGrid(gridHD)); + MakeLowerDimGrid(gridLD,gridHD); const int Ntlocal{gridHD->LocalDimensions()[Tdir]}; // Temporaries envTmpLat(GaugeField, "Umu_stout"); @@ -305,7 +305,7 @@ void TLapEvec::execute(void) } GridLogIRL.Active( PreviousIRLLogState ); gridHD->GlobalSum(ConvergenceErrors); - if(!ConvergenceErrors==0) + if(ConvergenceErrors!=0) { HADRONS_ERROR(Program,"The eingensolver failed to find enough eigenvectors on at least one node"); } diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 29745751..11721eb6 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -112,7 +112,7 @@ void TPerambFromSolve::setup(void) const int Nt{env().getDim(Tdir)}; const bool full_tdil{ dp.TI == Nt }; const int Nt_inv{ full_tdil ? 1 : dp.TI }; - grid3d.reset( MakeLowerDimGrid( env().getGrid() ) ); + MakeLowerDimGrid( grid3d, env().getGrid() ); const int nvec_reduced{par().nvec_reduced}; const int LI_reduced{ par().LI_reduced}; envCreate(PerambTensor, getName(), 1, Nt,nvec_reduced,LI_reduced,dp.nnoise,Nt_inv,dp.SI); diff --git a/Hadrons/Modules/MDistil/Perambulator.hpp b/Hadrons/Modules/MDistil/Perambulator.hpp index 2ffaf6fc..fb9d16dc 100644 --- a/Hadrons/Modules/MDistil/Perambulator.hpp +++ b/Hadrons/Modules/MDistil/Perambulator.hpp @@ -99,7 +99,7 @@ std::vector TPerambulator::getOutput(void) template void TPerambulator::setup(void) { - grid3d.reset( MakeLowerDimGrid( env().getGrid() ) ); + MakeLowerDimGrid(grid3d, env().getGrid()); const DistilParameters &dp = envGet(DistilParameters, par().DistilParams); const int Nt{env().getDim(Tdir)}; const bool full_tdil{ dp.TI == Nt }; From 13a0db7162bbd7c7c31b7c63077494365a447f36 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Mon, 18 Nov 2019 12:34:49 +0000 Subject: [PATCH 344/347] Reverse changes not intended to be part of distillation release --- Hadrons/Application.cc | 21 +-- Hadrons/Application.hpp | 3 +- Hadrons/Utilities/Contractor.cc | 172 ++++++++++++------------ Hadrons/Utilities/Contractor.hpp | 70 ---------- Hadrons/Utilities/HadronsXmlValidate.cc | 48 +------ 5 files changed, 98 insertions(+), 216 deletions(-) diff --git a/Hadrons/Application.cc b/Hadrons/Application.cc index d059703f..ad21e199 100644 --- a/Hadrons/Application.cc +++ b/Hadrons/Application.cc @@ -179,7 +179,7 @@ void Application::parseParameterFile(const std::string parameterFileName) pop(reader); } -void Application::saveParameterFile(const std::string ¶meterFileName, const std::vector &Except, unsigned int prec) +void Application::saveParameterFile(const std::string parameterFileName, unsigned int prec) { LOG(Message) << "Saving application to '" << parameterFileName << "'..." << std::endl; if (env().getGrid()->IsBoss()) @@ -193,27 +193,18 @@ void Application::saveParameterFile(const std::string ¶meterFileName, const push(writer, "modules"); for (unsigned int i = 0; i < nMod; ++i) { + push(writer, "module"); id.name = vm().getModuleName(i); - if( std::find( Except.begin(), Except.end(), id.name ) == Except.end() ) - { - push(writer, "module"); - id.type = vm().getModule(i)->getRegisteredName(); - write(writer, "id", id); - vm().getModule(i)->saveParameters(writer, "options"); - pop(writer); - } + id.type = vm().getModule(i)->getRegisteredName(); + write(writer, "id", id); + vm().getModule(i)->saveParameters(writer, "options"); + pop(writer); } pop(writer); pop(writer); } } -void Application::saveParameterFile(const std::string ¶meterFileName, unsigned int prec) -{ - const std::vector Except; - saveParameterFile(parameterFileName, Except, prec); -} - // schedule computation //////////////////////////////////////////////////////// void Application::schedule(void) { diff --git a/Hadrons/Application.hpp b/Hadrons/Application.hpp index 38442ea3..36179c5f 100644 --- a/Hadrons/Application.hpp +++ b/Hadrons/Application.hpp @@ -81,8 +81,7 @@ public: void run(void); // XML parameter file I/O void parseParameterFile(const std::string parameterFileName); - void saveParameterFile(const std::string ¶meterFileName, unsigned int prec=15); - void saveParameterFile(const std::string ¶meterFileName, const std::vector &Except, unsigned int prec=15); + void saveParameterFile(const std::string parameterFileName, unsigned int prec=15); // schedule computation void schedule(void); void saveSchedule(const std::string filename); diff --git a/Hadrons/Utilities/Contractor.cc b/Hadrons/Utilities/Contractor.cc index 053b4ceb..7e4b8b26 100644 --- a/Hadrons/Utilities/Contractor.cc +++ b/Hadrons/Utilities/Contractor.cc @@ -25,10 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ - -#include -#include -#include +#include #include #include #include @@ -36,8 +33,66 @@ See the full license in the file "LICENSE" in the top level distribution directo using namespace Grid; using namespace Hadrons; -// Separator to be used between contraction terms only (underscores elsewhere) -std::string Separator{ "_" }; +#define TIME_MOD(t) (((t) + par.global.nt) % par.global.nt) + +namespace Contractor +{ + class TrajRange: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(TrajRange, + unsigned int, start, + unsigned int, end, + unsigned int, step); + }; + + class GlobalPar: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(GlobalPar, + TrajRange, trajCounter, + unsigned int, nt, + std::string, diskVectorDir, + std::string, output); + }; + + class A2AMatrixPar: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(A2AMatrixPar, + std::string, file, + std::string, dataset, + unsigned int, cacheSize, + std::string, name); + }; + + class ProductPar: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(ProductPar, + std::string, terms, + std::vector, times, + std::string, translations, + bool, translationAverage); + }; + + class CorrelatorResult: Serializable + { + public: + GRID_SERIALIZABLE_CLASS_MEMBERS(CorrelatorResult, + std::vector, a2aMatrix, + ProductPar, contraction, + std::vector, times, + std::vector, correlator); + }; +} + +struct ContractorPar +{ + Contractor::GlobalPar global; + std::vector a2aMatrix; + std::vector product; +}; void makeTimeSeq(std::vector> &timeSeq, const std::vector> ×, @@ -63,10 +118,10 @@ void makeTimeSeq(std::vector> &timeSeq, { std::vector current(times.size()); - makeTimeSeq(timeSeq, times, current, static_cast(times.size())); + makeTimeSeq(timeSeq, times, current, times.size()); } -void saveCorrelator(const Contractor::CorrelatorResult &result, const std::string dir, +void saveCorrelator(const Contractor::CorrelatorResult &result, const std::string dir, const unsigned int dt, const unsigned int traj) { std::string fileStem = "", filename; @@ -74,12 +129,12 @@ void saveCorrelator(const Contractor::CorrelatorResult &result, const std::strin for (unsigned int i = 0; i < terms.size() - 1; i++) { - fileStem += terms[i] + Separator + std::to_string(result.times[i]) + Separator; + fileStem += terms[i] + "_" + std::to_string(result.times[i]) + "_"; } fileStem += terms.back(); if (!result.contraction.translationAverage) { - fileStem += Separator + "dt" + Separator + std::to_string(dt); + fileStem += "_dt_" + std::to_string(dt); } filename = dir + "/" + RESULT_FILE_NAME(fileStem, traj); std::cout << "Saving correlator to '" << filename << "'" << std::endl; @@ -183,76 +238,31 @@ inline std::ostream & operator<< (std::ostream& s, const Bytes &&b) int main(int argc, char* argv[]) { // parse command line - std::string parFilename; - bool bOnlyWriteUsedA2AMatrices{ false }; - int ArgCount{ 0 }; - bool bCmdLineError{ false }; - for( int i = 1; i < argc; i++ ) { - if( argv[i][0] == '-' ) { - // Switches - bool bSwitchOK = false; - switch( argv[i][1] ) { - case 'a': - if( argv[i][2] == 0 ) { - bOnlyWriteUsedA2AMatrices = true; - bSwitchOK = true; - std::cout << "Only A2AMatrices used in each contraction will be written" << std::endl; - } - break; - case 's': - if( argv[i][2] ) - Separator = &argv[i][2]; - else - Separator = "."; - bSwitchOK = true; - std::cout << "Using \"" << Separator << "\" as name separator" << std::endl; - break; - } - if( !bSwitchOK ) { - std::cerr << "Urecognised switch \"" << argv[i] << "\"" << std::endl; - bCmdLineError = true; - } - } else { - // Arguments - switch( ++ArgCount ) { - case 1: - parFilename = argv[i]; - break; - default: - std::cerr << "Unused argument \"" << argv[i] << "\"" << std::endl; - break; - } - } - } + std::string parFilename; - if (ArgCount != 1 or bCmdLineError) + if (argc != 2) { - std::cerr << "usage: " << argv[0] << " " - "\n\t-a\tSimple Correlators (only describe A2AMatrices used for contraction)" - "\n\t-s[sep]\tSeparator \"sep\" used between name components." - "\n\t\tDefaults to \"_\", or \".\" if -s specified without sep" - << std::endl; + std::cerr << "usage: " << argv[0] << " "; + std::cerr << std::endl; return EXIT_FAILURE; } - - // Log what file we're processing and when we started - const std::chrono::system_clock::time_point start{ std::chrono::system_clock::now() }; - std::time_t now = std::chrono::system_clock::to_time_t( start ); - std::cout << "Start " << parFilename << " " << std::ctime( &now ); + parFilename = argv[1]; // parse parameter file - Contractor::ContractorPar par; + ContractorPar par; + // unsigned int nMat,nCont; XmlReader reader(parFilename); read(reader, "global", par.global); read(reader, "a2aMatrix", par.a2aMatrix); read(reader, "product", par.product); - const unsigned int nMat { static_cast(par.a2aMatrix.size()) }; - const unsigned int nCont { static_cast(par.product.size()) }; + // nMat = par.a2aMatrix.size(); + // nCont = par.product.size(); // create diskvectors std::map> a2aMat; + // unsigned int cacheSize; for (auto &p: par.a2aMatrix) { @@ -268,15 +278,14 @@ int main(int argc, char* argv[]) std::cout << ":::::::: Trajectory " << traj << std::endl; // load data - int iSeq = 0; for (auto &p: par.a2aMatrix) { std::string filename = p.file; double t; + // double size; tokenReplace(filename, "traj", traj); - std::cout << "======== Loading '" << filename << "'" - << "\nA2AMatrix " << ++iSeq << " of " << nMat << " = " << p.name << std::endl; + std::cout << "======== Loading '" << filename << "'" << std::endl; A2AMatrixIo a2aIo(filename, p.dataset, par.global.nt); @@ -288,7 +297,6 @@ int main(int argc, char* argv[]) // contract EigenDiskVector::Matrix buf; - iSeq = 0; for (auto &p: par.product) { std::vector term = strToVec(p.terms); @@ -299,10 +307,11 @@ int main(int argc, char* argv[]) A2AMatrix prod, buf, tmp; TimerArray tAr; double fusec, busec, flops, bytes; + // double tusec; Contractor::CorrelatorResult result; tAr.startTimer("Total"); - std::cout << "======== Contraction " << ++iSeq << " of " << nCont << " tr("; + std::cout << "======== Contraction tr("; for (unsigned int g = 0; g < term.size(); ++g) { std::cout << term[g] << ((g == term.size() - 1) ? ')' : '*'); @@ -320,9 +329,7 @@ int main(int argc, char* argv[]) } for (auto &m: par.a2aMatrix) { - // For simple correlators, only include A2AMatrix info for correlators in this contraction - if ( ( !bOnlyWriteUsedA2AMatrices or std::find( term.begin(), term.end(), m.name ) != term.end() ) - and std::find(result.a2aMatrix.begin(), result.a2aMatrix.end(), m) == result.a2aMatrix.end()) + if (std::find(result.a2aMatrix.begin(), result.a2aMatrix.end(), m) == result.a2aMatrix.end()) { result.a2aMatrix.push_back(m); tokenReplace(result.a2aMatrix.back().file, "traj", traj); @@ -347,11 +354,11 @@ int main(int argc, char* argv[]) tAr.startTimer("Transpose caching"); lastTerm[t].resize(ref.rows(), ref.cols()); thread_for( j,ref.cols(),{ - for (unsigned int i = 0; i < ref.rows(); ++i) - { - lastTerm[t](i, j) = ref(i, j); - } - }); + for (unsigned int i = 0; i < ref.rows(); ++i) + { + lastTerm[t](i, j) = ref(i, j); + } + }); tAr.stopTimer("Transpose caching"); } bytes = par.global.nt*lastTerm[0].rows()*lastTerm[0].cols()*sizeof(ComplexD); @@ -445,13 +452,6 @@ int main(int argc, char* argv[]) printTimeProfile(tAr.getTimings(), tAr.getTimer("Total")); } } - - // Mention that we're finished, what the time is and how long it took - const std::chrono::system_clock::time_point stop{ std::chrono::system_clock::now() }; - now = std::chrono::system_clock::to_time_t( stop ); - const std::chrono::duration duration_seconds = stop - start; - const double hours{ ( duration_seconds.count() + 0.5 ) / 3600 }; - std::cout << "Stop " << parFilename << " " << std::ctime( &now ) - << "Total duration " << std::fixed << std::setprecision(1) << hours << " hours." << std::endl; + return EXIT_SUCCESS; } diff --git a/Hadrons/Utilities/Contractor.hpp b/Hadrons/Utilities/Contractor.hpp index 27e55b50..decd13aa 100644 --- a/Hadrons/Utilities/Contractor.hpp +++ b/Hadrons/Utilities/Contractor.hpp @@ -36,74 +36,4 @@ BEGIN_HADRONS_NAMESPACE END_HADRONS_NAMESPACE -#define BEGIN_CONTRACTOR_NAMESPACE namespace Contractor{ -BEGIN_CONTRACTOR_NAMESPACE - -using Grid::Serializable; -using Grid::Reader; -using Grid::Writer; -using Grid::ComplexD; - -class TrajRange: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(TrajRange, - unsigned int, start, - unsigned int, end, - unsigned int, step); -}; - -class GlobalPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(GlobalPar, - TrajRange, trajCounter, - unsigned int, nt, - std::string, diskVectorDir, - std::string, output); -}; - -class A2AMatrixPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(A2AMatrixPar, - std::string, file, - std::string, dataset, - unsigned int, cacheSize, - std::string, name); -}; - -class ProductPar: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(ProductPar, - std::string, terms, - std::vector, times, - std::string, translations, - bool, translationAverage); -}; - -class CorrelatorResult: Serializable -{ -public: - GRID_SERIALIZABLE_CLASS_MEMBERS(CorrelatorResult, - std::vector, a2aMatrix, - ProductPar, contraction, - std::vector, times, - std::vector, correlator); -}; - -struct ContractorPar -{ - Contractor::GlobalPar global; - std::vector a2aMatrix; - std::vector product; -}; - -// Useful ... so long as there's a ContractorPar named par in scope -#define TIME_MOD(t) (((t) + par.global.nt) % par.global.nt) - -#define END_CONTRACTOR_NAMESPACE } -END_CONTRACTOR_NAMESPACE - #endif // Hadrons_Contractor_hpp_ diff --git a/Hadrons/Utilities/HadronsXmlValidate.cc b/Hadrons/Utilities/HadronsXmlValidate.cc index 637f4484..73cf3139 100644 --- a/Hadrons/Utilities/HadronsXmlValidate.cc +++ b/Hadrons/Utilities/HadronsXmlValidate.cc @@ -27,51 +27,24 @@ See the full license in the file "LICENSE" in the top level distribution directo /* END LEGAL */ #include -#include -#include -#include using namespace Grid; +using namespace QCD; using namespace Hadrons; -// Does the specified file exist? -bool FileExists(const std::string& Filename) -{ - struct stat buf; - return stat(Filename.c_str(), &buf) != -1; -} - -void Shorten( Application &app, const std::string &FileList, const std::string OutFileName ) -{ - std::vector Except; - std::ifstream list{ FileList }; - for( std::string s; std::getline(list, s); ) { - //const std::string::size_type l{ s.find_first_of( '.' ) }; - //if( l != std::string::npos ) - //s.resize( l ); - if( s.length() ) - Except.push_back( s ); - } - std::sort( Except.begin(), Except.end() ); - for( const std::string &s : Except ) - std::cout << s << std::endl; - app.saveParameterFile( OutFileName, Except ); -} - int main(int argc, char *argv[]) { // parse command line std::string parameterFileName; - if (argc != 2 && argc != 4) + if (argc != 2) { - std::cerr << "usage: " << argv[0] << " [filelist.txt output.xml]"; + std::cerr << "usage: " << argv[0] << " "; std::cerr << std::endl; std::exit(EXIT_FAILURE); } parameterFileName = argv[1]; - if( argc == 4 ) - Grid_init(&argc, &argv); + try { Application application(parameterFileName); @@ -81,22 +54,11 @@ int main(int argc, char *argv[]) vm.getModuleGraph(); LOG(Message) << "Application valid (check XML warnings though)" << std::endl; - if( argc == 4 ) { - const std::string FileList{ argv[3] }; - const std::string OutFileName{ argv[2] }; - if( !FileExists( FileList ) ) - std::cout << "File list \"" << FileList << "\" does not exist" << std::endl; - else { - Shorten( application, FileList, OutFileName ); - } - } } catch (const std::exception& e) { Exceptions::abort(e); } - if( argc == 4 ) - Grid_finalize(); - + return EXIT_SUCCESS; } From b350a24dede2faa8c766bba15537143f660f5283 Mon Sep 17 00:00:00 2001 From: ferben Date: Mon, 18 Nov 2019 15:29:20 +0000 Subject: [PATCH 345/347] fixed test_distil --- Hadrons/Modules/MIO/LoadDistilNoise.hpp | 2 +- Hadrons/Modules/MIO/LoadPerambulator.hpp | 2 +- tests/hadrons/Test_distil.cc | 28 ++++-------------------- 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/Hadrons/Modules/MIO/LoadDistilNoise.hpp b/Hadrons/Modules/MIO/LoadDistilNoise.hpp index cf3513fe..d23a0d02 100644 --- a/Hadrons/Modules/MIO/LoadDistilNoise.hpp +++ b/Hadrons/Modules/MIO/LoadDistilNoise.hpp @@ -77,7 +77,7 @@ TLoadDistilNoise::TLoadDistilNoise(const std::string name) : Module std::vector TLoadDistilNoise::getInput(void) { - return {par().NoiseFileName, par().DistilParams}; + return {par().DistilParams}; } template diff --git a/Hadrons/Modules/MIO/LoadPerambulator.hpp b/Hadrons/Modules/MIO/LoadPerambulator.hpp index 5b7d361b..cd5a4cc8 100644 --- a/Hadrons/Modules/MIO/LoadPerambulator.hpp +++ b/Hadrons/Modules/MIO/LoadPerambulator.hpp @@ -77,7 +77,7 @@ TLoadPerambulator::TLoadPerambulator(const std::string name) : Module std::vector TLoadPerambulator::getInput(void) { - return {par().PerambFileName, par().DistilParams}; + return {par().DistilParams}; } template diff --git a/tests/hadrons/Test_distil.cc b/tests/hadrons/Test_distil.cc index 79b1fae6..0156f340 100644 --- a/tests/hadrons/Test_distil.cc +++ b/tests/hadrons/Test_distil.cc @@ -90,7 +90,7 @@ void test_LapEvec(Application &application) p.Stout.rho = 0.2; p.Cheby.PolyOrder = 11; p.Cheby.alpha = 0.55; - p.Cheby.beta = 12.5; + p.Cheby.beta = 35.5; p.Lanczos.Nvec = 5; p.Lanczos.Nk = 6; p.Lanczos.Np = 2; @@ -180,9 +180,9 @@ std::string PerambulatorName( const char * pszSuffix = nullptr ) void test_LoadPerambulators( Application &application, const char * pszSuffix = nullptr ) { - std::string sModuleName{ PerambulatorName( pszSuffix ) }; + std::string sModuleName{ "Peramb_load" }; MIO::LoadPerambulator::Par PerambPar; - PerambPar.PerambFileName = sModuleName; + PerambPar.PerambFileName = "Peramb"; PerambPar.DistilParams = "DPar_l"; test_Noises(application, sModuleName); // I want these written after solver stuff application.createModule( sModuleName, PerambPar ); @@ -359,30 +359,10 @@ int main(int argc, char *argv[]) test_LapEvec( application ); test_DPar( application ); test_LoadPerambulators( application ); - test_DistilVectors( application ); + test_DistilVectors( application, "_load" ); test_MesonField( application, "Phi", "phi" ); test_MesonField( application, "Rho", "rho" ); break; - case 2: - LOG(Message) << "Computing Meson 2pt-function for two quark flavours" << std::endl; - test_Global( application ); - test_LapEvec( application ); - test_DPar( application ); - test_Perambulators( application ); - test_DistilVectors( application ); - test_Perambulators( application, "S" ); - test_DistilVectors( application, "S" ); - test_MesonField( application, "SPhi", "S_phi" ); - test_MesonField( application, "SRho", "S_rho" ); - break; - case 3: - LOG(Message) << "Computing Meson 2pt-function with current insertion" << std::endl; - test_Global( application ); - test_LapEvec( application ); - test_DPar( application ); - test_Perambulators( application ); - test_MesonSink( application ); - break; } // execution static const char XmlFileName[] = "test_distil.xml"; From 6418f067715525e24f9202104c90ccd952fd95de Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 29 Nov 2019 18:06:18 +0000 Subject: [PATCH 346/347] Add option to save the eigenvectors of the Laplacian. If they are saved, then metadata saved are: solverXml Parameters for this LapEvec module instance OperatorXml module type and parameters (if any) for the module that created the gauge field --- Hadrons/Modules/MDistil/LapEvec.hpp | 26 ++++++++++++++------- Hadrons/Modules/MDistil/PerambFromSolve.hpp | 6 ++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Hadrons/Modules/MDistil/LapEvec.hpp b/Hadrons/Modules/MDistil/LapEvec.hpp index 80ac25df..9b5197c2 100644 --- a/Hadrons/Modules/MDistil/LapEvec.hpp +++ b/Hadrons/Modules/MDistil/LapEvec.hpp @@ -85,7 +85,8 @@ struct LapEvecPar: Serializable { ,std::string, gauge ,StoutParameters, Stout ,ChebyshevParameters, Cheby - ,LanczosParameters, Lanczos) + ,LanczosParameters, Lanczos + ,std::string, FileName) }; /****************************************************************************** @@ -309,15 +310,22 @@ void TLapEvec::execute(void) { HADRONS_ERROR(Program,"The eingensolver failed to find enough eigenvectors on at least one node"); } -#if DEBUG // Now write out the 4d eigenvectors - eig4d.record.operatorXml = "Distillation"; - eig4d.record.solverXml = "CG"; - std::string sEigenPackName(getName()); - sEigenPackName.append("."); - sEigenPackName.append(std::to_string(vm().getTrajectory())); - eig4d.write(sEigenPackName,false); -#endif + std::string sEigenPackName(par().FileName); + if( !sEigenPackName.empty() ) + { + eig4d.record.solverXml = parString(); + ModuleBase * b{vm().getModule(par().gauge)}; + std::string sOperatorXml{ "" }; + sOperatorXml.append( b->getRegisteredName() ); + sOperatorXml.append( "" ); + sOperatorXml.append( b->parString() ); + sOperatorXml.append( "" ); + eig4d.record.operatorXml = sOperatorXml; + sEigenPackName.append("."); + sEigenPackName.append(std::to_string(vm().getTrajectory())); + eig4d.write(sEigenPackName,false); + } } END_MODULE_NAMESPACE diff --git a/Hadrons/Modules/MDistil/PerambFromSolve.hpp b/Hadrons/Modules/MDistil/PerambFromSolve.hpp index 11721eb6..a0a9b61c 100644 --- a/Hadrons/Modules/MDistil/PerambFromSolve.hpp +++ b/Hadrons/Modules/MDistil/PerambFromSolve.hpp @@ -54,7 +54,7 @@ class PerambFromSolvePar: Serializable { public: GRID_SERIALIZABLE_CLASS_MEMBERS(PerambFromSolvePar, - std::string, eigenPack, + std::string, lapevec, std::string, PerambFileName, std::string, solve, int, nvec_reduced, @@ -95,7 +95,7 @@ TPerambFromSolve::TPerambFromSolve(const std::string name) : Module std::vector TPerambFromSolve::getInput(void) { - return {par().solve, par().eigenPack, par().DistilParams}; + return {par().solve, par().lapevec, par().DistilParams}; } template @@ -137,7 +137,7 @@ void TPerambFromSolve::execute(void) const int LI_reduced{ par().LI_reduced}; auto &perambulator = envGet(PerambTensor, getName()); auto &solve = envGet(std::vector, par().solve); - auto &epack = envGet(Grid::Hadrons::EigenPack, par().eigenPack); + auto &epack = envGet(Grid::Hadrons::EigenPack, par().lapevec); envGetTmp(LatticeColourVector, result4d_nospin); envGetTmp(LatticeColourVector, result3d_nospin); From 2db814f2b79a7c52af234d7a5bb347b502ea8106 Mon Sep 17 00:00:00 2001 From: Michael Marshall <43034299+mmphys@users.noreply.github.com> Date: Fri, 29 Nov 2019 18:19:35 +0000 Subject: [PATCH 347/347] Resolve conflicts in BaryonUtils (just use latest from develop) --- Grid/qcd/utils/BaryonUtils.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Grid/qcd/utils/BaryonUtils.h b/Grid/qcd/utils/BaryonUtils.h index ad3236da..73d41422 100644 --- a/Grid/qcd/utils/BaryonUtils.h +++ b/Grid/qcd/utils/BaryonUtils.h @@ -43,7 +43,7 @@ public: typedef typename ComplexField::vector_object vobj; static constexpr int epsilon[6][3] = {{0,1,2},{1,2,0},{2,0,1},{0,2,1},{2,1,0},{1,0,2}}; - static constexpr int epsilon_sgn[6]= {1,1,1,-1,-1,-1}; + static constexpr Complex epsilon_sgn[6]= {1,1,1,-1,-1,-1}; private: template @@ -86,7 +86,7 @@ public: template constexpr int BaryonUtils::epsilon[6][3]; template -constexpr int BaryonUtils::epsilon_sgn[6]; +constexpr Complex BaryonUtils::epsilon_sgn[6]; template template @@ -123,7 +123,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1()(gamma_left,gamma_left)(c_right,c_left)*D2g()(alpha_right,beta_left)(a_right,a_left)*gD3()(alpha_right,beta_left)(b_right,b_left); + result()()() += epsilon_sgn[ie_left] * epsilon_sgn[ie_right] * pD1()(gamma_left,gamma_left)(c_right,c_left)*D2g()(alpha_right,beta_left)(a_right,a_left)*gD3()(alpha_right,beta_left)(b_right,b_left); }}} } //This is the \delta_{456}^{231} part @@ -132,7 +132,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1g()(gamma_left,beta_left)(c_right,a_left)*D2()(alpha_right,beta_left)(a_right,b_left)*gD3()(alpha_right,gamma_left)(b_right,c_left); + result()()() += epsilon_sgn[ie_left] * epsilon_sgn[ie_right] * pD1g()(gamma_left,beta_left)(c_right,a_left)*D2()(alpha_right,beta_left)(a_right,b_left)*gD3()(alpha_right,gamma_left)(b_right,c_left); }}} } //This is the \delta_{456}^{312} part @@ -141,7 +141,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1()(gamma_left,beta_left)(c_right,b_left)*D2()(alpha_right,gamma_left)(a_right,c_left)*gD3g()(alpha_right,beta_left)(b_right,a_left); + result()()() += epsilon_sgn[ie_left] * epsilon_sgn[ie_right] * pD1()(gamma_left,beta_left)(c_right,b_left)*D2()(alpha_right,gamma_left)(a_right,c_left)*gD3g()(alpha_right,beta_left)(b_right,a_left); }}} } //This is the \delta_{456}^{132} part @@ -150,7 +150,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1()(gamma_left,gamma_left)(c_right,c_left)*D2()(alpha_right,beta_left)(a_right,b_left)*gD3g()(alpha_right,beta_left)(b_right,a_left); + result()()() -= epsilon_sgn[ie_left] * epsilon_sgn[ie_right] * pD1()(gamma_left,gamma_left)(c_right,c_left)*D2()(alpha_right,beta_left)(a_right,b_left)*gD3g()(alpha_right,beta_left)(b_right,a_left); }}} } //This is the \delta_{456}^{321} part @@ -159,7 +159,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1()(gamma_left,beta_left)(c_right,b_left)*D2g()(alpha_right,beta_left)(a_right,a_left)*gD3()(alpha_right,gamma_left)(b_right,c_left); + result()()() -= epsilon_sgn[ie_left] * epsilon_sgn[ie_right] * pD1()(gamma_left,beta_left)(c_right,b_left)*D2g()(alpha_right,beta_left)(a_right,a_left)*gD3()(alpha_right,gamma_left)(b_right,c_left); }}} } //This is the \delta_{456}^{213} part @@ -168,7 +168,7 @@ void BaryonUtils::baryon_site(const mobj &D1, for (int alpha_right=0; alpha_right(epsilon_sgn[ie_left] * epsilon_sgn[ie_right]) * pD1g()(gamma_left,beta_left)(c_right,a_left)*D2()(alpha_right,gamma_left)(a_right,c_left)*gD3()(alpha_right,beta_left)(b_right,b_left); + result()()() -= epsilon_sgn[ie_left] * epsilon_sgn[ie_right] * pD1g()(gamma_left,beta_left)(c_right,a_left)*D2()(alpha_right,gamma_left)(a_right,c_left)*gD3()(alpha_right,beta_left)(b_right,b_left); }}} } }