diff --git a/.github/workflows/ci_cmake.yml b/.github/workflows/ci_cmake.yml new file mode 100644 index 000000000..41aa64c46 --- /dev/null +++ b/.github/workflows/ci_cmake.yml @@ -0,0 +1,304 @@ +name: BuildCM + +on: + push: + branches: + - master + pull_request: + +jobs: + build_repo: + strategy: + fail-fast: false + matrix: + build_type : [ Release, Debug ] + os : [ macos-latest, ubuntu-20.04 ] + subproject : [ ON, OFF ] + exclude: + - build_type: Release + subproject: ON + include: + - os: ubuntu-20.04 + cxx: g++-10 + cc: gcc-10 + compressor: gzip + - os: macos-latest + cxx: clang++ + cc: clang + compressor: bzip2 + - build_type: Debug + build_shared_libs: OFF + do_fortran: ON + - build_type: Release + build_shared_libs: ON + os: ubuntu-20.04 + do_fortran: ON + - build_type: Release + build_shared_libs: ON + os: macos-latest + do_fortran: OFF + # someday when mac+fortran+shared is fixed (plus universal do_fortran: ON) + #- build_type: Release + # build_shared_libs: ON + + name: "Repo • ${{ matrix.os }}: ${{ matrix.cxx }} ${{ matrix.build_type }} shared=${{ matrix.build_shared_libs }} fetch=${{ matrix.subproject }}" + runs-on: ${{ matrix.os }} + env: + CC: ${{ matrix.cc }} + CXX : ${{ matrix.cxx }} + CCACHE_DIR : ${{github.workspace}}/build/.ccache + CCACHE_COMPRESS : true + CCACHE_COMPRESSLEVEL : 6 + BUILD_CONFIG : > + -G Ninja + -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} + -D BUILD_SHARED_LIBS=${{ matrix.build_shared_libs }} + -D LIBINT_BUILD_LIBRARY_AS_SUBPROJECT=${{ matrix.subproject }} + -D CMAKE_INSTALL_PREFIX=${{github.workspace}}/build/library + -D WITH_MAX_AM=2;2 + -D WITH_ERI_MAX_AM=2;2 + -D WITH_ERI3_MAX_AM=3;2 + -D ENABLE_ERI=1 + -D ENABLE_ERI3=1 + -D ENABLE_ONEBODY=1 + -D DISABLE_ONEBODY_PROPERTY_DERIVS=ON + -D MULTIPOLE_MAX_ORDER=2 + -D LIBINT2_REQUIRE_CXX_API_COMPILED=ON + -D LIBINT2_ENABLE_FORTRAN=${{ matrix.do_fortran }} + -D LIBINT2_ENABLE_PYTHON=ON + -D Python_EXECUTABLE=${{ matrix.os == 'macos-latest' && '/usr/local/Frameworks/Python.framework/Versions/3.11/bin/python3.11' || '/usr/bin/python3.8' }} + -D EXPORT_COMPRESSOR=${{ matrix.compressor }} + + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + # fetch-depth: 0 for git history to compute version + + - id: skip_check + name: Check if can skip + uses: fkirc/skip-duplicate-actions@v5 + with: + cancel_others: 'true' + + - name: Create Build Environment + if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + run: | + cmake -E make_directory ${{github.workspace}}/build/compiler + cmake -E make_directory ${{github.workspace}}/build/library + cmake -E make_directory ${{github.workspace}}/build/library_test + + - name: Install prerequisite MacOS packages + if: ${{ steps.skip_check.outputs.should_skip != 'true' && matrix.os == 'macos-latest' }} + run: | + brew install ninja gcc@10 boost eigen ccache python@3.11 numpy scipy + echo "FC=/usr/local/bin/gfortran-10" >> $GITHUB_ENV + echo "EIGEN3_INCLUDE_DIR=/usr/local/include/eigen3" >> $GITHUB_ENV + + - name: Install prerequisite Ubuntu packages + if: ${{ steps.skip_check.outputs.should_skip != 'true' && matrix.os == 'ubuntu-20.04' }} + run: | + sudo apt-get update + sudo apt-get install ninja-build g++-10 gfortran-10 liblapack-dev libboost-dev libeigen3-dev ccache python3-numpy python3-scipy + echo "FC=/usr/bin/gfortran-10" >> $GITHUB_ENV + echo "EIGEN3_INCLUDE_DIR=/usr/include/eigen3" >> $GITHUB_ENV + + - name: Prepare ccache timestamp + if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + id: ccache_cache_timestamp + shell: cmake -P {0} + run: | + string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC) + message("\"timestamp=${current_date}\" >> $GITHUB_OUTPUT") + + - name: Setup ccache cache files + if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + uses: actions/cache@v3 + with: + path: ${{github.workspace}}/build/.ccache + key: ${{ matrix.config.name }}-ccache-${{ steps.ccache_cache_timestamp.outputs.timestamp }} + restore-keys: | + ${{ matrix.config.name }}-ccache- + + - name: Build Libint generator + if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{github.workspace}}/build/compiler + run: | + git describe --tags + cmake -S ../.. -B build $BUILD_CONFIG --log-level=DEBUG + cmake --build build --target check-libint2compiler + + - name: Generate Libint library tarball + if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + shell: bash + working-directory: ${{github.workspace}}/build/compiler + run: | + cmake --build build --target export + cd build && echo "ARTIFACT=`ls -1 libint*z*`" >> $GITHUB_ENV + + - name: Archive Libint library tarball + if: ${{ steps.skip_check.outputs.should_skip != 'true' && matrix.build_type == 'Release' }} + uses: actions/upload-artifact@v3 + with: + if-no-files-found: error + name: ${{ runner.os }}-${{ matrix.cxx }} + path: ${{github.workspace}}/build/compiler/build/${{ env.ARTIFACT }} + retention-days: 1 + + - name: Build Libint library (FetchContent=${{ matrix.subproject }}) + if: ${{ steps.skip_check.outputs.should_skip }} + shell: bash + working-directory: ${{github.workspace}}/build/compiler + run: cmake --build build + + - name: Test Libint library - unit tests + if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + shell: bash + working-directory: ${{github.workspace}}/build/compiler + run: CTEST_PARALLEL_LEVEL=2 cmake --build build --target check-libint2 + + - name: Build & Test Python bindings + if: ${{ steps.skip_check.outputs.should_skip != 'true' }} + shell: bash + working-directory: ${{github.workspace}}/build/compiler + run: cmake --build build --target check-python + + build_export: + # to debug the second stage, it is handy to short-circuit the first stage: + # * suppress steps beyond "Archive Library Tarball" with "if: false" + # * for further reduction, run mostly Linux lanes with "os : [ ubuntu-20.04 ]" + needs: build_repo + if: always() && (needs.build_repo.outputs.should_skip != 'true') + strategy: + fail-fast: false + matrix: + cfg: + - runs-on: ubuntu-latest + lane: ubuntu-gnu + libargs: > + -D BUILD_SHARED_LIBS=ON + -D LIBINT2_ENABLE_FORTRAN=ON + testargs: "" + + - runs-on: windows-latest + lane: windows-clang-cl + libargs: > + -G Ninja + -D CMAKE_BUILD_TYPE=Release + -D BUILD_SHARED_LIBS=OFF + -D CMAKE_CXX_COMPILER=clang-cl + -D CMAKE_C_COMPILER=clang-cl + testargs: > + -G Ninja + -D CMAKE_BUILD_TYPE=Release + -D CMAKE_CXX_COMPILER=clang-cl + -D CMAKE_C_COMPILER=clang-cl + + - runs-on: macos-latest + lane: macos-clang + libargs: > + -D BUILD_SHARED_LIBS=OFF + -D LIBINT2_ENABLE_FORTRAN=ON + # return to shared when mac+fortran+shared is fixed + testargs: "" + + - runs-on: ubuntu-latest + lane: ubuntu-intel + libargs: > + -D CMAKE_CXX_COMPILER=icpx + -D CMAKE_CXX_FLAGS="--gcc-toolchain=${CONDA_PREFIX} --sysroot=${CONDA_PREFIX}/${HOST}/sysroot -target ${HOST}" + testargs: > + -D CMAKE_CXX_COMPILER=icpx + -D CMAKE_CXX_FLAGS="--gcc-toolchain=${CONDA_PREFIX} --sysroot=${CONDA_PREFIX}/${HOST}/sysroot -target ${HOST}" + + name: "Export • ${{ matrix.cfg.lane }}" + runs-on: ${{ matrix.cfg.runs-on }} + + steps: + # Note we're not checking out the repo. All src from Linux tarball generated above. + + - uses: actions/download-artifact@v3 + with: + name: Linux-g++-10 + + - name: Write a Conda Env File + shell: bash -l {0} + run: | + cat > export.yaml <>> ls +# cmake/ COPYING src/ tests/ ... +# >>> cmake -S. -Bbuild -GNinja -DCMAKE_INSTALL_PREFIX=/path/to/install-libint ... +# ... +# -- Generating done +# -- Build files have been written to: /current/dir/build +# >>> cmake --build build --target install + +# The Libint build is structured into three parts: +# +# * generator/compiler +# - (1) build src/bin/libint/ into compiler executable `build_libint` +# - pretty quick, runs in parallel +# - consumes all the enable/max/opt/orderings integral options +# - (2) optionally testable +# * export +# - (3) run `build_libint` to generate library source (C++) files (that upon +# compilation can become a Libint2 library) and combine them with other +# static source files in src/lib/libint/ and general static files (e.g., +# include/ and docs/) into an independent tarball ready for distribution +# (with its own CMake configuration, tests, etc.). +# - really slow for non-trivial angular momenta; runs in serial +# - consumes no options +# - build target `export` to stop after this step and collect source tarball +# * library +# - can be built as a subproject (FetchContent) or completely insulated (bare +# ExternalProject; default; -or- a tarball start). For FetchContent, must +# build libint-library-export target before library build targets appear +# - (4) unpack the export tarball and build the library and install into \/library-install-stage/ +# - duration depends on number of integrals requested; runs in parallel +# - consumes language-interface and the CMAKE_INSTALL_[DATA|INCLUDE|LIB]DIR paths options +# - the default build target includes this final library build +# - (5) optionally testable +# - (6) install into CMAKE_INSTALL_PREFIX +# - (7) optional Python build alongside library or afterwards. Optional testing requires library install + +#################################### Guide ##################################### + +# See INSTALL.md for elaboration of steps above, options below, & translations from libtool. + +################################### Options #################################### +include(options) +include(GNUInstallDirs) +include(CTest) +message(STATUS "Building using CMake ${CMAKE_VERSION} Generator ${CMAKE_GENERATOR}") + +# <<< General >>> + +option_with_default(CMAKE_BUILD_TYPE "Build type (Release or Debug)" Release) + +### compiler-only +option_with_print(LIBINT_BUILD_LIBRARY_AS_SUBPROJECT + "[EXPERT] Build generated library as a subproject: if FALSE will configure and build separately" OFF) + +### library-only +option_with_print(LIBINT2_REQUIRE_CXX_API + "C++11 Libint API: define library targets + test (requires Eigen3, Boost is optional but strongly recommended)" ON) +option_with_print(LIBINT2_REQUIRE_CXX_API_COMPILED + "Build C++11 Compiled (not just header-only) targets (requires Eigen3, Boost strongly recommended)" ON) +option_with_print(LIBINT2_ENABLE_FORTRAN + "Build Fortran03+ Libint interface (requires Fortran)" 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(BUILD_SHARED_LIBS + "Build Libint library as shared, not static" OFF) +option_with_print(LIBINT2_BUILD_SHARED_AND_STATIC_LIBS + "Build both shared and static Libint libraries in one shot. Uses -fPIC." OFF) + +# <<< Which Integrals Classes, Which Derivative Levels >>> + +option_with_default(ENABLE_ONEBODY + "Compile with support for up to N-th derivatives of 1-body integrals (-1 for OFF)" 0) +option_with_default(ENABLE_ERI + "Compile with support for up to N-th derivatives of 4-center electron repulsion integrals (-1 for OFF)" 0) +option_with_default(ENABLE_ERI3 + "Compile with support for up to N-th derivatives of 3-center electron repulsion integrals (-1 for OFF)" -1) +option_with_default(ENABLE_ERI2 + "Compile with support for up to N-th derivatives of 2-center electron repulsion integrals (-1 for OFF)" -1) +option_with_default(ENABLE_G12 + "Compile with support for N-th derivatives of MP2-F12 energies with Gaussian factors (-1 for OFF)" -1) +option_with_default(ENABLE_G12DKH + "Compile with support for N-th derivatives of DKH-MP2-F12 energies with Gaussian factors (-1 for OFF)" -1) + +option_with_print(DISABLE_ONEBODY_PROPERTY_DERIVS + "Disable geometric derivatives of 1-body property integrals (all but overlap, kinetic, elecpot). + These derivatives are disabled by default to save compile time. (enable with OFF) + Note that the libtool build won't enable this- if forcibly enabled, build_libint balks." ON) +option_with_print(ENABLE_T1G12_SUPPORT + "Enable Ti,G12 integrals when G12 integrals are enabled. Irrelevant when `ENABLE_G12=OFF`. (disable with OFF)" ON) + +# <<< Ordering Conventions >>> + +option_with_default(LIBINT2_SHGAUSS_ORDERING + "Ordering for shells of solid harmonic Gaussians: + standard -- standard ordering (-l, -l+1 ... l) + gaussian -- the Gaussian ordering (0, 1, -1, 2, -2, ... l, -l) + See https://github.com/evaleev/libint/blob/master/INSTALL.md#solid-harmonic-ordering-scope-and-history ." standard) +option_with_default(LIBINT2_CARTGAUSS_ORDERING + "Orderings for shells of cartesian Gaussians: + standard -- standard ordering (xxx, xxy, xxz, xyy, xyz, xzz, yyy, ...) + intv3 -- intv3 ordering (yyy, yyz, yzz, zzz, xyy, xyz, xzz, xxy, xxz, xxx) + gamess -- GAMESS ordering (xxx, yyy, zzz, xxy, xxz, yyx, yyz, zzx, zzy, xyz) + orca -- ORCA ordering (hydrid between GAMESS and standard) + bagel -- axis-permuted version of intv3 (xxx, xxy, xyy, yyy, xxz, xyz, yyz, xzz, yzz, zzz)" standard) +option_with_default(LIBINT2_SHELL_SET + "Support computation of shell sets sets subject to these restrictions: + 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: + for (ab|cd): + l(a) <= l(b), + l(c) <= l(d), + l(a) < l(c) || (l(a) == l(c) && l(b) < l(d)) + for (b|cd): + l(c) <= l(d)" standard) + +# <<< How High Angular Momentum >>> + +# example for "semicolon-separated string": `-DENABLE_ERI3=2 -DWITH_ERI3_MAX_AM="5;4;3"` + +# special considerations for high-AM library builds: +# * high MAX_AM generates a large number of source files. If unity builds are disabled, more than +# ~20k files may require `ulimit -s 65535` for linking the library target on Linux to avert +# "ld: Argument list too long". +# * Ninja builds use beyond max threads and can run out of memory, resulting in errorless stops or +# "CMake Error: Generator: execution of make failed". Throttle it to physical threads with +# `export CMAKE_BUILD_PARALLEL_LEVEL=N`. + +option_with_default(WITH_MAX_AM + "Support Gaussians of angular momentum up to N. + Can specify values for each derivative level as a semicolon-separated string. + If ERI3 ints are enabled, this option also controls the AM of the paired centers." 4) +option_with_default(WITH_OPT_AM + "Optimize maximally for up to angular momentum N (N <= max-am). + Can specify values for each derivative level as a semicolon-separated string. (default: (libint_max_am/2)+1)" -1) + +option_with_default(MULTIPOLE_MAX_ORDER + "Maximum order of spherical multipole integrals. There is no maximum" 4) +option_with_default(WITH_ONEBODY_MAX_AM + "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: max_am)" -1) +option_with_default(WITH_ONEBODY_OPT_AM + "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: (max_am/2)+1)" -1) + +option_with_default(WITH_ERI_MAX_AM + "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: max_am)" -1) +option_with_default(WITH_ERI_OPT_AM + "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: (max_am/2)+1)" -1) + +option_with_default(WITH_ERI3_MAX_AM + "Support 3-center ERIs for Gaussians of angular momentum up to N. + Can specify values for each derivative level as a semicolon-separated string. (default: max_am) + This option controls only the single fitting center. The paired centers use WITH_MAX_AM." -1) +option_with_default(WITH_ERI3_OPT_AM + "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: (max_am/2)+1)" -1) +option_with_print(ERI3_PURE_SH + "Assume the 'unpaired' center of 3-center ERIs will be transformed to pure solid harmonics" OFF) + +option_with_default(WITH_ERI2_MAX_AM + "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: max_am)" -1) +option_with_default(WITH_ERI2_OPT_AM + "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: (max_am/2)+1)" -1) +option_with_print(ERI2_PURE_SH + "Assume the 2-center ERIs will be transformed to pure solid harmonics" OFF) + +option_with_default(WITH_G12_MAX_AM + "Support integrals for G12 methods of angular momentum up to N. (default: max_am)" -1) +option_with_default(WITH_G12_OPT_AM + "Optimize G12 integrals for up to angular momentum N (N <= max-am). (default: (max_am/2)+1)" -1) + +option_with_default(WITH_G12DKH_MAX_AM + "Support integrals for relativistic G12 methods of angular momentum up to N. (default: max_am)" -1) +option_with_default(WITH_G12DKH_OPT_AM + "Optimize G12DKH integrals for up to angular momentum N (N <= max-am). (default: (max_am/2)+1)" -1) + +# <<< Miscellaneous >>> + +option_with_print(LIBINT_CONTRACTED_INTS + "Turn on support for contracted integrals." ON) +option_with_default(LIBINT_ERI_STRATEGY + "(EXPERT) Compute ERIs using the following strategy. (0 for OS, 1 for HGP, 2 for HL)" 1) +option_with_print(LIBINT_USE_COMPOSITE_EVALUATORS + "Libint will use composite evaluators (i.e. every evaluator will compute one integral type only)" ON) +option_with_print(LIBINT_SINGLE_EVALTYPE + "Generate single evaluator type (i.e. all tasks use the same evaluator). OFF is NYI" ON) +option_with_default(LIBINT_ENABLE_UNROLLING + "Unroll shell sets into integrals (will unroll shell sets larger than N) (0 for never, N for N, 1000000000 for always)" 100) +option_with_default(LIBINT_ALIGN_SIZE + "(EXPERT) 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." 0) +mark_as_advanced(LIBINT_ALIGN_SIZE) +option_with_default(LIBINT2_REALTYPE + "Specifies the floating-point data type used by the library. Consumed at library build-time." double) +option_with_print(LIBINT_USER_DEFINED_REAL_INCLUDES + "Additional #includes necessary to use the real type." OFF) +include(int_userreal) +option_with_print(LIBINT_GENERATE_FMA + "Generate FMA (fused multiply-add) instructions (to benefit must have FMA-capable hardware and compiler)" OFF) +option_with_print(LIBINT_ENABLE_GENERIC_CODE + "Use manually-written generic code" OFF) +option_with_print(LIBINT_API_PREFIX + "Prepend this string to every name in the library API (except for the types)." OFF) +option_with_print(LIBINT_VECTOR_LENGTH + "Compute integrals in vectors of length N." OFF) +option_with_default(LIBINT_VECTOR_METHOD + "Specifies how to vectorize integrals. Irrelevant when `LIBINT_VECTOR_LENGTH=OFF. Allowed values are 'block' (default), and 'line'." block) +option_with_print(LIBINT_ACCUM_INTS + "Accumulate integrals to the buffer, rather than copy (OFF for copy, ON for accum)." OFF) +option_with_print(LIBINT_FLOP_COUNT + "Support (approximate) FLOP counting by the library. (Generated code will require C++11!)" OFF) +option_with_print(LIBINT_PROFILE + "Turn on profiling instrumentation of the library. (Generated code will require C++11!)" OFF) +option_with_print(LIBINT2_ENABLE_MPFR + "Use GNU MPFR library for high-precision testing (EXPERTS ONLY). Consumed at library build-time." OFF) +option_with_default(EXPORT_COMPRESSOR + "Export tarball with compression gzip or bzip2" gzip) +# next one defined by `include(CTest)` +message(STATUS "Showing option BUILD_TESTING: ${BUILD_TESTING}") + +# <<< Path >>> + +######################## Process & Validate Options ########################### +include(autocmake_safeguards) +include(CheckFunctionExists) +include(CheckIncludeFileCXX) +include(FeatureSummary) +include(int_orderings) +include(int_am) + +check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN) +if (NOT HAVE_POSIX_MEMALIGN) + message(FATAL_ERROR "did not find posix_memalign ... this SHOULD NOT happen. Cannot proceed.") +endif() + +check_include_file_cxx(stdint.h HAVE_STDINT_H) # limits.h? + +if (cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) + set(LIBINT_HAS_CXX11 1) +endif() + +booleanize01(ERI3_PURE_SH) +booleanize01(ERI2_PURE_SH) +booleanize01(LIBINT_SINGLE_EVALTYPE) +booleanize01(LIBINT_CONTRACTED_INTS) +booleanize01(DISABLE_ONEBODY_PROPERTY_DERIVS) +booleanize01(LIBINT_GENERATE_FMA) +booleanize01(LIBINT_ENABLE_GENERIC_CODE) +booleanize01(SUPPORT_T1G12) +booleanize01(LIBINT_ACCUM_INTS) +booleanize01(LIBINT_FLOP_COUNT) +booleanize01(LIBINT_PROFILE) + +if (EXPORT_COMPRESSOR STREQUAL "gzip") + set(EXPORT_COMPRESSOR_CMD "cfz") + set(EXPORT_COMPRESSOR_EXT "tgz") +elseif (EXPORT_COMPRESSOR STREQUAL "bzip2") + set(EXPORT_COMPRESSOR_CMD "jcf") + set(EXPORT_COMPRESSOR_EXT "tbz2") +else() + message(FATAL_ERROR "No valid compressor; invoke CMake with -DEXPORT_COMPRESSOR=gzip|bzip2") +endif() + + +################################## 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}) + +configure_file( + cmake/modules/int_computed.cmake.in + cmake/modules/int_computed.cmake + @ONLY) + +# CMake data transmitted to C++ via config.h for generator/compiler (_EXPORT_MODE=0). +# Same info is positioned for the library export, but _EXPORT_MODE=1 turns on +# inclusion of config2.h to be filled in later by user re-set-able options at +# library build time. +set(_EXPORT_MODE 0) +configure_file( + include/libint2/config.h.cmake.in + include/libint2/config.h + @ONLY) +set(_EXPORT_MODE 1) +configure_file( + include/libint2/config.h.cmake.in + ${EXPORT_STAGE_DIR}/include/libint2/config.h + @ONLY) +configure_file( + include/libint2/config2.h.cmake.in + ${EXPORT_STAGE_DIR}/include/libint2/config2.h.cmake.in + COPYONLY) + +add_subdirectory(src) + +message("") +feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Libint Enabled features:") +feature_summary(WHAT DISABLED_FEATURES DESCRIPTION "Libint Disabled features:") diff --git a/INSTALL.md b/INSTALL.md index 33c868df6..28389d2b1 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,3 +1,498 @@ +#### Note: If you received this file from a `libint-2.*.tgz` source archive, `(TARBALL)` marks portions of this document relevant to you. + + +# Libint Compiler vs Library + +Before you read on: + +* If you want a pre-built Libint library, packages may be available: + * conda-forge: `conda install libint -c conda-forge` for Linux, Apple (Intel), Apple Silicon, and Windows. See https://github.com/conda-forge/libint-feedstock/tree/main/recipe for configuration, tarball, and build conditions. + * Debian (TBD) + * Fedora (TBD) +* If you want to know how to _use_ a libint library in your code: + * if you use C++11 or later (strongly recommended): read the [Libint Wiki](https://github.com/evaleev/libint/wiki/using-modern-CPlusPlus-API) + * if you use pre-2011 C++, C, Fortran, or any other language, refer to the [Libint Programmer's Manual](https://sourceforge.net/projects/libint/files/libint-for-beginners/progman-2.0.3-stable.pdf/download) +* If you want to _build and use_ a libint _library_: + * if all you want is a basic library that computes integrals necessary to compute energies, use the pre-generated library labeled "lmax=6 library (standard ints only)" from the [latest release](https://github.com/evaleev/libint/releases/latest) of Libint + * many codes using libint, e.g. orca and mpqc, already include an appropriately configured libint library, and you do not need to generate it yourself + * if you need compilation directions, _read on_, skipping the compiler/generation parts. (TARBALL) +* If you want to know how to _generate_ a libint _library_ using the libint _compiler_, these are some compelling circumstances: + * if you need a custom libint library with choice of integral types, AM, orderings, language interfaces, etc. + * if you want to develop libint with new integral types, recurrence relations, and computation + strategies, you'll need to edit the compiler. If you are interested in working on the compiler + code please consider consulting with one of the Libint authors to avoid duplication of effort. + * if you do need to generate a custom library, _read on_. + + +----------------------------------------------------------------------------- + +# Overview + +The Libint build is structured into three parts: + +* generator/compiler + - (1) build src/bin/libint/ into compiler executable `build_libint` + - pretty quick, runs in parallel + - consumes all the enable/max/opt/orderings integral options + - (2) optionally testable +* export + - (3) run `build_libint` to generate library source (C++) files (that upon + compilation can become a Libint2 library) and combine them with other + static source files in src/lib/libint/ and general static files (e.g., + include/ and docs/) into an independent tarball ready for distribution + (with its own CMake configuration, tests, etc.). + - really slow for non-trivial angular momenta; runs in serial + - consumes no options + - build target `export` to stop after this step and collect source tarball +* library (TARBALL) + - from the repo, can be built as a subproject (FetchContent) or completely insulated (bare ExternalProject; default). + For FetchContent, must build libint-library-export target before library build targets appear. + Alternately, can start from independent TARBALL generated by (3) and build as standalone project + or dependency (FetchContent or ExternalProject) + - (4) unpack the export tarball and build the library and install into \/library-install-stage/ + - duration depends on number of integrals requested; runs in parallel + - consumes language-interface and the CMAKE_INSTALL_[DATA|INCLUDE|LIB]DIR paths options + - the default build target includes this final library build + - (5) optionally testable + - (6) install into CMAKE_INSTALL_PREFIX + - (7) optional Python build alongside library or afterwards. optional testing requires library install + + +Command-line synopsis. See [table](#Build-Targets) for `--target` choices (steps refer to numbered bullets above) and [section](#Configuring-Libint) for `-D options` choices. + +```bash +>>> git clone https://github.com/evaleev/libint.git && cd libint +>>> ls +cmake/ COPYING src/ tests/ ... +>>> cmake -S. -Bbuild -GNinja -DCMAKE_INSTALL_PREFIX=/path/to/future/install-libint -D options ... +... +-- Generating done +-- Build files have been written to: /current/dir/build +>>> cmake --build build --target install +``` + +### Build Targets + +| `--target ...` [^27] | incl. | steps | ( | see | above | ) | | (TARBALL) `--target ...` [^25] | +| -------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ------------------------------ | +| `build_libint` | 1 | - | - | - | - | - | - | n/a | +| `check-libint2compiler` | 1 | 2 | - | - | - | - | - | n/a | +| `export` | 1 | - | 3 | - | - | - | - | n/a | +| `library` (default) [^26] | 1 | - | 3 | 4 | - | - | - | (default) [^26] | +| `check` | 1 | 2 | 3 | 4 | 5 | - | - | `check` | + +Use combined targets like `cmake --target check install` to avoid some unnecessary rebuilding (esp. of build_libint) that occurs with successive targets. The CMake dependency structure is imperfect. + +[^25]: (TARBALL) targets can include steps 4 onwards; the starting tarball itself is the product of step 3. + +[^26]: See [see "Internal Targets" column in table](#consumption-targets) for individual library targets. + +[^27]: For FetchContent/`LIBINT_BUILD_LIBRARY_AS_SUBPROJECT=ON`, build target `export` aka `libint-library-export`. Then, as a separate command, build further targets like `check` or `install`; target plain `library` is not available. + +----------------------------------------------------------------------------- + +# 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, Valeev-group-tweaked source is fetched from GitHub. + +----------------------------------------------------------------------------- + +# Configuring Libint + +* Notes + * Codes "G", "L", or "C" for each option indicate whether it is consumed by the _g_enerator, the _l_ibrary, the library _c_onsumer, or a combination. + * If your final target is the export tarball, use options that include the letter "G". + * If you're building a library from an export TARBALL, use options that include the letter "L". + * For a continuous generator->export->library build, options supplied at the top level will be properly handed off to generator and library build. + * See [Update Guide](gnu-autotools-update-guide) for new names for old options. + + +### 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] + +* `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] + + +### 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) + 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: + 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: + for (ab|cd): + l(a) <= l(b), + l(c) <= l(d), + l(a) < l(c) || (l(a) == l(c) && l(b) < l(d)) + for (b|cd): + l(c) <= l(d) + +#### Solid Harmonic Ordering Scope and History + +The use of the `LIBINT2_SHGAUSS_ORDERING` option has changed recently (Dec 2023). See also discussion in the "2023-12-12: 2.8.0" section of [CHANGES](https://github.com/evaleev/libint/blob/master/CHANGES) and in the "cdbb9f3" comment of [engine.h](https://github.com/evaleev/libint/blob/master/include/libint2/engine.h). + +Previous to v2.8.0, `LIBINT2_SHGAUSS_ORDERING` was set at generator-build-time for `Operator::sphemultipole` but was re-set-able at library-build-time for other integral classes for both the C and C++ interfaces. Certain macros, `INT_SOLIDHARMINDEX` and `FOR_SOLIDHARM` depended on the library-build-time choice for both the C and C++ interfaces. + +Starting at v2.8.0, the generator-build-time construction of `Operator::sphemultipole` has been _fixed at Standard ordering_; thus, the `LIBINT2_SHGAUSS_ORDERING` setting does not influence it. For the C++ interface, solid harmonic ordering for other integrals classes can be toggled at library runtime through `libint2::set_solid_harmonics_ordering`, and the "SOLIDHARM" macros have different forms for accessing either ordering; thus the library build-time `LIBINT2_SHGAUSS_ORDERING` setting is non-constraining. For the C interface, solid harmonic ordering for other integrals classes and the output of "SOLIDHARM" macros _is constrained_ by the `LIBINT2_SHGAUSS_ORDERING` setting. Currently, this setting is fixed at generator-build-time. The build system could be adjusted so that it's re-set-able at library-build-time, but the C interface is discouraged anyways. + +Note that options, docs, and CMake components are focused on the C++ interface, and the only remaining constraining influence of the `LIBINT2_SHGAUSS_ORDERING` option -- for the C interface -- may never be acknowledged beyond the previous paragraph. + + +### How High Angular Momentum (G) + +* Notes + * example for "semicolon-separated string": `-DENABLE_ERI3=2 -DWITH_ERI3_MAX_AM="5;4;3"`. cmake configuration prints: + + ``` + -- Setting option ENABLE_ERI3: 2 + -- Setting option WITH_ERI3_MAX_AM: 5;4;3 + ``` + + * special considerations for high-AM library (L) builds: + * high MAX_AM generates a large number of source files. If unity builds are disabled, more than + ~20k files may require `ulimit -s 65535` for linking the library target on Linux to avert + "ld: Argument list too long". + * Ninja builds use beyond max threads and can run out of memory, resulting in errorless stops or + "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. Can specify values for each derivative level as a semicolon-separated string. Specify values greater or equal to `WITH__MAX_AM`; often mirrors `WITH_ERI3_MAX_AM`. [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] + +* `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_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_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`] + + +### Compilers and Flags (G L) (TARBALL) + + +### Build Library What (L) (TARBALL) + +* `LIBINT2_REQUIRE_CXX_API` — L — Build C++11 Libint API. Define header-only library target and check target (requires Eigen3; Boost recommended; [see prereq line](#prerequisites)). [Default=ON] +* `LIBINT2_REQUIRE_CXX_API_COMPILED` — L — Build C++11 Libint API. Define compiled (not just header-only) targets (requires Eigen3; Boost recommended). [Default=ON] +* `LIBINT2_ENABLE_FORTRAN` — L — Build Fortran03+ module/bindings (requires C and Fortran compilers and Python). [Default=OFF] +* `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) + +* `CMAKE_BUILD_TYPE` — G L — [Standard CMake variable](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) [Default=Release] +* `BUILD_SHARED_LIBS` — L — Build Libint library as shared, not static. [Standard CMake variable](https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html) [Default=OFF] +* `LIBINT2_BUILD_SHARED_AND_STATIC_LIBS` — L — Build both shared and static Libint libraries in one shot. Uses `-fPIC`. [Default=OFF] +* `LIBINT_BUILD_LIBRARY_AS_SUBPROJECT` — G — If building compiler and library in continuous command, build generated library as a subproject (FetchContent); if OFF will configure and build separately (ExternalProject) (expert only). [Default=OFF] + +### 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] + 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; + * `libint2::simd::VectorAVXDouble` -- vector of 4 packed doubles that can be used with [AVX](http://en.wikipedia.org/wiki/Advanced_Vector_Extensions) instructions available on reasonably-modern x86 hardware (starting with Intel Sandy Bridge and AMD Bulldozer microarchitectures, available in processors since 2011); + * `libint2::simd::VectorSSEDouble` -- vector of 2 packed doubles that can be used with [SSE2](http://en.wikipedia.org/wiki/SSE2) instructions available on all x86 platforms, including those released before 2011; + * `libint2::simd::VectorSSEFloat` -- vector of 4 packed floats that can be used with [SSE](http://en.wikipedia.org/wiki/Streaming_SIMD_Extensions) instructions available on all x86 platforms, including those released before 2011; + * `libint2::simd::VectorQPXDouble` -- vector of 4 packed doubles that can be used with QPX instructions available on recent PowerPC hardware (IBM Blue Gene/Q); + * `libint2::simd::VectorFP2Double` -- vector of 2 packed doubles that can be used with FP2 (Double Hummer) instructions available on older PowerPC hardware (IBM Blue Gene/P). + + With the exception of `float`, these are vector types implemented in Libint using compiler _intrinsics_, functions that translate directly into vector instructions. To use these vector types you may need to provide additional compiler flags that will enable support for vector instructions. For example, to enable support for AVX in Clang use the `-mavx` compiler flag. With Intel compiler use flag `-xHOST` to enable all vector instruction sets supported by the processor on which you are compiling. + + **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] + +----------------------------------------------------------------------------- + +# Consuming Libint + + +### Programming to Access Integrals + +* if you use C++11 or later (strongly recommended): read the [Libint Wiki](https://github.com/evaleev/libint/wiki/using-modern-CPlusPlus-API) +* if you use pre-2011 C++, C, Fortran, or any other language, refer to the [Libint Programmer's Manual](https://sourceforge.net/projects/libint/files/libint-for-beginners/progman-2.0.3-stable.pdf/download) for brief information on how to use the library in your code. + + +### Consumption Targets + +| Namespaced Target[^15] | CMake[^16] Component | Built by Default | Ensure Built | Ensure Excluded | Internal Target(s)[^17] | Alias[^18] | +| ---------------------- | -------------------- | ---------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------ | +| | | yes | always | impossible | `int-obj` | | +| `Libint2::int2` | `C` | yes | always | impossible | `int-{static,shared}` | `libint2` | +| `Libint2::cxx` | `CXX_ho` | yes | `LIBINT2_REQUIRE_CXX_API=ON` | `LIBINT2_REQUIRE_CXX_API=OFF` & withhold Eigen3 & `LIBINT2_REQUIRE_CXX_API_COMPILED=OFF` & `LIBINT2_ENABLE_PYTHON=OFF` | `int-cxx-headeronly-{static,shared}` | `libint2_cxx` | +| `Libint2::int2-cxx` | `CXX` | yes | `LIBINT2_REQUIRE_CXX_API_COMPILED=ON` | `LIBINT2_REQUIRE_CXX_API_COMPILED=OFF` | `int-cxx-{static,shared}` | | +| Fortran local[^19] | (NYI) | no | `LIBINT2_ENABLE_FORTRAN=ON` | `LIBINT2_ENABLE_FORTRAN=OFF` | `libint_f` | | + +[^15]: Targets for library consumer use. These are available after `find_package(Libint2)` or `add_subdirectory()`. +[^16]: Ensure target found in installation after `find_package(Libint2 COMPONENTS ...)`. +[^17]: Targets in src/lib/libint/CMakeLists.txt.export . Names subject to change. Use namespaced target names in any consuming code. +[^18]: Deprecated legacy aliases. Update any uses to namespaced target. +[^19]: The `libint_f` internal target defines the Fortran interface to Libint2. One must also link to `Libint2::int2` or `Libint2::cxx`. At present, it is not exported, and a namespaced target is not defined. + + +----------------------------------------------------------------------------- + +# GNU Autotools Update Guide + +* Notes + * Multiple option names can be from any long-lived branch but usually libtool+cmake --> final cmake+cmake. + +* `--enable-1body=N` --> `-D ENABLE_ONEBODY=N` +* `--enable-eri=N` --> `-D ENABLE_ERI=N` +* `--disable-eri` --> `-D ENABLE_ERI=-1` +* `--enable-eri3=N` --> `-D ENABLE_ERI3=N` +* `--enable-eri2=N` --> `-D ENABLE_ERI2=N` + +* `--with-shgauss-ordering=label` --> `-D LIBINT2_SHGAUSS_ORDERING=label` +* `--with-cartgauss-ordering=label` --> `-D LIBINT2_CARTGAUSS_ORDERING=label` +* `--with-shell-set=label` --> `-D LIBINT2_SHELL_SET=label` +* `--enable-eri3-pure-sh` --> `-D ERI3_PURE_SH=ON` +* `--enable-eri2-pure-sh` --> `-D ERI2_PURE_SH=ON` + +* `--with-max-am=N` --> `-D WITH_MAX_AM=N` +* `--with-max-am=N0,N1,N2` --> `-D WITH_MAX_AM="N0;N1;N2"` (notice semicolons and quotes. This is standard CMake list syntax) +* `--with-opt-am=N` --> `-D WITH_OPT_AM=N` +* `--with-opt-am=N0,N1,N2` --> `-D WITH_OPT_AM="N0;N1;N2"` + +* `--with-multipole-max-order=N` --> `-D MULTIPOLE_MAX_ORDER=N` + +* `--with-1body-max-am=N` --> `-D WITH_ONEBODY_MAX_AM=N` +* `--with-1body-max-am=N0,N1,N2` --> `-D WITH_ONEBODY_MAX_AM="N0;N1;N2"` +* `--with-1body-opt-am=N` --> `-D WITH_ONEBODY_OPT_AM=N` +* `--with-1body-opt-am=N0,N1,N2` --> `-D WITH_ONEBODY_OPT_AM="N0;N1;N2"` + +* `--with-eri-max-am=N` --> `-D WITH_ERI_MAX_AM=N` +* `--with-eri-max-am=N0,N1,N2` --> `-D WITH_ERI_MAX_AM="N0;N1;N2"` +* `--with-eri-opt-am=N` --> `-D WITH_ERI_OPT_AM=N` +* `--with-eri-opt-am=N0,N1,N2` --> `-D WITH_ERI_OPT_AM="N0;N1;N2"` + +* `--with-eri3-max-am=N` --> `-D WITH_ERI3_MAX_AM=N` +* `--with-eri3-max-am=N0,N1,N2` --> `-D WITH_ERI3_MAX_AM="N0;N1;N2"` +* `--with-eri3-opt-am=N` --> `-D WITH_ERI3_OPT_AM=N` +* `--with-eri3-opt-am=N0,N1,N2` --> `-D WITH_ERI3_OPT_AM="N0;N1;N2"` + +* `--with-eri2-max-am=N` --> `-D WITH_ERI2_MAX_AM=N` +* `--with-eri2-max-am=N0,N1,N2` --> `-D WITH_ERI2_MAX_AM="N0;N1;N2"` +* `--with-eri2-opt-am=N` --> `-D WITH_ERI2_OPT_AM=N` +* `--with-eri2-opt-am=N0,N1,N2` --> `-D WITH_ERI2_OPT_AM="N0;N1;N2"` + +* `--enable-g12=N` --> `-D ENABLE_G12=N` +* `--enable-g12dkh=N` --> `-D ENABLE_G12DKH` +* `--disable-t1g12-support` --> `-D ENABLE_T1G12_SUPPORT=OFF` +* `--with-g12-max-am=N` --> `-D WITH_G12_MAX_AM=N` +* `--with-g12-opt-am=N` --> `-D WITH_G12_OPT_AM=N` +* `--with-g12dkh-max-am=N` --> `-D WITH_G12DKH_MAX_AM=N` +* `--with-g12dkh-opt-am=N` --> `-D WITH_G12DKH_OPT_AM=N` + +* `--disable-1body-property-derivs` --> `-D DISABLE_ONEBODY_PROPERTY_DERIVS=ON` + +* `--enable-shared` --> `-D BUILD_SHARED=ON` --> `-D BUILD_SHARED_LIBS=ON` (standard CMake variable) +* `--enable-static` --> `-D BUILD_STATIC=ON` --> `-D BUILD_SHARED_LIBS=OFF` (standard CMake variable) +* `--enable-shared --enable-static` --> `-D BUILD_SHARED=ON -D BUILD_STATIC=ON` --> `-D LIBINT2_BUILD_SHARED_AND_STATIC_LIBS=ON` +* `-D REQUIRE_CXX_API=ON` --> `-D ENABLE_CXX11API=ON` --> `-D LIBINT2_REQUIRE_CXX_API=ON` +* `--enable-mpfr` --> assumed present --> `-D ENABLE_MPFR=ON` --> `-D LIBINT2_ENABLE_MPFR=ON` + +* (target) `libint2` --> `Libint2::int2` +* (target) `libint2_cxx` --> `Libint2::cxx` + +* `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 ENABLE_FORTRAN=ON` --> `-D LIBINT2_ENABLE_FORTRAN=ON` +* `-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` +* `--enable-unrolling=yes` --> `-D LIBINT_ENABLE_UNROLLING=1000000000` +* `--enable-unrolling=no` --> `-D LIBINT_ENABLE_UNROLLING=0` +* `--enable-unrolling=S` --> `-D LIBINT_ENABLE_UNROLLING=S` +* `--enable-generic-code` --> `-D LIBINT_ENABLE_GENERIC_CODE=ON` +* `--with-vector-length=N` --> `-D LIBINT_VECTOR_LENGTH=N` +* `--with-vector-method=choice` --> `-D LIBINT_VECTOR_METHOD=choice` +* `--with-align-size=N` --> `-D LIBINT_ALIGN_SIZE=N` (G) (`-D LIBINT2_ALIGN_SIZE=N` for L) +* `--enable-fma` --> `-D LIBINT_GENERATE_FMA=ON` +* `--enable-accum-ints` --> `-D LIBINT_ACCUM_INTS=ON` +* `--enable-flop-counter` -> `-D LIBINT_FLOP_COUNT=ON` +* `--enable-profile` --> `-D LIBINT_PROFILE=ON` +* `--disable-contracted-ints` --> `-D LIBINT_CONTRACTED_INTS=OFF` +* `--disable-single-evaltype` --> `-D LIBINT_SINGLE_EVALTYPE=OFF` (NYI) +* `--enable-composite-evaluators` --> `-D LIBINT_USE_COMPOSITE_EVALUATORS=ON` +* `--disable-composite-evaluators` --> `-D LIBINT_USE_COMPOSITE_EVALUATORS=OFF` +* `--with-eri-strategy=OS` --> `-D LIBINT_ERI_STRATEGY=0` +* `--with-eri-strategy=HL` --> `-D LIBINT_ERI_STRATEGY=2` +* `--with-real-type=type` --> `-D LIBINT2_REALTYPE=type` +* `--with-real-type-inclues=inc` --> `-D LIBINT_USER_DEFINED_REAL_INCLUDES="#include "` + + + + + ### Run-Time Compatibility Functions are provided to check the library configuration and solid harmonics orderings at runtime: @@ -92,7 +587,7 @@ Eventually, these will be CMake Components, too. "h" (h=spdfghikl...; s,p not enumerated) and derivative order "D" (D=0,1,2,...). For example, the presence of "eri_ffff_d1" means 4-center gradient ints are available for L=3. That is, the library was configured with at least - "--enable-eri=1 --with-eri-max-am=?,>=3". + '-D ENABLE_ERI=1 -D WITH_ERI_MAX_AM="?;>=3"'. eri_hhL_dD - library includes 2-body integrals with 3 centers and max angular momentum up to eri_hhl_dD Cartesian "h" for the two paired centers and Cartesian "l" or solid harmonics "L" for the unpaired/fitting center, (h/l=spdfghikl..., L=SPDFGHIKL...; l>=h @@ -101,9 +596,9 @@ Eventually, these will be CMake Components, too. solid harmonics are assumed for 3-center ints, "eri_hhl_dD" will *not be available*. For example, the presence of "eri_ffG_d0" means 3-center energy ints are available for L=3 (paired centers) and L=4 (fitting center). That is, the library - was configured with at least "--enable-eri3=0 --with-max-am=3 --with-eri3-max-am=4". + was configured with at least "-D ENABLE_ERI3=0 -D WITH_MAX_AM=3 -D WITH_ERI3_MAX_AM=4". The presence of "eri_ffg_d0" means the library configuration did not additionally - include "--enable-eri3-pure-sh[=yes]". + include "-D ERI3_PURE_SH=ON". eri_HH_dD - library includes 2-body integrals with 2 centers and max angular momentum up to eri_hh_dD Cartesian "h" or solid harmonics "H", (h=spdfghikl..., H=SPDFGHIKL...; s,p,S,P not enumerated) and derivative order "D" (D=0,1,2,...). The "eri_HH_dD" component is @@ -111,8 +606,8 @@ Eventually, these will be CMake Components, too. assumed for 2-center ints, "eri_hh_dD" will *not be available*. For example, the presence of "eri_FF_d2" means 2-center Hessian ints are available for L=3. That is, the library was configured with at least - "--enable-eri2=2 --with-eri2-max-am=?,?,>=3". The presence of "eri_ff_d2" means the - library configuration did not additionally include "--enable-eri2-pure-sh[=yes]". + '-D ENABLE_ERI2=2 -D WITH_ERI2_MAX_AM="?;?;>=3"'. The presence of "eri_ff_d2" means the + library configuration did not additionally include "-D ERI2_PURE_SH=ON". g12_hhhh_dD - library includes F12 integrals with Gaussian factors and max angular momentum up to "h" (h=spdfghikl...; s,p not enumerated) and derivative order "D" (D=0,1,2,...). For example, the presence of "g12_iiii_d2" means g12 Hessian ints are available for L=6. @@ -130,3 +625,27 @@ Eventually, these will be CMake Components, too. bs - library integrals use ordering bagel + standard = bagel bo - library integrals use ordering + orca ``` + +### Interfacing + +Eventually (approximately 2.9.0 CMake-based), additional functions will be available to retrive Libint version, commit, and literature citation. Below are outputs at the libtool stage. + +``` +auto Mmp = libint2::libint_version(); +printf("Version: Numeric=%s Sortable=%s Commit=%s\n", libint2::libint_version_string(false).c_str(), libint2::libint_version_string(true).c_str(), libint2::libint_commit().c_str()); +printf("Version: Major=%d minor=%d patch=%d\n", std::get<0>(Mmp), std::get<1>(Mmp), std::get<2>(Mmp)); +printf("Citation: DOI=%s Ref=%s\n", libint2::libint_reference_doi().c_str(), libint2::libint_reference().c_str()); +printf("Citation: BibTex=%s\n", libint2::libint_bibtex().c_str()); +``` +``` +Version: Numeric=2.8.0 Sortable= Commit= +Version: Major=2 minor=8 patch=0 +Citation: DOI= Ref=Libint: , Version Edward F. Valeev, http://libint.valeyev.net/ +Citation: BibTex=@Misc{Libint2, + author = {E.~F.~Valeev}, + title = {\textsc{Libint}: }, + howpublished = {http://libint.valeyev.net/}, + note = {version }, + year = {} +} +``` diff --git a/cmake/libint2-config.cmake.in b/cmake/libint2-config.cmake.in new file mode 100644 index 000000000..4061ef5c3 --- /dev/null +++ b/cmake/libint2-config.cmake.in @@ -0,0 +1,83 @@ +# libint2-config.cmake +# -------------------- +# +# Libint2 cmake module. +# This module sets the following variables in your project: +# +# :: +# +# Libint2_EXT_VERSION - Libint2 version including buildid, such as beta.3. Prefer target variable. +# Libint2_MAX_AM_ERI - maximum angular momentum level of Libint2 libraries for 4-center energy +# integrals. Prefer target variable. +# +# +# Available components: +# +# :: +# +# See https://github.com/evaleev/libint/blob/master/INSTALL.md#configuration-codes +# for more details on these codes. +# +# multipole_hh_dD - search for including spherical multipole integrals +# onebody_hh_dD - search for library including 1-body integrals +# eri_hhhh_dD - search for library including 2-body integrals with 4 centers +# eri_hhL_dD - search for library including 2-body integrals with 3 centers +# eri_hhl_dD - ditto +# eri_HH_dD - search for library including 2-body integrals with 2 centers +# eri_hh_dD - ditto +# g12_hhhh_dD - search for library including F12 integrals with Gaussian factors +# +# cart shell_set used_by +# -------- --------- ------- +# ( psi4 requires runtime-setting of ) +# ss - search for standard + standard = mpqc4, cp2k, psi4 ( solid harmonic ordering to Gaussian ) +# so - search for + orca +# is - search for intv3 + standard = mpqc3 +# io - search for + orca +# gs - search for gamess + standard = gamess +# go - search for + orca +# os - search for orca + standard +# oo - search for + orca = orca +# bs - search for bagel + standard = bagel +# bo - search for + orca + +@PACKAGE_INIT@ + +set(pnv libint2) # projectnameversion +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@ +# * LIBINT2_CARTGAUSS_ORDERING = @LIBINT2_CARTGAUSS_ORDERING@ +# * LIBINT2_SHELL_SET = @LIBINT2_SHELL_SET@ +set(${L2}_MAX_AM_ERI @Libint2_MAX_AM_ERI@) # Libint2_MAX_AM_ERI +foreach(_eri @Libint2_CONFIG_COMPONENTS@) # Libint2_CONFIG_COMPONENTS + set(${L2}_${_eri}_FOUND 1) +endforeach() + + + diff --git a/cmake/modules/.git_archival.txt b/cmake/modules/.git_archival.txt new file mode 100644 index 000000000..9d376cd1d --- /dev/null +++ b/cmake/modules/.git_archival.txt @@ -0,0 +1,4 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true,match=?[0-9.]*)$ +ref-names: $Format:%D$ diff --git a/cmake/modules/AddCustomTargetSubproject.cmake b/cmake/modules/AddCustomTargetSubproject.cmake new file mode 100644 index 000000000..d575d9fc4 --- /dev/null +++ b/cmake/modules/AddCustomTargetSubproject.cmake @@ -0,0 +1,46 @@ +# Copyright 2020 Eduard F Valeyev +# Distributed under the OSI-approved BSD 3-Clause License. +# See https://opensource.org/licenses/BSD-3-Clause for details. + +# copy of https://github.com/BTAS/BTAS/blob/master/cmake/modules/AddCustomTargetSubproject.cmake +# +# add_custom_target_subproject(proj X ...) defines custom target X-proj and +# - if target X already exists, makes it depend on X-proj +# - else creates target X depending on X-proj +# +# use case: if custom target names (e.g. "check", "doc", etc.) clash +# with other project's target when used as a subproject +# +# example: add_custom_target_subproject(myproject check USES_TERMINAL COMMAND ${CMAKE_CTEST_COMMAND} -V) +# + +macro(add_custom_target_subproject _subproj _name) + + set(extra_args "${ARGN}") + add_custom_target(${_name}-${_subproj} ${extra_args}) + + # does the newly-created target get compiled by default? + list(FIND extra_args "ALL" extra_args_has_all) + if (NOT (extra_args_has_all EQUAL -1)) + set (target_built_by_default ON) + endif() + + if (TARGET ${_name}) + # is existing target ${_name} also compiled by default? + # warn if not, but this project's target is since that + # may indicate inconsistent creation of generic targets + get_target_property(supertarget_not_built_by_default ${_name} EXCLUDE_FROM_ALL) + if (target_built_by_default AND supertarget_not_built_by_default) + message(WARNING "Created target ${_name}-${_subproj} is built by default but \"super\"-target ${_name} is not; perhaps it should be?") + endif() + add_dependencies(${_name} ${_name}-${_subproj}) + else (TARGET ${_name}) + # use ALL if given + if (target_built_by_default) + add_custom_target(${_name} ALL DEPENDS ${_name}-${_subproj}) + else (target_built_by_default) + add_custom_target(${_name} DEPENDS ${_name}-${_subproj}) + endif(target_built_by_default) + endif (TARGET ${_name}) + +endmacro() diff --git a/cmake/modules/DynamicVersion.cmake b/cmake/modules/DynamicVersion.cmake new file mode 100644 index 000000000..4c62477fa --- /dev/null +++ b/cmake/modules/DynamicVersion.cmake @@ -0,0 +1,661 @@ +# copied from https://github.com/LecrisUT/CMakeExtraUtils/blob/main/cmake/DynamicVersion.md 15 Jan 2023 + +#[===[.md: +# DynamicVersion + +Helper module to get the project's version dynamically. Format is compatible with python's +[`setuptools_scm`](https://github.com/pypa/setuptools_scm#git-archives) + +## Commands + +- {command}`dynamic_version` + +]===] + +include_guard() +list(APPEND CMAKE_MESSAGE_CONTEXT DynamicVersion) +if (POLICY CMP0140) + # Enable using return(PROPAGATE) + # TODO: Remove when cmake 3.25 is commonly distributed + cmake_policy(SET CMP0140 NEW) +endif () + +#[==============================================================================================[ +# Preparations # +]==============================================================================================] + +# No specific preparations + +#[==============================================================================================[ +# Main interface # +]==============================================================================================] + +function(dynamic_version) + #[===[.md: + # dynamic_version + + Configure project to use dynamic versioning + + ## Synopsis + ```cmake + Main interface + dynamic_version(PROJECT_PREFIX ) + dynamic_version(PROJECT_PREFIX + [OUTPUT_VERSION ] [OUTPUT_VERSION_FULL ] + [OUTPUT_DESCRIBE ] [OUTPUT_COMMIT ] + [OUTPUT_DISTANCE ] [OUTPUT_SHORT_HASH ] + [VERSION_FULL_MODE ] + [PROJECT_SOURCE ] [GIT_ARCHIVAL_FILE ] + ) + + Fallbacks + dynamic_version(... + [ALLOW_FAILS] [FALLBACK_VERSION ] [FALLBACK_HASH ]) + + Additional configurations + dynamic_version(... + [TMP_FOLDER ] [FALLBACK_VERSION ] [FALLBACK_HASH ]) + ``` + + ## Options + `PROJECT_PREFIX` + Prefix to be used for namespacing targets, typically ${PROJECT_NAME} + + `OUTPUT_VERSION` [Default: PROJECT_VERSION] + Variable where to save the calculated version + + `OUTPUT_VERSION_FULL` [Default: PROJECT_VERSION_FULL] + Variable where to save the full version in the format + + `VERSION_FULL_MODE` [Default: DEV] + Format of the `OUTPUT_VERSION_FULL`. Must be one of [`DEV`, `POST`] + + `OUTPUT_DESCRIBE` [Default: GIT_DESCRIBE] + Variable where to save the pure `git describe` output + + `OUTPUT_COMMIT` [Default: GIT_COMMIT] + Variable where to save the current git commit hash + + `OUTPUT_DISTANCE` [Default: GIT_DISTANCE] + Variable where to save the distance from git tag + + `OUTPUT_SHORT_HASH` [Default: GIT_SHORT_HASH] + Variable where to save the shortened git commit hash + + `PROJECT_SOURCE` [Default: `${CMAKE_CURRENT_SOURCE_DIR}`] + Location of the project source. Has to be either an extracted git archive or a git clone + + `GIT_ARCHIVAL_FILE` [Default: `${PROJECT_SOURCE}/.git_archival.txt`] + Location of `.git_archival.txt` file. See [pypa/setuptools_scm](https://github.com/pypa/setuptools_scm#git-archives) + for more details + + `FALLBACK_VERSION` + Fallback version to be set if version cannot be dynamically determined. Implies `ALLOW_FAILS` + + `FALLBACK_HASH` + Fallback git hash to be used in `OUTPUT_COMMIT` if commit cannot be determined. + If not defined target GitHash will not be created if project is not a git repo + + `ALLOW_FAILS` + Do not return with `FATAL_ERROR` if version cannot be dynamically determined. CMakeLists author is responsible + for setting appropriate version if fails + + ### Additional configuration options + + `TMP_FOLDER` [Default: `${CMAKE_CURRENT_BINARY_DIR}/tmp`] + Temporary path to store `DynamicVersion`'s temporary files + + `OUTPUT_FOLDER` [Default: `${CMAKE_CURRENT_BINARY_DIR}`] + Path where to store generated files + + ## Targets + `${PROJECT_PREFIX}Version` + Target that recalculates the dynamic version each time. See [](#Output-files) for using dependencies that only + change when the actual commit/describe/version change. + + `${PROJECT_PREFIX}GitHash` + Target that recalculates the git hash each time. + + ## Output files + :::{note} + These files are updated only when the contents change. You can use them as dependencies for files generated from + . See for more + info on how to add file-level dependency + ::: + + `${OUTPUT_FOLDER}/.DynamicVersion.json` + All computed data of `DynamicVersion` + + `${OUTPUT_FOLDER}/.version` + Computed version + + `${OUTPUT_FOLDER}/.git_describe` + Computed git describe + + `${OUTPUT_FOLDER}/.git_commit` + Current commit + + ## See also + - [pypa/setuptools_scm](https://github.com/pypa/setuptools_scm) + + ]===] + + list(APPEND CMAKE_MESSAGE_CONTEXT dynamic_version) + set(ARGS_Options + ALLOW_FAILS + ) + set(ARGS_OneValue + PROJECT_PREFIX + OUTPUT_VERSION + OUTPUT_VERSION_FULL + VERSION_FULL_MODE + OUTPUT_DESCRIBE + OUTPUT_COMMIT + OUTPUT_DISTANCE + OUTPUT_SHORT_HASH + PROJECT_SOURCE + GIT_ARCHIVAL_FILE + FALLBACK_VERSION + FALLBACK_HASH + TMP_FOLDER + OUTPUT_FOLDER + ) + set(ARGS_MultiValue + ) + + cmake_parse_arguments(PARSE_ARGV 0 ARGS "${ARGS_Options}" "${ARGS_OneValue}" "${ARGS_MultiValue}") + + set(DynamicVersion_ARGS) + + # Set default values + if (NOT DEFINED ARGS_OUTPUT_VERSION) + set(ARGS_OUTPUT_VERSION PROJECT_VERSION) + endif () + if (NOT DEFINED ARGS_OUTPUT_VERSION_FULL) + set(ARGS_OUTPUT_VERSION_FULL PROJECT_VERSION_FULL) + endif () + if (NOT DEFINED ARGS_VERSION_FULL_MODE) + set(ARGS_VERSION_FULL_MODE DEV) + elseif (NOT ARGS_VERSION_FULL_MODE MATCHES "(DEV|POST)") + message(FATAL_ERROR "Unsupported VERSION_FULL_MODE = ${ARGS_VERSION_FULL_MODE}") + endif () + if (NOT DEFINED ARGS_OUTPUT_DESCRIBE) + set(ARGS_OUTPUT_DESCRIBE GIT_DESCRIBE) + endif () + if (NOT DEFINED ARGS_OUTPUT_COMMIT) + set(ARGS_OUTPUT_COMMIT GIT_COMMIT) + endif () + if (NOT DEFINED ARGS_OUTPUT_DISTANCE) + set(ARGS_OUTPUT_DISTANCE GIT_DISTANCE) + endif () + if (NOT DEFINED ARGS_OUTPUT_SHORT_HASH) + set(ARGS_OUTPUT_SHORT_HASH GIT_SHORT_HASH) + endif () + if (NOT DEFINED ARGS_PROJECT_SOURCE) + set(ARGS_PROJECT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) + endif () + if (NOT DEFINED ARGS_GIT_ARCHIVAL_FILE) + set(ARGS_GIT_ARCHIVAL_FILE ${ARGS_PROJECT_SOURCE}/.git_archival.txt) + endif () + if (DEFINED ARGS_FALLBACK_VERSION OR ARGS_ALLOW_FAILS) + # If we have a fallback version or it is specified it is ok if this fails, don't make messages FATAL_ERROR + set(error_message_type AUTHOR_WARNING) + else () + # Otherwise it should + set(error_message_type FATAL_ERROR) + endif () + if (NOT ARGS_PROJECT_PREFIX) + message(AUTHOR_WARNING + "No PROJECT_PREFIX was given. Please provide one to avoid target name clashes" + ) + elseif (NOT ARGS_PROJECT_PREFIX MATCHES ".*_$") + # Append an underscore _ to the prefix if not provided + message(AUTHOR_WARNING + "PROJECT_PREFIX did not contain an underscore, please add it for clarity" + ) + set(ARGS_PROJECT_PREFIX ${ARGS_PROJECT_PREFIX}_) + endif () + if (NOT DEFINED ARGS_TMP_FOLDER) + set(ARGS_TMP_FOLDER ${CMAKE_CURRENT_BINARY_DIR}/tmp) + endif () + if (NOT DEFINED ARGS_OUTPUT_FOLDER) + set(ARGS_OUTPUT_FOLDER ${CMAKE_CURRENT_BINARY_DIR}) + endif () + if (ARGS_OUTPUT_FOLDER EQUAL ARGS_TMP_FOLDER) + message(FATAL_ERROR + "OUTPUT_FOLDER and TMP_FOLDER cannot point to the same path" + ) + endif () + + list(APPEND DynamicVersion_ARGS + PROJECT_SOURCE ${ARGS_PROJECT_SOURCE} + GIT_ARCHIVAL_FILE ${ARGS_GIT_ARCHIVAL_FILE} + TMP_FOLDER ${ARGS_TMP_FOLDER} + VERSION_FULL_MODE ${ARGS_VERSION_FULL_MODE} + ) + if (DEFINED ARGS_FALLBACK_VERSION) + list(APPEND DynamicVersion_ARGS + FALLBACK_VERSION ${ARGS_FALLBACK_VERSION}) + endif () + if (DEFINED ARGS_FALLBACK_HASH) + list(APPEND DynamicVersion_ARGS + FALLBACK_HASH ${ARGS_FALLBACK_HASH}) + endif () + if (ARGS_ALLOW_FAILS) + list(APPEND DynamicVersion_ARGS ALLOW_FAILS) + endif () + # Normalize DynamicVersion_ARGS to be passed as string + list(JOIN DynamicVersion_ARGS "\\;" DynamicVersion_ARGS) + + # Execute get_dynamic_version once to know the current configuration + execute_process(COMMAND ${CMAKE_COMMAND} + -DDynamicVersion_RUN:BOOL=True + # Note: DynamicVersion_ARGS cannot be escaped with "" + -DDynamicVersion_ARGS:STRING=${DynamicVersion_ARGS} + -P ${CMAKE_CURRENT_FUNCTION_LIST_FILE} + COMMAND_ERROR_IS_FATAL ANY) + + # Copy all configured files + foreach (file IN ITEMS .DynamicVersion.json .version .git_describe .git_commit) + if (EXISTS ${ARGS_TMP_FOLDER}/${file}) + if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.21) + file(COPY_FILE ${ARGS_TMP_FOLDER}/${file} ${ARGS_OUTPUT_FOLDER}/${file}) + else () + file(COPY ${ARGS_TMP_FOLDER}/${file} DESTINATION ${ARGS_OUTPUT_FOLDER}/) + endif () + endif () + endforeach () + + # Check configuration state + file(READ ${ARGS_TMP_FOLDER}/.DynamicVersion.json data) + # failed, mode, and version are always set if get_dynamic_version did not exit with failure + string(JSON failed GET ${data} failed) + string(JSON ${ARGS_OUTPUT_VERSION} GET ${data} version) + string(JSON ${ARGS_OUTPUT_VERSION_FULL} GET ${data} version-full) + # Other outputs are optional, populate the variables if found + # These are populated if failed = false + string(JSON ${ARGS_OUTPUT_SHORT_HASH} ERROR_VARIABLE _ GET ${data} short-hash) + string(JSON ${ARGS_OUTPUT_DISTANCE} ERROR_VARIABLE _ GET ${data} distance) + # These may not be populated depending on mode + string(JSON ${ARGS_OUTPUT_COMMIT} ERROR_VARIABLE _ GET ${data} commit) + string(JSON ${ARGS_OUTPUT_DESCRIBE} ERROR_VARIABLE _ GET ${data} describe) + + # Configure targets + if (failed) + # If configuration failed, create dummy targets + add_custom_target(${ARGS_PROJECT_PREFIX}Version + COMMAND ${CMAKE_COMMAND} -E true) + add_custom_target(${ARGS_PROJECT_PREFIX}GitHash + COMMAND ${CMAKE_COMMAND} -E true) + else () + # Otherwise create the targets outputting to the appropriate files + add_custom_target(${ARGS_PROJECT_PREFIX}DynamicVersion ALL + BYPRODUCTS ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${ARGS_TMP_FOLDER}/.git_describe ${ARGS_TMP_FOLDER}/.version + COMMAND ${CMAKE_COMMAND} + -DDynamicVersion_RUN:BOOL=True + # Note: For some reason DynamicVersion_ARGS needs "" here, but it doesn't in execute_process + -DDynamicVersion_ARGS:STRING="${DynamicVersion_ARGS}" + -P ${CMAKE_CURRENT_FUNCTION_LIST_FILE} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${ARGS_OUTPUT_FOLDER}/.DynamicVersion.json + ) + set(extra_version_args) + # .git_describe might not be generated, e.g. if it's an sdist. Make it optional + if (EXISTS ${ARGS_OUTPUT_FOLDER}/.git_describe) + list(APPEND extra_version_args + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.git_describe ${ARGS_OUTPUT_FOLDER}/.git_describe + ) + endif () + add_custom_target(${ARGS_PROJECT_PREFIX}Version ALL + DEPENDS ${ARGS_PROJECT_PREFIX}DynamicVersion + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.version ${ARGS_OUTPUT_FOLDER}/.version + ${extra_version_args} + ) + # .git_commit might not exist, make the target a no-op in that case + if (NOT EXISTS ${ARGS_OUTPUT_FOLDER}/.git_commit) + add_custom_target(${ARGS_PROJECT_PREFIX}GitHash + COMMAND ${CMAKE_COMMAND} -E true) + else () + add_custom_target(${ARGS_PROJECT_PREFIX}GitHash + DEPENDS ${ARGS_PROJECT_PREFIX}DynamicVersion + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${ARGS_TMP_FOLDER}/.git_commit ${ARGS_OUTPUT_FOLDER}/.git_commit + ) + endif () + endif () + + # This ensures that the project is reconfigured (at least at second run) whenever the version changes + set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${ARGS_OUTPUT_FOLDER}/.version) + + message(VERBOSE + "Calculated version = ${${ARGS_OUTPUT_VERSION}}" + ) + + if (CMAKE_VERSION VERSION_LESS 3.25) + # TODO: Remove when cmake 3.25 is commonly distributed + set(${ARGS_OUTPUT_DESCRIBE} ${${ARGS_OUTPUT_DESCRIBE}} PARENT_SCOPE) + set(${ARGS_OUTPUT_VERSION} ${${ARGS_OUTPUT_VERSION}} PARENT_SCOPE) + set(${ARGS_OUTPUT_VERSION_FULL} ${${ARGS_OUTPUT_VERSION_FULL}} PARENT_SCOPE) + set(${ARGS_OUTPUT_COMMIT} ${${ARGS_OUTPUT_COMMIT}} PARENT_SCOPE) + set(${ARGS_OUTPUT_DISTANCE} ${${ARGS_OUTPUT_DISTANCE}} PARENT_SCOPE) + set(${ARGS_OUTPUT_SHORT_HASH} ${${ARGS_OUTPUT_SHORT_HASH}} PARENT_SCOPE) + endif () + return(PROPAGATE + ${ARGS_OUTPUT_DESCRIBE} + ${ARGS_OUTPUT_VERSION} + ${ARGS_OUTPUT_VERSION_FULL} + ${ARGS_OUTPUT_COMMIT} + ${ARGS_OUTPUT_DISTANCE} + ${ARGS_OUTPUT_SHORT_HASH} + ) +endfunction() + + +#[==============================================================================================[ +# Auxiliary interface # +]==============================================================================================] + +function(get_dynamic_version) + #[===[.md: + # get_dynamic_version + + Internal function that is called to calculate the dynamic version. This function is called by the + `${PROJECT_PREFIX}DynamicVersion` targets generated by {command}`dynamic_version`. + + In a nutshell, the `DynamicVersion.cmake` is executed with the variable `DynamicVersion_RUN` set to true. + + ## Synopsis + ```cmake + get_dynamic_version(PROJECT_SOURCE GIT_ARCHIVAL_FILE + TMP_FOLDER VERSION_FULL_MODE + [FALLBACK_VERSION ] [FALLBACK_HASH ] + ) + ``` + + ## Options + See {command}`dynamic_version` for details + + ## See also + - [pypa/setuptools_scm](https://github.com/pypa/setuptools_scm) + + ]===] + + list(APPEND CMAKE_MESSAGE_CONTEXT get_dynamic_version) + set(ARGS_Options + ALLOW_FAILS + ) + set(ARGS_OneValue + PROJECT_SOURCE + VERSION_FULL_MODE + GIT_ARCHIVAL_FILE + FALLBACK_VERSION + FALLBACK_HASH + TMP_FOLDER + ) + set(ARGS_MultiValue + ) + + cmake_parse_arguments(PARSE_ARGV 0 ARGS "${ARGS_Options}" "${ARGS_OneValue}" "${ARGS_MultiValue}") + + if (DEFINED ARGS_FALLBACK_VERSION OR ARGS_ALLOW_FAILS) + # If we have a fallback version or it is specified it is ok if this fails, don't make messages FATAL_ERROR + set(error_message_type AUTHOR_WARNING) + else () + # Otherwise it should fail + set(error_message_type FATAL_ERROR) + endif () + + set(data "{}") + # Default set + string(JSON data SET ${data} failed true) + if (ARGS_ALLOW_FAILS) + string(JSON data SET ${data} allow-fails true) + else () + string(JSON data SET ${data} allow-fails false) + endif () + + # Set fallback values + if (DEFINED ARGS_FALLBACK_VERSION) + string(JSON data SET + ${data} version \"${ARGS_FALLBACK_VERSION}\") + string(JSON data SET + ${data} version-full \"${ARGS_FALLBACK_VERSION}\") + file(WRITE ${ARGS_TMP_FOLDER}/.version ${ARGS_FALLBACK_VERSION}) + endif () + if (DEFINED ARGS_FALLBACK_HASH) + string(JSON data SET + ${data} commit \"${ARGS_FALLBACK_HASH}\") + file(WRITE ${ARGS_TMP_FOLDER}/.git_commit ${ARGS_FALLBACK_HASH}) + endif () + + file(WRITE ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${data}) + + # Check git_archival.txt file is present and properly written + if (NOT EXISTS ${ARGS_GIT_ARCHIVAL_FILE}) + # If git_archival.txt is missing, project is ill-formed + message(${error_message_type} + "Missing file .git_archival.txt\n" + " .git_archival.txt: ${ARGS_GIT_ARCHIVAL_FILE}" + ) + return() + endif () + file(STRINGS ${ARGS_GIT_ARCHIVAL_FILE} describe-name + REGEX "^describe-name:.*") + if (NOT describe-name) + # If git_archival.txt does not contain the field "describe-name:", it is ill-formed + message(${error_message_type} + "Missing string \"describe-name\" in .git_archival.txt\n" + " .git_archival.txt: ${ARGS_GIT_ARCHIVAL_FILE}" + ) + return() + endif () + + # Try to get the version statically, and if it fails, get it from VCS + if (EXISTS ${ARGS_PROJECT_SOURCE}/PKG-INFO) + # Case1: Python sdist archive. Get everything from PKG-INFO file + set(mode pkg-info) + file(STRINGS ${ARGS_PROJECT_SOURCE}/PKG-INFO version + REGEX "^Version:[ ]?(([0-9\\.]+)([a-zA-Z0-9]*)?(\\.(dev|post)([0-9]+)\\+g([a-f0-9]+))?)$") + # Cannot use Regex match from here, need to run string(REGEX MATCH) again + # https://gitlab.kitware.com/cmake/cmake/-/issues/23770 + string(REGEX MATCH "^Version:[ ]?(([0-9\\.]+)([a-zA-Z0-9]*)?(\\.(dev|post)([0-9]+)\\+g([a-f0-9]+))?)$" version "${version}") + # Regex match groups: https://regex101.com/r/G4Ox4X/5 + # 1: Full version string + # 2: Version string + # 3: Version suffix (e.g. rc, alpha, etc.) + # 4: Development suffix + # 5: dev/post + # 6: git distance + # 7: short_hash + set(version-full ${CMAKE_MATCH_1}) + set(version ${CMAKE_MATCH_2}) + set(version-suffix ${CMAKE_MATCH_3}) + if (CMAKE_MATCH_4) + set(distance ${CMAKE_MATCH_6}) + set(short-hash ${CMAKE_MATCH_7}) + string(JSON data SET + ${data} dev-type \"${CMAKE_MATCH_5}\") + else () + set(distance 0) + endif () + message(DEBUG "Found version in PKG-INFO") + elseif (describe-name MATCHES "^describe-name:[ ]?(v?([0-9\\.]+)(-?[a-zA-z0-9]*)?(-([0-9]+)-g([a-f0-9]+))?)$") + # Case2: Git archive. Get everything from git_archival.txt file + set(mode git-archive) + # Regex match groups: https://regex101.com/r/osVZpm/4 + # 1: Git describe + # 2: Version string + # 3: Version suffix (e.g. rc, alpha, etc.) + # 4: Development suffix + # 5: git distance + # 6: short_hash + set(describe ${CMAKE_MATCH_1}) + set(version ${CMAKE_MATCH_2}) + set(version-suffix ${CMAKE_MATCH_3}) + if (CMAKE_MATCH_4) + set(distance ${CMAKE_MATCH_5}) + set(short-hash ${CMAKE_MATCH_6}) + else () + set(distance 0) + endif () + # Get commit hash + file(STRINGS ${ARGS_GIT_ARCHIVAL_FILE} node + REGEX "^node:[ ]?(.*)") + # Cannot use Regex match from here, need to run string(REGEX MATCH) again + # https://gitlab.kitware.com/cmake/cmake/-/issues/23770 + string(REGEX MATCH "^node:[ ]?(.*)" node "${node}") + set(commit ${CMAKE_MATCH_1}) + message(DEBUG "Found version in git-archival.txt") + else () + # Default: Git repository. Call git commands + set(mode git) + find_package(Git REQUIRED) + # Test if project is a git repository + execute_process(COMMAND ${GIT_EXECUTABLE} status + WORKING_DIRECTORY ${ARGS_PROJECT_SOURCE} + RESULT_VARIABLE git_status_result + OUTPUT_QUIET) + if (NOT git_status_result EQUAL 0) + message(${error_message_type} + "Project source is neither a git repository nor a git archive:\n" + " Source: ${ARGS_PROJECT_SOURCE}" + ) + return() + endif () + # Get version and describe-name + execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long --match=?[0-9.]* + WORKING_DIRECTORY ${ARGS_PROJECT_SOURCE} + OUTPUT_VARIABLE describe-name + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + # Match any part containing digits and periods (strips out rc and so on) + if (NOT describe-name MATCHES "^(v?([0-9\\.]+)(-?[a-zA-z0-9]*)?(-([0-9]+)-g([a-f0-9]+))?)$") + message(${error_message_type} + "Version tag is ill-formatted\n" + " Describe-name: ${describe-name}" + ) + return() + endif () + # Regex match groups: https://regex101.com/r/GIfYI1/2 + # 1: Git describe + # 2: Version string + # 3: Version suffix (e.g. rc, alpha, etc.) + # 4: Development suffix + # 5: git distance + # 6: short_hash + set(describe ${CMAKE_MATCH_1}) + set(version ${CMAKE_MATCH_2}) + set(version-suffix ${CMAKE_MATCH_3}) + if (CMAKE_MATCH_4) + set(distance ${CMAKE_MATCH_5}) + set(short-hash ${CMAKE_MATCH_6}) + else () + set(distance 0) + endif () + # Get commit hash + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY ${ARGS_PROJECT_SOURCE} + OUTPUT_VARIABLE commit + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + message(DEBUG "Found version git repo") + endif () + + # Construct the version_full if it was not already provided + if (NOT version-full) + string(REGEX REPLACE "[-_]" "" version-suffix-sanitized "${version-suffix}") + if (distance EQUAL 0) + # If the distance is 0, just use the original tag version with sanitized suffix + set(version-full "${version}${version-suffix-sanitized}") + else () + # Otherwise construct it according to VERSION_FULL_MODE + if (ARGS_VERSION_FULL_MODE STREQUAL DEV) + # In DEV mode, we bump the last digit of the version. If this is in version-suffix, like `-rcX`, then + # this must be bumped instead + if (version-suffix-sanitized MATCHES "([a-zA-Z]*)([0-9]+)") + math(EXPR bumped_number "${CMAKE_MATCH_2} + 1") + set(version-suffix-sanitized "${CMAKE_MATCH_1}${bumped_number}") + elseif (version MATCHES "([0-9\\.]*\\.)([0-9]+)") + math(EXPR bumped_number "${CMAKE_MATCH_2} + 1") + set(version "${CMAKE_MATCH_1}${bumped_number}") + else () + message(FATAL_ERROR "Assert False: version = ${version}") + endif () + set(version-full "${version}${version-suffix-sanitized}.dev${distance}+g${short-hash}") + elseif (ARGS_VERSION_FULL_MODE STREQUAL POST) + set(version-full "${version}${version-suffix-sanitized}.post${distance}+g${short-hash}") + else () + message(FATAL_ERROR "Assert False: VERSION_FULL_MODE = ${ARGS_VERSION_FULL_MODE}") + endif () + endif () + endif () + + # Construct the JSON data + string(JSON data SET ${data} + mode \"${mode}\" + ) + string(JSON data SET ${data} + version \"${version}\" + ) + file(WRITE ${ARGS_TMP_FOLDER}/.version ${version}) + string(JSON data SET ${data} + version-full \"${version-full}\" + ) + if (describe) + string(JSON data SET ${data} + describe \"${describe}\" + ) + file(WRITE ${ARGS_TMP_FOLDER}/.git_describe ${describe}) + endif () + if (commit) + string(JSON data SET ${data} + commit \"${commit}\" + ) + file(WRITE ${ARGS_TMP_FOLDER}/.git_commit ${commit}) + endif () + set(JSON data SET ${data} + version-full \"${version-full}\" + ) + string(JSON data SET ${data} + version-suffix \"${version-suffix}\" + ) + string(JSON data SET ${data} + distance ${distance} + ) + if (short-hash) + string(JSON data SET ${data} + short-hash \"${short-hash}\" + ) + endif () + + # Mark success and output results + string(JSON data SET ${data} failed false) + message(DEBUG + "Computed data:\n" + " data = ${data}" + ) + file(WRITE ${ARGS_TMP_FOLDER}/.DynamicVersion.json ${data}) +endfunction() + + +#[==============================================================================================[ +# Private interface # +]==============================================================================================] + +# No private interface + + +#[==============================================================================================[ +# Misc # +]==============================================================================================] + +# Logic to run get_dynamic_version() by running this script +if (DynamicVersion_RUN) + if (NOT DEFINED DynamicVersion_ARGS) + message(FATAL_ERROR + "DynamicVersion_ARGS not defined" + ) + endif () + get_dynamic_version(${DynamicVersion_ARGS}) +endif () + +list(POP_BACK CMAKE_MESSAGE_CONTEXT) 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/autocmake_safeguards.cmake b/cmake/modules/autocmake_safeguards.cmake new file mode 100644 index 000000000..cf7d56757 --- /dev/null +++ b/cmake/modules/autocmake_safeguards.cmake @@ -0,0 +1,27 @@ +# Downloaded from +# https://github.com/coderefinery/autocmake/blob/master/modules/safeguards.cmake +# * changed text of in-source message + +#.rst: +# +# Provides safeguards against in-source builds and bad build types. +# +# Variables used:: +# +# PROJECT_SOURCE_DIR +# PROJECT_BINARY_DIR +# CMAKE_BUILD_TYPE + +if(${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR}) + message(FATAL_ERROR "In-source builds not allowed. Please run CMake from top directory and specify a build directory (e.g., cmake -S. -Bbuild).") +endif() + +string(TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_tolower) +string(TOUPPER "${CMAKE_BUILD_TYPE}" cmake_build_type_toupper) + +if(NOT cmake_build_type_tolower STREQUAL "debug" AND + NOT cmake_build_type_tolower STREQUAL "release" AND + NOT cmake_build_type_tolower STREQUAL "minsizerel" AND + NOT cmake_build_type_tolower STREQUAL "relwithdebinfo") + message(FATAL_ERROR "Unknown build type \"${CMAKE_BUILD_TYPE}\". Allowed values are Debug, Release, MinSizeRel, RelWithDebInfo (case-insensitive).") +endif() diff --git a/cmake/modules/int_am.cmake b/cmake/modules/int_am.cmake new file mode 100644 index 000000000..cdf6ec50e --- /dev/null +++ b/cmake/modules/int_am.cmake @@ -0,0 +1,456 @@ +# handle the defaulting and setting of the following variables +# * ENABLE_[ONEBODY|ERI2|ERI3|ERI|G12|G12DKH] +# * [LIBINT|ONEBODY|ERI2|ERI3|ERI|G12|G12DKH]_[MAX|OPT]_AM[|_LIST] +# * MULTIPOLE_MAX_ORDER +# * LIBINT_ONEBODY_DERIV +# * LIBINT_SUPPORTS_ONEBODY +# * SUPPORT_T1G12 + +# "_candidate" variables are not needed for config.h but are used to figure out +# the AM limits at the CMake level so that libint2-config.cmake components may +# be defined and consuming codes can state their requirements. For example, +# `find_package(Libint2 REQUIRED COMPONENTS eri_hhhh_d1)` requires the detected +# library to include gradient integrals of at least AM=5. +# See INSTALL.md for details. + +set(LIBINT_HARD_MAX_AM 12) +# amstr = "SPDFGHIKLMNOPQRTUVWXYZ" +set(_am2 "d") +set(_am3 "f") +set(_am4 "g") +set(_am5 "h") +set(_am6 "i") +set(_am7 "k") +set(_am8 "l") +set(_am9 "m") +set(_am10 "n") +set(_am11 "o") +set(_am12 "p") +set(_AM2 "D") +set(_AM3 "F") +set(_AM4 "G") +set(_AM5 "H") +set(_AM6 "I") +set(_AM7 "K") +set(_AM8 "L") +set(_AM9 "M") +set(_AM10 "N") +set(_AM11 "O") +set(_AM12 "P") + +macro(numerical_max_of_list ansvar liste) + set(_max "-100") + foreach(_i ${liste}) + if (${_i} GREATER _max) + set(_max "${_i}") + endif() + endforeach() + set(${ansvar} "${_max}") +endmacro() + + +message(STATUS "Processing integrals classes ...") + +# <<< overall derivatives level >>> + +set(_glob_classes_derivs ${ENABLE_ONEBODY};${ENABLE_ERI};${ENABLE_ERI3};${ENABLE_ERI2};${ENABLE_G12};${ENABLE_G12DKH}) +numerical_max_of_list(_max_deriv "${_glob_classes_derivs}") +message(STATUS "Preparing highest derivative level ${_max_deriv}") + +# <<< overall max_am defaults >>> + +list(LENGTH WITH_MAX_AM _ntokens_maxam) +if (_ntokens_maxam GREATER 1) + math(EXPR _ntokens_xptd_max_deriv "${_max_deriv} + 1") + if (NOT _ntokens_xptd_max_deriv EQUAL _ntokens_maxam) + message(FATAL_ERROR "Invalid value for WITH_MAX_AM (${WITH_MAX_AM}). Highest ENABLE_ derivative (${_max_deriv}) requires list length ${_ntokens_xptd_max_deriv}, not ${_ntokens_maxam}.") + endif() + + numerical_max_of_list(_max_am "${WITH_MAX_AM}") + list(JOIN WITH_MAX_AM "," _sam) + set(LIBINT_MAX_AM_LIST ${_sam}) + # when LIST populated, only overall LIBINT (not specific ints classes) sets both MAX_AM & MAX_AM_LIST + set(LIBINT_MAX_AM ${_max_am}) + set(_max_LIBINT_MAX_AM ${LIBINT_MAX_AM}) +else() + set(LIBINT_MAX_AM_LIST "") + set(LIBINT_MAX_AM ${WITH_MAX_AM}) +endif() + +foreach(_d RANGE 0 ${_max_deriv}) + if (${_d} LESS _ntokens_maxam) + list(GET WITH_MAX_AM ${_d} _candidate0_d${_d}) + else() + set(_candidate0_d${_d} "-1") + endif() + # _candidate0_dD=int_am defined up to highest ENABLE_cls deriv D from best info from WITH_MAX_AM + message(VERBOSE "setting _candidate0_d${_d}=${_candidate0_d${_d}}") +endforeach() + +if (LIBINT_MAX_AM GREATER_EQUAL ${LIBINT_HARD_MAX_AM}) + message(WARNING "LIBINT_MAX_AM=${LIBINT_MAX_AM} is greater than ${LIBINT_HARD_MAX_AM}. Are you sure you know what you are doing?") +elseif (LIBINT_MAX_AM LESS_EQUAL 0) + message(FATAL_ERROR "Invalid value for LIBINT_MAX_AM (${LIBINT_MAX_AM}).") +endif() + +message(STATUS "Preparing generic LIBINT_MAX_AM_LIST ${LIBINT_MAX_AM_LIST} and LIBINT_MAX_AM ${LIBINT_MAX_AM} for integrals class defaults.") + +# <<< overall opt_am defaults >>> + +list(LENGTH WITH_OPT_AM _ntokens_optam) +if (NOT WITH_OPT_AM EQUAL -1) + if (NOT _ntokens_optam EQUAL _ntokens_maxam) + # discard two cases: scalar opt and list max -and- list opt and scalar max + message(FATAL_ERROR "Invalid format for WITH_OPT_AM (${WITH_OPT_AM}). Use the same format and length like `N` or `N0;N1;N2` as WITH_MAX_AM (${WITH_MAX_AM}).") + endif() +endif() +if (_ntokens_optam GREATER 1) + # list opt and list max: use list opt validating aginst max + set(_processed_OPT_AM_LIST ) + math(EXPR _range_limit "${_ntokens_maxam} - 1") + foreach(_d RANGE ${_range_limit}) + list(GET WITH_MAX_AM ${_d} _max_am) + list(GET WITH_OPT_AM ${_d} _opt_am) + if (_opt_am LESS_EQUAL _max_am) + list(APPEND _processed_OPT_AM_LIST ${_opt_am}) + else() + list(APPEND _processed_OPT_AM_LIST ${_max_am}) + endif() + endforeach() + + list(JOIN _processed_OPT_AM_LIST "," LIBINT_OPT_AM_LIST) + numerical_max_of_list(LIBINT_OPT_AM "${_processed_OPT_AM_LIST}") +else() + if(WITH_OPT_AM EQUAL -1) + # first branch is a nice default pattern but not exactly what configure.ac prescribes, so bypassing it + # if (_ntokens_maxam GREATER 1) + if (FALSE) + # no opt and list max: default list opt from max + set(_processed_OPT_AM_LIST ) + math(EXPR _range_limit "${_ntokens_maxam} - 1") + foreach(_d RANGE ${_range_limit}) + list(GET WITH_MAX_AM ${_d} _max_am) + math(EXPR _opt_am "${_max_am}/2 + 1") + list(APPEND _processed_OPT_AM_LIST ${_opt_am}) + endforeach() + + list(JOIN _processed_OPT_AM_LIST "," LIBINT_OPT_AM_LIST) + numerical_max_of_list(LIBINT_OPT_AM "${_processed_OPT_AM_LIST}") + else() + # no opt and scalar max: default scalar opt from max + set(LIBINT_OPT_AM_LIST "") + math(EXPR LIBINT_OPT_AM "${LIBINT_MAX_AM}/2 + 1") + endif() + else() + # scalar opt and scalar max: use scalar opt validating aginst max + set(LIBINT_OPT_AM_LIST "") + set(LIBINT_OPT_AM ${WITH_OPT_AM}) + + if (LIBINT_OPT_AM GREATER LIBINT_MAX_AM) + set(LIBINT_OPT_AM ${LIBINT_MAX_AM}) + endif() + endif() +endif() + +message(STATUS "Preparing generic LIBINT_OPT_AM_LIST ${LIBINT_OPT_AM_LIST} and LIBINT_OPT_AM ${LIBINT_OPT_AM} for integrals class defaults.") + +# <<< Macro >>> + +macro(process_integrals_class class) + + list(LENGTH ENABLE_${class} _ntokens) + if (NOT _ntokens EQUAL 1) + message(FATAL_ERROR "Invalid value for ENABLE_${class} (${ENABLE_${class}}). Use scalar of maximum derivative level, not list.") + endif() + + if (ENABLE_${class} GREATER_EQUAL 0) + set(INCLUDE_${class} ${ENABLE_${class}}) + + foreach(_d RANGE 0 ${_max_deriv}) + if (${_d} LESS_EQUAL ${INCLUDE_${class}}) + set(_candidate0_${class}_d${_d} ${_candidate0_d${_d}}) + message(VERBOSE "setting _candidate0_${class}_d${_d}=${_candidate0_${class}_d${_d}}") + endif() + endforeach() + + set(LIBINT_SUPPORTS_${class} yes) + set(LIBINT_${class}_DERIV ${INCLUDE_${class}}) + message(STATUS "Enabling integrals class ${class} to derivative ${INCLUDE_${class}}") + else() + set(INCLUDE_${class} "-1") + set(${class}_MAX_AM "") + set(${class}_MAX_AM_LIST "") + message(STATUS "Disabling integrals class ${class}") + endif() + + if (ENABLE_${class} GREATER_EQUAL 0) + list(LENGTH WITH_${class}_MAX_AM _ntokens) + if (_ntokens GREATER 1) + math(EXPR _ntokens_xptd_max_deriv "${INCLUDE_${class}} + 1") + if (NOT _ntokens_xptd_max_deriv EQUAL _ntokens) + message(FATAL_ERROR "Invalid value for WITH_${class}_MAX_AM (${WITH_${class}_MAX_AM}). ENABLE_${class} derivative (${INCLUDE_${class}}) requires list length ${_ntokens_xptd_max_deriv}, not ${_ntokens}.") + endif() + + foreach(_d RANGE ${INCLUDE_${class}}) + list(GET WITH_${class}_MAX_AM ${_d} _candidate_${class}_d${_d}) + message(VERBOSE "setting _candidate_${class}_d${_d}=${_candidate_${class}_d${_d}}") + + if (_candidate_${class}_d${_d} LESS_EQUAL 0) + message(FATAL_ERROR "Invalid value for WITH_${class}_MAX_AM derivative element ${_d} (${_candidate_${class}_d${_d}} <= 0).") + endif() + endforeach() + + list(JOIN WITH_${class}_MAX_AM "," ${class}_MAX_AM_LIST) + set(${class}_MAX_AM "") + else() + set(${class}_MAX_AM_LIST "") + if (WITH_${class}_MAX_AM EQUAL -1) + foreach(_d RANGE ${INCLUDE_${class}}) + if (${_candidate0_${class}_d${_d}} EQUAL -1) + set(_candidate_${class}_d${_d} ${_candidate0_${class}_d0}) + else() + set(_candidate_${class}_d${_d} ${_candidate0_${class}_d${_d}}) + endif() + message(VERBOSE "setting _candidate_${class}_d${_d}=${_candidate_${class}_d${_d}}") + endforeach() + + set(${class}_MAX_AM "") + # note: could set class_MAX_AM/LIST from default (in configure.ac, looks like at least scalar var set) + # but philosophy is to set user-only intent and leave further defaulting to compiled code. wrong? + else() + set(${class}_MAX_AM ${WITH_${class}_MAX_AM}) + + foreach(_d RANGE ${INCLUDE_${class}}) + set(_candidate_${class}_d${_d} ${${class}_MAX_AM}) + message(VERBOSE "setting _candidate_${class}_d${_d}=${_candidate_${class}_d${_d}}") + endforeach() + + if (${class}_MAX_AM GREATER_EQUAL ${LIBINT_HARD_MAX_AM}) + message(WARNING "Value for ${class}_MAX_AM too high (${${class}_MAX_AM} >= ${LIBINT_HARD_MAX_AM}). Are you sure you know what you are doing?") + elseif (${class}_MAX_AM LESS_EQUAL 0) + message(FATAL_ERROR "Invalid value for ${class}_MAX_AM (${${class}_MAX_AM} <= 0).") + endif() + endif() + endif() + if (LIBINT_MAX_AM_LIST) + set(_msg ${LIBINT_MAX_AM_LIST}) + else() + set(_msg ${LIBINT_MAX_AM}) + endif() + message(STATUS "Enabling integrals class ${class} to max AM ${${class}_MAX_AM}${${class}_MAX_AM_LIST} (else ${_msg})") + + list(LENGTH WITH_${class}_OPT_AM _ntokens) + if (_ntokens GREATER 1) + if (NOT _ntokens_xptd_max_deriv EQUAL _ntokens) + message(FATAL_ERROR "Invalid value for WITH_${class}_OPT_AM (${WITH_${class}_OPT_AM}). ENABLE_${class} derivative (${INCLUDE_${class}}) requires list length ${_ntokens_xptd_max_deriv}, not ${_ntokens}.") + endif() + + list(JOIN WITH_${class}_OPT_AM "," ${class}_OPT_AM_LIST) + set(${class}_OPT_AM "") + else() + set(${class}_OPT_AM_LIST "") + if (WITH_${class}_OPT_AM EQUAL -1) + set(${class}_OPT_AM "") + else() + set(${class}_OPT_AM ${WITH_${class}_OPT_AM}) + endif() + endif() + if (LIBINT_OPT_AM_LIST) + set(_msg ${LIBINT_OPT_AM_LIST}) + else() + set(_msg ${LIBINT_OPT_AM}) + endif() + message(STATUS "Enabling integrals class ${class} to opt AM ${${class}_OPT_AM}${${class}_OPT_AM_LIST} (else ${_msg})") + endif() +endmacro() + + +macro(process_integrals_class_alt class) + + list(LENGTH ENABLE_${class} _ntokens) + if (NOT _ntokens EQUAL 1) + message(FATAL_ERROR "Invalid value for ENABLE_${class} (${ENABLE_${class}}). Use scalar of maximum derivative level, not list.") + endif() + + if (ENABLE_${class} GREATER_EQUAL 0) + set(INCLUDE_${class} ${ENABLE_${class}}) + + foreach(_d RANGE 0 ${_max_deriv}) + if (${_d} LESS_EQUAL ${INCLUDE_${class}}) + # no per-d defaults. use energy + set(_candidate0_${class}_d${_d} ${_candidate0_d0}) + message(VERBOSE "setting _candidate0_${class}_d${_d}=${_candidate0_${class}_d${_d}}") + endif() + endforeach() + + set(LIBINT_SUPPORTS_${class} yes) + set(LIBINT_${class}_DERIV ${INCLUDE_${class}}) + message(STATUS "Enabling integrals class ${class} to derivative ${INCLUDE_${class}}") + else() + set(INCLUDE_${class} "-1") + set(${class}_MAX_AM "") + message(STATUS "Disabling integrals class ${class}") + endif() + + if (ENABLE_${class} GREATER_EQUAL 0) + list(LENGTH WITH_${class}_MAX_AM _ntokens) + if (_ntokens GREATER 1) + message(FATAL_ERROR "Invalid value for WITH_${class}_MAX_AM (${WITH_${class}_MAX_AM}). ENABLE_${class} derivative supports only scalar, not list length ${_ntokens}.") + + else() + if (WITH_${class}_MAX_AM EQUAL -1) + foreach(_d RANGE ${INCLUDE_${class}}) + set(_candidate_${class}_d${_d} ${_candidate0_${class}_d0}) + message(VERBOSE "setting _candidate_${class}_d${_d}=${_candidate_${class}_d0}") + endforeach() + + set(_${class}_MAX_AM_pre "") + set(${class}_MAX_AM ${_candidate0_${class}_d0}) + # note: unlike usual classes, C++ code seems to want class_MAX_AM set explicitly to config.h + else() + set(_${class}_MAX_AM_pre ${WITH_${class}_MAX_AM}) + set(${class}_MAX_AM ${WITH_${class}_MAX_AM}) + + foreach(_d RANGE ${INCLUDE_${class}}) + set(_candidate_${class}_d${_d} ${${class}_MAX_AM}) + message(VERBOSE "setting _candidate_${class}_d${_d}=${_candidate_${class}_d${_d}}") + endforeach() + + if (${class}_MAX_AM GREATER_EQUAL ${LIBINT_HARD_MAX_AM}) + message(WARNING "Value for ${class}_MAX_AM too high (${${class}_MAX_AM} >= ${LIBINT_HARD_MAX_AM}). Are you sure you know what you are doing?") + elseif (${class}_MAX_AM LESS_EQUAL 0) + message(FATAL_ERROR "Invalid value for ${class}_MAX_AM (${${class}_MAX_AM} <= 0).") + endif() + endif() + endif() + message(STATUS "Enabling integrals class ${class} to max AM ${_${class}_MAX_AM_pre} (else ${LIBINT_MAX_AM})") + + list(LENGTH WITH_${class}_OPT_AM _ntokens) + if (_ntokens GREATER 1) + message(FATAL_ERROR "Invalid value for WITH_${class}_OPT_AM (${WITH_${class}_OPT_AM}). ENABLE_${class} derivative supports only scalar, not list length ${_ntokens}.") + + else() + if (WITH_${class}_OPT_AM EQUAL -1) + set(_${class}_OPT_AM_pre "") + set(${class}_OPT_AM ${LIBINT_OPT_AM}) + # note: unlike usual classes, C++ code seems to want class_MAX_AM set explicitly + else() + set(_${class}_OPT_AM_pre ${WITH_${class}_OPT_AM}) + set(${class}_OPT_AM ${WITH_${class}_OPT_AM}) + endif() + endif() + message(STATUS "Enabling integrals class ${class} to opt AM ${_${class}_OPT_AM_pre} (else ${LIBINT_OPT_AM})") + endif() +endmacro() + + +process_integrals_class(ONEBODY) +process_integrals_class(ERI) +process_integrals_class(ERI3) +process_integrals_class(ERI2) +# unlike above, these classes (1) don't do AM_LIST and (2) require value in config.h if enabled +process_integrals_class_alt(G12) +process_integrals_class_alt(G12DKH) + +if (ENABLE_G12 GREATER_EQUAL 0) + set(SUPPORT_T1G12 ${ENABLE_T1G12_SUPPORT}) +else() + set(SUPPORT_T1G12 OFF) +endif() + +add_feature_info( + "general integral" + "ON" + "config.h: LIBINT_MAX_AM=${LIBINT_MAX_AM} LIBINT_MAX_AM_LIST=${LIBINT_MAX_AM_LIST} LIBINT_OPT_AM=${LIBINT_OPT_AM} LIBINT_OPT_AM_LIST=${LIBINT_OPT_AM_LIST}" + ) + +# form list of active class + deriv + max_am strings to use in libint2-config.cmake +# * this generates components in same order as export/cmake/configuration-gen.py +set(Libint2_ERI_COMPONENTS "") +set(_amlist "") +set(_eri3_impure_sh "") +set(_eri2_impure_sh "") +add_feature_info( + "integral class MULTIPOLE derivative 0" + "MULTIPOLE_MAX_ORDER" + "max_am ${MULTIPOLE_MAX_ORDER}" + ) +foreach(_l RANGE 2 ${MULTIPOLE_MAX_ORDER}) + # RANGE with count-down works but docs say behavior is undefined, so we count-up and reverse + # RANGE starting at 2 avoids enumerating s, p + set(_lbl "multipole_${_am${_l}}${_am${_l}}_d0") + list(APPEND _amlist "${_lbl}") +endforeach() +list(REVERSE _amlist) +list(APPEND Libint2_ERI_COMPONENTS "${_amlist}") +message(VERBOSE "setting components ${_amlist}") + +foreach(_cls ONEBODY;ERI;ERI3;ERI2;G12;G12DKH) + if((_cls STREQUAL G12) OR (_cls STREQUAL G12DKH)) + add_feature_info( + "integral class ${_cls}" + "INCLUDE_${_cls} GREATER -1" + "config.h: INCLUDE_${_cls}=${INCLUDE_${_cls}} ${_cls}_MAX_AM=${${_cls}_MAX_AM} ${_cls}_OPT_AM=${${_cls}_OPT_AM}" + ) + else() + add_feature_info( + "integral class ${_cls}" + "INCLUDE_${_cls} GREATER -1" + "config.h: INCLUDE_${_cls}=${INCLUDE_${_cls}} ${_cls}_MAX_AM=${${_cls}_MAX_AM} ${_cls}_MAX_AM_LIST=${${_cls}_MAX_AM_LIST} ${_cls}_OPT_AM=${${_cls}_OPT_AM} ${_cls}_OPT_AM_LIST=${${_cls}_OPT_AM_LIST}" + ) + endif() + if(_cls STREQUAL "G12DKH") + # add G12DKH below when it's granted components + continue() + endif() + if (INCLUDE_${_cls} GREATER -1) + foreach (_d RANGE 0 ${INCLUDE_${_cls}}) + add_feature_info( + "integral class ${_cls} derivative ${_d}" + "INCLUDE_${_cls} GREATER -1" + "max_am ${_candidate_${_cls}_d${_d}}" + ) + set(_amlist "") + set(_pureamlist "") + foreach(_l RANGE 2 ${_candidate_${_cls}_d${_d}}) # LIBINT_cls_MAX_AM[_LIST] + if (_cls STREQUAL "ERI") + list(APPEND _amlist "eri_${_am${_l}}${_am${_l}}${_am${_l}}${_am${_l}}_d${_d}") + elseif (_cls STREQUAL "ERI2") + list(APPEND _amlist "eri_${_AM${_l}}${_AM${_l}}_d${_d}") + list(APPEND _pureamlist "eri_${_am${_l}}${_am${_l}}_d${_d}") + elseif (_cls STREQUAL "ONEBODY") + list(APPEND _amlist "onebody_${_am${_l}}${_am${_l}}_d${_d}") + elseif (_cls STREQUAL "G12") + list(APPEND _amlist "g12_${_am${_l}}${_am${_l}}${_am${_l}}${_am${_l}}_d${_d}") + endif() + endforeach() + if (_cls STREQUAL "ERI3") + foreach(_lpr RANGE 2 ${_candidate0_d${_d}}) # LIBINT_MAX_AM[_LIST] + foreach(_lfit RANGE 2 ${_candidate_${_cls}_d${_d}}) # LIBINT_ERI3_MAX_AM[_LIST] + if (_lfit GREATER_EQUAL _lpr) + list(APPEND _amlist "eri_${_am${_lpr}}${_am${_lpr}}${_AM${_lfit}}_d${_d}") + list(APPEND _pureamlist "eri_${_am${_lpr}}${_am${_lpr}}${_am${_lfit}}_d${_d}") + endif() + endforeach() + endforeach() + endif() + list(REVERSE _amlist) + list(APPEND Libint2_ERI_COMPONENTS "${_amlist}") + message(VERBOSE "setting component ${_amlist}") + list(REVERSE _pureamlist) + if (_cls STREQUAL "ERI2") + list(APPEND _eri2_impure_sh "${_pureamlist}") + elseif (_cls STREQUAL "ERI3") + list(APPEND _eri3_impure_sh "${_pureamlist}") + endif() + endforeach() + if ((_cls STREQUAL "ERI3") AND NOT ERI3_PURE_SH) + list(APPEND Libint2_ERI_COMPONENTS "${_eri3_impure_sh}") + message(VERBOSE "setting component ${_eri3_impure_sh}") + elseif ((_cls STREQUAL "ERI2") AND NOT ERI2_PURE_SH) + list(APPEND Libint2_ERI_COMPONENTS "${_eri2_impure_sh}") + message(VERBOSE "setting component ${_eri2_impure_sh}") + endif() + endif() +endforeach() +message(STATUS "Library will satisfy ERI AM components: ${Libint2_ERI_COMPONENTS}") 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/cmake/modules/int_computed.cmake.in b/cmake/modules/int_computed.cmake.in new file mode 100644 index 000000000..51d79d527 --- /dev/null +++ b/cmake/modules/int_computed.cmake.in @@ -0,0 +1,89 @@ +# This file communicates top-level CMake configuration results to +# export stage (in a very minor way that could be worked around) and to +# library stage (in a major way). If the superbuild was run continuously +# (generator->export->library), these would be more properly passed +# through the ExternalProject_Add commands. However, since library is +# commonly built separately from tarball, this file is better storage. +# To contrast, config.h transmits CMake data to C++, while this file transmits +# CMake data to (discontinuous) CMake. + + +# <<< Sortable Version >>> + +# for configuration.cc and `project(Libint2 VERSION` in CMakeLists.txt.export files +set(LIBINT_DESCRIPTION "@LIBINT_DESCRIPTION@") + +# for configuration.cc file +set(LIBINT_DOI "@LIBINT_DOI@") +set(LIBINT_GIT_COMMIT "@LIBINT_GIT_COMMIT@") +set(LIBINT_VERSION_YEAR "@LIBINT_VERSION_YEAR@") +set(LIBINT_SORTABLE_VERSION "@LIBINT_SORTABLE_VERSION@") + + +# <<< Build Version >>> + +# for libint2-config.cmake and `project(Libint2 VERSION` in CMakeLists.txt.export files +set(LIBINT_MAJOR_VERSION "@LIBINT_MAJOR_VERSION@") +set(LIBINT_MINOR_VERSION "@LIBINT_MINOR_VERSION@") +set(LIBINT_MICRO_VERSION "@LIBINT_MICRO_VERSION@") +set(LIBINT_VERSION ${LIBINT_MAJOR_VERSION}.${LIBINT_MINOR_VERSION}.${LIBINT_MICRO_VERSION}) + +# for macros.tex +set(LIBINT_MMM_VERSION "${LIBINT_VERSION}") + +# <<< Dev Version >>> + +# for libint2-config.cmake +set(LIBINT_EXT_VERSION "@LIBINT_EXT_VERSION@") +message(STATUS "Version: Full ${LIBINT_EXT_VERSION} Numeric ${LIBINT_VERSION} Sortable ${LIBINT_SORTABLE_VERSION}") + + +# <<< ABI Version >>> + +# for SOVERSION in CMakeLists.txt.export +set(LIBINT_SO_VERSION "@LIBINT_SO_VERSION@") +set(LIBINT_MAJOR_SO_VERSION "@LIBINT_MAJOR_SO_VERSION@") +message(STATUS "SO Version: Full ${LIBINT_SO_VERSION} Major ${LIBINT_MAJOR_SO_VERSION}") + + +# <<< Fixed Orderings >>> + +# for CMakeLists.txt.export and libint2-config.cmake +set(LIBINT2_SHGAUSS_ORDERING "@LIBINT2_SHGAUSS_ORDERING@") # added Jan 2023 +set(LIBINT2_CARTGAUSS_ORDERING "@LIBINT2_CARTGAUSS_ORDERING@") +set(LIBINT2_SHELL_SET "@LIBINT2_SHELL_SET@") + + +# <<< Features List >>> + +if (@LIBINT_CGSHELL_ORDERING@ EQUAL 1) + set(_o1st "s") +elseif(@LIBINT_CGSHELL_ORDERING@ EQUAL 2) + set(_o1st "i") +elseif(@LIBINT_CGSHELL_ORDERING@ EQUAL 3) + set(_o1st "g") +elseif(@LIBINT_CGSHELL_ORDERING@ EQUAL 4) + set(_o1st "o") +elseif(@LIBINT_CGSHELL_ORDERING@ EQUAL 5) + set(_o1st "b") +else() + message(STATUS "int_computed.cmake: indeterminate orderings: cgshell") +endif() + +if (@LIBINT_SHELL_SET@ EQUAL 1) + set(_o2nd "s") +elseif(@LIBINT_SHELL_SET@ EQUAL 2) + set(_o2nd "o") +else() + message(STATUS "int_computed.cmake: indeterminate orderings: shell_set") +endif() + +# for configuration.cc and libint2-config.cmake files +set(Libint2_CONFIG_COMPONENTS "@Libint2_ERI_COMPONENTS@") +list(PREPEND Libint2_CONFIG_COMPONENTS "${_o1st}${_o2nd}") + + +# <<< AM Components >>> + +# for features and libint2-config.cmake files +set(Libint2_MAX_AM_ERI "@_candidate_ERI_d0@") diff --git a/cmake/modules/int_orderings.cmake b/cmake/modules/int_orderings.cmake new file mode 100644 index 000000000..5732fbfd5 --- /dev/null +++ b/cmake/modules/int_orderings.cmake @@ -0,0 +1,61 @@ +# handle the setting of the following variables +# * LIBINT_SHGSHELL_ORDERING* +# * LIBINT_CGSHELL_ORDERING* +# * LIBINT_SHELL_SET* + + +# <<< solid harmonic Gaussian orderings >>> + +set(LIBINT_SHGSHELL_ORDERING_STANDARD 1) +set(LIBINT_SHGSHELL_ORDERING_GAUSSIAN 2) + +string(TOLOWER "${LIBINT2_SHGAUSS_ORDERING}" LIBINT2_SHGAUSS_ORDERING_lower) + +if (LIBINT2_SHGAUSS_ORDERING_lower STREQUAL "standard") + set(LIBINT_SHGSHELL_ORDERING ${LIBINT_SHGSHELL_ORDERING_STANDARD}) +elseif (LIBINT2_SHGAUSS_ORDERING_lower STREQUAL "gaussian") + set(LIBINT_SHGSHELL_ORDERING ${LIBINT_SHGSHELL_ORDERING_GAUSSIAN}) +else() + message(FATAL_ERROR "Invalid value for LIBINT2_SHGAUSS_ORDERING (${LIBINT2_SHGAUSS_ORDERING})") +endif() + + +# <<< Cartesian Gaussian orderings >>> + +set(LIBINT_CGSHELL_ORDERING_STANDARD 1) +set(LIBINT_CGSHELL_ORDERING_INTV3 2) +set(LIBINT_CGSHELL_ORDERING_GAMESS 3) +set(LIBINT_CGSHELL_ORDERING_ORCA 4) +set(LIBINT_CGSHELL_ORDERING_BAGEL 5) + +string(TOLOWER "${LIBINT2_CARTGAUSS_ORDERING}" LIBINT2_CARTGAUSS_ORDERING_lower) + +if (LIBINT2_CARTGAUSS_ORDERING_lower STREQUAL "standard") + set(LIBINT_CGSHELL_ORDERING ${LIBINT_CGSHELL_ORDERING_STANDARD}) +elseif (LIBINT2_CARTGAUSS_ORDERING_lower STREQUAL "intv3") + set(LIBINT_CGSHELL_ORDERING ${LIBINT_CGSHELL_ORDERING_INTV3}) +elseif (LIBINT2_CARTGAUSS_ORDERING_lower STREQUAL "gamess") + set(LIBINT_CGSHELL_ORDERING ${LIBINT_CGSHELL_ORDERING_GAMESS}) +elseif (LIBINT2_CARTGAUSS_ORDERING_lower STREQUAL "orca") + set(LIBINT_CGSHELL_ORDERING ${LIBINT_CGSHELL_ORDERING_ORCA}) +elseif (LIBINT2_CARTGAUSS_ORDERING_lower STREQUAL "bagel") + set(LIBINT_CGSHELL_ORDERING ${LIBINT_CGSHELL_ORDERING_BAGEL}) +else() + message(FATAL_ERROR "Invalid value for LIBINT2_CARTGAUSS_ORDERING (${LIBINT2_CARTGAUSS_ORDERING})") +endif() + + +# <<< shell subset orderings >>> + +set(LIBINT_SHELL_SET_STANDARD 1) +set(LIBINT_SHELL_SET_ORCA 2) + +string(TOLOWER "${LIBINT2_SHELL_SET}" LIBINT2_SHELL_SET_lower) + +if (LIBINT2_SHELL_SET_lower STREQUAL "standard") + set(LIBINT_SHELL_SET ${LIBINT_SHELL_SET_STANDARD}) +elseif (LIBINT2_SHELL_SET_lower STREQUAL "orca") + set(LIBINT_SHELL_SET ${LIBINT_SHELL_SET_ORCA}) +else() + message(FATAL_ERROR "Invalid value for LIBINT2_SHELL_SET (${LIBINT2_SHELL_SET})") +endif() diff --git a/cmake/modules/int_userreal.cmake b/cmake/modules/int_userreal.cmake new file mode 100644 index 000000000..cbd6d9a03 --- /dev/null +++ b/cmake/modules/int_userreal.cmake @@ -0,0 +1,41 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0075 NEW) # support CMAKE_REQUIRED_LIBRARIES + +include(CMakePushCheckState) + +cmake_push_check_state() +# needed for #include . works for top-level but not for EP; check again after more installs +list(APPEND CMAKE_REQUIRED_INCLUDES "${PROJECT_SOURCE_DIR}/include;${PROJECT_SOURCE_DIR}/src/lib/libint") + +if(NOT(LIBINT2_REALTYPE STREQUAL "double")) + set(LIBINT_USER_DEFINED_REAL "${LIBINT2_REALTYPE}") + if(NOT(LIBINT_USER_DEFINED_REAL_INCLUDES)) + set(LIBINT_USER_DEFINED_REAL_INCLUDES "") + endif() + + check_cxx_source_compiles(" +//#include +${LIBINT_USER_DEFINED_REAL_INCLUDES} + +int main(void) { + ${LIBINT_USER_DEFINED_REAL} x1; + ${LIBINT_USER_DEFINED_REAL} x2 = 2.0; + ${LIBINT_USER_DEFINED_REAL} x3 = x2; + ${LIBINT_USER_DEFINED_REAL} x4 = x2 + x3; + ${LIBINT_USER_DEFINED_REAL} x5 = x2 - x3; + ${LIBINT_USER_DEFINED_REAL} x6 = x2 * x3; + ${LIBINT_USER_DEFINED_REAL} x7 = 2 * x2; + ${LIBINT_USER_DEFINED_REAL} x8 = x2 * 3; + x6 += x2 * x3; + x7 -= 3 * x3; +} +" + _user_defined_real_compiles) + + if (NOT _user_defined_real_compiles) + message(FATAL_ERROR "LIBINT2_REALTYPE ${LIBINT_USER_DEFINED_REAL} is not usable, perhaps extra -I directories or extra #include's are needed?") + endif() +endif() + +cmake_pop_check_state() +cmake_policy(POP) diff --git a/cmake/modules/int_versions.cmake b/cmake/modules/int_versions.cmake new file mode 100644 index 000000000..460fd7ef9 --- /dev/null +++ b/cmake/modules/int_versions.cmake @@ -0,0 +1,76 @@ +# top-level CMakeLists.txt has defined: +# * PROJECT_VERSION_{MAJOR|MINOR|PATCH} through `project(... VERSION)` +# * Libint2Compiler_DESCRIPTION through `project(... DESCRIPTION)` +# * LIBINT_BUILDID +# * LIBINT_SO_VERSION +# * LIBINT_DOI +# * LibintRepository_{VERSION|DESCRIBE|COMMIT|DISTANCE} through `dynamic_version()` + +# note that 3rd version integer is PATCH in CMake and MICRO in Libint +# see also int_computed.cmake.in for transmitting these values to the library's CMake +# note that with the dynamic/git scheme, it's important for repo to be up-to-date with tags + + +# <<< Sortable Version >>> + +message(DEBUG "LibintRepository_VERSION ${LibintRepository_VERSION}") +message(DEBUG "LibintRepository_VERSION_FULL ${LibintRepository_VERSION_FULL}") +message(DEBUG "LibintRepository_COMMIT ${LibintRepository_COMMIT}") +message(DEBUG "LibintRepository_SHORT_HASH ${LibintRepository_SHORT_HASH}") +message(DEBUG "LibintRepository_DISTANCE ${LibintRepository_DISTANCE}") +message(DEBUG "LibintRepository_DESCRIBE ${LibintRepository_DESCRIBE}") + +if (LibintRepository_DISTANCE STREQUAL "0") + set(LIBINT_SORTABLE_VERSION "${LibintRepository_VERSION}") +else() + set(LIBINT_SORTABLE_VERSION "${LibintRepository_VERSION}.post${LibintRepository_DISTANCE}") +endif() + +# short hash from DynVer is variable length from `git describe`. +# * use below for fixed length +# string(SUBSTRING ${LibintRepository_COMMIT} 0 7 LIBINT_GIT_COMMIT) +message(DEBUG "LIBINT_GIT_COMMIT ${LibintRepository_SHORT_HASH}") + +# Below goes into BibTeX citation. Currently year of export. For year of tag, parse: +# `git show -s --no-notes --date=short --pretty='%cd' v2.7.2` responds: 2022-06-20 +string(TIMESTAMP LIBINT_VERSION_YEAR "%Y") +message(DEBUG "LIBINT_VERSION_YEAR ${LIBINT_VERSION_YEAR}") + +set(LIBINT_DESCRIPTION "${Libint2Compiler_DESCRIPTION}") +message(DEBUG "LIBINT_DESCRIPTION ${LIBINT_DESCRIPTION}") + +# <<< Build Version >>> + +set(LIBINT_MAJOR_VERSION ${PROJECT_VERSION_MAJOR}) +set(LIBINT_MINOR_VERSION ${PROJECT_VERSION_MINOR}) +set(LIBINT_MICRO_VERSION ${PROJECT_VERSION_PATCH}) + +set(LIBINT_VERSION ${LIBINT_MAJOR_VERSION}.${LIBINT_MINOR_VERSION}.${LIBINT_MICRO_VERSION}) + + +# <<< Dev Version >>> + +if (LIBINT_BUILDID) + set(LIBINT_EXT_VERSION ${LIBINT_VERSION}-${LIBINT_BUILDID}) +else() + set(LIBINT_EXT_VERSION ${LIBINT_VERSION}) +endif() + +message(STATUS "Version: Full ${LIBINT_EXT_VERSION} Numeric ${LIBINT_VERSION} Sortable ${LIBINT_SORTABLE_VERSION} SCM ${LibintRepository_VERSION_FULL}") + +if (NOT(LibintRepository_VERSION STREQUAL LIBINT_VERSION)) + message(AUTHOR_WARNING + "Version processing has gone wrong: ${LibintRepository_VERSION} != ${LIBINT_VERSION}") +endif() + + +# <<< ABI Version >>> + +string(REPLACE ":" ";" LIBINT_SO_VERSION_LIST ${LIBINT_SO_VERSION}) + +list(GET LIBINT_SO_VERSION_LIST 0 LIBINT_CURRENT_SO_VERSION) +list(GET LIBINT_SO_VERSION_LIST 1 LIBINT_REVISION_SO_VERSION) +list(GET LIBINT_SO_VERSION_LIST 2 LIBINT_AGE_SO_VERSION) + +math(EXPR LIBINT_MAJOR_SO_VERSION "${LIBINT_CURRENT_SO_VERSION} - ${LIBINT_AGE_SO_VERSION}") +message(STATUS "SO Version: Full ${LIBINT_SO_VERSION} Major ${LIBINT_MAJOR_SO_VERSION}") diff --git a/cmake/modules/options.cmake b/cmake/modules/options.cmake new file mode 100644 index 000000000..921dc7371 --- /dev/null +++ b/cmake/modules/options.cmake @@ -0,0 +1,187 @@ +### based on https://github.com/psi4/psi4/blob/master/cmake/psi4OptionsTools.cmake + +#Macro for printing an option in a consistent manner +# +#Syntax: print_option(