diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e31998e78..5c0b93e17 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -41,6 +41,7 @@ Version 2022-dev - updated benchmark (#714) - reworked commandline options (#715) - renamed cmd line arguments in xtp_parallel (#718) +- added incremental Fock matrix building (#716) - disable codeql check in GitHub Actions (#720) Version 2021.1 (released XX.03.21) diff --git a/include/votca/xtp/IncrementalFockBuilder.h b/include/votca/xtp/IncrementalFockBuilder.h new file mode 100644 index 000000000..d3c13d316 --- /dev/null +++ b/include/votca/xtp/IncrementalFockBuilder.h @@ -0,0 +1,106 @@ +/* + * Copyright 2009-2020 The VOTCA Development Team + * (http://www.votca.org) + * + * Licensed under the Apache License, Version 2.0 (the "License") + * + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once +#ifndef VOTCA_XTP_INCREMENTALFOCKBUILDER_H +#define VOTCA_XTP_INCREMENTALFOCKBUILDER_H + +#include "votca/xtp/logger.h" +#include +namespace votca { +namespace xtp { + +// Small Wrapper class to build incremental fock matrix +class IncrementalFockBuilder { + public: + IncrementalFockBuilder(Logger& log, double start_threshold, + Index fock_matrix_reset) + : log_(log), + start_incremental_F_threshold_(start_threshold), + fock_matrix_reset_(fock_matrix_reset) {} + + void Configure(const Eigen::MatrixXd& dmat) { + Ddiff_ = dmat; + Dlast_ = dmat; + } + + void Start(Index iteration, double DiisError) { + if (!incremental_Fbuild_started_ && + DiisError < start_incremental_F_threshold_) { + incremental_Fbuild_started_ = true; + reset_incremental_fock_formation_ = false; + last_reset_iteration_ = iteration - 1; + next_reset_threshold_ = DiisError / 10.0; + XTP_LOG(Log::error, log_) + << TimeStamp() << " Using incremental 4c build from here" + << std::flush; + } + } + + void resetMatrices(Eigen::MatrixXd& J, Eigen::MatrixXd& K, + const Eigen::MatrixXd& dmat) { + if (reset_incremental_fock_formation_ || !incremental_Fbuild_started_) { + J.setZero(); + K.setZero(); + Ddiff_ = dmat; + } + } + + const Eigen::MatrixXd& getDmat_diff() const { return Ddiff_; } + + void UpdateCriteria(double DiisError, Index Iteration) { + if (reset_incremental_fock_formation_ && incremental_Fbuild_started_) { + reset_incremental_fock_formation_ = false; + last_reset_iteration_ = Iteration; + next_reset_threshold_ = DiisError / 10.0; + XTP_LOG(Log::error, log_) + << TimeStamp() << " Reset incremental 4c build" << std::flush; + } + } + + void UpdateDmats(const Eigen::MatrixXd& dmat, double DiisError, + Index Iteration) { + if (DiisError < next_reset_threshold_ || + Iteration - last_reset_iteration_ > fock_matrix_reset_) { + reset_incremental_fock_formation_ = true; + } + Ddiff_ = dmat - Dlast_; + Dlast_ = dmat; + } + + private: + Logger& log_; + double start_incremental_F_threshold_; // Diis error from which to start + // using incremental builds + Index fock_matrix_reset_; // After how many iterations the fock matrix should + // be reset regardless + + Eigen::MatrixXd Ddiff_; + Eigen::MatrixXd Dlast_; + + bool reset_incremental_fock_formation_ = false; + bool incremental_Fbuild_started_ = false; + double next_reset_threshold_ = 0.0; + Index last_reset_iteration_ = 0; +}; // namespace xtp + +} // namespace xtp +} // namespace votca + +#endif // VOTCA_XTP_INCREMENTALFOCKBUILDER_H diff --git a/include/votca/xtp/dftengine.h b/include/votca/xtp/dftengine.h index af41c7c4d..085c51165 100644 --- a/include/votca/xtp/dftengine.h +++ b/include/votca/xtp/dftengine.h @@ -115,8 +115,7 @@ class DFTEngine { bool with_ecp_; - std::string four_center_method_; // direct | cache - + Index fock_matrix_reset_; // Pre-screening double screening_eps_; diff --git a/share/xtp/data/qmpackage_defaults.xml b/share/xtp/data/qmpackage_defaults.xml index e457cb019..89fbf37a6 100644 --- a/share/xtp/data/qmpackage_defaults.xml +++ b/share/xtp/data/qmpackage_defaults.xml @@ -22,10 +22,9 @@ - true 1e-9 - RI + 5 1e-7 DIIS diff --git a/src/libxtp/dftengine/dftengine.cc b/src/libxtp/dftengine/dftengine.cc index 407230168..fc4636bd1 100644 --- a/src/libxtp/dftengine/dftengine.cc +++ b/src/libxtp/dftengine/dftengine.cc @@ -26,6 +26,7 @@ #include // Local VOTCA includes +#include "votca/xtp/IncrementalFockBuilder.h" #include "votca/xtp/aomatrix.h" #include "votca/xtp/aopotential.h" #include "votca/xtp/density_integration.h" @@ -34,7 +35,6 @@ #include "votca/xtp/logger.h" #include "votca/xtp/mmregion.h" #include "votca/xtp/orbitals.h" - using boost::format; using namespace boost::filesystem; using namespace std; @@ -54,13 +54,11 @@ void DFTEngine::Initialize(Property& options) { auxbasis_name_ = options.get(key + ".auxbasisset").as(); } - four_center_method_ = - options.get(key_xtpdft + ".four_center_method").as(); - - if (four_center_method_ != "RI") { + if (!auxbasis_name_.empty()) { screening_eps_ = options.get(key_xtpdft + ".screening_eps").as(); + fock_matrix_reset_ = + options.get(key_xtpdft + ".fock_matrix_reset").as(); } - if (options.get(key + ".use_ecp").as()) { ecp_name_ = options.get(key + ".ecp").as(); with_ecp_ = true; @@ -153,7 +151,7 @@ void DFTEngine::CalcElDipole(const Orbitals& orb) const { std::array DFTEngine::CalcERIs_EXX( const Eigen::MatrixXd& MOCoeff, const Eigen::MatrixXd& Dmat, double error) const { - if (four_center_method_ == "RI") { + if (!auxbasis_name_.empty()) { if (conv_accelerator_.getUseMixing() || MOCoeff.rows() == 0) { return ERIs_.CalculateERIs_EXX_3c(Eigen::MatrixXd::Zero(0, 0), Dmat); } else { @@ -168,7 +166,7 @@ std::array DFTEngine::CalcERIs_EXX( Eigen::MatrixXd DFTEngine::CalcERIs(const Eigen::MatrixXd& Dmat, double error) const { - if (four_center_method_ == "RI") { + if (!auxbasis_name_.empty()) { return ERIs_.CalculateERIs_3c(Dmat); } else { return ERIs_.CalculateERIs_4c(Dmat, error); @@ -240,6 +238,20 @@ bool DFTEngine::Evaluate(Orbitals& orb) { "----------------------------" << flush; + Eigen::MatrixXd J = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols()); + Eigen::MatrixXd K; + if (ScaHFX_ > 0) { + K = Eigen::MatrixXd::Zero(Dmat.rows(), Dmat.cols()); + } + + double start_incremental_F_threshold = 1e-4; + if (!auxbasis_name_.empty()) { + start_incremental_F_threshold = 0.0; // Disable if RI is used + } + IncrementalFockBuilder incremental_fock(*pLog_, start_incremental_F_threshold, + fock_matrix_reset_); + incremental_fock.Configure(Dmat); + for (Index this_iter = 0; this_iter < max_iter_; this_iter++) { XTP_LOG(Log::error, *pLog_) << flush; XTP_LOG(Log::error, *pLog_) << TimeStamp() << " Iteration " << this_iter + 1 @@ -254,20 +266,29 @@ bool DFTEngine::Evaluate(Orbitals& orb) { double Etwo = e_vxc.energy(); double exx = 0.0; + incremental_fock.Start(this_iter, conv_accelerator_.getDIIsError()); + incremental_fock.resetMatrices(J, K, Dmat); + incremental_fock.UpdateCriteria(conv_accelerator_.getDIIsError(), + this_iter); + + double integral_error = + std::min(conv_accelerator_.getDIIsError() * 1e-5, 1e-5); if (ScaHFX_ > 0) { - std::array both = - CalcERIs_EXX(MOs.eigenvectors(), Dmat, 1e-12); - H += both[0]; - Etwo += 0.5 * Dmat.cwiseProduct(both[0]).sum(); - H += 0.5 * ScaHFX_ * both[1]; - exx = ScaHFX_ / 4 * Dmat.cwiseProduct(both[1]).sum(); + std::array both = CalcERIs_EXX( + MOs.eigenvectors(), incremental_fock.getDmat_diff(), integral_error); + J += both[0]; + H += J; + Etwo += 0.5 * Dmat.cwiseProduct(J).sum(); + K += both[1]; + H += 0.5 * ScaHFX_ * K; + exx = 0.25 * ScaHFX_ * Dmat.cwiseProduct(K).sum(); XTP_LOG(Log::info, *pLog_) << TimeStamp() << " Filled F+K matrix " << flush; } else { - Eigen::MatrixXd Hartree = CalcERIs(Dmat, 1e-12); + J += CalcERIs(incremental_fock.getDmat_diff(), integral_error); XTP_LOG(Log::info, *pLog_) << TimeStamp() << " Filled F matrix " << flush; - H += Hartree; - Etwo += 0.5 * Dmat.cwiseProduct(Hartree).sum(); + H += J; + Etwo += 0.5 * Dmat.cwiseProduct(J).sum(); } Etwo += exx; @@ -288,6 +309,8 @@ bool DFTEngine::Evaluate(Orbitals& orb) { << std::setprecision(12) << totenergy << flush; Dmat = conv_accelerator_.Iterate(Dmat, H, MOs, totenergy); + incremental_fock.UpdateDmats(Dmat, conv_accelerator_.getDIIsError(), + this_iter); PrintMOs(MOs.eigenvalues(), Log::info); @@ -439,7 +462,7 @@ void DFTEngine::SetupInvariantMatrices() { conv_accelerator_.setOverlap(dftAOoverlap_, 1e-8); conv_accelerator_.PrintConfigOptions(); - if (four_center_method_ == "RI") { + if (!auxbasis_name_.empty()) { // prepare invariant part of electron repulsion integrals ERIs_.Initialize(dftbasis_, auxbasis_); XTP_LOG(Log::info, *pLog_) @@ -558,12 +581,16 @@ Eigen::MatrixXd DFTEngine::RunAtomicDFT_unrestricted( double E_two_alpha = 0.0; double E_two_beta = 0.0; + double integral_error = std::min(1e-5 * 0.5 * + (Convergence_alpha.getDIIsError() + + Convergence_beta.getDIIsError()), + 1e-5); if (ScaHFX_ > 0) { std::array both_alpha = - ERIs_atom.CalculateERIs_EXX_4c(dftAOdmat_alpha, 1e-15); + ERIs_atom.CalculateERIs_EXX_4c(dftAOdmat_alpha, integral_error); std::array both_beta = - ERIs_atom.CalculateERIs_EXX_4c(dftAOdmat_beta, 1e-15); + ERIs_atom.CalculateERIs_EXX_4c(dftAOdmat_beta, integral_error); Eigen::MatrixXd Hartree = both_alpha[0] + both_beta[0]; E_two_alpha += Hartree.cwiseProduct(dftAOdmat_alpha).sum(); E_two_beta += Hartree.cwiseProduct(dftAOdmat_beta).sum(); @@ -573,8 +600,8 @@ Eigen::MatrixXd DFTEngine::RunAtomicDFT_unrestricted( H_beta += Hartree + ScaHFX_ * both_beta[1]; } else { - Eigen::MatrixXd Hartree = - ERIs_atom.CalculateERIs_4c(dftAOdmat_alpha + dftAOdmat_beta, 1e-15); + Eigen::MatrixXd Hartree = ERIs_atom.CalculateERIs_4c( + dftAOdmat_alpha + dftAOdmat_beta, integral_error); E_two_alpha += Hartree.cwiseProduct(dftAOdmat_alpha).sum(); E_two_beta += Hartree.cwiseProduct(dftAOdmat_beta).sum(); H_alpha += Hartree; @@ -708,7 +735,7 @@ void DFTEngine::ConfigOrbfile(Orbitals& orb) { if (with_ecp_) { orb.setECPName(ecp_name_); } - if (four_center_method_ == "RI") { + if (!auxbasis_name_.empty()) { orb.setAuxbasisName(auxbasis_name_); } @@ -772,7 +799,7 @@ void DFTEngine::Prepare(QMMolecule& mol) { << TimeStamp() << " Loaded DFT Basis Set " << dftbasis_name_ << " with " << dftbasis_.AOBasisSize() << " functions" << flush; - if (four_center_method_ == "RI") { + if (!auxbasis_name_.empty()) { BasisSet auxbasisset; auxbasisset.Load(auxbasis_name_); auxbasis_.Fill(auxbasisset, mol); diff --git a/src/libxtp/qmpackage.cc b/src/libxtp/qmpackage.cc index 0dbe5357a..de0780c53 100644 --- a/src/libxtp/qmpackage.cc +++ b/src/libxtp/qmpackage.cc @@ -35,7 +35,6 @@ using std::flush; tools::Property QMPackage::ParseCommonOptions(const tools::Property& options) { std::string key = "package"; - settings_.read_property(options, key); if (tools::VotcaShareSet()) { diff --git a/src/libxtp/qmpackages/xtpdft.cc b/src/libxtp/qmpackages/xtpdft.cc index 0bbbe7f9c..9e07b4f05 100644 --- a/src/libxtp/qmpackages/xtpdft.cc +++ b/src/libxtp/qmpackages/xtpdft.cc @@ -37,7 +37,7 @@ namespace xtp { using namespace std; void XTPDFT::Initialize(const tools::Property& options) { - const std::string& job_name = + const std::string job_name = options.ifExistsReturnElseReturnDefault("job_name", "votca"); log_file_name_ = job_name + ".orb"; mo_file_name_ = log_file_name_; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index e525485e5..c92e1b7bd 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -79,6 +79,7 @@ if(ENABLE_TESTING) list(APPEND test_cases test_orbreorder) list(APPEND test_cases test_molden) list(APPEND test_cases test_gaussianwriter) + list(APPEND test_cases test_incrementalfockbuilder) if(USE_CUDA) list(APPEND test_cases test_cudapipeline) list(APPEND test_cases test_cudamatrix) diff --git a/src/tests/test_dftengine.cc b/src/tests/test_dftengine.cc index 030972131..1e4aab214 100644 --- a/src/tests/test_dftengine.cc +++ b/src/tests/test_dftengine.cc @@ -1,3 +1,5 @@ + + /* * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org) * @@ -31,14 +33,6 @@ using namespace votca::xtp; BOOST_AUTO_TEST_SUITE(dftengine_test) QMMolecule Water() { - std::ofstream xyzfile("molecule.xyz"); - xyzfile << "3" << std::endl; - xyzfile << "Water molecule" << std::endl; - xyzfile << "O 0.00000 0.00000 0.11779" << std::endl; - xyzfile << "H 0.00000 0.75545 -0.47116" << std::endl; - xyzfile << "H 0.00000 -0.75545 -0.47116" << std::endl; - - xyzfile.close(); QMMolecule mol(" ", 1); mol.LoadFromFile(std::string(XTP_TEST_DATA_FOLDER) + "/espfit/molecule.xyz"); return mol; @@ -119,7 +113,7 @@ BOOST_AUTO_TEST_CASE(dft_full) { Orbitals orb; orb.QMAtoms() = Water(); - std::ofstream xml("dftengine.xml"); + std::ofstream xml("dftengine2.xml"); xml << "" << std::endl; xml << "1" << std::endl; xml << "xtp" << std::endl; @@ -134,7 +128,7 @@ BOOST_AUTO_TEST_CASE(dft_full) { xml << "false" << std::endl; xml << "true\n"; xml << "1e-9\n"; - xml << "cache\n"; + xml << "5\n"; xml << "" << std::endl; xml << " 1e-7" << std::endl; xml << " DIIS" << std::endl; @@ -156,7 +150,7 @@ BOOST_AUTO_TEST_CASE(dft_full) { xml << "" << std::endl; xml.close(); votca::tools::Property prop; - prop.LoadFromXML("dftengine.xml"); + prop.LoadFromXML("dftengine2.xml"); Logger log; dft.setLogger(&log); @@ -234,7 +228,8 @@ BOOST_AUTO_TEST_CASE(density_guess) { xml << "false" << std::endl; xml << "true\n"; xml << "1e-9\n"; - xml << "cache\n"; + xml << "direct\n"; + xml << "5\n"; xml << "" << std::endl; xml << " 1e-7" << std::endl; xml << " DIIS" << std::endl; diff --git a/src/tests/test_incrementalfockbuilder.cc b/src/tests/test_incrementalfockbuilder.cc new file mode 100644 index 000000000..9aadab774 --- /dev/null +++ b/src/tests/test_incrementalfockbuilder.cc @@ -0,0 +1,92 @@ +/* + * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#define BOOST_TEST_MAIN + +#define BOOST_TEST_MODULE incrementalfockbuilder_test + +// Third party includes +#include + +// Local VOTCA includes +#include "votca/xtp/IncrementalFockBuilder.h" + +using namespace votca::xtp; + +BOOST_AUTO_TEST_SUITE(incrementalfockbuilder) + +BOOST_AUTO_TEST_CASE(off_test) { + Logger log; + + // switched off + IncrementalFockBuilder fb(log, 0.0, 3); + + Eigen::MatrixXd dmat = Eigen::MatrixXd::Random(10, 10); + fb.Configure(dmat); + fb.Start(2, 1e-5); + + Eigen::MatrixXd J = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd K = Eigen::MatrixXd::Random(10, 10); + fb.resetMatrices(J, K, dmat); + BOOST_CHECK(J.isApproxToConstant(0.0)); + BOOST_CHECK(K.isApproxToConstant(0.0)); + + BOOST_CHECK(fb.getDmat_diff().isApprox(dmat)); + + fb.UpdateDmats(dmat, 1e-5, 2); + + // basically checks that using the same dmat twice leads to a diff of zero + BOOST_CHECK(fb.getDmat_diff().isApproxToConstant(0.0)); +} + +BOOST_AUTO_TEST_CASE(on_test) { + Logger log; + + // switched on + IncrementalFockBuilder fb(log, 1e-5, 2); + + Eigen::MatrixXd dmat = Eigen::MatrixXd::Random(10, 10); + fb.Configure(dmat); + votca::Index iteration = 2; + fb.Start(iteration, 1e-6); + + Eigen::MatrixXd J = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd J2 = J; + Eigen::MatrixXd K = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd K2 = K; + + fb.resetMatrices(J, K, dmat); + BOOST_CHECK(J.isApprox(J2)); + BOOST_CHECK(K.isApprox(K2)); + + fb.UpdateCriteria(1e-6, iteration); + fb.UpdateDmats(dmat, 1e-6, iteration); + // basically checks that using the same dmat twice leads to a diff of zero + BOOST_CHECK(fb.getDmat_diff().isApproxToConstant(0.0)); + iteration++; + fb.UpdateCriteria(1e-6, iteration); + fb.UpdateDmats(dmat, 1e-6, iteration); + iteration++; + fb.UpdateCriteria(1e-6, iteration); + fb.UpdateDmats(dmat, 1e-6, iteration); + + // now reset should trigger + fb.resetMatrices(J, K, dmat); + BOOST_CHECK(J.isApproxToConstant(0.0)); + BOOST_CHECK(K.isApproxToConstant(0.0)); +} + +BOOST_AUTO_TEST_SUITE_END()