diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b5d1bfd4..e14b2a836 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,19 @@ include(GNUInstallDirs) include(CTest) message(STATUS "Building using CMake ${CMAKE_VERSION} Generator ${CMAKE_GENERATOR}") +# <<< General >>> + +### compiler-only + +### library-only +option_with_default(LIBINT2_ENABLE_PYTHON + "Build Python bindings (requires Python and Pybind11 and Eigen3)" OFF) +option_with_default(LIBINT2_PREFIX_PYTHON_INSTALL + "For LIBINT2_ENABLE_PYTHON=ON, whether to install the Python module in the Linux manner to CMAKE_INSTALL_PREFIX or to not install it. See target libint2-python-wheel for alternate installation in the Python manner to Python_EXECUTABLE's site-packages." OFF) + +option_with_print(LIBINT2_ENABLE_MPFR + "Use GNU MPFR library for high-precision testing (EXPERTS ONLY). Consumed at library build-time." OFF) +## next one defined by `include(CTest)` # <<< Which Integrals Classes, Which Derivative Levels >>> @@ -270,7 +283,7 @@ endif() check_include_file_cxx(stdint.h HAVE_STDINT_H) # limits.h? -if(cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) +if (cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) set(LIBINT_HAS_CXX11 1) endif() @@ -287,6 +300,35 @@ booleanize01(LIBINT_FLOP_COUNT) booleanize01(LIBINT_PROFILE) +################################## Dependencies ################################# + +# See notes at https://github.com/evaleev/libint/blob/master/INSTALL.md#prerequisites + +if (LIBINT2_ENABLE_MPFR) + # mpfr detected in CMakeLists.txt.export at appropriate time for library, but prechecking here + find_package(Multiprecision MODULE REQUIRED COMPONENTS gmpxx mpfr) + set(LIBINT_HAS_MPFR 1) +else() + find_package(Multiprecision MODULE REQUIRED COMPONENTS gmpxx) +endif() + +get_property(_loc TARGET Multiprecision::gmp PROPERTY LOCATION) +message(VERBOSE "${Cyan}Found GMP${ColourReset}: ${_loc}") +get_property(_loc TARGET Multiprecision::gmpxx PROPERTY LOCATION) +message(VERBOSE "${Cyan}Found GMPXX${ColourReset}: ${_loc}") +if (TARGET Multiprecision::mpfr) + get_property(_loc TARGET Multiprecision::mpfr PROPERTY LOCATION) + message(VERBOSE "${Cyan}Found MPFR${ColourReset}: ${_loc} (found version ${MPFR_VERSION})") +endif() + +find_package(Boost 1.57 REQUIRED) +if (TARGET Boost::headers) + set(LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS 1) +endif() + +# deferring find_package(Eigen3) to library (CMakeLists.txt.export) + + ################################## Main Project ################################# set(EXPORT_STAGE_DIR ${PROJECT_BINARY_DIR}/libint-${LIBINT_EXT_VERSION}) diff --git a/INSTALL.md b/INSTALL.md index d81a16165..efff9a4bf 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -58,6 +58,70 @@ The Libint build is structured into three parts: - (7) optional Python build alongside library or afterwards. optional testing requires library install +----------------------------------------------------------------------------- + +# Prerequisites + +(TARBALL): all but first `build_libint` line. + +| Task | Compilers | CMake[^3] | CMake generator[^20] | Py | Boost[^7] | Eigen | GMPXX[^13] | MPFR[^14] | Pybind11 | +| :------------------------------------------------------------------- | :---------------------: | :-------: | -------------------- | :-----: | :-------: | :-----: | :--------: | :-------: | :------: | +| build target `build_libint` | C++[^1] | 🔵[^4] | Ninja | – | 🔵[^8] | – | 🔵 | – | – | +| build target `library` | C++[^1], C | 🔵[^5] | Ninja | 🔸[^21] | – | – | – | – | – | +|   `-D LIBINT2_REQUIRE_CXX_API=ON` | C++[^1], C | 🔵[^5] | Ninja | 🔸[^21] | 🔸[^9] | 🔵[^11] | – | – | – | +|   `-D LIBINT2_ENABLE_FORTRAN=ON` | C++[^1], Fortran[^2], C | 🔵[^5] | Ninja | 🔵[^22] | – | – | – | – | – | +|   `-D LIBINT2_ENABLE_PYTHON=ON` | C++[^1], C | 🔵[^5] | Ninja | 🔵[^23] | 🔸[^9] | 🔵[^11] | – | – | 🔵[^24] | +| build project _consuming_ Libint2 library | +|  C interface (I/F), `Libint2::int2` | C++[^1] | 🔸[^6] | Ninja, Makefile | – | – | – | – | – | – | +|  C++11 header I/F, `Libint2::cxx` | C++[^1] | 🔸[^6] | Ninja, Makefile | – | 🔸[^10] | 🔵 | – | – | – | +|   `-D LIBINT2_ENABLE_MPFR=ON` | C++[^1] | 🔸[^6] | Ninja, Makefile | – | 🔸[^10] | 🔵 | 🔵 | 🔵 | – | +|  C++11 compiled I/F, `Libint2::int2-cxx` | C++[^1] | 🔸[^6] | Ninja, Makefile | – | 🔸[^10] | 🔵[^12] | – | – | – | +|  Fortran I/F, `Libint2::fortran` | Fortran[^2] | 🔸[^6] | Ninja, Makefile | – | | | – | – | – | + +* `🔵` required +* `🔸` required or recommended, but there's a path forward without +* `-` not involved + +[^1]: C++ compiler that supports C++11 standard. C++11 standard is the fourth most recent international standard for C++, hence most modern compilers support it fully. A common compiler flag is `-std=c++11`, which CMake will impose on the compilation. + +[^2]: Fortran 2003 compiler to enable Fortran bindings generation. + +[^3]: [CMake](https://cmake.org/) 3.16 or higher for library; 3.19 or higher for generator/compiler. Certain speciality targets (e.g., pylibint for Windows) may require higher. + +[^4]: Since Libint2 v2.9 TODO, the GNU toolchain has been replaced by CMake as the sole buildsystem for the Libint2 compiler, `build_libint`. See [update guide](#GNU-Autotools-Update-Guide). + +[^5]: Since Libint2 v2.9 TODO, the CMake buildsystem for the exported library has been reworked. See [update guide](#GNU-Autotools-Update-Guide). + +[^6]: Consuming an installed Libint2 library is simplest with CMake by employing `find_package(Libint2)` and `target_link_libraries(... Libint2::...)` commands. To facilitate consumption outside CMake, pkgconfig files are available for the C interface, and more could be provided. + +[^7]: [Boost](https://www.boost.org/) 1.57 or higher. Only header-only (no compiled libraries) components needed. + +[^8]: Building the Libint2 compiler needs several Boost components including MPL, Type Traits, and Preprocessor. A detectable system installation is required. (That is, "bundled Boost" is insufficient.) + +[^9]: Building the Libint2 library with C++11 API needs the Boost Preprocessor (PP) component. For the compiled C++11 interface, `Libint2::int2-cxx`, the PP is actually compiled against, but for the header-only target, `Libint2::cxx`, the PP only sets up the usage dependency. A system installation of Boost is sought, but if none suitable found, a bundled version of PP is installed within the Libint2 header namespace. + +[^10]: Consuming an installed Libint2 library through a C++11 interface requires the Boost Preprocessor (PP) component. Depending on the library *build* environment, a copy may have been bundled/vendored with the install at `CMAKE_INSTALL_PREFIX/CMAKE_INSTALL_INCLUDEDIR/libint2/boost/`. + +[^11]: Building the Libint2 library with C++11 API needs the header-only [Eigen](https://eigen.tuxfamily.org/) library. For the compiled C++11 interface, `Libint2::int-cxx`, Eigen is actually compiled against, but for the header-only target `Libint2::cxx`, Eigen only sets up the usage dependency. A detectable (either through Eigen3Config.cmake or through location-hinting) system installation is required. + +[^12]: Consuming an installed Libint2 library through the compiled C++11 interface, `Libint2::int2-cxx` requires [Eigen](https://eigen.tuxfamily.org/). It is *strongly* recommended that the same installation of Eigen be used both to build and consume the `Libint2::int2-cxx` target, especially as regards configuring BLAS and other backends. See `LIBINT2_LOCAL_Eigen3_FIND`. + +[^13]: Building the Libint2 compiler or building the Libint2 library with `-D LIBINT2_ENABLE_MPFR=ON` for high-precision testing requires the [GNU Multiple Precision (GMP)](https://gmplib.org/) library. A detectable system installation is required, and it must include C++ support. For Windows, the [MPIR](https://www.mpir.org) project satisfies the requirement. + +[^14]: Building against the Libint2 library for the purpose of high-precision testing with define `LIBINT_HAS_MPFR=1` requires the [MPFR](https://www.mpfr.org/) library. A detectable system installation is required. + +[^20]: Tested CMake generators are [Ninja](https://ninja-build.org/) or [GNU Make](https://www.gnu.org/software/make/). The use of Ninja is **strongly** recommended! + +[^21]: Python used for testing. + +[^22]: Python used to process files for Fortran binding. + +[^23]: Python headers and interpreter needed for Pybind11 module. + +[^24]: [Pybind11](https://github.com/pybind/pybind11) used to export Libint2 C++11 API into a Python module. If a system installation is not detected, the pybind11 source is fetched from GitHub. + +----------------------------------------------------------------------------- + # Configuring Libint * Notes @@ -70,41 +134,41 @@ The Libint build is structured into three parts: ### Which Integrals Classes, Which Derivative Levels (G) -* `ENABLE_ONEBODY` — G — Compile with support for up to N-th derivatives of 1-body integrals. Use -1 for OFF. [Default=0] -* `ENABLE_ERI` — G — Compile with support for up to N-th derivatives of 4-center electron repulsion integrals. Use -1 for OFF. [Default=0] -* `ENABLE_ERI3` — G — Compile with support for up to N-th derivatives of 3-center electron repulsion integrals. Use -1 for OFF. [Default=-1] -* `ENABLE_ERI2` — G — Compile with support for up to N-th derivatives of 2-center electron repulsion integrals. Use -1 for OFF. [Default=-1] -* `ENABLE_G12` — G — Compile with support for N-th derivatives of MP2-F12 energies with Gaussian factors. Use -1 for OFF. [Default=-1] -* `ENABLE_G12DKH` — G — Compile with support for N-th derivatives of DKH-MP2-F12 energies with Gaussian factors. Use -1 for OFF. [Default=-1] +* `ENABLE_ONEBODY` - G - Compile with support for up to N-th derivatives of 1-body integrals. Use -1 for OFF. [Default=0] +* `ENABLE_ERI` - G - Compile with support for up to N-th derivatives of 4-center electron repulsion integrals. Use -1 for OFF. [Default=0] +* `ENABLE_ERI3` - G - Compile with support for up to N-th derivatives of 3-center electron repulsion integrals. Use -1 for OFF. [Default=-1] +* `ENABLE_ERI2` - G - Compile with support for up to N-th derivatives of 2-center electron repulsion integrals. Use -1 for OFF. [Default=-1] +* `ENABLE_G12` - G - Compile with support for N-th derivatives of MP2-F12 energies with Gaussian factors. Use -1 for OFF. [Default=-1] +* `ENABLE_G12DKH` - G - Compile with support for N-th derivatives of DKH-MP2-F12 energies with Gaussian factors. Use -1 for OFF. [Default=-1] -* `DISABLE_ONEBODY_PROPERTY_DERIVS` — G — Disable geometric derivatives of 1-body property integrals (all but overlap, kinetic, elecpot). +* `DISABLE_ONEBODY_PROPERTY_DERIVS` - G - Disable geometric derivatives of 1-body property integrals (all but overlap, kinetic, elecpot). These derivatives are disabled by default to save compile time. Use OFF to enable. Note that the libtool build won't enable this- if forcibly enabled, build_libint balks. [Default=ON] -* `ENABLE_T1G12_SUPPORT` — G — Enable [Ti,G12] integrals when G12 integrals are enabled. Irrelevant when `ENABLE_G12=OFF`. Use OFF to disable. [Default=ON] +* `ENABLE_T1G12_SUPPORT` - G - Enable [Ti,G12] integrals when G12 integrals are enabled. Irrelevant when `ENABLE_G12=OFF`. Use OFF to disable. [Default=ON] ### Which Ordering Conventions (G) -* `LIBINT2_SHGAUSS_ORDERING` — G — Ordering for shells of solid harmonic Gaussians. [Default=standard] - * `standard` — standard ordering (-l, -l+1 ... l) - * `gaussian` — the Gaussian ordering (0, 1, -1, 2, -2, ... l, -l) +* `LIBINT2_SHGAUSS_ORDERING` - G - Ordering for shells of solid harmonic Gaussians. [Default=standard] + * `standard` - standard ordering (-l, -l+1 ... l) + * `gaussian` - the Gaussian ordering (0, 1, -1, 2, -2, ... l, -l) See [Solid Harmonic Ordering Scope and History](solid-harmonic-ordering-scope-and-history) -* `LIBINT2_CARTGAUSS_ORDERING` — G — Orderings for shells of cartesian Gaussians. [Default=standard] - * `standard` — standard ordering (xxx, xxy, xxz, xyy, xyz, xzz, yyy, ...) This is ordering of the Common Component Architecture (CCA) standard for molecular integral data exchange described in ["Components for Integral Evaluation in Quantum Chemistry", J. P. Kenny, C. L. Janssen, E. F. Valeev, and T. L. Windus, J. Comp. Chem. 29, 562 (2008)](http://dx.doi.org/10.1002/jcc.20815). - * `intv3` — intv3 ordering (yyy, yyz, yzz, zzz, xyy, xyz, xzz, xxy, xxz, xxx) This is used by IntV3, the default integral engine of [MPQC](https://github.com/evaleev/libint/wiki/www.mpqc.org). Use this to make Libint and IntV3 engines in MPQC interoperable. - * `gamess` — [GAMESS](http://www.msg.ameslab.gov/gamess/) ordering (xxx, yyy, zzz, xxy, xxz, yyx, yyz, zzx, zzy, xyz) - * `orca` — [ORCA](http://cec.mpg.de/forum/) ordering (hydrid between GAMESS and standard) - * `bagel` — [BAGEL](https://github.com/evaleev/libint/wiki/nubakery.org) axis-permuted version of intv3 (xxx, xxy, xyy, yyy, xxz, xyz, yyz, xzz, yzz, zzz) -* `LIBINT2_SHELL_SET` — G — Support computation of shell sets sets subject to these restrictions. [Default=standard] - * `standard` — standard ordering: +* `LIBINT2_CARTGAUSS_ORDERING` - G - Orderings for shells of cartesian Gaussians. [Default=standard] + * `standard` - standard ordering (xxx, xxy, xxz, xyy, xyz, xzz, yyy, ...) This is ordering of the Common Component Architecture (CCA) standard for molecular integral data exchange described in ["Components for Integral Evaluation in Quantum Chemistry", J. P. Kenny, C. L. Janssen, E. F. Valeev, and T. L. Windus, J. Comp. Chem. 29, 562 (2008)](http://dx.doi.org/10.1002/jcc.20815). + * `intv3` - intv3 ordering (yyy, yyz, yzz, zzz, xyy, xyz, xzz, xxy, xxz, xxx) This is used by IntV3, the default integral engine of [MPQC](https://github.com/evaleev/libint/wiki/www.mpqc.org). Use this to make Libint and IntV3 engines in MPQC interoperable. + * `gamess` - [GAMESS](http://www.msg.ameslab.gov/gamess/) ordering (xxx, yyy, zzz, xxy, xxz, yyx, yyz, zzx, zzy, xyz) + * `orca` - [ORCA](http://cec.mpg.de/forum/) ordering (hydrid between GAMESS and standard) + * `bagel` - [BAGEL](https://github.com/evaleev/libint/wiki/nubakery.org) axis-permuted version of intv3 (xxx, xxy, xyy, yyy, xxz, xyz, yyz, xzz, yzz, zzz) +* `LIBINT2_SHELL_SET` - G - Support computation of shell sets sets subject to these restrictions. [Default=standard] + * `standard` - standard ordering: for (ab|cd): l(a) >= l(b), l(c) >= l(d), l(a)+l(b) <= l(c)+l(d) for (b|cd): l(c) >= l(d) - * `orca` — ORCA ordering: + * `orca` - ORCA ordering: for (ab|cd): l(a) <= l(b), l(c) <= l(d), @@ -141,37 +205,98 @@ Note that options, docs, and CMake components are focused on the C++ interface, "CMake Error: Generator: execution of make failed". Throttle it to physical threads with `export CMAKE_BUILD_PARALLEL_LEVEL=N`. -* `WITH_MAX_AM` — G — Support Gaussians of angular momentum up to N. If ERI3 ints are enabled, specifing values for each derivative level as a semicolon-separated string also controls the AM of the paired centers. [Default=4] -* `WITH_OPT_AM` — G — Optimize maximally for up to angular momentum N (N <= WITH_MAX_AM). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `(WITH_MAX_AM/2)+1`] +* `WITH_MAX_AM` - G - Support Gaussians of angular momentum up to N. If ERI3 ints are enabled, specifing values for each derivative level as a semicolon-separated string also controls the AM of the paired centers. [Default=4] +* `WITH_OPT_AM` - G - Optimize maximally for up to angular momentum N (N <= WITH_MAX_AM). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `(WITH_MAX_AM/2)+1`] -* `MULTIPOLE_MAX_ORDER` — G — Maximum order of spherical multipole integrals. There is no maximum. [Default=4] +* `MULTIPOLE_MAX_ORDER` - G - Maximum order of spherical multipole integrals. There is no maximum. [Default=4] -* `WITH_ONEBODY_MAX_AM` — G — Support 1-body ints for Gaussians of angular momentum up to N. Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_MAX_AM`] -* `WITH_ONEBODY_OPT_AM` — G — Optimize 1-body ints maximally for up to angular momentum N (N <= max-am). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_OPT_AM`] +* `WITH_ONEBODY_MAX_AM` - G - Support 1-body ints for Gaussians of angular momentum up to N. Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_MAX_AM`] +* `WITH_ONEBODY_OPT_AM` - G - Optimize 1-body ints maximally for up to angular momentum N (N <= max-am). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_OPT_AM`] -* `WITH_ERI_MAX_AM` — G — Support 4-center ERIs for Gaussians of angular momentum up to N. Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_MAX_AM`] -* `WITH_ERI_OPT_AM` — G — Optimize 4-center ERIs maximally for up to angular momentum N (N <= max-am). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_OPT_AM`] +* `WITH_ERI_MAX_AM` - G - Support 4-center ERIs for Gaussians of angular momentum up to N. Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_MAX_AM`] +* `WITH_ERI_OPT_AM` - G - Optimize 4-center ERIs maximally for up to angular momentum N (N <= max-am). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_OPT_AM`] -* `WITH_ERI3_MAX_AM` — G — Support 3-center ERIs for Gaussians of angular momentum up to N. Can specify values for each derivative level as a semicolon-separated string. This option controls only the single fitting center; the paired centers use WITH_MAX_AM. [Default=-1 -> `WITH_MAX_AM`] -* `WITH_ERI3_OPT_AM` — G — Optimize 3-center ERIs maximally for up to angular momentum N (N <= max-am). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_OPT_AM`] -* `ERI3_PURE_SH` — G — Assume the 'unpaired' center of 3-center ERIs will be transformed to pure solid harmonics. [Default=OFF] +* `WITH_ERI3_MAX_AM` - G - Support 3-center ERIs for Gaussians of angular momentum up to N. Can specify values for each derivative level as a semicolon-separated string. This option controls only the single fitting center; the paired centers use WITH_MAX_AM. [Default=-1 -> `WITH_MAX_AM`] +* `WITH_ERI3_OPT_AM` - G - Optimize 3-center ERIs maximally for up to angular momentum N (N <= max-am). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_OPT_AM`] +* `ERI3_PURE_SH` - G - Assume the 'unpaired' center of 3-center ERIs will be transformed to pure solid harmonics. [Default=OFF] -* `WITH_ERI2_MAX_AM` — G — Support 2-center ERIs for Gaussians of angular momentum up to N. Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_MAX_AM`] -* `WITH_ERI2_OPT_AM` — G — Optimize 2-center ERIs maximally for up to angular momentum N (N <= max-am). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_OPT_AM`] -* `ERI2_PURE_SH` — G — Assume the 2-center ERIs will be transformed to pure solid harmonics. [Default=OFF] +* `WITH_ERI2_MAX_AM` - G - Support 2-center ERIs for Gaussians of angular momentum up to N. Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_MAX_AM`] +* `WITH_ERI2_OPT_AM` - G - Optimize 2-center ERIs maximally for up to angular momentum N (N <= max-am). Can specify values for each derivative level as a semicolon-separated string. [Default=-1 -> `WITH_OPT_AM`] +* `ERI2_PURE_SH` - G - Assume the 2-center ERIs will be transformed to pure solid harmonics. [Default=OFF] -* `WITH_G12_MAX_AM` — G — Support integrals for G12 methods of angular momentum up to N. No specification with per-derivative list. [Default=-1 -> `WITH_MAX_AM`] -* `WITH_G12_OPT_AM` — G — Optimize G12 integrals for up to angular momentum N (N <= max-am). No specification with per-derivative list. [Default=-1 `WITH_OPT_AM`] +* `WITH_G12_MAX_AM` - G - Support integrals for G12 methods of angular momentum up to N. No specification with per-derivative list. [Default=-1 -> `WITH_MAX_AM`] +* `WITH_G12_OPT_AM` - G - Optimize G12 integrals for up to angular momentum N (N <= max-am). No specification with per-derivative list. [Default=-1 `WITH_OPT_AM`] -* `WITH_G12DKH_MAX_AM` — G — Support integrals for relativistic G12 methods of angular momentum up to N. No specification with per-derivative list. [Default=-1 -> `WITH_MAX_AM`] -* `WITH_G12DKH_OPT_AM` — G — Optimize G12DKH integrals for up to angular momentum N (N <= max-am). No specification with per-derivative list. [Default=-1 `WITH_OPT_AM`] +* `WITH_G12DKH_MAX_AM` - G - Support integrals for relativistic G12 methods of angular momentum up to N. No specification with per-derivative list. [Default=-1 -> `WITH_MAX_AM`] +* `WITH_G12DKH_OPT_AM` - G - Optimize G12DKH integrals for up to angular momentum N (N <= max-am). No specification with per-derivative list. [Default=-1 `WITH_OPT_AM`] ### Compilers and Flags (G L) (TARBALL) + +### Build Library What (L) (TARBALL) + +* `LIBINT2_ENABLE_MPFR` - L - Use MPFR library to test Libint integrals in high precision (requires MPFR; experts only). [Default=OFF] +* `LIBINT2_LOCAL_Eigen3_INSTALL` - L - Install an exported target with hard-coded Eigen3 dependency paths. This is potentially useful and important when consuming the compiled C++11 interface library so that the Libint library build and Libint consumer build use the same Eigen3 installation & ABI. This is at most a convenience when consuming the header-only C++11 interface library. See `LIBINT2_LOCAL_Eigen3_FIND`. [Default=OFF] +* `LIBINT2_ENABLE_PYTHON` - L - Build Python bindings (requires Python and Eigen3; Boost and pybind11 recommended; [see prereq line](#prerequisites)). Can instead be enabled and built through separate CMake configuration after library build. [Default=OFF] + + +### Build Library How (G L) (TARBALL) + + +### Detecting Dependencies (G L C) (TARBALL) + +* `Python_EXECUTABLE` - L - Path to Python interpreter. +* `CMAKE_PREFIX_PATH` - G L - Set to list of root directories to look for external dependencies. [Standard CMake variable](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html) +* `BOOST_ROOT` - G L C - Prefix to installation location (`BOOST_ROOT/include/boost/` exists) +* `Boost_DIR` - G L C - Path to installation location of Boost's config file (`Boost_DIR/BoostConfig.cmake` exists) +* `CMAKE_DISABLE_FIND_PACKAGE_Boost` - L - When Boost required for C++11 Libint API, disable its detection, thereby forcing use of bundled Boost. Note that this (and other Boost-hinting variables) can affect what is installed [see here](#packagers). [Standard CMake variable](https://cmake.org/cmake/help/latest/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.html). [Default=OFF] +* `Eigen3_ROOT` - L C - Prefix to installation location (`Eigen3_ROOT/include/eigen3/Eigen/Core` exists) +* `EIGEN3_INCLUDE_DIR` - L C - Path to installation location of Eigen's header files (`EIGEN3_INCLUDE_DIR/include/eigen3` exists) +* `Eigen3_DIR` - L C - Path to installation location of Eigen's config file (`Eigen3_DIR/Eigen3Config.cmake` exists) +* `Multiprecision_ROOT` - G L - Prefix to installation location (`Multiprecision_ROOT/` contains headers like gmp.h, gmpxx.h, mpfr.h) +* `LIBINT2_LOCAL_Eigen3_FIND` - C - Set to `ON` before `find_package(Libint2)` to load the Eigen3 target exported by `LIBINT2_LOCAL_Eigen3_INSTALL=ON` if Libint library built locally. [Default=OFF] + +* Hint dependency locations all at the same installation prefix: + + ``` + -D CMAKE_PREFIX_PATH="/path/to/installation/prefix" + ``` + + ``` + -D CMAKE_PREFIX_PATH="/home/miniconda/envs/l2dev" + ``` + +* Hint dependency locations all at different installation prefixes: + + ``` + -D CMAKE_PREFIX_PATH="/home/miniconda/envs/onlyboost;/home/miniconda/envs/onlygmp;/home/miniconda/envs/onlyeigen" + ``` + +* Hint dependency locations targeted by package: + + ``` + -D BOOST_ROOT="/home/miniconda/envs/onlyboost" + -D Multiprecision_ROOT="/home/miniconda/envs/onlygmp" + -D Eigen3_ROOT="/home/miniconda/envs/onlyeigen" + ``` + +* Hint dependency locations targeted by Config.cmake (most CMake-like): + + ``` + -D Eigen3_DIR="/home/miniconda/envs/onlyeigen/share/eigen3/cmake" + -D Boost_DIR="/home/miniconda/envs/onlyboost/lib/cmake/Boost-1.73.0" + ``` + + +### Install Paths (L) (TARBALL) + +* `LIBINT2_PREFIX_PYTHON_INSTALL` - L - For `LIBINT2_ENABLE_PYTHON=ON`, whether to install the Python module in the Linux manner to `CMAKE_INSTALL_PREFIX` or to not install it. Note: not a path; the installation sub-path below `CMAKE_INSTALL_PREFIX` is determined by querying `Python_EXECUTABLE`. For alternate installation in the Python manner to `Python_EXECUTABLE`'s site-packages, see target libint2-python-wheel. [Default=OFF] + + ### Miscellaneous (G L) -* `LIBINT2_REALTYPE` — L — Specifies the floating-point data type used by the library. [Default=double] +* `LIBINT2_REALTYPE` - L - Specifies the floating-point data type used by the library. [Default=double] By overriding the default it is possible to customize the library to use a lower-precision representation (which typically results in a performance boost) and/or to generate [SIMD](http://en.wikipedia.org/wiki/SIMD) vectorized code. *N.B. C++11 interface cannot be currently used with SIMD vectorized libraries!* The following values are valid: * `double` -- double-precision floating-point representation of a real number; * `float` -- single-precision floating-point number; @@ -185,21 +310,21 @@ Note that options, docs, and CMake components are focused on the C++ interface, **N.B.** It is also possible to use real vector types of [Agner Fog's vectorclass library](http://www.agner.org/optimize/#vectorclass), e.g. `Vec4d` and `Vec8f` for AVX. To use this library you need to add this to CPPFLAGS or CXXFLAGS: `-Ipath_to_vectorclass -DLIBINT2_HAVE_AGNER_VECTORCLASS` . On macOS, we only succeeded in using this library with a recent GNU C++ compiler, not with Clang. Not tested after CMake rework. -* `LIBINT_USER_DEFINED_REAL_INCLUDES` — L — Additional #includes necessary to use the real type. [Defaults=none] -* `LIBINT_CONTRACTED_INTS` — G — Turn on support for contracted integrals. [Default=ON] -* `LIBINT_ERI_STRATEGY` — G — Compute ERIs using the following strategy (experts only). (0 for OS, 1 for HGP, 2 for HL). [Default=1] -* `LIBINT_USE_COMPOSITE_EVALUATORS` — G — Libint will use composite evaluators (i.e. every evaluator will compute one integral type only). [Default=ON] -* `LIBINT_SINGLE_EVALTYPE` — G — Generate single evaluator type (i.e. all tasks use the same evaluator). OFF is NYI [Default=ON] -* `LIBINT_ENABLE_UNROLLING` — G — Unroll shell sets into integrals (will unroll shell sets larger than N) (0 for never, N for N, 1000000000 for always). [Default=100] -* `LIBINT_ALIGN_SIZE` — G — If posix_memalign is available, this will specify alignment of Libint data, in units of sizeof(LIBINT2_REALTYPE). Default is to use built-in heuristics: system-determined for vectorization off (default) or veclen * sizeof(LIBINT2_REALTYPE) for vectorization on. (experts only). [Default=0] -* `LIBINT_GENERATE_FMA` — G — Generate FMA (fused multiply-add) instructions (to benefit must have FMA-capable hardware and compiler). [Default=OFF] -* `LIBINT_ENABLE_GENERIC_CODE` — G — Use manually-written generic code. [Default=OFF] -* `LIBINT_API_PREFIX` — G — Prepend this string to every name in the library API (except for the types). [Default=OFF] -* `LIBINT_VECTOR_LENGTH` — G — Compute integrals in vectors of length N. [Default=OFF] -* `LIBINT_VECTOR_METHOD` — G — Specifies how to vectorize integrals. Irrelevant when `LIBINT_VECTOR_LENGTH=OFF. Allowed values are 'block' and 'line'. [Default=block] -* `LIBINT_ACCUM_INTS` — G — Accumulate integrals to the buffer, rather than copy (OFF for copy, ON for accum). [Default=OFF] -* `LIBINT_FLOP_COUNT` — G — Support (approximate) FLOP counting by the library. (Generated code will require C++11!). [Default=OFF] -* `LIBINT_PROFILE` — G — Turn on profiling instrumentation of the library. (Generated code will require C++11!). [Default=OFF] +* `LIBINT_USER_DEFINED_REAL_INCLUDES` - L - Additional #includes necessary to use the real type. [Defaults=none] +* `LIBINT_CONTRACTED_INTS` - G - Turn on support for contracted integrals. [Default=ON] +* `LIBINT_ERI_STRATEGY` - G - Compute ERIs using the following strategy (experts only). (0 for OS, 1 for HGP, 2 for HL). [Default=1] +* `LIBINT_USE_COMPOSITE_EVALUATORS` - G - Libint will use composite evaluators (i.e. every evaluator will compute one integral type only). [Default=ON] +* `LIBINT_SINGLE_EVALTYPE` - G - Generate single evaluator type (i.e. all tasks use the same evaluator). OFF is NYI [Default=ON] +* `LIBINT_ENABLE_UNROLLING` - G - Unroll shell sets into integrals (will unroll shell sets larger than N) (0 for never, N for N, 1000000000 for always). [Default=100] +* `LIBINT_ALIGN_SIZE` - G - If posix_memalign is available, this will specify alignment of Libint data, in units of sizeof(LIBINT2_REALTYPE). Default is to use built-in heuristics: system-determined for vectorization off (default) or veclen * sizeof(LIBINT2_REALTYPE) for vectorization on. (experts only). [Default=0] +* `LIBINT_GENERATE_FMA` - G - Generate FMA (fused multiply-add) instructions (to benefit must have FMA-capable hardware and compiler). [Default=OFF] +* `LIBINT_ENABLE_GENERIC_CODE` - G - Use manually-written generic code. [Default=OFF] +* `LIBINT_API_PREFIX` - G - Prepend this string to every name in the library API (except for the types). [Default=OFF] +* `LIBINT_VECTOR_LENGTH` - G - Compute integrals in vectors of length N. [Default=OFF] +* `LIBINT_VECTOR_METHOD` - G - Specifies how to vectorize integrals. Irrelevant when `LIBINT_VECTOR_LENGTH=OFF. Allowed values are 'block' and 'line'. [Default=block] +* `LIBINT_ACCUM_INTS` - G - Accumulate integrals to the buffer, rather than copy (OFF for copy, ON for accum). [Default=OFF] +* `LIBINT_FLOP_COUNT` - G - Support (approximate) FLOP counting by the library. (Generated code will require C++11!). [Default=OFF] +* `LIBINT_PROFILE` - G - Turn on profiling instrumentation of the library. (Generated code will require C++11!). [Default=OFF] # GNU Autotools Update Guide @@ -257,6 +382,16 @@ Note that options, docs, and CMake components are focused on the C++ interface, * `--disable-1body-property-derivs` --> `-D DISABLE_ONEBODY_PROPERTY_DERIVS=ON` +* `--enable-mpfr` --> assumed present --> `-D ENABLE_MPFR=ON` --> `-D LIBINT2_ENABLE_MPFR=ON` + +* `ENV(CPPFLAGS)=-I/path/to/boost/includes` --> `-D BOOST_ROOT=/path/to/boost/prefix` + +* `-D LIBINT2_PYTHON=ON` --> `-D LIBINT2_ENABLE_PYTHON=ON` +* `-D LIBINT_USE_BUNDLED_BOOST=ON` --> `-D CMAKE_DISABLE_FIND_PACKAGE_Boost=ON` (standard CMake variable) +* `--with-boost` & `--with-boost-libdir` --> see `BOOST_ROOT` & `Boost_DIR` +* `-D LIBINT_LOCAL_Eigen3_INSTALL` --> `-D LIBINT2_LOCAL_Eigen3_INSTALL` +* `-D LIBINT_LOCAL_Eigen3_FIND` --> `-D LIBINT2_LOCAL_Eigen3_FIND` + * Defunct as non-CMake-like: `--build`, `--host`, `--target`, * `--with-api-prefix=pfx` --> `-D LIBINT_API_PREFIX=pfx` diff --git a/cmake/libint2-config.cmake.in b/cmake/libint2-config.cmake.in index 99cfc6d2b..4061ef5c3 100644 --- a/cmake/libint2-config.cmake.in +++ b/cmake/libint2-config.cmake.in @@ -48,6 +48,27 @@ set(L2 Libint2) # NameSpace set(Libint2_EXT_VERSION "@LIBINT_EXT_VERSION@") +# make detectable the various cmake modules exported alongside +# * prepend to trump any pre-target FindEigen3.cmake modules lying around +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +# check library language component +include(CMakeFindDependencyMacro) + + if(NOT TARGET Eigen3::Eigen) + find_dependency(Eigen3 REQUIRED) + endif() + + if (@LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS@) # LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS + # Boost headers _not_ unpacked to within `include/libint2/` + if (NOT TARGET Boost::headers) + find_dependency(Boost 1.57 REQUIRED) + endif() + else() + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Boost detected. satisfied by headers bundled with ${L2} distribution") + endif() + endif() # check orderings, AM, & derivatives components # * LIBINT2_SHGAUSS_ORDERING = @LIBINT2_SHGAUSS_ORDERING@ diff --git a/cmake/modules/FindEigen3.cmake b/cmake/modules/FindEigen3.cmake new file mode 100644 index 000000000..00b2aa30a --- /dev/null +++ b/cmake/modules/FindEigen3.cmake @@ -0,0 +1,134 @@ +# FindEigen3.cmake +# ---------------- +# +# Eigen3 cmake module to wrap Eigen3 suitable for Libint2. Copied from Eigen v3.4.0 source and modified as follows: +# * Added `NO_CMAKE_PACKAGE_REGISTRY` to `find_package(Eigen3 ...)` to avoid issues with wiped build +# directory when looking for installed eigen. Eigen3 registers its *build* tree with the user package registry. +# * Added `LIBINT2_LOCAL_Eigen3_FIND` block to forcibly load hard-coded Eigen3 location detected during Libint2 library build. +# * Move default Eigen3_FIND_VERSION_* from 2.91.0 to 3.0.0 so that it doesn't reject Eigen v3 installations. +# +# - Try to find Eigen3 lib +# +# This module supports requiring a minimum version, e.g. you can do +# find_package(Eigen3 3.1.2) +# to require version 3.1.2 or newer of Eigen3. +# +# Once done this will define +# +# EIGEN3_FOUND - system has eigen lib with correct version +# EIGEN3_INCLUDE_DIR - the eigen include directory +# EIGEN3_VERSION - eigen version +# +# and the following imported target: +# +# Eigen3::Eigen - The header-only Eigen library +# +# This module reads hints about search locations from +# the following environment variables: +# +# EIGEN3_ROOT +# EIGEN3_ROOT_DIR + +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# Redistribution and use is allowed according to the terms of the 2-clause BSD license. + +if(NOT Eigen3_FIND_VERSION) + if(NOT Eigen3_FIND_VERSION_MAJOR) + set(Eigen3_FIND_VERSION_MAJOR 3) + endif() + if(NOT Eigen3_FIND_VERSION_MINOR) + set(Eigen3_FIND_VERSION_MINOR 0) + endif() + if(NOT Eigen3_FIND_VERSION_PATCH) + set(Eigen3_FIND_VERSION_PATCH 0) + endif() + + set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") +endif() + +macro(_eigen3_check_version) + file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) + + string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") + set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") + set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") + set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") + + set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) + if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK FALSE) + else() + set(EIGEN3_VERSION_OK TRUE) + endif() + + if(NOT EIGEN3_VERSION_OK) + + message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " + "but at least version ${Eigen3_FIND_VERSION} is required") + endif() +endmacro() + +if (LIBINT2_LOCAL_Eigen3_FIND) + include("${CMAKE_CURRENT_LIST_DIR}/libint2-targets-eigen3.cmake") + + if (TARGET Libint2::Eigen) + get_property(_EIGEN3_INCLUDE_DIRS TARGET Libint2::Eigen PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + list(GET _EIGEN3_INCLUDE_DIRS 0 EIGEN3_INCLUDE_DIR) + + _eigen3_check_version() + set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) + set(Eigen3_FOUND ${EIGEN3_VERSION_OK}) + + add_library(Eigen3::Eigen ALIAS Libint2::Eigen) + else() + message(STATUS "Eigen3 exact installation detected/used by Libint library build requested " + "from ${CMAKE_CURRENT_LIST_DIR}/libint2-targets-eigen3.cmake but failed.") + endif() + +elseif (EIGEN3_INCLUDE_DIR) + + # in cache already + _eigen3_check_version() + set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) + set(Eigen3_FOUND ${EIGEN3_VERSION_OK}) + +else () + + # search first if an Eigen3Config.cmake is available in the system, + # if successful this would set EIGEN3_INCLUDE_DIR and the rest of + # the script will work as usual + find_package(Eigen3 ${Eigen3_FIND_VERSION} NO_MODULE QUIET NO_CMAKE_PACKAGE_REGISTRY) + + if(NOT EIGEN3_INCLUDE_DIR) + find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library + HINTS + ENV EIGEN3_ROOT + ENV EIGEN3_ROOT_DIR + PATHS + ${CMAKE_INSTALL_PREFIX}/include + ${KDE4_INCLUDE_DIR} + PATH_SUFFIXES eigen3 eigen + ) + endif() + + if(EIGEN3_INCLUDE_DIR) + _eigen3_check_version() + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) + + mark_as_advanced(EIGEN3_INCLUDE_DIR) + +endif() + +if(EIGEN3_FOUND AND NOT TARGET Eigen3::Eigen) + add_library(Eigen3::Eigen INTERFACE IMPORTED) + set_target_properties(Eigen3::Eigen PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${EIGEN3_INCLUDE_DIR}") +endif() + diff --git a/cmake/modules/FindMultiprecision.cmake b/cmake/modules/FindMultiprecision.cmake new file mode 100644 index 000000000..7bca2bd42 --- /dev/null +++ b/cmake/modules/FindMultiprecision.cmake @@ -0,0 +1,329 @@ +# FindMultiprecision.cmake +# ------------------------ +# +# GMP, GMP++, MPIR, MPFR CMake module for Unix and Windows. +# +# Built upon FindMPFR.cmake by +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2010 Jitse Niesen, +# Copyright (c) 2015 Jack Poulson, +# Redistribution and use is allowed according to the terms of the BSD license. +# +# +# This module sets the following variables in your project: +# +# :: +# +# Multiprecision_FOUND - true if all required components of MPFR found on the system with correct version +# MPFR_VERSION - MPFR version if mpfr library detected +# +# +# Available components: +# +# :: +# +# gmp - search for at least Multiprecision::gmp target +# gmpxx - search for at least Multiprecision::gmpxx target +# mpfr - search for at least Multiprecision::mpfr target +# +# +# Exported targets: +# +# :: +# +# If no components are requested, this module defines at least the following +# :prop_tgt:`IMPORTED` target. :: +# +# Multiprecision::gmp - gmp.h and GMP library +# +# Depending on components requested, this module defines up to the following +# :prop_tgt:`IMPORTED` targets. :: +# +# Multiprecision::gmp - gmp.h and C GMP library +# Multiprecision::mpfr - mpfr.h and MPFR library (needs Multiprecision::gmp) +# Multiprecision::gmpxx - gmpxx.h and C++ GMP library (needs Multiprecision::gmp) +# +# +# Suggested usage: +# +# :: +# +# find_package(Multiprecision) +# find_package(Multiprecision 4.0.0 REQUIRED COMPONENTS mpfr) +# +# +# The following variables can be set to guide the search for this package: +# +# :: +# +# Multiprecision_ROOT - CMake variable, set to root directory of this package +# CMAKE_PREFIX_PATH - CMake variable, set to root directory of this package + +# if no components given, act like a FindGMP +list(LENGTH Multiprecision_FIND_COMPONENTS _lcomp) +if (_lcomp EQUAL 0) + list(APPEND Multiprecision_FIND_COMPONENTS gmp) +endif() + +if(WIN32) + list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 ".dll") +endif() + +find_path( + MPFR_INCLUDE + NAMES + mpfr.h + PATHS + $ENV{GMPDIR} + $ENV{MPFRDIR} + ${INCLUDE_INSTALL_DIR} + ) + +# Set MPFR_FIND_VERSION to 1.0.0 if no minimum version is specified +if(NOT MPFR_FIND_VERSION) + if(NOT MPFR_FIND_VERSION_MAJOR) + set(MPFR_FIND_VERSION_MAJOR 1) + endif() + if(NOT MPFR_FIND_VERSION_MINOR) + set(MPFR_FIND_VERSION_MINOR 0) + endif() + if(NOT MPFR_FIND_VERSION_PATCH) + set(MPFR_FIND_VERSION_PATCH 0) + endif() + set(MPFR_FIND_VERSION + "${MPFR_FIND_VERSION_MAJOR}.${MPFR_FIND_VERSION_MINOR}.${MPFR_FIND_VERSION_PATCH}") +endif() + +if(MPFR_INCLUDE) + # Query MPFR_VERSION + file(READ "${MPFR_INCLUDE}/mpfr.h" _mpfr_version_header) + + string(REGEX MATCH "define[ \t]+MPFR_VERSION_MAJOR[ \t]+([0-9]+)" + _mpfr_major_version_match "${_mpfr_version_header}") + set(MPFR_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+MPFR_VERSION_MINOR[ \t]+([0-9]+)" + _mpfr_minor_version_match "${_mpfr_version_header}") + set(MPFR_MINOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+MPFR_VERSION_PATCHLEVEL[ \t]+([0-9]+)" + _mpfr_patchlevel_version_match "${_mpfr_version_header}") + set(MPFR_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}") + + set(MPFR_VERSION + ${MPFR_MAJOR_VERSION}.${MPFR_MINOR_VERSION}.${MPFR_PATCHLEVEL_VERSION}) + + # Check whether found version exceeds minimum required + if(${MPFR_VERSION} VERSION_LESS ${MPFR_FIND_VERSION}) + set(MPFR_VERSION_OK FALSE) + message(STATUS "MPFR version ${MPFR_VERSION} found in ${MPFR_INCLUDE}, " + "but at least version ${MPFR_FIND_VERSION} is required") + else() + set(MPFR_VERSION_OK TRUE) + endif() +endif() + +find_library( + MPFR_LIBRARY + NAMES + mpfr + PATHS + $ENV{GMPDIR} + $ENV{MPFRDIR} + ${LIB_INSTALL_DIR} + PATH_SUFFIXES + bin + ${MPFR_ROOT}/bin + ) + +find_path( + GMP_INCLUDE + NAMES + gmp.h + PATHS + $ENV{GMPDIR} + $ENV{MPFRDIR} + ${INCLUDE_INSTALL_DIR} + ) + +find_library( + GMP_LIBRARY + NAMES + gmp + PATHS + $ENV{GMPDIR} + $ENV{MPFRDIR} + ${LIB_INSTALL_DIR} + PATH_SUFFIXES + bin + ${MPFR_ROOT}/bin + ) + +find_path( + GMPXX_INCLUDE + NAMES + gmpxx.h + PATHS + $ENV{GMPDIR} + $ENV{MPFRDIR} + ${INCLUDE_INSTALL_DIR} + ) + +find_library( + GMPXX_LIBRARY + NAMES + gmpxx + gmp # gmp.dll on Win c-f conda package contains cxx (actually a copy of mpir, a drop-in replacement for gmp) + PATHS + $ENV{GMPDIR} + $ENV{MPFRDIR} + ${LIB_INSTALL_DIR} + PATH_SUFFIXES + bin + ${MPFR_ROOT}/bin + ) + +if (CMAKE_VERSION VERSION_GREATER 3.15) + message(VERBOSE "GMP inc=${GMP_INCLUDE} lib=${GMP_LIBRARY}") + message(VERBOSE "GMPXX inc=${GMPXX_INCLUDE} lib=${GMPXX_LIBRARY}") + message(VERBOSE "MPFR inc=${MPFR_INCLUDE} lib=${MPFR_LIBRARY}") +endif() + +if (GMP_INCLUDE AND GMP_LIBRARY) + set(Multiprecision_gmp_FOUND 1) # Multiprecision::gmp + + if (MPFR_INCLUDE AND MPFR_LIBRARY AND MPFR_VERSION_OK) + set(Multiprecision_mpfr_FOUND 1) # Multiprecision::mpfr + endif() + + if (GMPXX_INCLUDE AND GMPXX_LIBRARY) + set(Multiprecision_gmpxx_FOUND 1) # Multiprecision::gmpxx + endif() +endif() + +# thanks, https://stackoverflow.com/a/9328525 +function(dump_cmake_variables) + get_cmake_property(_variableNames VARIABLES) + list (SORT _variableNames) + set(founds "") + foreach (_variableName ${_variableNames}) + if (ARGV0) + unset(MATCHED) + string(REGEX MATCH ${ARGV0} MATCHED ${_variableName}) + if (NOT MATCHED) + continue() + endif() + if (NOT ${${_variableName}}) + continue() + endif() + endif() + list(APPEND founds ${CMAKE_MATCH_1}) + endforeach() + message(STATUS "${ARGV1}${founds}") +endfunction() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "FindMultiprecision components requested: ${Multiprecision_FIND_COMPONENTS}") + dump_cmake_variables("^Multiprecision_([A-Za-z0-9_]+)_FOUND$" "FindMultiprecision components found: ") +endif() + +include(FindPackageHandleStandardArgs) +set(Multiprecision_FOUND 1) +check_required_components(Multiprecision) +find_package_handle_standard_args( + Multiprecision + REQUIRED_VARS # can be removed upon CMake 3.18 + GMP_LIBRARY + GMP_INCLUDE + Multiprecision_FOUND + VERSION_VAR MPFR_VERSION + HANDLE_COMPONENTS + ) + +if(WIN32) + string(REPLACE ".lib" ".dll" GMP_LIBRARY_DLL "${GMP_LIBRARY}") + string(REPLACE ".lib" ".dll" GMPXX_LIBRARY_DLL "${GMPXX_LIBRARY}") + string(REPLACE ".lib" ".dll" MPFR_LIBRARY_DLL "${MPFR_LIBRARY}") +endif() + +# now that `find_package(Multiprecision COMPONENTS ...)` will succeed, create targets +if ((Multiprecision_gmp_FOUND EQUAL 1) AND NOT TARGET Multiprecision::gmp) + if (EXISTS "${GMP_LIBRARY_DLL}") + add_library(Multiprecision::gmp SHARED IMPORTED) + set_target_properties( + Multiprecision::gmp + PROPERTIES + IMPORTED_LOCATION "${GMP_LIBRARY_DLL}" + IMPORTED_IMPLIB "${GMP_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + ) + else() + add_library(Multiprecision::gmp UNKNOWN IMPORTED) + set_target_properties( + Multiprecision::gmp + PROPERTIES + IMPORTED_LOCATION "${GMP_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + ) + endif() +endif() + +if ((Multiprecision_mpfr_FOUND EQUAL 1) AND NOT TARGET Multiprecision::mpfr) + if (EXISTS "${MPFR_LIBRARY_DLL}") + add_library(Multiprecision::mpfr SHARED IMPORTED) + set_target_properties( + Multiprecision::mpfr + PROPERTIES + IMPORTED_LOCATION "${MPFR_LIBRARY_DLL}" + IMPORTED_IMPLIB "${MPFR_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${MPFR_INCLUDE}" + INTERFACE_LINK_LIBRARIES Multiprecision::gmp + ) + else() + add_library(Multiprecision::mpfr UNKNOWN IMPORTED) + set_target_properties( + Multiprecision::mpfr + PROPERTIES + IMPORTED_LOCATION "${MPFR_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${MPFR_INCLUDE}" + INTERFACE_LINK_LIBRARIES Multiprecision::gmp + ) + endif() +endif() + +if ((Multiprecision_gmpxx_FOUND EQUAL 1) AND NOT TARGET Multiprecision::gmpxx) + if (EXISTS "${GMPXX_LIBRARY_DLL}") + add_library(Multiprecision::gmpxx SHARED IMPORTED) + set_target_properties( + Multiprecision::gmpxx + PROPERTIES + IMPORTED_LOCATION "${GMPXX_LIBRARY_DLL}" + IMPORTED_IMPLIB "${GMPXX_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GMPXX_INCLUDE}" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + INTERFACE_LINK_LIBRARIES Multiprecision::gmp + ) + else() + add_library(Multiprecision::gmpxx UNKNOWN IMPORTED) + set_target_properties( + Multiprecision::gmpxx + PROPERTIES + IMPORTED_LOCATION "${GMPXX_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GMPXX_INCLUDE}" + IMPORTED_LINK_INTERFACE_LANGUAGES "CXX" + INTERFACE_LINK_LIBRARIES Multiprecision::gmp + ) + endif() +endif() + diff --git a/cmake/modules/int_checkboost.cmake b/cmake/modules/int_checkboost.cmake new file mode 100644 index 000000000..016a788c1 --- /dev/null +++ b/cmake/modules/int_checkboost.cmake @@ -0,0 +1,27 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0075 NEW) # support CMAKE_REQUIRED_LIBRARIES + +include(CMakePushCheckState) + +cmake_push_check_state() +list(APPEND CMAKE_REQUIRED_LIBRARIES Boost::headers) +#list(APPEND CMAKE_REQUIRED_FLAGS "-std=c++11") # set CMAKE_CXX_STANDARD and 0067 NEW ? + +check_cxx_source_compiles(" +#include + +int main(void) { +#if not BOOST_PP_VARIADICS // no variadic macros? your compiler is out of date! (should not be possible since variadic macros are part of C++11) +# error \"your compiler does not provide variadic macros (but does support C++11), something is seriously broken, please create an issue at https://github.com/evaleev/libint/issues\" +#endif + return 0; +} +" + _boost_pp_variadics) + +if (NOT _boost_pp_variadics) + message(FATAL_ERROR "BOOST_PP_VARIADICS is oddly missing from detected installation") +endif() + +cmake_pop_check_state() +cmake_policy(POP) diff --git a/include/libint2/config.h.cmake.in b/include/libint2/config.h.cmake.in index 92e439a1b..c9c871ec5 100644 --- a/include/libint2/config.h.cmake.in +++ b/include/libint2/config.h.cmake.in @@ -234,6 +234,13 @@ /* C++ compiler allows template with default params as template template parameter (check is NYI) */ #undef CXX_ALLOWS_DEFPARAMTEMPLATE_AS_TEMPTEMPPARAM +/* define if Eigen library is available. (not implemented for CMake) */ +/* #cmakedefine LIBINT_HAS_EIGEN @LIBINT_HAS_EIGEN@ */ +#undef LIBINT_HAS_EIGEN + +/* define if system-wide Boost.Preprocessor is available */ +#cmakedefine LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS @LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS@ + /* Known orderings of cartesian Gaussians */ @@ -262,6 +269,9 @@ /* have stdint.h ? */ #cmakedefine HAVE_STDINT_H @HAVE_STDINT_H@ +/* have MPFR library ? */ +#cmakedefine LIBINT_HAS_MPFR @LIBINT_HAS_MPFR@ + /* have posix_memalign ? */ #cmakedefine HAVE_POSIX_MEMALIGN @HAVE_POSIX_MEMALIGN@ diff --git a/include/libint2/config2.h.cmake.in b/include/libint2/config2.h.cmake.in index 20058ffb7..0344ee87b 100644 --- a/include/libint2/config2.h.cmake.in +++ b/include/libint2/config2.h.cmake.in @@ -29,9 +29,13 @@ #define _libint2_include_libint2_config_h_1 #undef LIBINT_ALIGN_SIZE +#undef LIBINT_HAS_MPFR #undef HAVE_POSIX_MEMALIGN #undef LIBINT_USER_DEFINED_REAL /* retired Jan 2023 #undef LIBINT_SHGSHELL_ORDERING */ +#undef LIBINT_HAS_EIGEN +#undef LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS + /* if can be controlled with posix_memalign, alignment size */ #define LIBINT2_ALIGN_SIZE @LIBINT2_ALIGN_SIZE@ @@ -41,6 +45,15 @@ /* Specifies the ordering of solid harmonics Gaussians in a shell. Retired Jan 2023 in favor of becoming a generator-time-only option */ /* #cmakedefine LIBINT_SHGSHELL_ORDERING @LIBINT_SHGSHELL_ORDERING@ */ +/* define if Eigen library is available. */ +#cmakedefine LIBINT_HAS_EIGEN @LIBINT_HAS_EIGEN@ + +/* define if system-wide Boost.Preprocessor is available */ +#cmakedefine LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS @LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS@ + +/* have MPFR library ? */ +#cmakedefine LIBINT_HAS_MPFR @LIBINT_HAS_MPFR@ + /* have posix_memalign ? */ #cmakedefine HAVE_POSIX_MEMALIGN @HAVE_POSIX_MEMALIGN@ diff --git a/src/lib/libint/CMakeLists.txt b/src/lib/libint/CMakeLists.txt index 6be004c93..fe5696e45 100644 --- a/src/lib/libint/CMakeLists.txt +++ b/src/lib/libint/CMakeLists.txt @@ -49,7 +49,35 @@ add_custom_target(libint-library-export DEPENDS "${EXPORT_STAGE_DIR}.tgz") list(APPEND library_CMAKE_ARGS -DLIBINT2_ALIGN_SIZE=${LIBINT_ALIGN_SIZE} -DLIBINT2_REALTYPE=${LIBINT2_REALTYPE} + -DLIBINT2_ENABLE_MPFR=${LIBINT2_ENABLE_MPFR} + -DLIBINT2_ENABLE_PYTHON=${LIBINT2_ENABLE_PYTHON} + -DLIBINT2_PREFIX_PYTHON_INSTALL=${LIBINT2_PREFIX_PYTHON_INSTALL} ) + if (Multiprecision_ROOT) + list(APPEND library_CMAKE_ARGS "-DMultiprecision_ROOT=${Multiprecision_ROOT}") + endif() + if (Boost_DIR) + list(APPEND library_CMAKE_ARGS "-DBoost_DIR=${Boost_DIR}") + endif() + if (BOOST_ROOT) + list(APPEND library_CMAKE_ARGS "-DBOOST_ROOT=${BOOST_ROOT}") + endif() + if (CMAKE_DISABLE_FIND_PACKAGE_Boost) + list(APPEND library_CMAKE_ARGS "-DCMAKE_DISABLE_FIND_PACKAGE_Boost=${CMAKE_DISABLE_FIND_PACKAGE_Boost}") + endif() + if (Eigen3_DIR) + list(APPEND library_CMAKE_ARGS "-DEigen3_DIR=${Eigen3_DIR}") + endif() + if (Eigen3_ROOT) + list(APPEND library_CMAKE_ARGS "-DEigen3_ROOT=${Eigen3_ROOT}") + endif() + if (LIBINT2_LOCAL_Eigen3_INSTALL) + list(APPEND library_CMAKE_ARGS "-DLIBINT2_LOCAL_Eigen3_INSTALL=${LIBINT2_LOCAL_Eigen3_INSTALL}") + endif() + + + + ExternalProject_Add( library diff --git a/src/lib/libint/CMakeLists.txt.export b/src/lib/libint/CMakeLists.txt.export index 3b09da11e..57e5a00c0 100644 --- a/src/lib/libint/CMakeLists.txt.export +++ b/src/lib/libint/CMakeLists.txt.export @@ -32,6 +32,19 @@ set(pnv libint2) # projectnameversion include(options) include(CheckFunctionExists) +# <<< General >>> + +option_with_print(LIBINT2_ENABLE_MPFR + "Use GNU MPFR library for high-precision testing (requires MPFR. EXPERTS ONLY)" OFF) +option_with_print(LIBINT2_ENABLE_PYTHON + "Build Python bindings (requires Python and Pybind11 and Eigen3)" OFF) +option_with_print(LIBINT2_PREFIX_PYTHON_INSTALL + "For LIBINT2_ENABLE_PYTHON=ON, whether to install the Python module in the Linux manner to CMAKE_INSTALL_PREFIX or to not install it. See target libint2-python-wheel for alternate installation in the Python manner to Python_EXECUTABLE's site-packages." OFF) +option_with_print(LIBINT2_LOCAL_Eigen3_INSTALL + "Install an exported target with hard-coded Eigen3 dependency paths. This is potentially useful and important when consuming the compiled C++11 interface library so that the Libint library build and Libint consumer build use the same Eigen3 installation & ABI. This is at most a convenience when consuming the header-only C++11 interface library. In consumer build, set `LIBINT2_LOCAL_Eigen3_FIND=ON` before `find_package(Libint2) to load the exported Eigen3." OFF) +option_with_print(CMAKE_DISABLE_FIND_PACKAGE_Boost + "When Boost required for C++11 API, disable its detection, thereby forcing use of bundled Boost (Standard CMake variable: https://cmake.org/cmake/help/latest/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.html)" OFF) + check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN) if (HAVE_POSIX_MEMALIGN) @@ -59,6 +72,69 @@ set(LIBINT2_SHELL_SET ${LIBINT2_SHELL_SET}) message(STATUS "Setting option LIBINT2_SHELL_SET: ${LIBINT2_SHELL_SET} (read-only from generation-time)") +################################## Dependencies ################################# + +# See notes at https://github.com/evaleev/libint/blob/master/INSTALL.md#prerequisites + +if (LIBINT2_ENABLE_MPFR) + find_package(Multiprecision MODULE REQUIRED COMPONENTS gmpxx mpfr) + set(LIBINT_HAS_MPFR 1) + + get_property(_loc TARGET Multiprecision::gmp PROPERTY LOCATION) + message(VERBOSE "${Cyan}Found GMP${ColourReset}: ${_loc}") + get_property(_loc TARGET Multiprecision::gmpxx PROPERTY LOCATION) + message(VERBOSE "${Cyan}Found GMPXX${ColourReset}: ${_loc}") + get_property(_loc TARGET Multiprecision::mpfr PROPERTY LOCATION) + message(VERBOSE "${Cyan}Found MPFR${ColourReset}: ${_loc} (found version ${MPFR_VERSION})") +endif() + +#if (LIBINT2_REQUIRE_CXX_API) + if (NOT TARGET Boost::headers) + find_package(Boost 1.57) + endif() + if (TARGET Boost::headers) + include(int_checkboost) + set(LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS 1) + else() + set(LIBINT_HAS_SYSTEM_BOOST_PREPROCESSOR_VARIADICS 0) + + file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/libint2) + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xzf ${PROJECT_SOURCE_DIR}/external/boost.tar.gz + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/include/libint2 + RESULT_VARIABLE UNPACK_BOOST_RESULT + OUTPUT_VARIABLE UNPACK_BOOST_OUTPUT + ERROR_VARIABLE UNPACK_BOOST_OUTPUT + ) + message(STATUS "Unpacking bundled Boost") + if (NOT UNPACK_BOOST_RESULT EQUAL 0) + message(FATAL_ERROR "Failed to unpack the bundled Boost! The tar command output:\n${UNPACK_BOOST_OUTPUT}") + endif() + endif() +#endif() + +find_package(Eigen3 MODULE) + +if (TARGET Eigen3::Eigen) + set(LIBINT_HAS_EIGEN 1) +endif() +#if (LIBINT2_REQUIRE_CXX_API AND NOT ${LIBINT_HAS_CXX_API}) +if (NOT LIBINT_HAS_EIGEN) # TODO tmp wrong logic + message(FATAL_ERROR "C++ API cannot be built without Eigen3; configure (via CMake) and install Eigen3 and add the install prefix to CMAKE_PREFIX_PATH, or add -DLIBINT2_REQUIRE_CXX_API=OFF to the CMake command line if the C++ API is not required") +endif() + +# Python is optionally used for testing. +# * But for Fortran, it's additionally required for preprocessing. +# * And for Python bindings, it's required along with its headers. +if (LIBINT2_ENABLE_PYTHON) + find_package(Python COMPONENTS Interpreter Development REQUIRED) +elseif (LIBINT2_ENABLE_FORTRAN) + find_package(Python COMPONENTS Interpreter REQUIRED) +else() + find_package(Python COMPONENTS Interpreter) +endif() + + ################################# Main Project ################################# include(CMakePackageConfigHelpers) @@ -81,6 +157,8 @@ configure_package_config_file( NO_SET_AND_CHECK_MACRO ) +# <<< Install >>> + install( DIRECTORY ${PROJECT_SOURCE_DIR}/include2/ # TODO temp for skeleton @@ -90,3 +168,43 @@ install( PATTERN "*.h" PATTERN "*.hpp" ) + +if (LIBINT2_LOCAL_Eigen3_INSTALL AND TARGET Eigen3::Eigen) + + add_library(Eigen INTERFACE) + foreach(prop + INTERFACE_INCLUDE_DIRECTORIES + INTERFACE_COMPILE_DEFINITIONS + INTERFACE_COMPILE_OPTIONS + INTERFACE_LINK_LIBRARIES + INTERFACE_POSITION_INDEPENDENT_CODE + ) + get_property(_propval TARGET Eigen3::Eigen PROPERTY ${prop}) + set_property(TARGET Eigen PROPERTY ${prop} ${_propval}) + endforeach() + + install( + TARGETS + Eigen + EXPORT local_set + LIBRARY + COMPONENT ${L2}_Eigen3 + NAMELINK_COMPONENT ${L2}_Eigen3 + ARCHIVE + COMPONENT ${L2}_Eigen3 + PUBLIC_HEADER + COMPONENT ${L2}_Eigen3 + ) +endif() + +# <<< Export Config >>> + +if (LIBINT2_LOCAL_Eigen3_INSTALL AND TARGET Eigen3::Eigen) + install( + EXPORT local_set + FILE "${pnv}-targets-eigen3.cmake" + NAMESPACE "${L2}::" + DESTINATION ${LIBINT2_INSTALL_CMAKEDIR} + COMPONENT ${L2}_Eigen3 + ) +endif() diff --git a/src/lib/libint/populate.cmake b/src/lib/libint/populate.cmake index 00166960e..0f478af3b 100644 --- a/src/lib/libint/populate.cmake +++ b/src/lib/libint/populate.cmake @@ -6,6 +6,9 @@ file( "${PROJECT_SOURCE_DIR}/cmake/modules/options.cmake" "${PROJECT_BINARY_DIR}/cmake/modules/int_computed.cmake" "${PROJECT_SOURCE_DIR}/cmake/modules/int_userreal.cmake" + "${PROJECT_SOURCE_DIR}/cmake/modules/int_checkboost.cmake" + "${PROJECT_SOURCE_DIR}/cmake/modules/FindMultiprecision.cmake" + "${PROJECT_SOURCE_DIR}/cmake/modules/FindEigen3.cmake" DESTINATION "${EXPORT_STAGE_DIR}/cmake/modules" )