133#define LIBINT2_CONSTEXPR_STATICS 0
134#if defined(__clang__)
135#pragma clang diagnostic push
136#pragma clang diagnostic ignored "-W#warnings"
137#elif defined(__GNUC__)
138#pragma GCC diagnostic push
139#pragma GCC diagnostic ignored "-Warray-bounds"
140#pragma GCC diagnostic ignored "-Wcpp"
142#include <libint2.hpp>
151#if defined(__clang__)
152#pragma clang diagnostic pop
153#elif defined(__GNUC__)
154#pragma GCC diagnostic pop
163std::vector<Eigen::MatrixXd>
ComputeAO3cBlock(
const libint2::Shell& auxshell,
164 const AOBasis& dftbasis,
165 libint2::Engine& engine);
189 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
212[[noreturn, maybe_unused]]
void ThrowNoDerivativeSupport(
213 const char* function_name,
const char* configure_flag) {
214 throw std::runtime_error(
215 std::string(function_name) +
216 ": this libint2 build was compiled without derivative integral "
217 "support for this specific operator category -- analytic nuclear "
218 "forces using this function are unavailable. Rebuild libint2 "
219 "with this configured (" +
221 " at libint2 configure time) to use this feature. Many "
222 "pre-packaged libint2 builds (Homebrew, Ubuntu apt, etc.) do not "
223 "enable this by default; note in particular that different "
224 "operator categories (one-body, the two-center Coulomb metric, "
225 "three-center RI) each have their own, independent flag -- "
226 "enabling one does not enable the others.");
302#if defined(LIBINT2_MAX_DERIV_ORDER) && LIBINT2_MAX_DERIV_ORDER >= 1 && \
303 (LIBINT_INCLUDE_ONEBODY >= 1)
304template <lib
int2::Operator obtype>
305std::vector<AOMatrixDerivative> computeOneBodyIntegralDerivatives(
306 const AOBasis& aobasis) {
309 libint2::operator_traits<obtype>::nopers == 1,
310 "computeOneBodyIntegralDerivatives only supports single-operator "
311 "types (overlap, kinetic) in this first pass; multipole-type "
312 "operators with multiple result matrices are not yet handled here.");
317 Index natoms =
static_cast<Index>(aobasis.getFuncPerAtom().size());
319 std::vector<libint2::Shell> shells = aobasis.GenerateLibintBasis();
320 std::vector<std::vector<Index>> shellpair_list = aobasis.ComputeShellPairs();
321 std::vector<Index> shell2bf = aobasis.getMapToBasisFunctions();
325 std::vector<Index> shell2atom;
326 shell2atom.reserve(shells.size());
327 for (
Index s = 0; s < aobasis.getNumofShells(); ++s) {
330 shell2atom.push_back(aobasis.getShell(s).getAtomIndex());
333 Index nbf = aobasis.AOBasisSize();
334 std::vector<AOMatrixDerivative> result(natoms);
335 for (
Index a = 0; a < natoms; ++a) {
336 for (
Index xyz = 0; xyz < 3; ++xyz) {
337 result[a][xyz] = Eigen::MatrixXd::Zero(nbf, nbf);
341 std::vector<libint2::Engine> engines(nthreads);
344 engines[0] = libint2::Engine(obtype, aobasis.getMaxNprim(),
345 static_cast<int>(aobasis.getMaxL()), 1);
346 for (
Index i = 1; i < nthreads; ++i) {
347 engines[i] = engines[0];
350 std::exception_ptr eptr =
nullptr;
351 std::atomic<bool> any_nonnull_buffer{
false};
352#pragma omp parallel for schedule(dynamic)
353 for (
Index s1 = 0; s1 < aobasis.getNumofShells(); ++s1) {
356 libint2::Engine& engine = engines[thread_id];
357 const libint2::Engine::target_ptr_vec& buf = engine.results();
359 Index bf1 = shell2bf[s1];
360 Index n1 = shells[s1].size();
361 Index atom1 = shell2atom[s1];
363 for (
Index s2 : shellpair_list[s1]) {
365 engine.compute(shells[s1], shells[s2]);
379 if (buf[0] ==
nullptr || buf[3] ==
nullptr) {
385 any_nonnull_buffer.store(
true, std::memory_order_relaxed);
394 Index bf2 = shell2bf[s2];
395 Index n2 = shells[s2].size();
396 Index atom2 = shell2atom[s2];
404 for (
Index xyz = 0; xyz < 3; ++xyz) {
405 Eigen::Map<const MatrixLibInt> buf_mat1(buf[xyz], n1, n2);
406 result[atom1][xyz].block(bf1, bf2, n1, n2) += buf_mat1;
408 result[atom1][xyz].block(bf2, bf1, n2, n1) += buf_mat1.transpose();
411 Eigen::Map<const MatrixLibInt> buf_mat2(buf[3 + xyz], n1, n2);
412 result[atom2][xyz].block(bf1, bf2, n1, n2) += buf_mat2;
414 result[atom2][xyz].block(bf2, bf1, n2, n1) += buf_mat2.transpose();
429 eptr = std::current_exception();
435 std::rethrow_exception(eptr);
437 if (!any_nonnull_buffer.load() && aobasis.getNumofShells() > 0) {
445 throw std::runtime_error(
446 "computeOneBodyIntegralDerivatives: engine.results() returned a "
447 "null buffer for EVERY shell pair -- this libint2 build does not "
448 "actually support this operator's derivative integrals at "
449 "runtime, even though it may report LIBINT2_MAX_DERIV_ORDER >= 1 "
450 "for other operators. Rebuild libint2 with this operator's "
451 "derivative support enabled (--enable-1body=1) to use this "
462 const AOBasis& aobasis) {
463 return computeOneBodyIntegralDerivatives<libint2::Operator::overlap>(aobasis);
467 const AOBasis& aobasis) {
468 return computeOneBodyIntegralDerivatives<libint2::Operator::kinetic>(aobasis);
479 ThrowNoDerivativeSupport(
"ComputeOverlapDerivatives",
"--enable-1body=1");
482 ThrowNoDerivativeSupport(
"ComputeKineticDerivatives",
"--enable-1body=1");
537#if (LIBINT_INCLUDE_ERI2 >= 1)
539 const AOBasis& aobasis) {
540 Index natoms =
static_cast<Index>(aobasis.getFuncPerAtom().size());
542 std::vector<libint2::Shell> shells = aobasis.GenerateLibintBasis();
543 std::vector<Index> shell2bf = aobasis.getMapToBasisFunctions();
545 std::vector<Index> shell2atom;
546 shell2atom.reserve(aobasis.getNumofShells());
547 for (
Index s = 0; s < aobasis.getNumofShells(); ++s) {
548 shell2atom.push_back(aobasis.getShell(s).getAtomIndex());
551 Index nbf = aobasis.AOBasisSize();
552 std::vector<AOMatrixDerivative> result(natoms);
553 for (
Index a = 0; a < natoms; ++a) {
554 for (
Index xyz = 0; xyz < 3; ++xyz) {
555 result[a][xyz] = Eigen::MatrixXd::Zero(nbf, nbf);
559 std::vector<libint2::Engine> engines(nthreads);
561 libint2::Engine(libint2::Operator::coulomb, aobasis.getMaxNprim(),
562 static_cast<int>(aobasis.getMaxL()), 1);
563 engines[0].set(libint2::BraKet::xs_xs);
564 for (
Index i = 1; i < nthreads; ++i) {
565 engines[i] = engines[0];
568 std::exception_ptr eptr_coulmetric =
nullptr;
569 std::atomic<bool> any_nonnull_buffer_coulmetric{
false};
570#pragma omp parallel for schedule(dynamic)
571 for (
Index s1 = 0; s1 < aobasis.getNumofShells(); ++s1) {
574 const libint2::Engine::target_ptr_vec& buf = engine.results();
576 Index bf1 = shell2bf[s1];
577 Index n1 = shells[s1].size();
578 Index atom1 = shell2atom[s1];
583 for (
Index s2 = 0; s2 <= s1; ++s2) {
584 engine.compute2<libint2::Operator::coulomb, libint2::BraKet::xs_xs, 1>(
585 shells[s1], libint2::Shell::unit(), shells[s2],
586 libint2::Shell::unit());
592 if (buf[0] ==
nullptr || buf[3] ==
nullptr) {
595 any_nonnull_buffer_coulmetric.store(
true, std::memory_order_relaxed);
597 Index bf2 = shell2bf[s2];
598 Index n2 = shells[s2].size();
599 Index atom2 = shell2atom[s2];
602 Eigen::Map<const MatrixLibInt> buf_mat1(buf[xyz], n1, n2);
603 result[atom1][
xyz].block(bf1, bf2, n1, n2) += buf_mat1;
605 result[atom1][
xyz].block(bf2, bf1, n2, n1) += buf_mat1.transpose();
608 Eigen::Map<const MatrixLibInt> buf_mat2(buf[3 + xyz], n1, n2);
609 result[atom2][
xyz].block(bf1, bf2, n1, n2) += buf_mat2;
611 result[atom2][
xyz].block(bf2, bf1, n2, n1) += buf_mat2.transpose();
618 if (!eptr_coulmetric) {
619 eptr_coulmetric = std::current_exception();
624 if (eptr_coulmetric) {
625 std::rethrow_exception(eptr_coulmetric);
627 if (!any_nonnull_buffer_coulmetric.load() && aobasis.getNumofShells() > 0) {
630 throw std::runtime_error(
631 "ComputeCoulombMetricDerivatives: engine.results() returned a "
632 "null buffer for EVERY shell pair -- this libint2 build does not "
633 "actually support this operator's derivative integrals at "
634 "runtime, even though it may report LIBINT2_MAX_DERIV_ORDER >= 1 "
635 "for other operators. Rebuild libint2 with this operator's "
636 "derivative support enabled (--enable-eri2=1) to use this "
644 ThrowNoDerivativeSupport(
"ComputeCoulombMetricDerivatives",
705#if (LIBINT_INCLUDE_ERI3 >= 1)
707 const AOBasis& auxbasis,
const AOBasis& dftbasis) {
708 Index natoms =
static_cast<Index>(dftbasis.getFuncPerAtom().size());
715 std::vector<libint2::Shell> dftshells = dftbasis.GenerateLibintBasis();
716 std::vector<libint2::Shell> auxshells = auxbasis.GenerateLibintBasis();
717 std::vector<Index> shell2bf = dftbasis.getMapToBasisFunctions();
718 std::vector<Index> auxshell2bf = auxbasis.getMapToBasisFunctions();
720 std::vector<Index> dftshell2atom;
721 dftshell2atom.reserve(dftbasis.getNumofShells());
722 for (
Index s = 0; s < dftbasis.getNumofShells(); ++s) {
723 dftshell2atom.push_back(dftbasis.getShell(s).getAtomIndex());
725 std::vector<Index> auxshell2atom;
726 auxshell2atom.reserve(auxbasis.getNumofShells());
727 for (
Index s = 0; s < auxbasis.getNumofShells(); ++s) {
728 auxshell2atom.push_back(auxbasis.getShell(s).getAtomIndex());
731 Index n_dft_bf = dftbasis.AOBasisSize();
732 Index n_aux_bf = auxbasis.AOBasisSize();
734 std::vector<ThreeCenterDerivative> result(natoms);
735 for (
Index a = 0; a < natoms; ++a) {
737 result[a][
xyz] = std::vector<Eigen::MatrixXd>(
738 n_aux_bf, Eigen::MatrixXd::Zero(n_dft_bf, n_dft_bf));
742 std::vector<libint2::Engine> engines(nthreads);
743 engines[0] = libint2::Engine(
744 libint2::Operator::coulomb,
745 std::max(dftbasis.getMaxNprim(), auxbasis.getMaxNprim()),
746 static_cast<int>(std::max(dftbasis.getMaxL(), auxbasis.getMaxL())), 1);
747 engines[0].set(libint2::BraKet::xs_xx);
748 for (
Index i = 1; i < nthreads; ++i) {
749 engines[i] = engines[0];
752 std::exception_ptr eptr_3c =
nullptr;
753 std::atomic<bool> any_nonnull_buffer_3c{
false};
754#pragma omp parallel for schedule(dynamic)
755 for (
Index aux = 0; aux < auxbasis.getNumofShells(); ++aux) {
758 const libint2::Engine::target_ptr_vec& buf = engine.results();
760 const libint2::Shell& auxshell = auxshells[aux];
761 Index aux_start = auxshell2bf[aux];
762 Index atom_aux = auxshell2atom[aux];
764 for (
Index row = 0; row < dftbasis.getNumofShells(); ++row) {
765 const libint2::Shell& shell_row = dftshells[row];
766 Index row_start = shell2bf[row];
767 Index atom_row = dftshell2atom[row];
779 for (
Index col = 0; col < dftbasis.getNumofShells(); ++col) {
780 const libint2::Shell& shell_col = dftshells[col];
781 Index col_start = shell2bf[col];
782 Index atom_col = dftshell2atom[col];
785 .compute2<libint2::Operator::coulomb, libint2::BraKet::xs_xx, 1>(
786 auxshell, libint2::Shell::unit(), shell_col, shell_row);
792 if (buf[0] ==
nullptr || buf[3] ==
nullptr || buf[6] ==
nullptr) {
795 any_nonnull_buffer_3c.store(
true, std::memory_order_relaxed);
806 Eigen::Tensor<const double, 3, Eigen::RowMajor>
const>
807 result_aux(buf[xyz], auxshell.size(), shell_col.size(),
810 Eigen::Tensor<const double, 3, Eigen::RowMajor>
const>
811 result_col(buf[3 + xyz], auxshell.size(), shell_col.size(),
814 Eigen::Tensor<const double, 3, Eigen::RowMajor>
const>
815 result_row(buf[6 + xyz], auxshell.size(), shell_col.size(),
818 for (
size_t aux_c = 0; aux_c < auxshell.size(); ++aux_c) {
819 Index global_aux = aux_start +
static_cast<Index>(aux_c);
820 for (
size_t col_c = 0; col_c < shell_col.size(); ++col_c) {
821 for (
size_t row_c = 0; row_c < shell_row.size(); ++row_c) {
822 double val_aux = result_aux(aux_c, col_c, row_c);
823 double val_col = result_col(aux_c, col_c, row_c);
824 double val_row = result_row(aux_c, col_c, row_c);
825 Index r = row_start +
static_cast<Index>(row_c);
826 Index c = col_start +
static_cast<Index>(col_c);
827 result[atom_aux][
xyz][global_aux](r, c) += val_aux;
828 result[atom_col][
xyz][global_aux](r, c) += val_col;
829 result[atom_row][
xyz][global_aux](r, c) += val_row;
840 eptr_3c = std::current_exception();
846 std::rethrow_exception(eptr_3c);
848 if (!any_nonnull_buffer_3c.load() && auxbasis.getNumofShells() > 0 &&
849 dftbasis.getNumofShells() > 0) {
852 throw std::runtime_error(
853 "ComputeThreeCenterDerivatives: engine.results() returned a null "
854 "buffer for EVERY shell triple -- this libint2 build does not "
855 "actually support this operator's derivative integrals at "
856 "runtime, even though it may report LIBINT2_MAX_DERIV_ORDER >= 1 "
857 "for other operators. Rebuild libint2 with this operator's "
858 "derivative support enabled (--enable-eri3=1) to use this "
866 ThrowNoDerivativeSupport(
"ComputeThreeCenterDerivatives",
"--enable-eri3=1");
887 std::vector<Eigen::MatrixXd> tensor(
891 libint2::Engine engine(
892 libint2::Operator::coulomb,
894 static_cast<int>(std::max(dftbasis.
getMaxL(), auxbasis.
getMaxL())), 0);
895 engine.set(libint2::BraKet::xs_xx);
898 std::vector<Eigen::MatrixXd> block =
900 Index aux_start = auxshell2bf[aux];
901 for (
size_t i = 0; i < block.size(); ++i) {
902 tensor[aux_start +
static_cast<Index>(i)] = block[i];
954#if defined(LIBINT2_MAX_DERIV_ORDER) && LIBINT2_MAX_DERIV_ORDER >= 1 && \
955 (LIBINT_INCLUDE_ONEBODY >= 1)
957 const AOBasis& aobasis,
const QMMolecule& mol) {
958 Index natoms = mol.size();
960 std::vector<libint2::Shell> shells = aobasis.GenerateLibintBasis();
961 std::vector<Index> shell2bf = aobasis.getMapToBasisFunctions();
963 std::vector<Index> shell2atom;
964 shell2atom.reserve(aobasis.getNumofShells());
965 for (
Index s = 0; s < aobasis.getNumofShells(); ++s) {
966 shell2atom.push_back(aobasis.getShell(s).getAtomIndex());
969 Index nbf = aobasis.AOBasisSize();
970 std::vector<std::vector<AOMatrixDerivative>> result_thread(nthreads);
971 for (
Index t = 0; t < nthreads; ++t) {
972 result_thread[t].resize(natoms);
973 for (
Index a = 0; a < natoms; ++a) {
974 for (
Index xyz = 0; xyz < 3; ++xyz) {
975 result_thread[t][a][xyz] = Eigen::MatrixXd::Zero(nbf, nbf);
980 std::vector<libint2::Engine> engines(nthreads);
981 for (
Index i = 0; i < nthreads; ++i) {
983 libint2::Engine(libint2::Operator::nuclear, aobasis.getMaxNprim(),
984 static_cast<int>(aobasis.getMaxL()), 1);
993 std::exception_ptr eptr_nucattr =
nullptr;
994 std::atomic<bool> any_nonnull_buffer_nucattr{
false};
995#pragma omp parallel for schedule(dynamic)
996 for (
Index A = 0; A < natoms; ++A) {
999 libint2::Engine& engine = engines[thread_id];
1001 std::vector<libint2::Atom> single_atom(1);
1002 single_atom[0].atomic_number =
static_cast<int>(mol[A].getNuccharge());
1003 single_atom[0].x = mol[A].getPos().x();
1004 single_atom[0].y = mol[A].getPos().y();
1005 single_atom[0].z = mol[A].getPos().z();
1006 engine.set_params(libint2::make_point_charges(single_atom));
1008 const libint2::Engine::target_ptr_vec& buf = engine.results();
1010 for (
Index s1 = 0; s1 < aobasis.getNumofShells(); ++s1) {
1011 Index bf1 = shell2bf[s1];
1012 Index n1 = shells[s1].size();
1013 Index atom1 = shell2atom[s1];
1015 for (
Index s2 = 0; s2 <= s1; ++s2) {
1016 engine.compute(shells[s1], shells[s2]);
1030 if (buf[0] ==
nullptr || buf[3] ==
nullptr || buf[6] ==
nullptr) {
1033 any_nonnull_buffer_nucattr.store(
true, std::memory_order_relaxed);
1035 Index bf2 = shell2bf[s2];
1036 Index n2 = shells[s2].size();
1037 Index atom2 = shell2atom[s2];
1048 Eigen::Map<const MatrixLibInt> buf_mat1(buf[xyz], n1, n2);
1049 result_thread[thread_id][atom1][
xyz].block(bf1, bf2, n1, n2) +=
1052 result_thread[thread_id][atom1][
xyz].block(bf2, bf1, n2, n1) +=
1053 buf_mat1.transpose();
1056 Eigen::Map<const MatrixLibInt> buf_mat2(buf[3 + xyz], n1, n2);
1057 result_thread[thread_id][atom2][
xyz].block(bf1, bf2, n1, n2) +=
1060 result_thread[thread_id][atom2][
xyz].block(bf2, bf1, n2, n1) +=
1061 buf_mat2.transpose();
1064 Eigen::Map<const MatrixLibInt> buf_mat3(buf[6 + xyz], n1, n2);
1065 result_thread[thread_id][A][
xyz].block(bf1, bf2, n1, n2) +=
1068 result_thread[thread_id][A][
xyz].block(bf2, bf1, n2, n1) +=
1069 buf_mat3.transpose();
1077 if (!eptr_nucattr) {
1078 eptr_nucattr = std::current_exception();
1084 std::rethrow_exception(eptr_nucattr);
1086 if (!any_nonnull_buffer_nucattr.load() && natoms > 0 &&
1087 aobasis.getNumofShells() > 0) {
1104 throw std::runtime_error(
1105 "ComputeNuclearAttractionDerivatives: engine.results() returned "
1106 "a null buffer for EVERY shell pair and point charge -- this "
1107 "libint2 build does not actually support nuclear-attraction "
1108 "derivative integrals at runtime, even though it may report "
1109 "LIBINT2_MAX_DERIV_ORDER >= 1 for other operators. Rebuild "
1110 "libint2 with this operator's derivative support enabled "
1111 "(--enable-1body=1, which nuclear attraction is part of) to "
1112 "use this feature.");
1125 double total_norm_sq_nucattr = 0.0;
1126 for (
Index a = 0; a < natoms; ++a) {
1128 for (
Index t = 0; t < nthreads; ++t) {
1129 total_norm_sq_nucattr += result_thread[t][a][
xyz].squaredNorm();
1133 if (total_norm_sq_nucattr < 1.e-20 && natoms > 0 &&
1134 aobasis.getNumofShells() > 0) {
1135 throw std::runtime_error(
1136 "ComputeNuclearAttractionDerivatives: the assembled result is "
1137 "exactly zero everywhere, which is physically impossible for a "
1138 "real molecule -- this libint2 build likely returns valid but "
1139 "zero-filled buffers for this operator (rather than either "
1140 "computing it correctly or returning null, which the separate, "
1141 "earlier check in this function already handles). Rebuild "
1142 "libint2 with this operator's derivative support enabled "
1143 "(--enable-1body=1) to use this feature.");
1146 std::vector<AOMatrixDerivative> result(natoms);
1147 for (
Index a = 0; a < natoms; ++a) {
1149 result[a][
xyz] = Eigen::MatrixXd::Zero(nbf, nbf);
1150 for (
Index t = 0; t < nthreads; ++t) {
1151 result[a][
xyz] += result_thread[t][a][
xyz];
1160 ThrowNoDerivativeSupport(
"ComputeNuclearAttractionDerivatives",
1161 "--enable-1body=1");
1183#if defined(LIBINT2_MAX_DERIV_ORDER) && LIBINT2_MAX_DERIV_ORDER >= 1 && \
1184 (LIBINT_INCLUDE_ONEBODY >= 1) && (LIBINT_INCLUDE_ERI2 >= 1) && \
1185 (LIBINT_INCLUDE_ERI3 >= 1)
Container to hold Basisfunctions for all atoms.
Index AOBasisSize() const
std::vector< Index > getMapToBasisFunctions() const
Index getNumofShells() const
Index getMaxNprim() const
std::vector< libint2::Shell > GenerateLibintBasis() const
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > MatrixLibInt
std::vector< AOMatrixDerivative > ComputeNuclearAttractionDerivatives(const AOBasis &aobasis, const QMMolecule &mol)
std::vector< AOMatrixDerivative > ComputeKineticDerivatives(const AOBasis &aobasis)
std::vector< AOMatrixDerivative > ComputeCoulombMetricDerivatives(const AOBasis &aobasis)
std::vector< AOMatrixDerivative > ComputeOverlapDerivatives(const AOBasis &aobasis)
std::vector< ThreeCenterDerivative > ComputeThreeCenterDerivatives(const AOBasis &auxbasis, const AOBasis &dftbasis)
std::vector< Eigen::MatrixXd > ComputeThreeCenterIntegrals(const AOBasis &auxbasis, const AOBasis &dftbasis)
std::vector< Eigen::MatrixXd > ComputeAO3cBlock(const libint2::Shell &auxshell, const AOBasis &dftbasis, libint2::Engine &engine)
std::array< std::vector< Eigen::MatrixXd >, 3 > ThreeCenterDerivative
std::array< Eigen::MatrixXd, 3 > AOMatrixDerivative
bool HasLibint2DerivativeSupport()
Provides a means for comparing floating point numbers.