diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6db0110..2b395f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,25 +14,43 @@ jobs: fail-fast: false matrix: include: - - name: gcc / serial / Release + # ---- alpscore-free core (default build, no ALPSCore on the box) ---- + - name: gcc / serial / Release / core cxx: g++ mpi: OFF build_type: Release + alps: OFF - - name: gcc / MPI / Release + - name: clang / serial / Release / core + cxx: clang++ + mpi: OFF + build_type: Release + alps: OFF + + - name: gcc / serial / Debug / core + cxx: g++ + mpi: OFF + build_type: Debug + alps: OFF + + - name: gcc / MPI / Release / core cxx: g++ mpi: ON build_type: Release + alps: OFF - - name: clang / serial / Release - cxx: clang++ + # ---- legacy ALPSCore compat layer (EDLIB_WITH_ALPSCORE=ON) ---- + - name: gcc / serial / Release / alpscore + cxx: g++ mpi: OFF build_type: Release + alps: ON - - name: gcc / serial / Debug + - name: gcc / MPI / Release / alpscore cxx: g++ - mpi: OFF - build_type: Debug + mpi: ON + build_type: Release + alps: ON steps: - uses: actions/checkout@v4 @@ -53,9 +71,11 @@ jobs: clang # ----------------------------------------------------------------------- - # ALPSCore (built from source, cached by commit hash) + # ALPSCore (only needed for the legacy compat layer; built from source, + # cached by commit hash) # ----------------------------------------------------------------------- - name: Cache ALPSCore + if: matrix.alps == 'ON' id: cache-alpscore uses: actions/cache@v4 with: @@ -63,7 +83,7 @@ jobs: key: alpscore-v2.3.3-${{ matrix.cxx }}-MPI${{matrix.mpi}}-${{ runner.os }} - name: Build and install ALPSCore - if: steps.cache-alpscore.outputs.cache-hit != 'true' + if: matrix.alps == 'ON' && steps.cache-alpscore.outputs.cache-hit != 'true' run: | git clone --depth 1 --branch v2.3.3 \ https://github.com/ALPSCore/ALPSCore.git /tmp/alpscore-src @@ -89,7 +109,8 @@ jobs: -G Ninja \ -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ - -DALPSCore_DIR=/opt/alpscore/share/ALPSCore \ + -DEDLIB_WITH_ALPSCORE=${{ matrix.alps }} \ + ${{ matrix.alps == 'ON' && '-DALPSCore_DIR=/opt/alpscore/share/ALPSCore' || '' }} \ -DTesting=ON \ -DExamples=OFF \ -DUSE_MPI=${{ matrix.mpi }} \ @@ -98,10 +119,14 @@ jobs: - name: Build EDLib run: cmake --build build -j$(nproc) + # Serial jobs: ctest runs the core suite, plus the legacy alps suite + # when EDLIB_WITH_ALPSCORE=ON registered it. - name: Run tests (serial) if: matrix.mpi == 'OFF' run: ctest --test-dir build -V --output-on-failure + # Core tests always exist; the legacy alps suite only when alps=ON. + # Symmetry tests run single-process; parallel-path tests on 2 ranks. - name: Run tests (MPI, 2 ranks) if: matrix.mpi == 'ON' run: | @@ -109,6 +134,13 @@ jobs: MPIRUN="mpirun --oversubscribe --mca osc pt2pt --mca btl ^sm -np 2" ./build/test/SzSymmetryTest ./build/test/NSymmetryTest - $MPIRUN ./build/test/HubbardModelTest + $MPIRUN ./build/test/HubbardTest $MPIRUN ./build/test/LanczosTest - $MPIRUN ./build/test/StaticObservablesTest + $MPIRUN ./build/test/MpiTest + if [ "${{ matrix.alps }}" = "ON" ]; then + ./build/test/alpscore/SzSymmetryAlpsCoreTest + ./build/test/alpscore/NSymmetryAlpsCoreTest + $MPIRUN ./build/test/alpscore/HubbardModelAlpsCoreTest + $MPIRUN ./build/test/alpscore/LanczosAlpsCoreTest + $MPIRUN ./build/test/alpscore/StaticObservablesAlpsCoreTest + fi diff --git a/CMakeLists.txt b/CMakeLists.txt index b6a963c..a2fd855 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.18) project(EDLib LANGUAGES C CXX - VERSION 2.0) + VERSION 3.0) set(CMAKE_DISABLE_SOURCE_CHANGES ON) set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) @@ -10,46 +10,43 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(CXXStandard) use_cxx17() -find_package(ALPSCore COMPONENTS params hdf5 gf REQUIRED) +option(EDLIB_WITH_ALPSCORE + "Pull EDLib-alpscore (legacy ALPSCore-based API: alps::params / alps::hdf5 / alps::gf) via FetchContent. Off = header-only edlib:: core only." + OFF) + find_package(BLAS REQUIRED) find_package(LAPACK REQUIRED) +find_package(Eigen3 REQUIRED NO_MODULE) include_directories(${Boost_INCLUDE_DIRS}) -include_directories(${ALPSCore_INCLUDES}) # cpp-arnoldi: header-only C++ Arnoldi/Lanczos eigensolver (replaces ARPACK). include(FetchContent) FetchContent_Declare(cpp_arnoldi - GIT_REPOSITORY https://github.com/Q-solvers/cpp-arnoldi.git - GIT_TAG 444304d88d6029f717031ea5b45294a4bd2a3517) + GIT_REPOSITORY https://github.com/Q-solvers/cpp-arnoldi.git + GIT_TAG 444304d88d6029f717031ea5b45294a4bd2a3517) FetchContent_MakeAvailable(cpp_arnoldi) if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) - message("setting build type to default, i.e. optimized build") + set(CMAKE_BUILD_TYPE Release) + message("setting build type to default, i.e. optimized build") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_DISABLE_ASSERTS -DNDEBUG -g") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m64") - set(extlibs ${Boost_LIBRARIES} ${HDF5_LIBRARIES} - ${ALPSCore_LIBRARIES} - arnoldi::arnoldi - ) + arnoldi::arnoldi) if(USE_MPI) - if(!ALPS_HAVE_MPI) - MESSAGE(FATAL_ERROR "Enable MPI support in ALPSCore.") - endif(!ALPS_HAVE_MPI) find_package(MPI REQUIRED) if(MPI_FOUND) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_MPI") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_MPI") set(parlibs MPI::MPI_CXX) - endif(MPI_FOUND) + endif() message(INFO " Using parallel libraries: ${parlibs}") -endif(USE_MPI) +endif() add_subdirectory(include/edlib) add_subdirectory(include/ext) @@ -57,13 +54,33 @@ add_subdirectory(include/ext) include(GNUInstallDirs) add_library(${PROJECT_NAME} INTERFACE) -add_library(EDLib::EDLib ALIAS ${PROJECT_NAME}) # Define namespaced alias target -target_include_directories( - ${PROJECT_NAME} - INTERFACE $ - $) +add_library(EDLib::EDLib ALIAS ${PROJECT_NAME}) +target_include_directories(${PROJECT_NAME} + INTERFACE $ + $) +target_link_libraries(${PROJECT_NAME} INTERFACE Eigen3::Eigen arnoldi::arnoldi) + +# Legacy ALPSCore-based shim layer lives in the separate EDLib-alpscore +# project, fetched here on demand. It defines the EDLib::Alpscore INTERFACE +# target and the EDLIB_WITH_ALPSCORE compile definition. +if(EDLIB_WITH_ALPSCORE) + FetchContent_Declare(edlib_alpscore + GIT_REPOSITORY https://github.com/Q-solvers/EDLib-alpscore.git + GIT_TAG master) + FetchContent_MakeAvailable(edlib_alpscore) + list(APPEND extlibs EDLib::Alpscore) + + # Fold EDLib-alpscore into the parent install: a single + # `find_package(EDLib)` provides both the core and the alpscore shim. + set_target_properties(EDLib-alpscore PROPERTIES EXPORT_NAME Alpscore) +endif() -install(TARGETS ${PROJECT_NAME} +set(_edlib_install_targets ${PROJECT_NAME}) +if(EDLIB_WITH_ALPSCORE) + list(APPEND _edlib_install_targets EDLib-alpscore) +endif() + +install(TARGETS ${_edlib_install_targets} EXPORT ${PROJECT_NAME}_targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -75,10 +92,10 @@ write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" COMPATIBILITY SameMajorVersion) configure_package_config_file( - "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in" - "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - INSTALL_DESTINATION - ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake) + "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in" + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION + ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake) install(EXPORT ${PROJECT_NAME}_targets FILE ${PROJECT_NAME}Targets.cmake @@ -89,41 +106,60 @@ install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake) -file (GLOB_RECURSE TARGET_INSTALL_LIB_HEADERS "${PROJECT_SOURCE_DIR}/include/edlib/*.h") -file (GLOB_RECURSE TARGET_INSTALL_EXT_HEADERS "${PROJECT_SOURCE_DIR}/include/ext/*.h") -install(FILES include/edlib.h - DESTINATION include) -install(FILES ${TARGET_INSTALL_LIB_HEADERS} - DESTINATION include/edlib) -install(FILES ${TARGET_INSTALL_EXT_HEADERS} - DESTINATION include/ext) +install(FILES include/edlib.h DESTINATION include) + +# Core API: always installed (header-only, no alpscore). +install(DIRECTORY include/edlib DESTINATION include + FILES_MATCHING PATTERN "*.h" PATTERN ".ipynb_checkpoints" EXCLUDE) +install(DIRECTORY include/ext DESTINATION include + FILES_MATCHING PATTERN "*.h" PATTERN ".ipynb_checkpoints" EXCLUDE) + +# Legacy alpscore shim headers come from the FetchContent'd EDLib-alpscore. +if(EDLIB_WITH_ALPSCORE) + install(DIRECTORY ${edlib_alpscore_SOURCE_DIR}/include/edlib/alpscore + DESTINATION include/edlib + FILES_MATCHING PATTERN "*.h" PATTERN ".ipynb_checkpoints" EXCLUDE) + install(DIRECTORY ${edlib_alpscore_SOURCE_DIR}/include/ext/alpscore + DESTINATION include/ext + FILES_MATCHING PATTERN "*.h" PATTERN ".ipynb_checkpoints" EXCLUDE) +endif() option(Testing "Enable testing" OFF) if(Testing) include(EnableGtests) - #UseGtest() - #include_directories(${Hubbard_SOURCE_DIR}/test) - add_subdirectory(test) include_directories(${GTEST_INCLUDE_DIR}) enable_testing() + # Core-only tests: build and run regardless of EDLIB_WITH_ALPSCORE. + add_subdirectory(test) + add_test(HubbardTest test/HubbardTest) + add_test(LanczosTest test/LanczosTest) add_test(SzSymmetryTest test/SzSymmetryTest) - add_test(NSymmetryTest test/NSymmetryTest) - add_test(HubbardModelTest test/HubbardModelTest) - add_test(LanczosTest test/LanczosTest) - add_test(StaticObservablesTest test/StaticObservablesTest) - - -endif (Testing) - + add_test(NSymmetryTest test/NSymmetryTest) + if(USE_MPI) + add_test(MpiTest test/MpiTest) + endif() + + # Legacy alps-using tests only when the option is enabled. + if(EDLIB_WITH_ALPSCORE) + add_subdirectory(test/alpscore) + add_test(SzSymmetryAlpsCoreTest test/alpscore/SzSymmetryAlpsCoreTest) + add_test(NSymmetryAlpsCoreTest test/alpscore/NSymmetryAlpsCoreTest) + add_test(HubbardModelAlpsCoreTest test/alpscore/HubbardModelAlpsCoreTest) + add_test(LanczosAlpsCoreTest test/alpscore/LanczosAlpsCoreTest) + add_test(StaticObservablesAlpsCoreTest test/alpscore/StaticObservablesAlpsCoreTest) + endif() +endif() option(Examples "Enable examples" ON) if(Examples) add_subdirectory(examples) - set(SOURCE_FILES main.cpp) - add_executable(Hubbard ${SOURCE_FILES}) - target_link_libraries(Hubbard ${PROJECT_NAME} ${extlibs}) - if(USE_MPI) - target_link_libraries(Hubbard ${parlibs}) - endif(USE_MPI) -endif(Examples) + if(EDLIB_WITH_ALPSCORE) + set(SOURCE_FILES main.cpp) + add_executable(Hubbard ${SOURCE_FILES}) + target_link_libraries(Hubbard ${PROJECT_NAME} ${extlibs}) + if(USE_MPI) + target_link_libraries(Hubbard ${parlibs}) + endif() + endif() +endif() diff --git a/cmake/EDLibConfig.cmake.in b/cmake/EDLibConfig.cmake.in index 9c15f36..a8bfdef 100644 --- a/cmake/EDLibConfig.cmake.in +++ b/cmake/EDLibConfig.cmake.in @@ -1,4 +1,20 @@ @PACKAGE_INIT@ +include(CMakeFindDependencyMacro) + +find_dependency(Eigen3 NO_MODULE) +find_dependency(BLAS) +find_dependency(LAPACK) + +# cpp-arnoldi installs only a *Targets.cmake (no Config.cmake), so include +# it directly out of either lib/cmake or lib64/cmake. +include("${PACKAGE_PREFIX_DIR}/lib/cmake/arnoldi/arnoldiTargets.cmake" OPTIONAL) +include("${PACKAGE_PREFIX_DIR}/lib64/cmake/arnoldi/arnoldiTargets.cmake" OPTIONAL) + +set(EDLIB_WITH_ALPSCORE @EDLIB_WITH_ALPSCORE@) +if(EDLIB_WITH_ALPSCORE) + find_dependency(ALPSCore COMPONENTS params hdf5 gf) +endif() + include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") diff --git a/examples/Anderson.cpp b/examples/Anderson.cpp index 6a56c4e..98ba824 100644 --- a/examples/Anderson.cpp +++ b/examples/Anderson.cpp @@ -3,19 +3,19 @@ // #include -#include +#include #include -#include "edlib/Hamiltonian.h" -#include "edlib/SzSymmetry.h" -#include "edlib/SOCRSStorage.h" -#include "edlib/CRSStorage.h" -#include "edlib/HubbardModel.h" -#include "edlib/GreensFunction.h" -#include "edlib/ChiLoc.h" -#include "edlib/HDF5Utils.h" -#include "edlib/SpinResolvedStorage.h" -#include "edlib/StaticObservables.h" -#include "edlib/MeshFactory.h" +#include "edlib/alpscore/Hamiltonian.h" +#include "edlib/alpscore/SzSymmetry.h" +#include "edlib/alpscore/SOCRSStorage.h" +#include "edlib/alpscore/CRSStorage.h" +#include "edlib/alpscore/HubbardModel.h" +#include "edlib/alpscore/GreensFunction.h" +#include "edlib/alpscore/ChiLoc.h" +#include "edlib/alpscore/HDF5Utils.h" +#include "edlib/alpscore/SpinResolvedStorage.h" +#include "edlib/alpscore/StaticObservables.h" +#include "edlib/alpscore/MeshFactory.h" int main(int argc, const char ** argv) { // Init MPI if enabled diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 21c6349..e3090f3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,16 +1,24 @@ -add_executable(hubbard-example Hubbard.cpp) -add_executable(anderson-example Anderson.cpp) -add_executable(holstein-example HolsteinAnderson.cpp) +if(EDLIB_WITH_ALPSCORE) + add_executable(hubbard-example Hubbard.cpp) + add_executable(anderson-example Anderson.cpp) + add_executable(holstein-example HolsteinAnderson.cpp) -target_link_libraries(hubbard-example EDLib ${extlibs}) + target_link_libraries(hubbard-example EDLib ${extlibs}) + target_link_libraries(anderson-example EDLib ${extlibs}) + target_link_libraries(holstein-example EDLib ${extlibs}) -target_link_libraries(anderson-example EDLib ${extlibs}) - -target_link_libraries(holstein-example EDLib ${extlibs}) + if(USE_MPI) + target_link_libraries(hubbard-example ${parlibs}) + target_link_libraries(anderson-example ${parlibs}) + target_link_libraries(holstein-example ${parlibs}) + endif(USE_MPI) +endif() +# Core-only example: builds with or without ALPSCore. +add_executable(hubbard-core-example HubbardCore.cpp) +target_link_libraries(hubbard-core-example EDLib ${Boost_LIBRARIES} arnoldi::arnoldi + ${BLAS_LIBRARIES} ${LAPACK_LIBRARIES}) if(USE_MPI) - target_link_libraries(hubbard-example ${parlibs}) - target_link_libraries(anderson-example ${parlibs}) - target_link_libraries(holstein-example ${parlibs}) -endif(USE_MPI) \ No newline at end of file + target_link_libraries(hubbard-core-example ${parlibs}) +endif(USE_MPI) diff --git a/examples/HolsteinAnderson.cpp b/examples/HolsteinAnderson.cpp index 55816bc..b67fcaa 100644 --- a/examples/HolsteinAnderson.cpp +++ b/examples/HolsteinAnderson.cpp @@ -4,19 +4,19 @@ #include -#include -#include "edlib/Hamiltonian.h" -#include "edlib/SzSymmetry.h" -#include "edlib/SOCRSStorage.h" -#include "edlib/CRSStorage.h" -#include "edlib/GreensFunction.h" -#include "edlib/ChiLoc.h" -#include "edlib/HDF5Utils.h" -#include "edlib/SpinResolvedStorage.h" -#include "edlib/StaticObservables.h" -#include "edlib/MeshFactory.h" -#include "ext/HolsteinAndersonModel.h" -#include "ext/HolsteinAndersonParameter.h" +#include +#include "edlib/alpscore/Hamiltonian.h" +#include "edlib/alpscore/SzSymmetry.h" +#include "edlib/alpscore/SOCRSStorage.h" +#include "edlib/alpscore/CRSStorage.h" +#include "edlib/alpscore/GreensFunction.h" +#include "edlib/alpscore/ChiLoc.h" +#include "edlib/alpscore/HDF5Utils.h" +#include "edlib/alpscore/SpinResolvedStorage.h" +#include "edlib/alpscore/StaticObservables.h" +#include "edlib/alpscore/MeshFactory.h" +#include "ext/alpscore/HolsteinAndersonModel.h" +#include "ext/alpscore/HolsteinAndersonParameter.h" int main(int argc, const char ** argv) { // Define Hamiltonian object type diff --git a/examples/Hubbard.cpp b/examples/Hubbard.cpp index 7c5ac9b..e1a0853 100644 --- a/examples/Hubbard.cpp +++ b/examples/Hubbard.cpp @@ -3,19 +3,19 @@ // #include -#include -#include "edlib/Hamiltonian.h" -#include "edlib/SzSymmetry.h" -#include "edlib/SOCRSStorage.h" -#include "edlib/CRSStorage.h" -#include "edlib/HubbardModel.h" -#include "edlib/GreensFunction.h" -#include "edlib/ChiLoc.h" -#include "edlib/HDF5Utils.h" -#include "edlib/SpinResolvedStorage.h" -#include "edlib/StaticObservables.h" -#include "edlib/MeshFactory.h" -#include "edlib/ExecutionStatistic.h" +#include +#include "edlib/alpscore/Hamiltonian.h" +#include "edlib/alpscore/SzSymmetry.h" +#include "edlib/alpscore/SOCRSStorage.h" +#include "edlib/alpscore/CRSStorage.h" +#include "edlib/alpscore/HubbardModel.h" +#include "edlib/alpscore/GreensFunction.h" +#include "edlib/alpscore/ChiLoc.h" +#include "edlib/alpscore/HDF5Utils.h" +#include "edlib/alpscore/SpinResolvedStorage.h" +#include "edlib/alpscore/StaticObservables.h" +#include "edlib/alpscore/MeshFactory.h" +#include "edlib/alpscore/ExecutionStatistic.h" diff --git a/examples/HubbardCore.cpp b/examples/HubbardCore.cpp new file mode 100644 index 0000000..2855b1a --- /dev/null +++ b/examples/HubbardCore.cpp @@ -0,0 +1,98 @@ +// Header-only edlib:: example: 4-site Hubbard ring at half-filling. +// +// Uses ONLY the new edlib:: API (no ALPSCore). Mirrors the parameters of the +// legacy test/input/4ring/ HDF5 input, but constructs the bath in code so we +// don't need any file I/O. + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef USE_MPI +#include +#endif + +int main(int argc, char** argv) { +#ifdef USE_MPI + MPI_Init(&argc, &argv); + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); +#else + int rank = 0; +#endif + + edlib::Parameters p; + p.nsites = 4; + p.nspins = 2; + p.arpack_nev = 1; + p.storage_max_size = 576; + p.storage_max_dim = 36; + p.lanc_beta = 10.0; + p.lanc_nomega = 32; + p.lanc_nlanc = 100; + p.lanc_emin = -3.0; + p.lanc_emax = 3.0; + p.lanc_boltzmann_cutoff = 1e-12; + + edlib::HubbardModel::ModelData bath; + bath.hopping = { + { 0.0, -1.0, 0.0, -1.0}, + {-1.0, 0.0, -1.0, 0.0}, + { 0.0, -1.0, 0.0, -1.0}, + {-1.0, 0.0, -1.0, 0.0} + }; + bath.U = {5.0, 5.0, 5.0, 5.0}; + bath.mu = {2.5, 2.5, 2.5, 2.5}; + bath.magnetic_field = {0.01, 0.01, 0.01, 0.01}; + +#ifdef USE_MPI + using HamType = edlib::SRSHubbardHamiltonian; + HamType ham(p, bath, MPI_COMM_WORLD); +#else + using HamType = edlib::SOCSRHubbardHamiltonian; + HamType ham(p, bath); +#endif + + ham.diag(); + + if (rank == 0) { + std::cout << std::setprecision(10); + std::cout << "Ground state: " + << ham.eigenpairs().begin()->eigenvalue() << std::endl; + } + + // Static observables. + edlib::StaticObservables so(p); + auto obs = so.calculate_static_observables(ham); + if (rank == 0) { + std::cout << " per orbital: "; + for (double v : obs[edlib::StaticObservables::_N_]) std::cout << v << " "; + std::cout << std::endl; + } + + // Single-particle Green's function (Matsubara). + edlib::MatsubaraMesh fmesh(p.lanc_beta, p.lanc_nomega, edlib::Statistics::Fermionic); + edlib::GreensFunction gf(p, ham, fmesh); + gf.compute(); + if (rank == 0) { + std::cout << "G(iw_0, orb=0, spin=0) = " << gf.G()(0, 0, 0) << std::endl; + } + + // Local Sz susceptibility (bosonic Matsubara). + edlib::MatsubaraMesh bmesh(p.lanc_beta, p.lanc_nomega, edlib::Statistics::Bosonic); + edlib::ChiLoc chi(p, ham, bmesh); + chi.compute>(); + if (rank == 0) { + std::cout << "ChiSz(iw_0, orb=0) = " << chi.G()(0, 0) << std::endl; + } + +#ifdef USE_MPI + MPI_Finalize(); +#endif + return 0; +} diff --git a/include/edlib.h b/include/edlib.h index bdceb07..b622ada 100644 --- a/include/edlib.h +++ b/include/edlib.h @@ -1,18 +1,37 @@ #ifndef EDLIB_H #define EDLIB_H -#include +// New ALPSCore-free API in namespace `edlib::`. Always available. +#include "edlib/ChiLoc.h" +#include "edlib/CommonUtils.h" +#include "edlib/Dyson.h" +#include "edlib/ExecutionStatistic.h" +#include "edlib/GreensFunction.h" #include "edlib/Hamiltonian.h" -#include "edlib/SzSymmetry.h" -#include "edlib/SOCRSStorage.h" -#include "edlib/CRSStorage.h" #include "edlib/HubbardModel.h" -#include "edlib/GreensFunction.h" -#include "edlib/ChiLoc.h" -#include "edlib/HDF5Utils.h" -#include "edlib/SpinResolvedStorage.h" -#include "edlib/StaticObservables.h" +#include "edlib/Lanczos.h" +#include "edlib/Mesh.h" #include "edlib/MeshFactory.h" -#include "edlib/ExecutionStatistic.h" +#include "edlib/Parameters.h" +#include "edlib/SingleImpurityAndersonModel.h" +#include "edlib/StaticObservables.h" + +// Legacy ALPSCore-based API in namespace `EDLib::`. Only when +// EDLIB_WITH_ALPSCORE is defined (set by CMake when the option is ON). +#ifdef EDLIB_WITH_ALPSCORE +#include "edlib/alpscore/EDParams.h" +#include "edlib/alpscore/Hamiltonian.h" +#include "edlib/alpscore/SzSymmetry.h" +#include "edlib/alpscore/SOCRSStorage.h" +#include "edlib/alpscore/CRSStorage.h" +#include "edlib/alpscore/HubbardModel.h" +#include "edlib/alpscore/GreensFunction.h" +#include "edlib/alpscore/ChiLoc.h" +#include "edlib/alpscore/HDF5Utils.h" +#include "edlib/alpscore/SpinResolvedStorage.h" +#include "edlib/alpscore/StaticObservables.h" +#include "edlib/alpscore/MeshFactory.h" +#include "edlib/alpscore/ExecutionStatistic.h" +#endif -#endif //EDLIB_H \ No newline at end of file +#endif //EDLIB_H diff --git a/include/edlib/CMakeLists.txt b/include/edlib/CMakeLists.txt index 9c973df..a4d6991 100644 --- a/include/edlib/CMakeLists.txt +++ b/include/edlib/CMakeLists.txt @@ -1,26 +1,5 @@ - project(edlib CXX) -add_custom_target(edlib SOURCES Combination.h - CRSStorage.h - EDParams.h - EigenPair.h - FermionicModel.h - GreensFunction.h - ChiLoc.h - Hamiltonian.h - HubbardModel.h - Lanczos.h - NSymmetry.h - SingleImpurityAndersonModel.h - SOCRSStorage.h - SpinResolvedStorage.h - StaticObservables.h - Storage.h - Symmetry.h - SzSymmetry.h - HDF5Utils.h - MeshFactory.h - CommonUtils.h - CRSMatrix.h - ExecutionStatistic.h) +file(GLOB EDLIB_HEADERS *.h) + +add_custom_target(edlib SOURCES ${EDLIB_HEADERS}) diff --git a/include/edlib/CRSMatrix.h b/include/edlib/CRSMatrix.h index 39a267a..5980926 100644 --- a/include/edlib/CRSMatrix.h +++ b/include/edlib/CRSMatrix.h @@ -1,127 +1,80 @@ -// -// Created by iskakoff on 20/04/17. -// +#ifndef EDLIB_CRSMATRIX_H +#define EDLIB_CRSMATRIX_H -#ifndef EDLIB_CSRMATRIX_H -#define EDLIB_CSRMATRIX_H +#include +#include +#include -namespace EDLib { - namespace Storage { - /** - * @brief CSRMatrix class - * - * @author iskakoff - */ - /** - * Simple CRS matrix class. This class is used to store hopping matrices and off-diagonal interactions - * @tparam p - */ - template - class CRSMatrix { - public: - CRSMatrix() { - } +namespace edlib { - /** - * init matrix arrays - * @param N -- leading dimension - * @param nnzl -- average number of non-zero elements per line - */ - void init(size_t N, size_t nnzl = 100) { - _nnz = N * nnzl; - _values.assign(_nnz, prec(0)); - _col_ind.assign(_nnz, 0); - _row_ptr.assign(N + 1, 0); - _vind = 0; - } + /** + * Simple compressed row sparse matrix used to store hopping matrices and + * off-diagonal interaction terms. + */ + template + class CRSMatrix { + public: + CRSMatrix() = default; - /** - * Add off-diagonal matrix element at the position (i,j) - * - * @param i - row number - * @param j - column number - * @param t - value - * @param sign - fermionic sign - */ - void inline addElement(int i, int j, prec t, int sign) { - /// flag that we already have data for (i,j) - bool hasstate = false; - /// index of (i,j) element in spare storage - size_t foundstate = 0; - if (std::abs(t) == 0) { - return; - } - /// check that there is no any data on the k state - /// In case of off-diagonal interaction there can be multiple possible transition from i-state to j-state - for (int iii = _row_ptr[i]; iii < _vind; ++iii) { - if (_col_ind[iii] == j) { - hasstate = true; - foundstate = iii; - } - } - if(hasstate) { - /// update existing value - _values[foundstate] += sign * t; - }else { - /// create new element in CRS arrays - _col_ind[_vind] = j; - _values[_vind] = sign * t; - ++_vind; - /// check that we have exceed the upper bound - if(_vind == _nnz) { - /// resize storage - _nnz *= 2; - _values.resize(_nnz); - _col_ind.resize(_nnz); - } + void init(std::size_t N, std::size_t nnzl = 100) { + _nnz = N * nnzl; + _values.assign(_nnz, Prec(0)); + _col_ind.assign(_nnz, 0); + _row_ptr.assign(N + 1, 0); + _vind = 0; + } + + inline void addElement(int i, int j, Prec t, int sign) { + if (std::abs(t) == Prec(0)) return; + + bool hasstate = false; + std::size_t foundstate = 0; + for (int k = _row_ptr[i]; k < _vind; ++k) { + if (_col_ind[k] == j) { hasstate = true; foundstate = k; } + } + if (hasstate) { + _values[foundstate] += static_cast(sign) * t; + } else { + _col_ind[_vind] = j; + _values[_vind] = static_cast(sign) * t; + ++_vind; + if (static_cast(_vind) == _nnz) { + _nnz *= 2; + _values.resize(_nnz); + _col_ind.resize(_nnz); } } + } - /// some of the interaction terms can compensate each other - /// in this case we need to remove zero elements from storage to reduce required memory and communications - void inline compress(int i) { - int shift = 0; - for (int iii = _row_ptr[i]; iii < _vind; ++iii){ - if(std::abs(_values[iii])<1e-15) { - for(int kkk = iii; kkk<_vind-1; ++kkk) { - _values[kkk] = _values[kkk+1]; - _col_ind[kkk] = _col_ind[kkk+1]; - } - --_vind; + inline void compress(int i) { + for (int k = _row_ptr[i]; k < _vind; ++k) { + if (std::abs(_values[k]) < 1e-15) { + for (int m = k; m < _vind - 1; ++m) { + _values[m] = _values[m + 1]; + _col_ind[m] = _col_ind[m + 1]; } + --_vind; } } + } - void inline endLine(int i) { - compress(i); - _row_ptr[i + 1] = _vind; - } - - std::vector < int > &row_ptr() { - return _row_ptr; - }; + inline void endLine(int i) { + compress(i); + _row_ptr[i + 1] = _vind; + } - std::vector < int > &col_ind() { - return _col_ind; - } + std::vector& row_ptr() { return _row_ptr; } + std::vector& col_ind() { return _col_ind; } + std::vector& values() { return _values; } - std::vector < prec > &values() { - return _values; - } + private: + std::vector _values; + std::vector _row_ptr; + std::vector _col_ind; + int _vind = 0; + std::size_t _nnz = 0; + }; - private: - /// matrix values - std::vector < prec > _values; - /// pointer to a row - std::vector < int > _row_ptr; - /// column indices - std::vector < int > _col_ind; - /// internal index of non-zero values - int _vind; - /// number of non-zero elements allocated in memory - size_t _nnz; - }; - } } -#endif //EDLIB_CSRMATRIX_H +#endif diff --git a/include/edlib/CRSStorage.h b/include/edlib/CRSStorage.h index bd77886..0ce030a 100644 --- a/include/edlib/CRSStorage.h +++ b/include/edlib/CRSStorage.h @@ -1,229 +1,203 @@ -// -// Created by iskakoff on 20/07/16. -// - #ifndef EDLIB_CRSSTORAGE_H #define EDLIB_CRSSTORAGE_H - -#include +#include #include -#include "Storage.h" - -namespace EDLib { - namespace Storage { - template - class CRSStorage : public Storage < typename ModelType::precision > { - typedef typename ModelType::precision prec; - using Storage < prec >::n; - using Storage < prec >::ntot; - public: - typedef ModelType Model; +#include +#include +#include +#include + +#include "edlib/Parameters.h" +#include "edlib/Storage.h" + +namespace edlib { + + template + class CRSStorage : public Storage { + public: + using Model = ModelType; + using prec = typename ModelType::precision; + using Storage::n; + using Storage::ntot; + #ifdef USE_MPI - CRSStorage(alps::params &p, Model &s, MPI_Comm comm) : Storage < prec >(p, comm), + CRSStorage(const Parameters& p, Model& m, MPI_Comm comm) + : Storage(p, comm), + _max_size(p.storage_max_size), + _max_dim(p.storage_max_dim), + _model(m) {} #else - CRSStorage(alps::params &p, Model &s) : Storage < prec >(p), + CRSStorage(const Parameters& p, Model& m) + : Storage(p), + _max_size(p.storage_max_size), + _max_dim(p.storage_max_dim), + _model(m) {} #endif - _vind(0), _model(s) { - _max_size = p["storage.MAX_SIZE"]; - _max_dim = p["storage.MAX_DIM"]; - // init what you need from parameters - }; - - void init() { - _model.symmetry().init(); - } - void reset() { - _model.symmetry().init(); - size_t sector_size = _model.symmetry().sector().size(); - if (sector_size > _max_dim) { - std::stringstream s; - s << "Current sector request more memory than allocated. Increase MAX_DIM parameter. Requested " << sector_size << ", allocated " << _max_dim << "."; - throw std::runtime_error(s.str().c_str()); - } - _vind = 0; - row_ptr.assign(_max_dim + 1, 0); - col_ind.assign(_max_size, 0); - values.assign(_max_size, prec(0.0)); - n() = 0; - ntot() = 0; + void init() { _model.symmetry().init(); } + + void reset() { + _model.symmetry().init(); + const std::size_t sector_size = _model.symmetry().sector().size(); + if (sector_size > _max_dim) { + std::stringstream s; + s << "CRSStorage: sector requests more memory than allocated. " + "Increase storage.MAX_DIM. Requested " << sector_size + << ", allocated " << _max_dim << "."; + throw std::runtime_error(s.str()); } - - /** - * Simple Compressed-Row-Storage Matrix-Vector product - */ - virtual void av(prec *v, prec *w, int n, bool clear = true) { - for (int i = 0; i < n; ++i) { - w[i] = clear ? 0.0 : w[i]; - for (int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) { - w[i] = w[i] + values[j] * v[col_ind[j]]; - } + _vind = 0; + row_ptr.assign(_max_dim + 1, 0); + col_ind.assign(_max_size, 0); + values .assign(_max_size, prec(0)); + n() = 0; + ntot() = 0; + } + + void av(prec* v, prec* w, int n_local, bool clear = true) override { + for (int i = 0; i < n_local; ++i) { + w[i] = clear ? prec(0) : w[i]; + for (int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) { + w[i] += values[j] * v[col_ind[j]]; } } - - void fill() { - reset(); - int i = 0; - long long k = 0; - int isign = 0; - while (_model.symmetry().next_state()) { - long long nst = _model.symmetry().state(); - // Compute diagonal element for current i state - addDiagonal(i, _model.diagonal(nst)); - // non-diagonal terms calculation - // hoppings - off_diagonal(nst, i, _model.T_states()); - // interactions - off_diagonal(nst, i, _model.V_states()); - i++; - } - endMatrix(); + } + + void fill() { + reset(); + int i = 0; + while (_model.symmetry().next_state()) { + long long nst = _model.symmetry().state(); + addDiagonal(i, _model.diagonal(nst)); + off_diagonal(nst, i, _model.T_states()); + off_diagonal(nst, i, _model.V_states()); + ++i; } + endMatrix(); + } - void print() { - std::cout << std::setprecision(2) << std::fixed; + void print() const { + std::cout << std::setprecision(2) << std::fixed << "{"; + for (int i = 0; i < n(); ++i) { std::cout << "{"; - for (int i = 0; i < n(); ++i) { - std::cout << "{"; - for (int j = 0; j < n(); ++j) { - bool f = true; - for (int k = row_ptr[i]; k < row_ptr[i + 1]; ++k) { - if ((col_ind[k]) == j) { - std::cout << std::setw(6) << values[k] << (j == n() - 1 ? "" : ", "); - f = false; - } /*else { - std::cout<<"0.0 "; - }*/ - } - if (f) { - std::cout << std::setw(6) << 0.0 << (j == n() - 1 ? "" : ", "); + for (int j = 0; j < n(); ++j) { + bool f = true; + for (int k = row_ptr[i]; k < row_ptr[i + 1]; ++k) { + if (col_ind[k] == j) { + std::cout << std::setw(6) << values[k] << (j == n() - 1 ? "" : ", "); + f = false; } } - std::cout << "}" << (i == n() - 1 ? "" : ", \n"); + if (f) std::cout << std::setw(6) << 0.0 << (j == n() - 1 ? "" : ", "); } - std::cout << "}" << std::endl; + std::cout << "}" << (i == n() - 1 ? "" : ", \n"); } + std::cout << "}" << std::endl; + } - virtual void zero_eigenapair() { - Storage < prec >::eigenvalues().resize(1); - Storage < prec >::eigenvalues()[0] = values[0]; - Storage < prec >::eigenvectors().assign(1, std::vector < prec >(1, prec(1.0))); - } - size_t vector_size(typename Model::Sector sector) { - return sector.size(); - } + void zero_eigenapair() override { + this->eigenvalues().resize(1); + this->eigenvalues()[0] = values[0]; + this->eigenvectors().assign(1, std::vector(1, prec(1))); + } + + std::size_t vector_size(typename Model::Sector sector) const { + return sector.size(); + } #ifdef USE_MPI - prec vv(const std::vector & v, const std::vector & w, MPI_Comm com) { - return vv(v, w); - } + prec vv(const std::vector& v, const std::vector& w, MPI_Comm /*com*/) const { + return vv(v, w); + } #endif - prec vv(const std::vector & v, const std::vector & w) { - prec alf = prec(0.0); - for (int k = 0; k < v.size(); ++k) { - alf += w[k] * v[k]; + prec vv(const std::vector& v, const std::vector& w) const { + prec alf = prec(0); + for (std::size_t k = 0; k < v.size(); ++k) alf += w[k] * v[k]; + return alf; + } + + void a_adag(int iii, const std::vector& invec, std::vector& outvec, + const typename Model::Sector& next_sec, bool a) { + long long k; + int sign; + int i = 0; + while (_model.symmetry().next_state()) { + long long nst = _model.symmetry().state(); + if (_model.checkState(nst, iii, _model.max_total_electrons()) == (a ? 1 : 0)) { + if (a) _model.a (iii, nst, k, sign); + else _model.adag(iii, nst, k, sign); + int i1 = _model.symmetry().index(k, next_sec); + outvec[i1] = sign * invec[i]; } - return alf; - } - - void a_adag(int iii, const std::vector < prec > &invec, std::vector < prec > &outvec, const typename Model::Sector& next_sec, bool a) { - long long k; - int sign; - int i = 0; - while (_model.symmetry().next_state()) { - long long nst = _model.symmetry().state(); - if (_model.checkState(nst, iii, _model.max_total_electrons()) == (a ? 1 : 0)) { - if(a) _model.a(iii, nst, k, sign); - else _model.adag(iii, nst, k, sign); - int i1 = _model.symmetry().index(k, next_sec); - outvec[i1] = sign * invec[i]; - } - ++i; - }; + ++i; } + } - void constant_shift(prec shift) { - for (int i = 0; i < n; ++i) { - for (int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) { - if(col_ind[j] == i) { - values[j] += shift; - } - } + void constant_shift(prec shift) { + for (int i = 0; i < n(); ++i) { + for (int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) { + if (col_ind[j] == i) values[j] += shift; } } - - private: - std::vector < prec > values; - std::vector < int > row_ptr; - std::vector < int > col_ind; - size_t _max_size; - size_t _max_dim; - - - size_t _vind; - - Model &_model; - - void inline addDiagonal(const int &i, prec v) { - row_ptr[i] = _vind; - col_ind[_vind] = i; - values[_vind] = v; + } + + private: + void addDiagonal(int i, prec v) { + row_ptr[i] = static_cast(_vind); + col_ind[_vind] = i; + values [_vind] = v; + ++_vind; + ++n(); + ++ntot(); + } + + void addElement(int i, int j, prec t, int sign) { + bool hasstate = false; + std::size_t foundstate = 0; + for (std::size_t k = row_ptr[i]; k < _vind; ++k) { + if (col_ind[k] == j) { hasstate = true; foundstate = k; } + } + if (hasstate) { + values[foundstate] += static_cast(sign) * t; + } else { + col_ind[_vind] = j; + values [_vind] = static_cast(sign) * t; ++_vind; - ++n(); - ++ntot(); } - - /** - * Add off-diagonal H(i,j) element - */ - void inline addElement(int i, int j, prec t, int sign) { - bool hasstate = false; - size_t foundstate = 0; - // check that there is no any data on the k state - for (size_t iii = row_ptr[i]; iii < _vind; ++iii) { - if (col_ind[iii] == j) { - hasstate = true; - foundstate = iii; - } - } - // In case of multi-orbital Coulomb interaction we can have contribution from different Coulomb interactions - if(hasstate) { - values[foundstate] += sign * t; - } else { - // create new element in CRS arrays - col_ind[_vind] = j; - values[_vind] = sign * t; - ++_vind; - } - if (_vind > _max_size) { - std::stringstream s; - s << "Current sector request more memory than allocated. Increase MAX_SIZE parameter. Requested " << _vind << ", allocated " << _max_size << "."; - throw std::runtime_error(s.str().c_str()); + if (_vind > _max_size) { + std::stringstream s; + s << "CRSStorage: sector requests more memory than allocated. " + "Increase storage.MAX_SIZE. Requested " << _vind + << ", allocated " << _max_size << "."; + throw std::runtime_error(s.str()); + } + } + + template + void off_diagonal(long long nst, int i, const TStates& states) { + long long k = 0; + int isign = 0; + for (std::size_t kkk = 0; kkk < states.size(); ++kkk) { + if (_model.valid(states[kkk], nst)) { + prec val = _model.set(states[kkk], nst, k, isign); + int k_index = _model.symmetry().index(k); + addElement(i, k_index, val, isign); } } + } - template - inline void off_diagonal(long long nst, int i, T_states states) { - long long k = 0; - int isign = 0; - for (int kkk = 0; kkk < states.size(); ++kkk) { - // check that there is transition for current state - if (_model.valid(states[kkk], nst)) { - // set new state - prec val = _model.set(states[kkk], nst, k, isign); - int k_index = _model.symmetry().index(k); - addElement(i, k_index, val, isign); - } - } - }; + void endMatrix() { row_ptr[n()] = static_cast(_vind); } - // update the reference to the matrix end - void endMatrix() { - row_ptr[n()] = _vind; - } - }; + std::vector values; + std::vector row_ptr; + std::vector col_ind; + std::size_t _max_size; + std::size_t _max_dim; + std::size_t _vind = 0; + Model& _model; + }; - } } -#endif //EDLIB_CRSSTORAGE_H + +#endif diff --git a/include/edlib/ChiLoc.h b/include/edlib/ChiLoc.h index d29f3a1..1b62b01 100644 --- a/include/edlib/ChiLoc.h +++ b/include/edlib/ChiLoc.h @@ -1,536 +1,345 @@ -// -// Created by iskakoff on 03/01/17. -// - #ifndef EDLIB_CHILOC_H #define EDLIB_CHILOC_H -#include +#include +#include +#include +#include #include -#include -#include "Lanczos.h" -#include "EigenPair.h" +#include +#include +#include +#include +#include +#include "edlib/EigenPair.h" +#include "edlib/Gf.h" +#include "edlib/Lanczos.h" +#include "edlib/Mesh.h" +#include "edlib/Parameters.h" -namespace EDLib { - namespace gf { - /** - * Base class for susceptibility bosonic operator - * @tparam precision - floating point precision - */ - template - class BosonicOperator{ - public: - /** - * @param avg - average value for zero Matsubara frequency correction - */ - BosonicOperator(precision avg) : _avg(avg) {} - precision average () const { - return _avg; - } - private: - /// operator average value - precision _avg; - }; +namespace edlib { - /** - * Spin operator - * @tparam precision -- floating point precision - */ - template - class SzOperator: public BosonicOperator { - public: - /** - * @param avg -- average value of spin operator. By default we consider paramagnetic system and - * average spin is 0. - */ - SzOperator(precision avg = 0.0) : BosonicOperator(avg) {}; - /** - * - * @tparam ModelType -- specific model type - * @param state - occupation basis state - * @param iii - electonic orbital - * @param model - model instance - * @return value of the total spin for site 'iii' in the basis state 'state' - */ - template - precision action(long long state, int iii, const ModelType & model) const { - // 0.5 * (n_up - n_down) - return 0.5*(model.checkState(state, iii, model.max_total_electrons()) - - model.checkState(state, iii + model.orbitals(), model.max_total_electrons())); - } - /** - * @return operator name - */ - std::string name() const {return "Sz";}; - }; + /** + * Base for bosonic operators used in local susceptibility evaluation. + */ + template + class BosonicOperator { + public: + explicit BosonicOperator(Precision avg) : _avg(avg) {} + Precision average() const { return _avg; } + private: + Precision _avg; + }; - /** - * Charge operator - * @tparam precision - floating point precision - */ - template - class NOperator: public BosonicOperator { - public: - NOperator(precision avg = 1.0) : BosonicOperator(avg) {}; - /** - * - * @tparam ModelType -- specific model type - * @param state - occupation basis state - * @param iii - electonic orbital - * @param model - model instance - * @return value of the total charge for site 'iii' in the basis state 'state' - */ - template - precision action(long long state, int iii, const ModelType & model) const { - // (n_up + n_down) - return (model.checkState(state, iii, model.max_total_electrons()) + - model.checkState(state, iii + model.orbitals(), model.max_total_electrons())); - } - std::string name() const {return "N";}; - }; + /// Sz operator: 0.5 * (n_up - n_down) on a site. + template + class SzOperator : public BosonicOperator { + public: + explicit SzOperator(Precision avg = Precision(0)) + : BosonicOperator(avg) {} - /** - * General class for local susceptibilities calculation. - * - * By default, all local Green functions are computed. An array of orbital - * pairs can be supplied in the input file as the ChiLoc_orbitals group. - * - * @tparam Hamiltonian - Hamiltonian instance type - * @tparam Mesh - type of frequency mesh. can be either alps::gf::real_frequency_mesh or alps::gf::matsubara_positive_only - * @tparam Args - additional parameters for Mesh. For matsubara mesh should be alps::gf::statistics::statistics_type - */ - template - class ChiLoc : public Lanczos < Hamiltonian, MeshFactory, Args... > { - using Lanczos < Hamiltonian, MeshFactory, Args... >::zero_freq; - using Lanczos < Hamiltonian, MeshFactory, Args... >::omega; - using Lanczos < Hamiltonian, MeshFactory, Args... >::lanczos; - using Lanczos < Hamiltonian, MeshFactory, Args... >::hamiltonian; - using Lanczos < Hamiltonian, MeshFactory, Args... >::beta; - using Lanczos < Hamiltonian, MeshFactory, Args... >::compute_sym_continued_fraction; - using typename Lanczos < Hamiltonian, MeshFactory, Args... >::Mesh; - using typename Lanczos < Hamiltonian, MeshFactory, Args... >::precision; - using Sector = typename Hamiltonian::ModelType::Sector; - /// Green's function conatainer type - typedef alps::gf::two_index_gf, Mesh, alps::gf::index_mesh> GF_TYPE; - public: - /** - * - * @param p - ALPSCore parameters - * @param h - Hamiltonian instance - * @param args - additional parameters for mesh. For example for matsubara mesh should it be alps::gf::statistics::statistics_type::BOSONIC - */ - ChiLoc(alps::params &p, Hamiltonian &h, Args... args) : Lanczos < Hamiltonian, MeshFactory, Args... >(p, h, args...), _model(h.model()), - gf(omega(), alps::gf::index_mesh(h.model().interacting_orbitals())), - gf_ij(omega(), alps::gf::index_mesh(h.model().interacting_orbitals()*h.model().interacting_orbitals())), - _cutoff(p["lanc.BOLTZMANN_CUTOFF"]), _type("Sz") { - // we can not evaluate Green's function if eigenvectors have not been computed. - if(p["storage.EIGENVALUES_ONLY"] == 1) { - throw std::logic_error("Eigenvectors have not been computed. Green's function can not be evaluated."); - } - std::string input = p["INPUT_FILE"]; - alps::hdf5::archive input_file(input.c_str(), "r"); - std::vector> gf_orbs; - if(input_file.is_data("ChiLoc_orbitals/values")){ - input_file >> alps::make_pvp("ChiLoc_orbitals/values", gf_orbs); - }else{ - // Or calculate only the diagonal part. - gf_orbs.clear(); - for(size_t i = 0; i < h.model().interacting_orbitals(); ++i){ - gf_orbs.push_back({i, i}); - } - } - input_file.close(); - for(size_t ii = 0; ii < gf_orbs.size(); ++ii){ - if(gf_orbs[ii][0] == gf_orbs[ii][1]){ - _g_orbs.push_back(gf_orbs[ii][0]); - }else{ - _g_ij_orb_pairs.push_back(std::array{size_t(gf_orbs[ii][0]), size_t(gf_orbs[ii][1])}); - } + template + Precision action(long long state, int site, const Model& model) const { + return Precision(0.5) * + (model.checkState(state, site, model.max_total_electrons()) - + model.checkState(state, site + model.orbitals(), model.max_total_electrons())); + } + std::string name() const { return "Sz"; } + }; + + /// Charge operator: n_up + n_down on a site. + template + class NOperator : public BosonicOperator { + public: + explicit NOperator(Precision avg = Precision(1)) + : BosonicOperator(avg) {} + + template + Precision action(long long state, int site, const Model& model) const { + return model.checkState(state, site, model.max_total_electrons()) + + model.checkState(state, site + model.orbitals(), model.max_total_electrons()); + } + std::string name() const { return "N"; } + }; + + /** + * Local susceptibility \chi_{ii}(\omega) and its non-local extension via + * Lanczos continued fractions. Stores the result in GF2 = Gf: + * shape [n_omega, n_orbital] for the diagonal and + * [n_omega, n_orbital*n_orbital] for the non-local block. + */ + template + class ChiLoc : public Lanczos { + using Base = Lanczos; + using Base::zero_freq; + using Base::lanczos; + using Base::hamiltonian; + using Base::compute_sym_continued_fraction; + using typename Base::precision; + using Sector = typename Hamiltonian::ModelType::Sector; + + public: + using Base::beta; + using Base::omega; + using GF_TYPE = GF2; + + ChiLoc(const Parameters& p, Hamiltonian& h, Mesh omega_mesh, + const std::vector>& orbital_pairs = {}) + : Base(p, h, std::move(omega_mesh)), + _model(h.model()), + gf ({omega().extent(), h.model().interacting_orbitals()}), + gf_ij({omega().extent(), + h.model().interacting_orbitals() * h.model().interacting_orbitals()}), + _cutoff(static_cast(p.lanc_boltzmann_cutoff)), + _type("Sz") { + if (p.eigenvalues_only) { + throw std::logic_error( + "ChiLoc: eigenvectors were not computed (eigenvalues_only=true)."); + } + const int n_orb = h.model().interacting_orbitals(); + if (orbital_pairs.empty()) { + for (int i = 0; i < n_orb; ++i) _g_orbs.push_back(i); + } else { + for (const auto& pr : orbital_pairs) { + if (pr[0] == pr[1]) _g_orbs.push_back(pr[0]); + else _g_ij_orb_pairs.push_back({pr[0], pr[1]}); } - // Find all unique indices for the diagonal part. std::sort(_g_orbs.begin(), _g_orbs.end()); _g_orbs.erase(std::unique(_g_orbs.begin(), _g_orbs.end()), _g_orbs.end()); - // Check that we will have the two local GFs required by nonlocal GF. - for(size_t ii = 0; ii < _g_ij_orb_pairs.size(); ++ii){ - for(size_t jj = 0; jj < 2; ++jj){ - bool found = false; - for(size_t kk = 0; kk < _g_orbs.size(); ++kk){ - if(_g_orbs[kk] == _g_ij_orb_pairs[ii][jj]){ - found = true; - break; - } - } - if(!found){ - _g_orbs.push_back(_g_ij_orb_pairs[ii][jj]); + for (const auto& pr : _g_ij_orb_pairs) { + for (int e : pr) { + if (std::find(_g_orbs.begin(), _g_orbs.end(), e) == _g_orbs.end()) { + _g_orbs.push_back(e); } } } } + } + + const GF_TYPE& G() const { return gf; } + const GF_TYPE& G_ij() const { return gf_ij; } - const GF_TYPE &G() const {return gf;} - const GF_TYPE &G_ij() const {return gf_ij;} + template > + void compute(const double* avg_ptr = nullptr) { + static_assert(std::is_base_of, Op>::value, + "ChiLoc::compute: Op must derive from BosonicOperator"); + gf *= std::complex(0); + gf_ij *= std::complex(0); + _Z = precision(0); + if (hamiltonian().eigenpairs().empty()) return; - /** - * Compute two-particle Green's function for specific operator Op. - * - * @tparam Op type of bosonic operator (should be either SzOperator or NOperator) - * @param avg_ptr - pointer to the operator average value. If null -- the default average vlues will be used for specific operator. - */ - template > - void compute(const double * avg_ptr = nullptr) { - static_assert(std::is_base_of, Op>::value, "Wrong bosonic operator."); - // reset to 0 - gf *= 0.0; - gf_ij *= 0.0; - _Z = 0.0; - // check that at least one eigen-value have been computed - if(hamiltonian().eigenpairs().empty()) - return; #ifdef USE_MPI - int rank; - MPI_Comm_rank(hamiltonian().storage().comm(), &rank); + int rank; + MPI_Comm_rank(hamiltonian().storage().comm(), &rank); #endif - // get groundstate - const EigenPair &groundstate = *hamiltonian().eigenpairs().begin(); - // compute partition function - for (auto kkk = hamiltonian().eigenpairs().begin(); kkk != hamiltonian().eigenpairs().end(); kkk++) { - const EigenPair &eigenpair = *kkk; - _Z += std::exp(-(eigenpair.eigenvalue() - groundstate.eigenvalue()) * beta()); - } - // init bosonic operator - const Op op = (avg_ptr == nullptr ? Op() : Op(*avg_ptr)); - _type = op.name(); - // loop over all eigen-pairs - for (auto kkk = hamiltonian().eigenpairs().begin(); kkk != hamiltonian().eigenpairs().end(); kkk++) { - const EigenPair& pair = *kkk; - precision boltzmann_f = std::exp(-(pair.eigenvalue() - groundstate.eigenvalue()) * beta()); - if (std::abs(_cutoff - boltzmann_f) > std::numeric_limits::epsilon() && boltzmann_f < _cutoff ) { - // Skipped by Boltzmann factor. - continue; - } + const auto& groundstate = *hamiltonian().eigenpairs().begin(); + for (const auto& pair : hamiltonian().eigenpairs()) { + _Z += std::exp(-(pair.eigenvalue() - groundstate.eigenvalue()) * beta()); + } + const Op op = (avg_ptr == nullptr ? Op() : Op(precision(*avg_ptr))); + _type = op.name(); + + for (const auto& pair : hamiltonian().eigenpairs()) { + precision boltzmann_f = + std::exp(-(pair.eigenvalue() - groundstate.eigenvalue()) * beta()); + if (std::abs(_cutoff - boltzmann_f) > std::numeric_limits::epsilon() + && boltzmann_f < _cutoff) continue; #ifdef USE_MPI - if(rank == 0) + if (rank == 0) #endif - std::cout << "Compute Green's function contribution for eigenvalue E=" << pair.eigenvalue() << " with Boltzmann factor = " + std::cout << "Compute Green's function contribution for eigenvalue E=" + << pair.eigenvalue() << " with Boltzmann factor = " << boltzmann_f << "; for sector" << pair.sector() << std::endl; - local_contribution(groundstate, op, pair); - nonlocal_contribution(groundstate, op, pair); - } + local_contribution (groundstate, op, pair); + nonlocal_contribution(groundstate, op, pair); + } #ifdef USE_MPI - if(rank == 0) { + if (rank == 0) { #endif - // normalize Green's function - gf /= _Z; - gf_ij /= _Z; - // Compute zero-frequency contribution + gf /= std::complex(_Z); + gf_ij /= std::complex(_Z); local_correction(op); - // Compute non-local correction non_local_correction(op); #ifdef USE_MPI - } -#endif } +#endif + } - /** - * Evaluates static correction at zero matsubara frequency - * - * @tparam O -- bosonic operator type - * @param op -- bosonic operator - */ - template - void local_correction(const O&op) { - for (int iorb = 0; iorb < _g_orbs.size(); ++iorb) { - zero_freq_contribution(op, gf, _g_orbs[iorb]); - } - } + template + void local_correction(const O& op) { + for (int orb : _g_orbs) zero_freq_contribution(op, gf, orb); + } - /** - * Computes non-local Green's function from symmetrized averaged: G_ij = 0.5( <(op_i + op_j)(op_i + op_j)> - - ), - * and evaluates static correction at zero matsubara frequency - * - * - * @tparam O -- bosonic operator type - * @param op -- bosonic operator - */ - template - void non_local_correction(const O& op) { - for (int iorb = 0; iorb < _g_ij_orb_pairs.size(); ++iorb) { - auto orbs = _g_ij_orb_pairs[iorb]; - zero_freq_contribution(op, gf_ij, orbs[0] * _model.interacting_orbitals() + orbs[1]); - } - for (int iomega = 0; iomega < omega().extent(); ++iomega) { - for (int iorb = 0; iorb < _g_ij_orb_pairs.size(); ++iorb) { - auto orbs = _g_ij_orb_pairs[iorb]; - for (int jj = 0; jj < 2; ++jj) { - gf_ij(typename Mesh::index_type(iomega), alps::gf::index_mesh::index_type(orbs[0] * _model.interacting_orbitals() + orbs[1])) -= gf(typename Mesh::index_type(iomega), alps::gf::index_mesh::index_type(orbs[jj])); - } - gf_ij(typename Mesh::index_type(iomega), alps::gf::index_mesh::index_type(orbs[0] * _model.interacting_orbitals() + orbs[1])) *= 0.5; - } - // and copy diagonal G for completeness - for (int iorb = 0; iorb < _g_orbs.size(); ++iorb) { - size_t orb = _g_orbs[iorb]; - gf_ij(typename Mesh::index_type(iomega), alps::gf::index_mesh::index_type(orb * _model.interacting_orbitals() + orb)) = gf(typename Mesh::index_type(iomega), alps::gf::index_mesh::index_type(orb)); - } + template + void non_local_correction(const O& op) { + const int n_orb = _model.interacting_orbitals(); + for (const auto& pr : _g_ij_orb_pairs) { + zero_freq_contribution(op, gf_ij, pr[0] * n_orb + pr[1]); + } + for (int iomega = 0; iomega < omega().extent(); ++iomega) { + for (const auto& pr : _g_ij_orb_pairs) { + int ij = pr[0] * n_orb + pr[1]; + for (int jj = 0; jj < 2; ++jj) gf_ij(iomega, ij) -= gf(iomega, pr[jj]); + gf_ij(iomega, ij) *= std::complex(0.5); } - }; + for (int orb : _g_orbs) gf_ij(iomega, orb * n_orb + orb) = gf(iomega, orb); + } + } - /** - * Compute local Green's function G_ii - * - * @tparam Op -- type of bosonic operator - * @param groundstate -- system groundstate - * @param op -- bosonic operator - * @param pair -- current Eigen-Pair - */ - template > - void local_contribution(const EigenPair &groundstate, const Op op, - const EigenPair &pair) { + template > + void local_contribution(const EigenPair& groundstate, const Op op, + const EigenPair& pair) { #ifdef USE_MPI - int rank; - MPI_Comm_rank(hamiltonian().storage().comm(), &rank); + int rank; MPI_Comm_rank(hamiltonian().storage().comm(), &rank); #endif - for (int iorb = 0; iorb < _g_orbs.size(); ++iorb) { - int orb = _g_orbs[iorb]; - std::vector < precision > outvec(1, precision(0.0)); - precision expectation_value = 0; - _model.symmetry().set_sector(pair.sector()); - if (operation(orb, pair.eigenvector(), outvec, expectation_value, op)) { - int nlanc = lanczos(outvec); + for (int orb : _g_orbs) { + std::vector outvec(1, precision(0)); + precision expectation_value = 0; + _model.symmetry().set_sector(pair.sector()); + if (operation(orb, pair.eigenvector(), outvec, expectation_value, op)) { + int nlanc = lanczos(outvec); #ifdef USE_MPI - if(rank==0){ -#endif - std::cout << "orbital: " << orb << " =" << expectation_value << " nlanc:" << nlanc << std::endl; - // compute symmetrized Lanczos continued fraction - compute_sym_continued_fraction(expectation_value, pair.eigenvalue(), groundstate.eigenvalue(), nlanc, 1, - gf, alps::gf::index_mesh::index_type(orb)); -#ifdef USE_MPI - } + if (rank == 0) #endif + { + std::cout << "orbital: " << orb << " =" << expectation_value << " nlanc:" << nlanc << std::endl; + compute_sym_continued_fraction(expectation_value, pair.eigenvalue(), + groundstate.eigenvalue(), + nlanc, 1, gf, orb); } } } - /** - * Computes symmetrized non-local Green's function for operator op: G_ij = <(op_i + op_j)(op_i + op_j)> - * - * @tparam Op -- type of bosonic operator - * @param groundstate -- system groundstate - * @param op -- bosonic operator - * @param pair -- current Eigen-Pair - */ - template - void nonlocal_contribution(const EigenPair &groundstate, const Op &op, - const EigenPair &pair) { -#ifdef USE_MPI - int rank; - MPI_Comm_rank(hamiltonian().storage().comm(), &rank); -#endif - for (int iorb = 0; iorb < _g_ij_orb_pairs.size(); ++iorb) { - auto orbs = _g_ij_orb_pairs[iorb]; - _model.symmetry().set_sector(pair.sector()); - std::vector < precision > outvec(1, precision(0.0)); - precision expectation_value = 0; - if(operation(orbs[0], orbs[1], pair.eigenvector(), outvec, expectation_value, op)) { - int nlanc = lanczos(outvec); + } + + template + void nonlocal_contribution(const EigenPair& groundstate, const Op& op, + const EigenPair& pair) { #ifdef USE_MPI - if(rank == 0) { + int rank; MPI_Comm_rank(hamiltonian().storage().comm(), &rank); #endif - std::cout << "orbitals: " << orbs[0] << ", " << orbs[1] << " =" << expectation_value << " nlanc:" << nlanc << std::endl; - // compute symmetrized Lanczos continued fraction - compute_sym_continued_fraction(expectation_value, pair.eigenvalue(), groundstate.eigenvalue(), nlanc, 1, - gf_ij, alps::gf::index_mesh::index_type(orbs[0] * _model.interacting_orbitals() + orbs[1])); + const int n_orb = _model.interacting_orbitals(); + for (const auto& pr : _g_ij_orb_pairs) { + _model.symmetry().set_sector(pair.sector()); + std::vector outvec(1, precision(0)); + precision expectation_value = 0; + if (operation(pr[0], pr[1], pair.eigenvector(), outvec, expectation_value, op)) { + int nlanc = lanczos(outvec); #ifdef USE_MPI - } + if (rank == 0) #endif + { + std::cout << "orbitals: " << pr[0] << ", " << pr[1] << " =" << expectation_value + << " nlanc:" << nlanc << std::endl; + compute_sym_continued_fraction(expectation_value, pair.eigenvalue(), + groundstate.eigenvalue(), + nlanc, 1, gf_ij, + pr[0] * n_orb + pr[1]); } } } + } - /** - * zero Matsubara frequency contribution. Since for bosonic Green's function Lanczos continued fraction can not - * properly evaluate zero frequency contribution we compute it from the following sum-rule: - * \Chi(w_0) = \beta \Chi(tau = 0) - 2 \sum_{n!=0} \Chi(w_n) - * - * @tparam O - type of operator - * @tparam M - Mesh type - * @param op - operator instance - */ - template - typename std::enable_if::value, void>::type zero_freq_contribution(const O& op, GF_TYPE& G, int i) { - // Compute static susceptibility for each orbital - + private: + // Matsubara zero-frequency correction: removes the analytical tail of + // Chi(W_n) ~ c2/W^2 + c4/W^4 and applies the sum rule. + template + void zero_freq_contribution(const O& op, GF_TYPE& G, int i) { + if constexpr (std::is_same_v) { double chiSum = 0.0; - // Susceptibility decays as c2/w^2 + c4/w^4 - // compute c2 and c4 from two largest freq points - // and for next pair as well to check convergence - double c2, c4; - double c2_2, c4_2; - double tail, tail2; - // check that we have enough Matsubara frequencies and we already sit in the high-frequency tail regime - get_tail(i, omega().extent()-1, G, c2, c4, tail); - get_tail(i, omega().extent()-2, G, c2_2, c4_2, tail2); - if(std::abs(tail-tail2)/std::abs(tail) > 1e-4) { - std::cerr<<"Not enough frequencies to compute high frequency tail. Please increase number of frequencies. Diff: "< 1e-4) { + std::cerr << "Not enough frequencies to compute high frequency tail. " + "Please increase number of frequencies. Diff: " + << std::abs(tail - tail2) / std::abs(tail) << std::endl; } - // loop over non-zero Matsubara frequencies for (int iomega = 1; iomega < omega().extent(); ++iomega) { double om = omega().points()[iomega]; - chiSum = chiSum + G(typename Mesh::index_type(iomega), alps::gf::index_mesh::index_type(i)).real() - c2/(om*om) - c4/(om*om*om*om); + chiSum += G(iomega, i).real() - c2 / (om * om) - c4 / (om * om * om * om); } - // computes zero-frequency contribution - G(typename Mesh::index_type(0), alps::gf::index_mesh::index_type(i)) -= 2 * chiSum + 2* tail - op.average()*op.average()*beta(); - }; - - /** - * For real frequency mesh there is nothing to do. Zero frequency has been already correctly computed. - */ - template - typename std::enable_if::value, void>::type zero_freq_contribution(const O& op, GF_TYPE& G, int i) { + G(0, i) -= std::complex(2 * chiSum + 2 * tail + - op.average() * op.average() * beta(), + 0.0); + } else { + (void)op; (void)G; (void)i; + } + } - }; + void get_tail(int i, int freq, const GF_TYPE& G, + double& c2, double& c4, double& tail) const { + double om1 = omega().points()[freq]; + double om2 = omega().points()[freq - 1]; + double om1_2 = om1 * om1; + double om2_2 = om2 * om2; + double g1 = G(freq, i).real(); + double g2 = G(freq - 1, i).real(); + c2 = -(g2 * om2_2 * om2_2 - g1 * om1_2 * om1_2) / (om1_2 - om2_2); + c4 = -(g1 * om1_2 * om1_2 * om2_2 - g2 * om2_2 * om2_2 * om1_2) / (om1_2 - om2_2); + tail = c2 * beta() * beta() / 24.0 + + c4 * beta() * beta() * beta() * beta() / 1440.0; + } - /** - * High frequency behaviour of Chi(W_n) = c_2/(W_n)^2 + c_4/(W_n)^4 - * - * @param i -- current orbital - * @param freq -- frequency - * @param c2 -- c_2 coefficient - * @param c4 -- c_4 coefficient - * @param tail -- analytical value of tail - */ - void get_tail(int i, int freq, GF_TYPE& G, double &c2, double& c4, double &tail) const { - double om1 = omega().points()[freq]; - double om2 = omega().points()[freq-1]; - double om1_2 = om1*om1; - double om2_2 = om2*om2; - double g1 = G(typename Mesh::index_type(freq), alps::gf::index_mesh::index_type(i)).real(); - double g2 = G(typename Mesh::index_type(freq - 1), alps::gf::index_mesh::index_type(i)).real(); - c2 = - (g2*om2_2*om2_2 - g1*om1_2*om1_2)/(om1_2-om2_2); - c4 = - (g1*om1_2*om1_2*om2_2 - g2*om2_2*om2_2*om1_2)/(om1_2-om2_2); - tail= c2 * beta() * beta() / 24.0 + c4 * beta() * beta() * beta() * beta() / 1440.0; + template + bool operation(int orbital, const std::vector& invec, + std::vector& outvec, precision& expectation_value, + const Op& o) { + hamiltonian().storage().reset(); + outvec.assign(hamiltonian().storage().vector_size(_model.symmetry().sector()), + precision(0)); + for (std::size_t i = 0; i < invec.size(); ++i) { + _model.symmetry().next_state(); + long long nst = _model.symmetry().state(); + outvec[i] = o.action(nst, orbital, _model) * invec[i]; } - - /** - * Save susceptibility to HDF5 archive and to the text-file - * @param ar - hdf5 archive file - * @param path - root path in hdf5 archive - */ - void save(alps::hdf5::archive& ar, const std::string & path) { + double norm = hamiltonian().storage().vv(outvec, outvec #ifdef USE_MPI - int rank; - MPI_Comm_rank(hamiltonian().storage().comm(), &rank); - if(rank == 0) { + , hamiltonian().comm() #endif - if(_g_orbs.size()){ - gf.save(ar, path + "/Chi" + _type +"_omega"); - std::ostringstream Gomega_name; - Gomega_name << "Chi"<<_type<<"_omega"; - std::ofstream G_omega_file(Gomega_name.str().c_str()); - G_omega_file << std::setprecision(14) << gf; - G_omega_file.close(); - } - std::cout << "Statsum: " << _Z << std::endl; - ar[path + "/@Statsum"] << _Z; - if(_g_ij_orb_pairs.size()){ - gf_ij.save(ar, path + "/Chi" + _type +"_ij_omega"); - std::ostringstream Gomega_name2; - Gomega_name2 << "Chi_ij_"<<_type<<"_omega"; - std::ofstream G_omega_file2(Gomega_name2.str().c_str()); - G_omega_file2<< std::setprecision(14) << gf_ij; - G_omega_file2.close(); - } -#ifdef USE_MPI - } -#endif - } + ); + for (auto& v : outvec) v /= std::sqrt(norm); + _model.symmetry().init(); + expectation_value = static_cast(norm); + return expectation_value > precision(1e-9); + } - private: - /// Green's function conatainer - GF_TYPE gf; - /// Nonlocal - GF_TYPE gf_ij; - /// Specific model - typename Hamiltonian::ModelType &_model; - /// Boltzmann-factor cut-off - precision _cutoff; - /// Partition function - precision _Z; - /// type of recently computed susceptibility - std::string _type; - /// Orbitals used for the diagonal Green's function calculation - std::vector _g_orbs; - /// Orbital pairs used for the offdiagonal Green's function calculation - std::vector > _g_ij_orb_pairs; - - /** - * @brief Perform the operation to the eigenstate - * - * @param orbital - the orbital to action - * @param invec - current eigenstate - * @param outvec - Op-vec product - * @param expectation_value - expectation value of aa* - * @return true if the particle has been created - */ - template - bool operation(int orbital, const std::vector < precision > &invec, std::vector < precision > &outvec, double &expectation_value, const Op& o) { - hamiltonian().storage().reset(); - long long k = 0; - int sign = 0; - outvec.assign(hamiltonian().storage().vector_size(_model.symmetry().sector()), 0.0); - for(int i = 0; i< invec.size(); ++i) { - _model.symmetry().next_state(); - long long nst = _model.symmetry().state(); - outvec[i] = o.action(nst, orbital, _model) * invec[i]; - }; - double norm = hamiltonian().storage().vv(outvec, outvec + template + bool operation(int mu, int nu, const std::vector& invec, + std::vector& outvec, precision& expectation_value, + const Op& o) { + hamiltonian().storage().reset(); + outvec.assign(hamiltonian().storage().vector_size(_model.symmetry().sector()), + precision(0)); + for (std::size_t i = 0; i < invec.size(); ++i) { + _model.symmetry().next_state(); + long long nst = _model.symmetry().state(); + outvec[i] = (o.action(nst, mu, _model) + o.action(nst, nu, _model)) * invec[i]; + } + double norm = hamiltonian().storage().vv(outvec, outvec #ifdef USE_MPI - , hamiltonian().comm() + , hamiltonian().comm() #endif - ); - for (int j = 0; j < outvec.size(); ++j) { - outvec[j] /= std::sqrt(norm); - } - _model.symmetry().init(); - expectation_value = norm; - return expectation_value > 1e-9; - }; + ); + for (auto& v : outvec) v /= std::sqrt(norm); + _model.symmetry().init(); + expectation_value = static_cast(norm); + return true; + } - /** - * @brief Perform the symmetrized operation on the eigenstate - * - * @param mu - the first orbital to action - * @param mu - the second orbital to action - * @param invec - current eigenstate - * @param outvec - Op-vec product - * @param expectation_value - expectation value of aa* - * @return true if the particle has been created - */ - template - bool operation(int mu, int nu, const std::vector < precision > &invec, std::vector < precision > &outvec, double &expectation_value, const Op& o) { - hamiltonian().storage().reset(); - long long k = 0; - int sign = 0; - outvec.assign(hamiltonian().storage().vector_size(_model.symmetry().sector()), 0.0); - for(int i = 0; i< invec.size(); ++i) { - _model.symmetry().next_state(); - long long nst = _model.symmetry().state(); - outvec[i] = (o.action(nst, mu, _model) + o.action(nst, nu, _model) )* invec[i]; - }; - double norm = hamiltonian().storage().vv(outvec, outvec -#ifdef USE_MPI - , hamiltonian().comm() -#endif - ); - for (int i = 0; i < outvec.size(); ++i) { - outvec[i] /= std::sqrt(norm); - } - _model.symmetry().init(); - expectation_value = norm; - return true;//expectation_value > 1e-9; - }; - }; - } -} + typename Hamiltonian::ModelType& _model; + GF_TYPE gf; + GF_TYPE gf_ij; + precision _cutoff; + precision _Z = precision(0); + std::string _type; + std::vector _g_orbs; + std::vector> _g_ij_orb_pairs; + }; +} -#endif //EDLIB_CHILOC_H +#endif diff --git a/include/edlib/Combination.h b/include/edlib/Combination.h index 5d77f8e..467d0cc 100644 --- a/include/edlib/Combination.h +++ b/include/edlib/Combination.h @@ -1,48 +1,32 @@ -// -// Created by iskakoff on 21/08/16. -// - #ifndef EDLIB_COMBINATION_H #define EDLIB_COMBINATION_H - +#include #include -namespace EDLib { +namespace edlib { class Combination { public: - Combination(int N) : _c_n_k(N + 1, std::vector < int >(N + 1, 0)) { + explicit Combination(int N) : _c_n_k(N + 1, std::vector(N + 1, 0)) { for (int i = 0; i <= N; ++i) { for (int j = 0; j <= N; ++j) { - _c_n_k[i][j] = C_n_k_i(i, j); + _c_n_k[i][j] = static_cast(C_n_k_i(i, j)); } } } - inline int c_n_k(int n, int k) const { - return _c_n_k[n][k]; - } + inline int c_n_k(int n, int k) const { return _c_n_k[n][k]; } - /** - * reset to initial state - */ - inline void init_state(int ik, std::vector < int > &vec) { - for (int i = 0; i < ik; i++) { - vec[i] = i; - } + inline void init_state(int ik, std::vector& vec) { + for (int i = 0; i < ik; ++i) vec[i] = i; } - /** - * compute next combination in lexicographicaly ordered basis - */ - inline bool next_combination(int n, int k, std::vector < int > &old) { - for (int i = k - 1; i >= 0; i--) { + inline bool next_combination(int n, int k, std::vector& old) { + for (int i = k - 1; i >= 0; --i) { if (old[i] < (n - 1 - k + (i + 1))) { old[i] += 1; - for (int j = i + 1; j < k; j++) { - old[j] = old[j - 1] + 1; - } + for (int j = i + 1; j < k; ++j) old[j] = old[j - 1] + 1; return true; } } @@ -50,30 +34,20 @@ namespace EDLib { } private: - /// chached values for combination k of n - std::vector < std::vector < int > > _c_n_k; + std::vector> _c_n_k; - /** - * Calculate number of combinations: - * C_k^n = n!/(k!*(n-k)!) - */ - int64_t C_n_k_i(int n, int k) { - if ((n - k) > k) { - return variation(n - k + 1, n) / variation(1, k); - } - return variation(k + 1, n) / variation(1, n - k); + static std::int64_t variation(std::int64_t n1, std::int64_t n2) { + std::int64_t r = 1; + for (std::int64_t i = n1; i <= n2; ++i) r *= i; + return r; } - /** - * Calculate n2!/n1! - */ - int64_t variation(int64_t n1, int64_t n2) { - int64_t result = 1; - for (int64_t i = n1; i <= n2; i++) { - result *= i; - } - return result; + static std::int64_t C_n_k_i(int n, int k) { + if ((n - k) > k) return variation(n - k + 1, n) / variation(1, k); + return variation(k + 1, n) / variation(1, n - k); } }; + } -#endif //EDLIB_COMBINATION_H + +#endif diff --git a/include/edlib/CommonUtils.h b/include/edlib/CommonUtils.h index 8ea302d..2cb6152 100644 --- a/include/edlib/CommonUtils.h +++ b/include/edlib/CommonUtils.h @@ -1,24 +1,23 @@ -// -// Created by iskakoff on 01/02/17. -// - #ifndef EDLIB_COMMONUTILS_H #define EDLIB_COMMONUTILS_H +#include #include -#include +#include "edlib/Mesh.h" + +namespace edlib { -namespace EDLib { - namespace common { - std::complex freq_point(int index, const alps::gf::matsubara_positive_mesh & mesh, double beta) { - return std::complex(0.0, mesh.points()[index]); - }; + inline std::complex + freq_point(int index, const MatsubaraMesh& mesh, double /*beta*/) { + return std::complex(0.0, mesh.points()[index]); + } - std::complex freq_point(int index, const alps::gf::real_frequency_mesh& mesh, double beta) { - return std::complex(mesh.points()[index], M_PI/beta); - }; + inline std::complex + freq_point(int index, const RealFreqMesh& mesh, double beta) { + return std::complex(mesh.points()[index], M_PI / beta); } + } -#endif //EDLIB_COMMONUTILS_H +#endif diff --git a/include/edlib/Dyson.h b/include/edlib/Dyson.h new file mode 100644 index 0000000..4bd9c64 --- /dev/null +++ b/include/edlib/Dyson.h @@ -0,0 +1,57 @@ +#ifndef EDLIB_DYSON_H +#define EDLIB_DYSON_H + +#include +#include + +#include +#include + +#include "edlib/Gf.h" + +namespace edlib { + + /** + * Solve Dyson's equation Sigma(iw) = G0^{-1}(iw) - G^{-1}(iw) for each + * frequency and spin slice. + * + * All three GFs share shape [n_omega, nsites*nsites, n_spin]. The second + * axis indexes orbital pairs as I*nsites + J, matching what + * HubbardModel::bare_greens_function emits. + */ + inline void solve_dyson(const Gf, 3>& bare, + const Gf, 3>& G, + Gf, 3>& sigma, + int nsites) { + if (bare.shape() != G.shape() || G.shape() != sigma.shape()) { + throw std::invalid_argument("solve_dyson: shape mismatch among bare, G, sigma"); + } + const int n_omega = bare.shape(0); + const int n_orb = bare.shape(1); + const int n_spin = bare.shape(2); + if (n_orb != nsites * nsites) { + throw std::invalid_argument("solve_dyson: shape(1) must equal nsites*nsites"); + } + for (int iw = 0; iw < n_omega; ++iw) { + for (int is = 0; is < n_spin; ++is) { + Eigen::MatrixXcd b(nsites, nsites); + Eigen::MatrixXcd g(nsites, nsites); + for (int I = 0; I < nsites; ++I) { + for (int J = 0; J < nsites; ++J) { + b(I, J) = bare(iw, I * nsites + J, is); + g(I, J) = G (iw, I * nsites + J, is); + } + } + Eigen::MatrixXcd s = b.inverse() - g.inverse(); + for (int I = 0; I < nsites; ++I) { + for (int J = 0; J < nsites; ++J) { + sigma(iw, I * nsites + J, is) = s(I, J); + } + } + } + } + } + +} + +#endif diff --git a/include/edlib/EDParams.h b/include/edlib/EDParams.h deleted file mode 100644 index 1e0e4c7..0000000 --- a/include/edlib/EDParams.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// Created by iskakoff on 18/08/16. -// - -#ifndef EDLIB_EDPARAMS_H -#define EDLIB_EDPARAMS_H - - -#include -namespace EDLib { - void define_parameters(alps::params ¶ms) { - // General parameters - params.define < int >("NSITES", 4, "Number of sites"); - params.define < int >("NSPINS", 2, "Number of spins"); - params.define < std::string >("INPUT_FILE", "input.h5", "File with initial data"); - params.define < std::string >("OUTPUT_FILE", "sim.h5", "File with results"); - // Symmetry parameters - params.define < bool >("arpack.SECTOR", "Read symmetry sectors from file"); - // Storage parameters - params.define < size_t >("storage.MAX_DIM", 5000, "Maximum dimension of the Hamiltonian matrix."); - params.define < size_t >("storage.MAX_SIZE", 70000, "Maximum size of the matrix arrays. Must be between MAX_DIM and MAX_DIM^2."); - params.define < int >("storage.EIGENVALUES_ONLY", 0, "Compute only eigenvalues."); - params.define < int >("spinstorage.ORBITAL_NUMBER", 1, "Number of orbitals with interaction"); - // ARPACK parameters - params.define < int >("arpack.NEV", 2, "Number of eigenvalues to find"); - params.define < int >("arpack.NCV", "Number of convergent values"); - // Lanczos parameters - params.define < int >("lanc.NOMEGA", 32, "Number of fermionic frequencies"); - params.define < double >("lanc.EMIN", -3, "Lowest real frequency value"); - params.define < double >("lanc.EMAX", 3, "Largest real frequency value"); - params.define < int >("lanc.NLANC", 100, "Number of Lanczos iterations"); - params.define < double >("lanc.BETA", 10.0, "Inverse temperature"); - params.define < double >("lanc.BOLTZMANN_CUTOFF", 1e-12, "Cutoff for Boltzmann factor"); - - // Anderson model - params.define < int >("siam.NORBITALS", 1, "Number of orbitals in single impurity Anderson Model."); - } -} -#endif //EDLIB_EDPARAMS_H diff --git a/include/edlib/EigenPair.h b/include/edlib/EigenPair.h index 0326b3f..54d85a7 100644 --- a/include/edlib/EigenPair.h +++ b/include/edlib/EigenPair.h @@ -1,51 +1,39 @@ -// -// Created by iskakoff on 27/07/16. -// - #ifndef EDLIB_EIGENPAIR_H #define EDLIB_EIGENPAIR_H #include -namespace EDLib { - template +namespace edlib { + + template class EigenPair { public: + EigenPair(const Precision& eval, + const std::vector& evec, + int id, + SectorType sec) + : _eigenvalue(eval), _sector(sec), _eigenvector(evec), _id(id) {} - EigenPair(const precision &eval, const std::vector < precision > &evec, int id, SectorType sec) : _eigenvalue(eval), _sector(sec), - _eigenvector(evec), _id(id) { - }; - - virtual ~EigenPair() { - - } + virtual ~EigenPair() = default; - precision eigenvalue() const { - return _eigenvalue; - } + Precision eigenvalue() const { return _eigenvalue; } + const std::vector& eigenvector() const { return _eigenvector; } + const SectorType& sector() const { return _sector; } - const std::vector < precision > &eigenvector() const { - return _eigenvector; + bool operator<(const EigenPair& o) const { + return (_eigenvalue < o._eigenvalue) || (_id < o._id); } - - const SectorType §or() const { - return _sector; + bool operator>(const EigenPair& o) const { + return (_eigenvalue > o._eigenvalue) || (_id > o._id); } - bool operator>(const EigenPair &pair) const { - return (_eigenvalue > pair._eigenvalue) || (_id > pair._id); - }; - - bool operator<(const EigenPair &pair) const { - return (_eigenvalue < pair._eigenvalue) || (_id < pair._id); - }; private: - precision _eigenvalue; - std::vector < precision > _eigenvector; - int _id; - SectorType _sector; + Precision _eigenvalue; + std::vector _eigenvector; + int _id; + SectorType _sector; }; } -#endif //EDLIB_EIGENPAIR_H +#endif diff --git a/include/edlib/ExecutionStatistic.h b/include/edlib/ExecutionStatistic.h index 9323d7e..e9aca7b 100644 --- a/include/edlib/ExecutionStatistic.h +++ b/include/edlib/ExecutionStatistic.h @@ -1,88 +1,57 @@ -// -// Created by iskakoff on 02/02/17. -// - #ifndef EDLIB_EXECUTIONSTATISTIC_H #define EDLIB_EXECUTIONSTATISTIC_H - #ifdef USE_MPI #include #endif -#include -#include #include +#include +#include +#include +#include -namespace EDLib { - namespace common { -/** - * @brief ExecutionStatistic class - * - * @author iskakoff - */ - class ExecutionStatistic { - public: +namespace edlib { - ExecutionStatistic() {} + class ExecutionStatistic { + public: + void updateEvent(const std::string& name) { + double t = time(); + _events[name] = {_events[name].first + t - _events[name].second, t}; + } - /** - * Update event time - * @param name - event name - */ - void updateEvent(const std::string& name) { - double time1 = time(); - _events[name] = std::make_pair(_events[name].first + time1 - _events[name].second, time1); - } + void registerEvent(const std::string& name) { + _events[name] = {_events[name].first, time()}; + } - /** - * register the start point of the event - * - * @param name - event name - */ - void registerEvent(const std::string& name) { - _events[name] = std::make_pair(_events[name].first, time()); + void print() const { + for (const auto& kv : _events) { + std::cout << "Event " << kv.first << " take " << kv.second.first << "s." << std::endl; } + } - /** - * Print all observed events - */ - void print() { - for (auto& kv : _events) { - std::cout <<"Event "<< kv.first << " take " << kv.second.first << "s." << std::endl; - } - } + std::pair event(const std::string& name) const { + auto it = _events.find(name); + if (it == _events.end()) return {0.0, 0.0}; + return it->second; + } - /** - * Return event timing pair - * @param event_name - event name - * @return event timing - */ - std::pair event(const std::string & event_name) { - if(_events.find(event_name) != _events.end()) { - return _events[event_name]; - } - return std::make_pair(0.0, 0.0); - }; - private: - // registered events timing pairs - // pair.first corresponds to total event time - // pair.second corresponds to last time when event was happened - std::map > _events; + private: + std::map> _events; - double time() const { + static double time() { #ifdef USE_MPI - return MPI_Wtime(); + return MPI_Wtime(); #else - return std::chrono::duration_cast >(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + return std::chrono::duration_cast>( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); #endif - } - }; - - static EDLib::common::ExecutionStatistic statistics; - } -} + } + }; + inline ExecutionStatistic statistics; +} -#endif //EDLIB_EXECUTIONSTATISTIC_H +#endif diff --git a/include/edlib/FermionicModel.h b/include/edlib/FermionicModel.h index 7e168ca..26afa9f 100644 --- a/include/edlib/FermionicModel.h +++ b/include/edlib/FermionicModel.h @@ -1,123 +1,55 @@ -// -// Created by iskakoff on 28/08/16. -// - #ifndef EDLIB_FERMIONICMODEL_H #define EDLIB_FERMIONICMODEL_H -#include - -#include - -namespace EDLib { - namespace Model { -/** - * @brief FermionicModel base class - * - * Define common fermionic routines for binary represented state - * - * @author iskakoff - */ - class FermionicModel { - public: - FermionicModel(alps::params &p) : _Ns(p["NSITES"]), _ms(p["NSPINS"]), _Ip(int(p["NSPINS"]) * int(p["NSITES"])) { - } - - /** - * @brief Check that im state is occupated - * - * @param nst - current state - * @param im - state to check - * @param Ip - total number of fermionic spins for all sites - * - * @return 0 if state is empty, 1 - otherwise - */ - int inline checkState(long long nst, const int im, int Ip) const { - return (int) ((nst & (1ll << (Ip - 1 - im))) >> (Ip - 1 - im)); - } - /** - * @brief Anihilate particle - * @param i [in] - site to anihilate particle - * @param jold [in] - current state - * @param k [out] - resulting state - * @param isign [out] - fermionic sign - */ - void inline a(int i, long long jold, long long &k, int &isign) { - long long sign = 0; - for (int ll = 0; ll < i; ++ll) { - sign += ((jold & (1ll << (_Ip - ll - 1))) != 0) ? 1 : 0; - } - isign = (sign % 2) == 0 ? 1 : -1; - k = jold - (1ll << (_Ip - i - 1)); - } - - /** - * @brief Create particle - * \param i [in] - site to create particle - * \param jold [in] - current state - * \param k [out] - resulting state - * \param isign [out] - fermionic sign - */ - void inline adag(int i, long long jold, long long &k, int &isign) { - long long sign = 0; - for (int ll = 0; ll < i; ++ll) { - sign += ((jold & (1ll << (_Ip - ll - 1))) != 0) ? 1 : 0; - } - isign = (sign % 2) == 0 ? 1 : -1; - k = jold + (1ll << (_Ip - i - 1)); - } - - - int orbitals() const { - return _Ns; - } - - int max_total_electrons() const { - return _Ip; - } - - int spins() const { - return _ms; - } - - template - void solve_dyson(const alps::gf::three_index_gf, Mesh, alps::gf::index_mesh, alps::gf::index_mesh >& bare_gf, - const alps::gf::three_index_gf, Mesh, alps::gf::index_mesh, alps::gf::index_mesh >& G_ij, - alps::gf::three_index_gf, Mesh, alps::gf::index_mesh, alps::gf::index_mesh >& sigma) { - // solve Dyson equation - for(int iw = 0; iw< bare_gf.mesh1().points().size(); ++iw) { - typename Mesh::index_type w(iw); - for (int is : bare_gf.mesh3().points()) { - Eigen::MatrixXcd bare(_Ns, _Ns); - Eigen::MatrixXcd bold(_Ns, _Ns); - Eigen::MatrixXcd sigm(_Ns, _Ns); - for (int im: bare_gf.mesh2().points()) { - int I = im / _Ns; - int J = im % _Ns; - bare(I, J) = bare_gf(w, alps::gf::index_mesh::index_type(im), alps::gf::index_mesh::index_type(is)); - bold(I, J) = G_ij(w, alps::gf::index_mesh::index_type(im), alps::gf::index_mesh::index_type(is)); - } - sigm = bare.inverse() - bold.inverse(); - for (int im: bare_gf.mesh2().points()) { - int I = im / _Ns; - int J = im % _Ns; - sigma(w, alps::gf::index_mesh::index_type(im), alps::gf::index_mesh::index_type(is)) = sigm(I, J); - } - } - } - } +#include "edlib/Parameters.h" + +namespace edlib { + + /** + * Base class for fermionic models with binary-state representation. + * + * solve_dyson and bare_greens_function (present in the legacy + * EDLib::Model::FermionicModel) are deliberately not here — they belong to + * derived models that know about Green's-function machinery (HubbardModel + * etc.) and are ported in Phase 3 alongside Eigen-based linear algebra. + */ + class FermionicModel { + public: + explicit FermionicModel(const Parameters& p) + : _Ns(p.nsites), _ms(p.nspins), _Ip(p.nspins * p.nsites) {} + + int orbitals() const { return _Ns; } + int spins() const { return _ms; } + int max_total_electrons() const { return _Ip; } + + inline int checkState(long long nst, int im, int Ip) const { + return static_cast((nst & (1ll << (Ip - 1 - im))) >> (Ip - 1 - im)); + } + + inline void a(int i, long long jold, long long& k, int& isign) const { + long long sign = 0; + for (int ll = 0; ll < i; ++ll) { + sign += ((jold & (1ll << (_Ip - ll - 1))) != 0) ? 1 : 0; + } + isign = (sign % 2) == 0 ? 1 : -1; + k = jold - (1ll << (_Ip - i - 1)); + } + + inline void adag(int i, long long jold, long long& k, int& isign) const { + long long sign = 0; + for (int ll = 0; ll < i; ++ll) { + sign += ((jold & (1ll << (_Ip - ll - 1))) != 0) ? 1 : 0; + } + isign = (sign % 2) == 0 ? 1 : -1; + k = jold + (1ll << (_Ip - i - 1)); + } + + protected: + int _Ns; ///< number of lattice sites + int _ms; ///< number of electron spins + int _Ip; ///< maximum number of electrons (= _Ns * _ms) + }; - protected: - /** - * _Ns - number of lattice sites - * _ms - number of electron spins - * _Ip - maximum number of electrons - */ - int _Ns; - int _ms; - int _Ip; - }; - } } -#endif //EDLIB_FERMIONICMODEL_H +#endif diff --git a/include/edlib/Gf.h b/include/edlib/Gf.h new file mode 100644 index 0000000..9e4b143 --- /dev/null +++ b/include/edlib/Gf.h @@ -0,0 +1,110 @@ +#ifndef EDLIB_GF_H +#define EDLIB_GF_H + +#include +#include +#include +#include +#include +#include + +namespace edlib { + + namespace detail { + template + std::size_t linear_index(const std::array& shape, + const std::array& idx) { + std::size_t off = 0; + for (int d = 0; d < N; ++d) { + off = off * static_cast(shape[d]) + + static_cast(idx[d]); + } + return off; + } + + template + std::size_t total_size(const std::array& shape) { + std::size_t s = 1; + for (int d = 0; d < N; ++d) s *= static_cast(shape[d]); + return s; + } + } + + /** + * Multi-index Green's function container. + * + * Row-major contiguous storage in `data`. The leading axis is the frequency + * axis (length matches the associated mesh). Remaining axes are integer + * index meshes (orbitals, spins, real-space, etc.). + */ + template + class Gf { + static_assert(N >= 1, "Gf rank must be >= 1"); + + public: + using value_type = T; + static constexpr int rank = N; + + Gf() { _shape.fill(0); } + + explicit Gf(const std::array& shape) : _shape(shape) { + _data.assign(detail::total_size(_shape), T{}); + } + + const std::array& shape() const { return _shape; } + int shape(int d) const { return _shape[d]; } + const std::vector& data() const { return _data; } + std::vector& data() { return _data; } + + template + T& operator()(Idx... ix) { + static_assert(sizeof...(Idx) == N, "operator(): wrong number of indices"); + return _data[detail::linear_index(_shape, {static_cast(ix)...})]; + } + + template + const T& operator()(Idx... ix) const { + static_assert(sizeof...(Idx) == N, "operator(): wrong number of indices"); + return _data[detail::linear_index(_shape, {static_cast(ix)...})]; + } + + Gf& operator+=(const Gf& o) { + check_shape(o); + for (std::size_t i = 0; i < _data.size(); ++i) _data[i] += o._data[i]; + return *this; + } + Gf& operator-=(const Gf& o) { + check_shape(o); + for (std::size_t i = 0; i < _data.size(); ++i) _data[i] -= o._data[i]; + return *this; + } + Gf& operator*=(const T& s) { + for (auto& v : _data) v *= s; + return *this; + } + Gf& operator/=(const T& s) { + for (auto& v : _data) v /= s; + return *this; + } + + friend Gf operator+(Gf a, const Gf& b) { a += b; return a; } + friend Gf operator-(Gf a, const Gf& b) { a -= b; return a; } + + private: + void check_shape(const Gf& o) const { + if (_shape != o._shape) { + throw std::invalid_argument("Gf: shape mismatch in element-wise op"); + } + } + + std::array _shape; + std::vector _data; + }; + + using GF2 = Gf, 2>; + using GF3 = Gf, 3>; + using GF4 = Gf, 4>; + +} + +#endif diff --git a/include/edlib/GreensFunction.h b/include/edlib/GreensFunction.h index 442307e..94982ee 100644 --- a/include/edlib/GreensFunction.h +++ b/include/edlib/GreensFunction.h @@ -1,467 +1,357 @@ -// -// Created by iskakoff on 01/08/16. -// - #ifndef EDLIB_GREENSFUNCTION_H #define EDLIB_GREENSFUNCTION_H +#include +#include +#include +#include +#include #include -#include -#include "Lanczos.h" -#include "EigenPair.h" -#include "ExecutionStatistic.h" +#include +#include -namespace EDLib { - namespace gf { - /** - * Class for evaluation of the single-particle Green's function. - * - * - * - * @tparam Hamiltonian - type of Hamiltonian object - * @tparam Mesh - type of frequency mesh. can be either alps::gf::real_frequency_mesh or alps::gf::matsubara_positive_only - * @tparam Args - additional Mesh parametrization for alps::gf::matsubara_positive_only - */ - template - class GreensFunction : public Lanczos < Hamiltonian, MeshFactory, Args...> { - using Lanczos < Hamiltonian, MeshFactory, Args... >::hamiltonian; - using Lanczos < Hamiltonian, MeshFactory, Args... >::lanczos; - using Lanczos < Hamiltonian, MeshFactory, Args... >::omega; - using Lanczos < Hamiltonian, MeshFactory, Args... >::beta; - using Lanczos < Hamiltonian, MeshFactory, Args... >::compute_continued_fraction; - using Lanczos < Hamiltonian, MeshFactory, Args... >::suffix; - using typename Lanczos < Hamiltonian, MeshFactory, Args... >::Mesh; - using typename Lanczos < Hamiltonian, MeshFactory, Args... >::precision; - public: +#include "edlib/Dyson.h" +#include "edlib/EigenPair.h" +#include "edlib/ExecutionStatistic.h" +#include "edlib/Gf.h" +#include "edlib/Lanczos.h" +#include "edlib/Parameters.h" - /// Green's function type - typedef alps::gf::three_index_gf, Mesh, alps::gf::index_mesh, alps::gf::index_mesh > GF_TYPE; - typedef typename alps::gf::index_mesh::index_type index_mesh_index; - typedef typename Mesh::index_type frequency_mesh_index; +namespace edlib { - /** - * Construct Green's function class for calculating - * G(t-t') = -, where T is time ordering operator - * - * By default, all local Green functions are computed. An array of orbital - * pairs can be supplied in the input file as the GreensFunction_orbitals - * group. - * - * @param p - AlpsCore parameter object - * @param h - Hamiltonain instance - * @param args - additional parameters for Mesh. For example for Matsubara mesh should it be alps::gf::statistics::statistics_type::FERMIONIC - */ - GreensFunction(alps::params &p, Hamiltonian &h, Args ... args) : Lanczos < Hamiltonian, MeshFactory, Args... >(p, h, args...), _model(h.model()), - _G_g(omega(), alps::gf::index_mesh(h.model().interacting_orbitals()), alps::gf::index_mesh(p["NSPINS"].as())), - _G_l(omega(), alps::gf::index_mesh(h.model().interacting_orbitals()), alps::gf::index_mesh(p["NSPINS"].as())), - _G(omega(), alps::gf::index_mesh(h.model().interacting_orbitals()), alps::gf::index_mesh(p["NSPINS"].as())), - _G_g_ij(omega(), alps::gf::index_mesh(h.model().interacting_orbitals() * h.model().interacting_orbitals()), alps::gf::index_mesh(p["NSPINS"].as())), - _G_l_ij(omega(), alps::gf::index_mesh(h.model().interacting_orbitals() * h.model().interacting_orbitals()), alps::gf::index_mesh(p["NSPINS"].as())), - _G_ij(omega(), alps::gf::index_mesh(h.model().interacting_orbitals() * h.model().interacting_orbitals()), alps::gf::index_mesh(p["NSPINS"].as())), - _cutoff(p["lanc.BOLTZMANN_CUTOFF"]) { - if(p["storage.EIGENVALUES_ONLY"] == 1) { - throw std::logic_error("Eigenvectors have not been computed. Green's function can not be evaluated."); - } - std::string input = p["INPUT_FILE"]; - alps::hdf5::archive input_file(input.c_str(), "r"); - std::vector> gf_orbs; - if(input_file.is_data("GreensFunction_orbitals/values")){ - input_file >> alps::make_pvp("GreensFunction_orbitals/values", gf_orbs); - }else{ - // Or calculate only the diagonal part. - gf_orbs.clear(); - for(int i = 0; i < h.model().interacting_orbitals(); ++i){ - gf_orbs.push_back({i, i}); - } - } - input_file.close(); - for(size_t ii = 0; ii < gf_orbs.size(); ++ii){ - if(gf_orbs[ii][0] == gf_orbs[ii][1]){ - _g_orbs.push_back(gf_orbs[ii][0]); - }else{ - _g_ij_orb_pairs.push_back(std::array{size_t(gf_orbs[ii][0]), size_t(gf_orbs[ii][1])}); - } + /** + * Single-particle Green's function via Lanczos continued fractions. + * + * GF3 shape is [n_omega, n_orbital, n_spin] for the local GF, and + * [n_omega, n_orbital * n_orbital, n_spin] for the non-local G_ij block. + * + * Orbital pairs to compute may be supplied at construction. If empty, all + * diagonal local Green's functions are computed. Pairs (i, j) with i != j + * additionally drive the non-local G_ij block. + */ + template + class GreensFunction : public Lanczos { + using Base = Lanczos; + using Base::hamiltonian; + using Base::lanczos; + using Base::compute_continued_fraction; + using Base::suffix; + using typename Base::precision; + + public: + using Base::beta; + using Base::omega; + using ModelType = typename Hamiltonian::ModelType; + using GF_TYPE = GF3; + + GreensFunction(const Parameters& p, Hamiltonian& h, Mesh omega_mesh, + const std::vector>& orbital_pairs = {}) + : Base(p, h, std::move(omega_mesh)), + _model(h.model()), + _G_g ({omega().extent(), h.model().interacting_orbitals(), + p.nspins}), + _G_l ({omega().extent(), h.model().interacting_orbitals(), + p.nspins}), + _G ({omega().extent(), h.model().interacting_orbitals(), + p.nspins}), + _G_g_ij({omega().extent(), + h.model().interacting_orbitals() * h.model().interacting_orbitals(), + p.nspins}), + _G_l_ij({omega().extent(), + h.model().interacting_orbitals() * h.model().interacting_orbitals(), + p.nspins}), + _G_ij ({omega().extent(), + h.model().interacting_orbitals() * h.model().interacting_orbitals(), + p.nspins}), + _cutoff(static_cast(p.lanc_boltzmann_cutoff)), + _nspins(p.nspins), + _nsites(p.nsites) { + if (p.eigenvalues_only) { + throw std::logic_error( + "GreensFunction: eigenvectors were not computed (eigenvalues_only=true)."); + } + const int n_orb = h.model().interacting_orbitals(); + if (orbital_pairs.empty()) { + for (int i = 0; i < n_orb; ++i) _g_orbs.push_back(i); + } else { + for (const auto& pr : orbital_pairs) { + if (pr[0] == pr[1]) _g_orbs.push_back(pr[0]); + else _g_ij_orb_pairs.push_back({pr[0], pr[1]}); } - // Find all unique indices for the diagonal part. std::sort(_g_orbs.begin(), _g_orbs.end()); _g_orbs.erase(std::unique(_g_orbs.begin(), _g_orbs.end()), _g_orbs.end()); - // Check that we will have the two local GFs required by nonlocal GF. - for(size_t ii = 0; ii < _g_ij_orb_pairs.size(); ++ii){ - for(size_t jj = 0; jj < 2; ++jj){ - bool found = false; - for(size_t kk = 0; kk < _g_orbs.size(); ++kk){ - if(_g_orbs[kk] == _g_ij_orb_pairs[ii][jj]){ - found = true; - break; - } - } - if(!found){ - _g_orbs.push_back(_g_ij_orb_pairs[ii][jj]); + // Ensure each non-local pair has its two local GFs available. + for (const auto& pr : _g_ij_orb_pairs) { + for (int e : pr) { + if (std::find(_g_orbs.begin(), _g_orbs.end(), e) == _g_orbs.end()) { + _g_orbs.push_back(e); } } } } + } - /** - * Evaluates Green's function by Lanczos continued fraction method. - * For each eigenvalue checks Boltsman factor cut-off - */ - void compute() { - // init Green's function with 0 - _G_g *= 0.0; - _G_l *= 0.0; - // init Partition function - _Z = 0.0; - // check that at least one eigen-value have been computed - if(hamiltonian().eigenpairs().empty()) - return; + /// Run the Lanczos continued-fraction sum over all eigenpairs. + void compute() { + _G_g *= std::complex(0); + _G_l *= std::complex(0); + _Z = precision(0); + if (hamiltonian().eigenpairs().empty()) return; #ifdef USE_MPI - int rank; - MPI_Comm_rank(hamiltonian().storage().comm(), &rank); + int rank; + MPI_Comm_rank(hamiltonian().storage().comm(), &rank); #endif - // get groundstate - const EigenPair &groundstate = *hamiltonian().eigenpairs().begin(); - // compute partition function - for (auto kkk = hamiltonian().eigenpairs().begin(); kkk != hamiltonian().eigenpairs().end(); kkk++) { - const EigenPair &eigenpair = *kkk; - _Z += std::exp(-(eigenpair.eigenvalue() - groundstate.eigenvalue()) * beta()); - } - // iterate over eigen-pairs - for (auto kkk = hamiltonian().eigenpairs().begin(); kkk != hamiltonian().eigenpairs().end(); kkk++) { - const EigenPair& pair = *kkk; - // compute Boltzmann-factor - precision boltzmann_f = std::exp(-(pair.eigenvalue() - groundstate.eigenvalue()) * beta()); - // Skip all eigenvalues with Boltzmann-factor smaller than cutoff - if (std::abs(_cutoff - boltzmann_f) > std::numeric_limits::epsilon() && boltzmann_f < _cutoff ) { - continue; - } -#ifdef USE_MPI - if(rank == 0) -#endif - std::cout << "Compute Green's function contribution for eigenvalue E=" << pair.eigenvalue() << " with Boltzmann factor = " << boltzmann_f << "; for sector" << pair.sector() << std::endl; - local_contribution(pair, groundstate); - nonlocal_contribution(pair, groundstate); + const auto& groundstate = *hamiltonian().eigenpairs().begin(); + for (const auto& pair : hamiltonian().eigenpairs()) { + _Z += std::exp(-(pair.eigenvalue() - groundstate.eigenvalue()) * beta()); + } + for (const auto& pair : hamiltonian().eigenpairs()) { + precision boltzmann_f = + std::exp(-(pair.eigenvalue() - groundstate.eigenvalue()) * beta()); + if (std::abs(_cutoff - boltzmann_f) > std::numeric_limits::epsilon() + && boltzmann_f < _cutoff) { + continue; } #ifdef USE_MPI - if(rank == 0) { -#endif - // normalize Green's function - _G_g /= _Z; - _G_l /= _Z; - _G_g_ij /= _Z; - _G_l_ij /= _Z; -#ifdef USE_MPI - } + if (rank == 0) #endif - non_local_gf(); - _G = _G_g + _G_l; - common::statistics.updateEvent("Greens function"); + std::cout << "Compute Green's function contribution for eigenvalue E=" + << pair.eigenvalue() << " with Boltzmann factor = " + << boltzmann_f << "; for sector" << pair.sector() << std::endl; + local_contribution (pair, groundstate); + nonlocal_contribution(pair, groundstate); } - - /** - * Save Green's function into the hdf5 archive and in plain text file - * - * @param ar -- hdf5 archive to save Green's function - * @param path -- root path in hdf5 archive - */ - void save(alps::hdf5::archive& ar, const std::string & path) { #ifdef USE_MPI - int rank; - MPI_Comm_rank(hamiltonian().storage().comm(), &rank); - if(rank == 0) { + if (rank == 0) { #endif - if(_g_orbs.size()){ - _G.save(ar, path + "/G_omega" + suffix()); - std::ostringstream Gomega_name; - Gomega_name << "G_omega"<(_Z); + _G_l /= std::complex(_Z); + _G_g_ij /= std::complex(_Z); + _G_l_ij /= std::complex(_Z); #ifdef USE_MPI - } -#endif - } - - /** - * Comutes self-energy from Dyson equation based on model specific bare Green's function - * - * @param ar -- hdf5 archive to save self-energy - * @param path -- root path in hdf5 archive - */ - GF_TYPE compute_selfenergy(alps::hdf5::archive &ar, const std::string &path){ - // Bare Green's function and Self-energy should be defined on the same grid - GF_TYPE bare(_G_ij.mesh1(), _G_ij.mesh2(), _G_ij.mesh3()); - GF_TYPE sigma(_G_ij.mesh1(), _G_ij.mesh2(), _G_ij.mesh3()); - // obtain model-specific bare Green's function - _model.bare_greens_function(bare, beta()); - bare.save(ar, path + "/G0_omega"); - std::ostringstream Gomega_name; - Gomega_name << "G0_omega"; - std::ofstream G_omega_file(Gomega_name.str().c_str()); - G_omega_file << std::setprecision(14) << bare; - G_omega_file.close(); - _model.solve_dyson(bare, _G_ij, sigma); - // store to file - sigma.save(ar, path + "/Sigma_omega"); - Gomega_name.str(""); - Gomega_name << "Sigma_omega"; - G_omega_file.open(Gomega_name.str().c_str()); - G_omega_file << std::setprecision(14) << sigma; - G_omega_file.close(); - return sigma; } +#endif + non_local_gf(); + _G = _G_g + _G_l; + edlib::statistics.updateEvent("Greens function"); + } + /** + * Compute the self-energy by Dyson's equation, using the model's bare + * Green's function for the same mesh. Returns the GF3-shaped sigma. + */ + GF_TYPE compute_selfenergy() { + GF_TYPE bare ({omega().extent(), _nsites * _nsites, _nspins}); + GF_TYPE sigma({omega().extent(), _nsites * _nsites, _nspins}); + _model.bare_greens_function(bare, omega(), beta()); + solve_dyson(bare, _G_ij, sigma, _nsites); + return sigma; + } - const GF_TYPE &G_g() const {return _G_g;} - const GF_TYPE &G_l() const {return _G_l;} - const GF_TYPE &G() const {return _G;} - const GF_TYPE &G_g_ij() const {return _G_g_ij;} - const GF_TYPE &G_l_ij() const {return _G_l_ij;} - const GF_TYPE &G_ij() const {return _G_ij;} - - private: + const GF_TYPE& G_g() const { return _G_g; } + const GF_TYPE& G_l() const { return _G_l; } + const GF_TYPE& G() const { return _G; } + const GF_TYPE& G_g_ij() const { return _G_g_ij; } + const GF_TYPE& G_l_ij() const { return _G_l_ij; } + const GF_TYPE& G_ij() const { return _G_ij; } - /** - * Compute local Green's function G_ii - * - * @param groundstate -- system groundstate - * @param pair -- current Eigen-Pair - */ - void local_contribution(const EigenPair& pair, const EigenPair& groundstate) { + private: + void local_contribution(const EigenPair& pair, + const EigenPair& groundstate) { #ifdef USE_MPI - int rank; - MPI_Comm_rank(hamiltonian().storage().comm(), &rank); + int rank; MPI_Comm_rank(hamiltonian().storage().comm(), &rank); #endif - /// iterate over orbitals for the diagonal Green's function - for (int iorb = 0; iorb < _g_orbs.size(); ++iorb) { - /// iterate over spins - for (int ispin = 0; ispin < _model.spins(); ++ispin) { - int orb = _g_orbs[iorb]; - std::vector < precision > outvec(1, precision(0.0)); - precision expectation_value = 0.0; - _model.symmetry().set_sector(pair.sector()); - /// first we are going to compute G_g = , for this we need to create particle in current eigenstate - if (create_particles(std::array{{size_t(orb)}}, ispin, pair.eigenvector(), outvec, expectation_value)) { - /// Perform Lanczos factorization for starting vector |outvec> - int nlanc = lanczos(outvec); + for (std::size_t io = 0; io < _g_orbs.size(); ++io) { + for (int ispin = 0; ispin < _model.spins(); ++ispin) { + int orb = _g_orbs[io]; + std::vector outvec(1, precision(0)); + precision expectation_value = 0; + _model.symmetry().set_sector(pair.sector()); + if (create_particles(std::array{{orb}}, ispin, + pair.eigenvector(), outvec, expectation_value)) { + int nlanc = lanczos(outvec); #ifdef USE_MPI - if(!rank) + if (rank == 0) #endif - { - std::cout << "orbital: " << orb << " spin: " << (ispin == 0 ? "up" : "down") << " =" << expectation_value << " nlanc:" << nlanc << std::endl; - /// Using computed Lanczos factorization compute approximation for \frac{1}{z - H} by calculation of a continued fraction - compute_continued_fraction(expectation_value, pair.eigenvalue(), groundstate.eigenvalue(), nlanc, 1, _G_g, index_mesh_index(orb), index_mesh_index(ispin)); - } + { + std::cout << "orbital: " << orb << " spin: " + << (ispin == 0 ? "up" : "down") + << " =" << expectation_value + << " nlanc:" << nlanc << std::endl; + compute_continued_fraction(expectation_value, pair.eigenvalue(), + groundstate.eigenvalue(), + nlanc, 1, _G_g, orb, ispin); } - /// restore symmetry sector - _model.symmetry().set_sector(pair.sector()); - /// then compute G_l = , for this we need to destroy particle - if (annihilate_particles(std::array{{size_t(orb)}}, ispin, pair.eigenvector(), outvec, expectation_value)) { - int nlanc = lanczos(outvec); + } + _model.symmetry().set_sector(pair.sector()); + if (annihilate_particles(std::array{{orb}}, ispin, + pair.eigenvector(), outvec, expectation_value)) { + int nlanc = lanczos(outvec); #ifdef USE_MPI - if(!rank) + if (rank == 0) #endif - { - std::cout << "orbital: " << orb << " spin: " << (ispin == 0 ? "up" : "down") << " =" << expectation_value << " nlanc:" << nlanc << std::endl; - compute_continued_fraction(expectation_value, pair.eigenvalue(), groundstate.eigenvalue(), nlanc, -1, _G_l, index_mesh_index(orb), index_mesh_index(ispin)); - } + { + std::cout << "orbital: " << orb << " spin: " + << (ispin == 0 ? "up" : "down") + << " =" << expectation_value + << " nlanc:" << nlanc << std::endl; + compute_continued_fraction(expectation_value, pair.eigenvalue(), + groundstate.eigenvalue(), + nlanc, -1, _G_l, orb, ispin); } } } } + } - /** - * Computes the part "<(c_i + c_j)(c_i^+ + c_j^+)>" of non-local Green's function G_ij - * - * @param groundstate -- system groundstate - * @param pair -- current Eigen-Pair - */ - void nonlocal_contribution(const EigenPair& pair, const EigenPair& groundstate) { + void nonlocal_contribution(const EigenPair& pair, + const EigenPair& groundstate) { #ifdef USE_MPI - int rank; - MPI_Comm_rank(hamiltonian().storage().comm(), &rank); + int rank; MPI_Comm_rank(hamiltonian().storage().comm(), &rank); #endif - for (int iorb = 0; iorb < _g_ij_orb_pairs.size(); ++iorb) { - for (int ispin = 0; ispin < _model.spins(); ++ispin) { - auto orbs = _g_ij_orb_pairs[iorb]; - std::vector < precision > outvec(1, precision(0.0)); - bool found[2]; - precision expectation_value = 0.0; - /// create particle on two different orbs, sum the resulting vectors and compute contribution to Green's function - _model.symmetry().set_sector(pair.sector()); - if(create_particles(orbs, ispin, pair.eigenvector(), outvec, expectation_value)) { - int nlanc = lanczos(outvec); + for (std::size_t io = 0; io < _g_ij_orb_pairs.size(); ++io) { + for (int ispin = 0; ispin < _model.spins(); ++ispin) { + auto orbs = _g_ij_orb_pairs[io]; + std::vector outvec(1, precision(0)); + precision expectation_value = 0; + _model.symmetry().set_sector(pair.sector()); + if (create_particles(std::array{{orbs[0], orbs[1]}}, ispin, + pair.eigenvector(), outvec, expectation_value)) { + int nlanc = lanczos(outvec); #ifdef USE_MPI - if(!rank) + if (rank == 0) #endif - { - std::cout << "orbitals: " << orbs[0] << ", " << orbs[1] << " spin: " << (ispin == 0 ? "up" : "down") << " =" << expectation_value << " nlanc:" << nlanc << std::endl; - compute_continued_fraction(expectation_value, pair.eigenvalue(), groundstate.eigenvalue(), nlanc, 1, _G_g_ij, index_mesh_index(_model.interacting_orbitals() * orbs[0] + orbs[1]), index_mesh_index(ispin)); - } + { + std::cout << "orbitals: " << orbs[0] << ", " << orbs[1] + << " spin: " << (ispin == 0 ? "up" : "down") + << " =" << expectation_value + << " nlanc:" << nlanc << std::endl; + compute_continued_fraction(expectation_value, pair.eigenvalue(), + groundstate.eigenvalue(), nlanc, 1, _G_g_ij, + _model.interacting_orbitals() * orbs[0] + orbs[1], + ispin); } - /// perform the same for destroying of a particle - /// restore symmetry sector - _model.symmetry().set_sector(pair.sector()); - if(annihilate_particles(orbs, ispin, pair.eigenvector(), outvec, expectation_value) ) { - int nlanc = lanczos(outvec); + } + _model.symmetry().set_sector(pair.sector()); + if (annihilate_particles(std::array{{orbs[0], orbs[1]}}, ispin, + pair.eigenvector(), outvec, expectation_value)) { + int nlanc = lanczos(outvec); #ifdef USE_MPI - if(!rank) + if (rank == 0) #endif - { - std::cout << "orbitals: " << orbs[0] << ", " << orbs[1] << " spin: " << (ispin == 0 ? "up" : "down") << " =" << expectation_value << " nlanc:" << nlanc << std::endl; - compute_continued_fraction(expectation_value, pair.eigenvalue(), groundstate.eigenvalue(), nlanc, -1, _G_l_ij, index_mesh_index(_model.interacting_orbitals() * orbs[0] + orbs[1]), index_mesh_index(ispin)); - } + { + std::cout << "orbitals: " << orbs[0] << ", " << orbs[1] + << " spin: " << (ispin == 0 ? "up" : "down") + << " =" << expectation_value + << " nlanc:" << nlanc << std::endl; + compute_continued_fraction(expectation_value, pair.eigenvalue(), + groundstate.eigenvalue(), nlanc, -1, _G_l_ij, + _model.interacting_orbitals() * orbs[0] + orbs[1], + ispin); } } } } + } - /** - * Computes non-local Green's function G_ij = 0.5( <(c_i + c_j)(c_i^+ + c_j^+)> - - ). - * - * @tparam O -- bosonic operator type - * @param op -- bosonic operator - */ - void non_local_gf() { - for (int iomega = 0; iomega < omega().extent(); ++iomega) { - for (int iorb = 0; iorb < _g_ij_orb_pairs.size(); ++iorb) { - for (int ispin = 0; ispin < _model.spins(); ++ispin) { - auto orbs = _g_ij_orb_pairs[iorb]; - for (int jj = 0; jj < 2; ++jj) { - _G_g_ij(frequency_mesh_index(iomega), index_mesh_index(_model.interacting_orbitals() * orbs[0] + orbs[1]), index_mesh_index(ispin)) -= _G_g(frequency_mesh_index(iomega), index_mesh_index(orbs[jj]), index_mesh_index(ispin)); - _G_l_ij(frequency_mesh_index(iomega), index_mesh_index(_model.interacting_orbitals() * orbs[0] + orbs[1]), index_mesh_index(ispin)) -= _G_l(frequency_mesh_index(iomega), index_mesh_index(orbs[jj]), index_mesh_index(ispin)); - } - _G_g_ij(frequency_mesh_index(iomega), index_mesh_index(_model.interacting_orbitals() * orbs[0] + orbs[1]), index_mesh_index(ispin)) *= 0.5; - _G_l_ij(frequency_mesh_index(iomega), index_mesh_index(_model.interacting_orbitals() * orbs[0] + orbs[1]), index_mesh_index(ispin)) *= 0.5; + void non_local_gf() { + const int n_orb = _model.interacting_orbitals(); + for (int iomega = 0; iomega < omega().extent(); ++iomega) { + for (std::size_t io = 0; io < _g_ij_orb_pairs.size(); ++io) { + for (int ispin = 0; ispin < _model.spins(); ++ispin) { + auto orbs = _g_ij_orb_pairs[io]; + int ij = n_orb * orbs[0] + orbs[1]; + for (int jj = 0; jj < 2; ++jj) { + _G_g_ij(iomega, ij, ispin) -= _G_g(iomega, orbs[jj], ispin); + _G_l_ij(iomega, ij, ispin) -= _G_l(iomega, orbs[jj], ispin); } + _G_g_ij(iomega, ij, ispin) *= std::complex(0.5, 0.0); + _G_l_ij(iomega, ij, ispin) *= std::complex(0.5, 0.0); } - // and copy diagonal G for completeness - for (int iorb = 0; iorb < _g_orbs.size(); ++iorb) { - size_t orb = _g_orbs[iorb]; - for (int ispin = 0; ispin < _model.spins(); ++ispin) { - _G_g_ij(frequency_mesh_index(iomega), index_mesh_index(_model.interacting_orbitals() * orb + orb), index_mesh_index(ispin)) = _G_g(frequency_mesh_index(iomega), index_mesh_index(orb), index_mesh_index(ispin)); - _G_l_ij(frequency_mesh_index(iomega), index_mesh_index(_model.interacting_orbitals() * orb + orb), index_mesh_index(ispin)) = _G_l(frequency_mesh_index(iomega), index_mesh_index(orb), index_mesh_index(ispin)); - } + } + for (std::size_t io = 0; io < _g_orbs.size(); ++io) { + int orb = _g_orbs[io]; + for (int ispin = 0; ispin < _model.spins(); ++ispin) { + _G_g_ij(iomega, n_orb * orb + orb, ispin) = _G_g(iomega, orb, ispin); + _G_l_ij(iomega, n_orb * orb + orb, ispin) = _G_l(iomega, orb, ispin); } } - _G_ij = _G_g_ij + _G_l_ij; } + _G_ij = _G_g_ij + _G_l_ij; + } - /// Green's function container object - GF_TYPE _G_g; - GF_TYPE _G_g_ij; - GF_TYPE _G_l; - GF_TYPE _G_l_ij; - GF_TYPE _G; - GF_TYPE _G_ij; - /// Model we are solving - typename Hamiltonian::ModelType &_model; - /// Boltzmann-factor cut-off - precision _cutoff; - /// Partition function - precision _Z; - /// Orbitals used for the diagonal Green's function calculation - std::vector _g_orbs; - /// Orbital pairs used for the offdiagonal Green's function calculation - std::vector > _g_ij_orb_pairs; - - /** - * @brief Perform the create operator action to the eigenstate - * - * @param orbitals - the orbital to create a particle - * @param spin - the spin of a particle to create - * @param invec - current eigenstate - * @param outvec - Op-vec product - * @param expectation_value - expectation value of aa* - * @return true if the particle has been created - */ - template - bool create_particles(std::array orbitals, int spin, const std::vector &invec, std::vector &outvec, double &expectation_value) { - // check that the particle can be annihilated - if (!_model.symmetry().can_create_particle(spin)) { - return false; - } - hamiltonian().storage().reset(); - int nup_new = _model.symmetry().sector().nup() + (1 - spin); - int ndn_new = _model.symmetry().sector().ndown() + spin; - typename Hamiltonian::ModelType::Sector next_sec = _model.symmetry().create_particle(spin); - outvec.assign(hamiltonian().storage().vector_size(next_sec), 0.0); - common::statistics.registerEvent("adag"); - for(auto orb : orbitals) { - hamiltonian().storage().init(); - std::vector tmpout(outvec.size()); - hamiltonian().storage().a_adag(orb + spin * _model.orbitals(), invec, tmpout, next_sec, false); - std::transform(tmpout.begin(), tmpout.end(), outvec.begin(), outvec.begin(), std::plus()); - } - common::statistics.updateEvent("adag"); - double norm = hamiltonian().storage().vv(outvec, outvec + template + bool create_particles(std::array orbitals, int spin, + const std::vector& invec, + std::vector& outvec, + precision& expectation_value) { + if (!_model.symmetry().can_create_particle(spin)) return false; + hamiltonian().storage().reset(); + auto next_sec = _model.symmetry().create_particle(spin); + outvec.assign(hamiltonian().storage().vector_size(next_sec), precision(0)); + edlib::statistics.registerEvent("adag"); + for (int orb : orbitals) { + hamiltonian().storage().init(); + std::vector tmp(outvec.size()); + hamiltonian().storage().a_adag(orb + spin * _model.orbitals(), + invec, tmp, next_sec, /*a=*/false); + std::transform(tmp.begin(), tmp.end(), outvec.begin(), outvec.begin(), + std::plus()); + } + edlib::statistics.updateEvent("adag"); + double norm = hamiltonian().storage().vv(outvec, outvec #ifdef USE_MPI - , hamiltonian().comm() + , hamiltonian().comm() #endif - ); - for (int j = 0; j < outvec.size(); ++j) { - outvec[j] /= std::sqrt(norm); - } - _model.symmetry().set_sector(next_sec); - expectation_value = norm; - return std::abs(norm) > 1.e-10; - }; + ); + for (auto& v : outvec) v /= std::sqrt(norm); + _model.symmetry().set_sector(next_sec); + expectation_value = static_cast(norm); + return std::abs(norm) > 1e-10; + } - /** - * @brief Perform the annihilator operator action to the eigenstate - * - * @param orbitals - the orbitals list to destroy a particle - * @param spin - the spin of a particle to destroy - * @param invec - current eigenstate - * @param outvec - Op-vec product - * @param expectation_value - expectation value of a*a - * @return true if the particle has been destroyed - */ - template - bool annihilate_particles(std::array orbitals, int spin, const std::vector &invec, std::vector &outvec, double &expectation_value) { - // check that the particle can be annihilated - if (!_model.symmetry().can_destroy_particle(spin)) { - return false; - } - hamiltonian().storage().reset(); - int nup_new = _model.symmetry().sector().nup() - (1 - spin); - int ndn_new = _model.symmetry().sector().ndown() - spin; - typename Hamiltonian::ModelType::Sector next_sec = _model.symmetry().destroy_particle(spin); - outvec.assign(hamiltonian().storage().vector_size(next_sec), precision(0.0)); - common::statistics.registerEvent("a"); - for(auto orb : orbitals) { - hamiltonian().storage().init(); - std::vector tmpout(outvec.size()); - hamiltonian().storage().a_adag(orb + spin * _model.orbitals(), invec, tmpout, next_sec, true); - std::transform(tmpout.begin(), tmpout.end(), outvec.begin(), outvec.begin(), std::plus()); - } - common::statistics.updateEvent("a"); - double norm = hamiltonian().storage().vv(outvec, outvec + template + bool annihilate_particles(std::array orbitals, int spin, + const std::vector& invec, + std::vector& outvec, + precision& expectation_value) { + if (!_model.symmetry().can_destroy_particle(spin)) return false; + hamiltonian().storage().reset(); + auto next_sec = _model.symmetry().destroy_particle(spin); + outvec.assign(hamiltonian().storage().vector_size(next_sec), precision(0)); + edlib::statistics.registerEvent("a"); + for (int orb : orbitals) { + hamiltonian().storage().init(); + std::vector tmp(outvec.size()); + hamiltonian().storage().a_adag(orb + spin * _model.orbitals(), + invec, tmp, next_sec, /*a=*/true); + std::transform(tmp.begin(), tmp.end(), outvec.begin(), outvec.begin(), + std::plus()); + } + edlib::statistics.updateEvent("a"); + double norm = hamiltonian().storage().vv(outvec, outvec #ifdef USE_MPI - , hamiltonian().comm() + , hamiltonian().comm() #endif - ); - for (int j = 0; j < outvec.size(); ++j) { - outvec[j] /= std::sqrt(norm); - } - _model.symmetry().set_sector(next_sec); - // - expectation_value = norm; - return std::abs(norm) > 1.e-10; - }; - }; - } + ); + for (auto& v : outvec) v /= std::sqrt(norm); + _model.symmetry().set_sector(next_sec); + expectation_value = static_cast(norm); + return std::abs(norm) > 1e-10; + } + + ModelType& _model; + GF_TYPE _G_g, _G_g_ij; + GF_TYPE _G_l, _G_l_ij; + GF_TYPE _G, _G_ij; + precision _cutoff; + precision _Z = precision(0); + int _nspins; + int _nsites; + std::vector _g_orbs; + std::vector> _g_ij_orb_pairs; + }; + } -#endif //EDLIB_GREENSFUNCTION_H +#endif diff --git a/include/edlib/HDF5Utils.h b/include/edlib/HDF5Utils.h deleted file mode 100644 index e94c407..0000000 --- a/include/edlib/HDF5Utils.h +++ /dev/null @@ -1,94 +0,0 @@ -// -// Created by iskakoff on 11/01/17. -// - -#ifndef EDLIB_HDF5UTILS_HPP -#define EDLIB_HDF5UTILS_HPP - - -/** - * @brief HDF5Utils class - * - * @author iskakoff - */ -#include "SzSymmetry.h" -#include "EigenPair.h" - -namespace EDLib { - namespace hdf5 { - template - struct HDF5Utils { - - /** - * Generic function to save objects into hdf5 archive - * @param t - obeject to be saved - * @param ar - hdf5 archive - * @param path - root path in the hdf5 archive - */ - void save(const T& t,alps::hdf5::archive & ar, const std::string& path); - }; - - /** - * Implementation for Sz-symmetry sector - */ - template<> - void HDF5Utils::save(const typename Symmetry::SzSymmetry::Sector& s, alps::hdf5::archive & ar, const std::string& path) { - ar[path + "/nup"]< - void HDF5Utils::save(const typename Symmetry::NSymmetry::Sector& s, alps::hdf5::archive & ar, const std::string& path) { - ar[path + "/n"]< - void save_eigen_pairs(const Ham &h, alps::hdf5::archive & ar, const std::string& path) { -#ifdef USE_MPI - int rank; - MPI_Comm_rank(h.comm(), &rank); - if(!rank){ -#endif - ar[path + "/eigenvalues/N"] << h.eigenpairs().size(); - int i = 0; - std::vector values; - for(const EigenPair& e : h.eigenpairs()) { - values.push_back(e.eigenvalue()); - HDF5Utils().save(e.sector(), ar, path + "/eigenvalues/sectors/" + boost::lexical_cast(i)); - ++i; - } - ar[path + "/eigenvalues/data/"]<> &observables, alps::hdf5::archive& ar, const std::string &root_path) { - for(auto ob = observables.begin(); ob != observables.end(); ++ob){ - ar[root_path + "/static_observables/" + ob->first]<second; - } - } - } -} - - -#endif //EDLIB_HDF5UTILS_HPP diff --git a/include/edlib/Hamiltonian.h b/include/edlib/Hamiltonian.h index 404c8a6..233b295 100644 --- a/include/edlib/Hamiltonian.h +++ b/include/edlib/Hamiltonian.h @@ -1,157 +1,125 @@ -// -// Created by iskakoff on 19/07/16. -// - #ifndef EDLIB_HAMILTONIAN_H #define EDLIB_HAMILTONIAN_H +#include +#include #include -#include -#include -#include -#include "SpinResolvedStorage.h" -#include "Symmetry.h" -#include "EigenPair.h" -#include "HubbardModel.h" -#include "CRSStorage.h" -#include "SOCRSStorage.h" -#include "SingleImpurityAndersonModel.h" - -namespace EDLib { - template +#include "edlib/CRSStorage.h" +#include "edlib/EigenPair.h" +#include "edlib/HubbardModel.h" +#include "edlib/Parameters.h" +#include "edlib/SOCRSStorage.h" +#include "edlib/SingleImpurityAndersonModel.h" +#include "edlib/SpinResolvedStorage.h" + +namespace edlib { + + /** + * Hamiltonian == Model + Storage. Diagonalisation iterates over the model's + * symmetry sectors, lets the storage build the per-sector matrix, and + * collects EigenPairs across sectors. + */ + template class Hamiltonian { public: - typedef typename Storage::Model Model; - typedef typename Storage::Model ModelType; - typedef Storage StorageType; - typedef typename Model::precision prec; - - /* - * Initialize Hamiltonian for specific model and allocate storage - * \param [in] p - alps::parameters - */ + using Model = typename Storage::Model; + using ModelType = typename Storage::Model; + using StorageType = Storage; + using prec = typename Model::precision; + #ifdef USE_MPI - Hamiltonian(alps::params &p, MPI_Comm comm) : - _comm(comm), - _model(p), - _storage(p, _model, comm) {}; + Hamiltonian(const Parameters& p, const typename Model::ModelData& model_data, MPI_Comm comm) + : _comm(comm), + _model(p, model_data), + _storage(p, _model, comm) {} #endif - Hamiltonian(alps::params &p) : - _model(p), - _storage(p, _model) {}; - /** - * fill current sector - */ - void fill() { - _storage.fill(); - } + Hamiltonian(const Parameters& p, const typename Model::ModelData& model_data) + : _model(p, model_data), + _storage(p, _model) {} + + void fill() { _storage.fill(); } - /** - * perform Hamiltonian diagonalization - * result will be stored in evals and evecs - */ void diag() { #ifdef USE_MPI - int rank; - MPI_Comm_rank(_comm, &rank); + int rank; MPI_Comm_rank(_comm, &rank); #endif - int k =0; + int k = 0; while (_model.symmetry().next_sector()) { #ifdef USE_MPI - if (rank == 0){ -#endif - std::cout<<"Diagonalize sector "<<_model.symmetry().sector()< &evals = _storage.eigenvalues(); - const std::vector < std::vector < prec > > &evecs = _storage.eigenvectors(); - for (int i = 0; i < evals.size(); ++i, ++k) { - _eigenpairs.insert(EigenPair < prec, typename Model::Sector >(evals[i], evecs[i], k, _model.symmetry().sector())); + const auto& evals = _storage.eigenvalues(); + const auto& evecs = _storage.eigenvectors(); + for (std::size_t i = 0; i < evals.size(); ++i, ++k) { + _eigenpairs.insert(EigenPair( + evals[i], evecs[i], k, _model.symmetry().sector())); } } } #ifdef USE_MPI - if (rank == 0){ + if (rank == 0) { #endif std::cout << "Here is the list of eigenvalues:" << std::endl; - std::streamsize precision = std::cout.precision(); - std::cout< >::iterator kkk = _eigenpairs.begin(); kkk != _eigenpairs.end(); kkk++) { - std::cout << kkk->eigenvalue() << " "; - kkk->sector().print(); + std::streamsize old_p = std::cout.precision(); + std::cout << std::setprecision(14); + for (auto it = _eigenpairs.begin(); it != _eigenpairs.end(); ++it) { + std::cout << it->eigenvalue() << " "; + it->sector().print(); std::cout << std::endl; } - std::cout< > &eigenpairs() const { + const std::set>& eigenpairs() const { return _eigenpairs; - }; - - Model &model() { - return _model; } - void constant_shift(prec shift) { - _storage.constant_shift(shift); - } + void constant_shift(prec shift) { _storage.constant_shift(shift); } #ifdef USE_MPI - const MPI_Comm& comm() const { - return _comm; - } + const MPI_Comm& comm() const { return _comm; } #endif private: - // CSR format Hamiltonian matrix storage - Storage _storage; - - // Eigen-pairs - std::set < EigenPair < prec, typename Model::Sector > > _eigenpairs; - - /** - * Model to diagonalize - */ - Model _model; - + // _model must be declared (and constructed) before _storage since the + // storage ctor takes a Model& reference. #ifdef USE_MPI MPI_Comm _comm; #endif - + Model _model; + Storage _storage; + std::set> _eigenpairs; }; - typedef Hamiltonian < Storage::CRSStorage < Model::HubbardModel < double > > > CSRHubbardHamiltonian; - typedef Hamiltonian < Storage::SpinResolvedStorage < Model::HubbardModel < double > > > SRSHubbardHamiltonian; - typedef Hamiltonian < Storage::SOCRSStorage < Model::HubbardModel < double > > > SOCSRHubbardHamiltonian; + using CSRHubbardHamiltonian = Hamiltonian>>; + using SRSHubbardHamiltonian = Hamiltonian>>; + using SOCSRHubbardHamiltonian = Hamiltonian>>; - typedef Hamiltonian < Storage::CRSStorage < Model::HubbardModel < float > > > CSRHubbardHamiltonian_float; - typedef Hamiltonian < Storage::SpinResolvedStorage < Model::HubbardModel < float > > > SRSHubbardHamiltonian_float; - typedef Hamiltonian < Storage::SOCRSStorage < Model::HubbardModel < float > > > SOCSRHubbardHamiltonian_float; + using CSRHubbardHamiltonian_float = Hamiltonian>>; + using SRSHubbardHamiltonian_float = Hamiltonian>>; + using SOCSRHubbardHamiltonian_float = Hamiltonian>>; - typedef Hamiltonian < Storage::CRSStorage < Model::SingleImpurityAndersonModel < double > > > CSRSIAMHamiltonian; - typedef Hamiltonian < Storage::CRSStorage < Model::SingleImpurityAndersonModel < float > > > CSRSIAMHamiltonian_float; + using CSRSIAMHamiltonian = Hamiltonian>>; + using CSRSIAMHamiltonian_float = Hamiltonian>>; + using SRSSIAMHamiltonian = Hamiltonian>>; + using SRSSIAMHamiltonian_float = Hamiltonian>>; - typedef Hamiltonian < Storage::SpinResolvedStorage < Model::SingleImpurityAndersonModel < double > > > SRSSIAMHamiltonian; - typedef Hamiltonian < Storage::SpinResolvedStorage < Model::SingleImpurityAndersonModel < float > > > SRSSIAMHamiltonian_float; } -#endif //EDLIB_HAMILTONIAN_H + +#endif diff --git a/include/edlib/HubbardModel.h b/include/edlib/HubbardModel.h index 9bf3c1e..5b4a458 100644 --- a/include/edlib/HubbardModel.h +++ b/include/edlib/HubbardModel.h @@ -1,226 +1,212 @@ -// -// Created by iskakoff on 29/07/16. -// - #ifndef EDLIB_HUBBARDMODEL_H #define EDLIB_HUBBARDMODEL_H +#include +#include +#include +#include +#include #include -#include -#include -#include -#include "SzSymmetry.h" -#include "FermionicModel.h" -#include "CommonUtils.h" - #include +#include -namespace EDLib { - namespace Model { - namespace Hubbard { - template - class InnerState { - public: - InnerState(int ii, int jj, int spin, prec val) : _indicies(ii, jj), _spin(spin), _value(val) {}; +#include "edlib/CommonUtils.h" +#include "edlib/FermionicModel.h" +#include "edlib/Gf.h" +#include "edlib/Mesh.h" +#include "edlib/Parameters.h" +#include "edlib/SzSymmetry.h" - const inline std::pair < int, int > &indicies() const { return _indicies; } +namespace edlib { - const inline prec &value() const { return _value; } + namespace hubbard { - inline int spin() const { return _spin; } + template + class InnerState { + public: + InnerState(int ii, int jj, int spin, Prec val) + : _indicies(ii, jj), _spin(spin), _value(val) {} - private: - std::pair < int, int > _indicies; - int _spin; - prec _value; - }; - } + const std::pair& indicies() const { return _indicies; } + const Prec& value() const { return _value; } + int spin() const { return _spin; } - template - class HubbardModel: public FermionicModel { - public: - typedef prcsn precision; - typedef typename Symmetry::SzSymmetry SYMMETRY; - typedef typename Hubbard::InnerState < precision > St; - typedef typename Symmetry::SzSymmetry::Sector Sector; - - HubbardModel(alps::params &p) : FermionicModel(p), _symmetry(p) { - _Eps.assign(p["NSITES"], std::vector < precision >(p["NSPINS"], precision(0.0))); - t.assign(p["NSITES"], std::vector < precision >(p["NSITES"], precision(0.0))); - U.assign(p["NSITES"], precision(0.0)); - J.assign(p["NSITES"], std::vector < precision >(p["NSITES"], precision(0.0))); - _xmu.assign(p["NSITES"], precision(0.0)); - _Hmag.assign(p["NSITES"], precision(0.0)); - std::string input = p["INPUT_FILE"]; - alps::hdf5::archive input_data(input.c_str(), "r"); - if(input_data.is_data("magnetic_field/values")) { - input_data >> alps::make_pvp("magnetic_field/values", _Hmag); - } + private: + std::pair _indicies; + int _spin; + Prec _value; + }; - input_data >> alps::make_pvp("hopping/values", t); - input_data >> alps::make_pvp("interaction/values", U); - if(input_data.is_data("exchange/values")) { - input_data >> alps::make_pvp("exchange/values", J); - } - input_data >> alps::make_pvp("chemical_potential/values", _xmu); - input_data.close(); - for (int ii = 0; ii < _Ns; ++ii) { - for (int jj = 0; jj < _Ns; ++jj) { - if (std::abs(t[ii][jj]) > 1e-10) { - for (int is = 0; is < _ms; ++is) { - _states.push_back(St(ii, jj, is, t[ii][jj])); - } - } - } - } - }; - - /** - * check that current basis vector get non-zero contribution - * - * @param state - electron state combination of spin and site indices - * @param nst - current basis state - * @return 1 if there is nonzero contribution, otherwise 0 - */ - inline int valid(const St &state, long long nst) { - return (checkState(nst, state.indicies().first + state.spin() * _Ns, _Ip) * (1 - checkState(nst, state.indicies().second + state.spin() * _Ns, _Ip))); - } + } - /** - * Compute off-diagonal term for transition from nst-state to k-state - * - * @param state - transition state - * @param nst - initial basis state - * @param k - resulting basis state - * @param sign - fermionic sign for transition - * @return contribution to off-diagonal element for transition state - */ - inline precision set(const St &state, long long nst, long long &k, int &sign) { - long long k1, k2; - int isign1, isign2; - a(state.indicies().first + state.spin() * _Ns, nst, k1, isign1); - adag(state.indicies().second + state.spin() * _Ns, k1, k2, isign2); - k = k2; - // -t c^+ c - sign = -isign1 * isign2; - return state.value(); - } + template + class HubbardModel : public FermionicModel { + public: + using precision = Prec; + using SYMMETRY = SzSymmetry; + using St = hubbard::InnerState; + using Sector = typename SzSymmetry::Sector; + + /** + * Per-instance Hubbard parameters. All arrays are caller-supplied; the + * model performs no file I/O. Sizes are validated against the Parameters + * passed to the constructor. + */ + struct ModelData { + std::vector> hopping; ///< [nsites][nsites] -- mandatory + std::vector U; ///< [nsites] -- mandatory + std::vector mu; ///< [nsites] -- mandatory + std::vector magnetic_field; ///< [nsites] -- optional (zeros) + std::vector> exchange; ///< [nsites][nsites] -- optional (zeros) + std::vector> site_energy; ///< [nsites][nspins] -- optional (zeros) + std::vector> sectors; ///< optional sector restriction + }; - /** - * Computes diagonal contribution for state s: - * - * @param state - current basis state - * @return value of - */ - inline precision diagonal(long long state) const { - precision xtemp = 0.0; - for (int im = 0; im < _Ns; ++im) { - for (int is = 0; is < _ms; ++is) { - xtemp += (_Eps[im][is] - _xmu[is]) * checkState(state, im + is * _Ns, _Ip); - } - xtemp += U[im] * checkState(state, im, _Ip) * checkState(state, im + _Ns, _Ip); - xtemp += _Hmag[im] * (checkState(state, im + _Ns, _Ip) - checkState(state, im, _Ip)); - for (int im2 = 0; im2 < _Ns; ++im2) { - xtemp += - J[im][im2] * - (checkState(state, im, _Ip) - checkState(state, im + _Ns, _Ip)) * - (checkState(state, im2, _Ip) - checkState(state, im2 + _Ns, _Ip)); + HubbardModel(const Parameters& p, const ModelData& model_data) + : FermionicModel(p), + _symmetry(p, model_data.sectors), + _t(model_data.hopping), + _U(model_data.U), + _xmu(model_data.mu), + _Hmag(model_data.magnetic_field.empty() + ? std::vector(p.nsites, Prec(0)) + : model_data.magnetic_field), + _J(model_data.exchange.empty() + ? std::vector>(p.nsites, + std::vector(p.nsites, Prec(0))) + : model_data.exchange), + _Eps(model_data.site_energy.empty() + ? std::vector>(p.nsites, + std::vector(p.nspins, Prec(0))) + : model_data.site_energy) { + validate_model_data(p); + for (int ii = 0; ii < _Ns; ++ii) { + for (int jj = 0; jj < _Ns; ++jj) { + if (std::abs(_t[ii][jj]) > 1e-10) { + for (int is = 0; is < _ms; ++is) { + _states.emplace_back(ii, jj, is, _t[ii][jj]); + } } } - return xtemp; - } - - /** - * @deprecated - */ - inline long long interacting_states(long long nst) { - return nst; } + } + inline int valid(const St& state, long long nst) const { + return checkState(nst, state.indicies().first + state.spin() * _Ns, _Ip) + * (1 - checkState(nst, state.indicies().second + state.spin() * _Ns, _Ip)); + } - /** - * @return hopping transitions - */ - const std::vector < St > &T_states() const { return _states; }; - // We have only diagonal interaction - /** - * @return off-diagonal interaction transitions - */ - const std::vector < St > &V_states() const { return _V_states; }; - - /** - * For Hubbard model all orbitals are interacting - * - * @return total number of sites - */ - int interacting_orbitals() const { - return _Ns; - } + inline Prec set(const St& state, long long nst, long long& k, int& sign) const { + long long k1, k2; + int isign1, isign2; + a (state.indicies().first + state.spin() * _Ns, nst, k1, isign1); + adag(state.indicies().second + state.spin() * _Ns, k1, k2, isign2); + k = k2; + sign = -isign1 * isign2; // -t c^+ c + return state.value(); + } - /** - * For Hubbard model Hamiltonian comutes with spin-operator ([Sz, H] = 0) - * @return symmetry object - */ - inline const Symmetry::SzSymmetry &symmetry() const { - return _symmetry; + inline Prec diagonal(long long state) const { + Prec xtemp = Prec(0); + for (int im = 0; im < _Ns; ++im) { + for (int is = 0; is < _ms; ++is) { + xtemp += (_Eps[im][is] - _xmu[is]) * checkState(state, im + is * _Ns, _Ip); + } + xtemp += _U[im] * checkState(state, im, _Ip) * checkState(state, im + _Ns, _Ip); + xtemp += _Hmag[im] * (checkState(state, im + _Ns, _Ip) - checkState(state, im, _Ip)); + for (int im2 = 0; im2 < _Ns; ++im2) { + xtemp += _J[im][im2] + * (checkState(state, im, _Ip) - checkState(state, im + _Ns, _Ip)) + * (checkState(state, im2, _Ip) - checkState(state, im2 + _Ns, _Ip)); + } } + return xtemp; + } - inline Symmetry::SzSymmetry &symmetry() { - return _symmetry; + /// @deprecated kept for API parity with legacy callers + inline long long interacting_states(long long nst) const { return nst; } + + const std::vector& T_states() const { return _states; } + const std::vector& V_states() const { return _V_states; } + + int interacting_orbitals() const { return _Ns; } + + const SzSymmetry& symmetry() const { return _symmetry; } + SzSymmetry& symmetry() { return _symmetry; } + + /// Read-only accessors used by legacy shims. Stable but not part of the + /// recommended public API; use bare_greens_function for the standard path. + const std::vector>& hopping_matrix() const { return _t; } + const std::vector& interaction() const { return _U; } + const std::vector& chem_potential() const { return _xmu; } + const std::vector>& site_energy() const { return _Eps; } + + /** + * Compute the non-interacting Green's function on the supplied frequency + * mesh and fill bare_gf with shape [n_omega, _Ns * _Ns, _ms]. + */ + template + void bare_greens_function(Gf, 3>& bare_gf, + const Mesh& mesh, + double beta) const { + const int n_omega = bare_gf.shape(0); + const int n_orb = bare_gf.shape(1); + const int n_spin = bare_gf.shape(2); + if (n_orb != _Ns * _Ns) { + throw std::invalid_argument("bare_greens_function: shape(1) must equal nsites*nsites"); } - - /** - * Compute bare Green's function for specific mesh - * - * @tparam Mesh - mesh-type - * @param bare_gf - bare Green's function container - * @param beta - inverse temperature - */ - template - void bare_greens_function(alps::gf::three_index_gf, Mesh, alps::gf::index_mesh, alps::gf::index_mesh >& bare_gf, double beta) { - for(int iw = 0; iw< bare_gf.mesh1().points().size(); ++iw) { - typename Mesh::index_type w(iw); - for (int is : bare_gf.mesh3().points()) { - Eigen::MatrixXcd G_inv = Eigen::MatrixXcd::Zero(_Ns, _Ns); - for(int I = 0; I<_Ns; ++I) { - G_inv(I, I) = (common::freq_point(iw, bare_gf.mesh1(), beta) + _xmu[I] - _Eps[I][is]); - for(int J = 0; J<_Ns; ++J){ - int im = I*_Ns + J; - G_inv(I, J) += t[I][J]; - } + for (int iw = 0; iw < n_omega; ++iw) { + std::complex z = freq_point(iw, mesh, beta); + for (int is = 0; is < n_spin; ++is) { + Eigen::MatrixXcd G_inv = Eigen::MatrixXcd::Zero(_Ns, _Ns); + for (int I = 0; I < _Ns; ++I) { + G_inv(I, I) = z + _xmu[I] - _Eps[I][is]; + for (int J = 0; J < _Ns; ++J) { + G_inv(I, J) += _t[I][J]; } - G_inv = G_inv.inverse().eval(); - for (int im: bare_gf.mesh2().points()) { - int I = im / _Ns; - int J = im % _Ns; - bare_gf(w, alps::gf::index_mesh::index_type(im), alps::gf::index_mesh::index_type(is)) = G_inv(I, J); + } + Eigen::MatrixXcd G = G_inv.inverse(); + for (int I = 0; I < _Ns; ++I) { + for (int J = 0; J < _Ns; ++J) { + bare_gf(iw, I * _Ns + J, is) = G(I, J); } } } } + } - private: - /// Symmetry - Symmetry::SzSymmetry _symmetry; - /// Hopping - std::vector < std::vector < precision > > t; - /// Interaction - std::vector < precision > U; - /// Exchange - std::vector < std::vector < precision > > J; - /// Chemical potential - std::vector < precision > _xmu; - /// Magnetic field - std::vector < precision > _Hmag; - /// site energy shift - std::vector < std::vector < precision > > _Eps; - - /// Non-diagonal states iterators - std::vector < St > _states; - std::vector < St > _V_states; - }; + private: + void validate_model_data(const Parameters& p) const { + const int N = p.nsites; + auto bad = [](const char* what) { throw std::invalid_argument(what); }; + + if (static_cast(_t.size()) != N) bad("HubbardModel: hopping must be [nsites][nsites]"); + for (const auto& row : _t) + if (static_cast(row.size()) != N) bad("HubbardModel: hopping rows must have size nsites"); + if (static_cast(_U.size()) != N) bad("HubbardModel: U must have size nsites"); + if (static_cast(_xmu.size()) != N) bad("HubbardModel: mu must have size nsites"); + if (static_cast(_Hmag.size())!= N) bad("HubbardModel: magnetic_field size mismatch"); + if (static_cast(_J.size()) != N) bad("HubbardModel: exchange must be [nsites][nsites]"); + for (const auto& row : _J) + if (static_cast(row.size()) != N) bad("HubbardModel: exchange rows must have size nsites"); + if (static_cast(_Eps.size()) != N) bad("HubbardModel: site_energy must be [nsites][nspins]"); + for (const auto& row : _Eps) + if (static_cast(row.size()) != p.nspins) bad("HubbardModel: site_energy rows must have size nspins"); + } + + SzSymmetry _symmetry; + std::vector> _t; + std::vector _U; + std::vector _xmu; + std::vector _Hmag; + std::vector> _J; + std::vector> _Eps; + + std::vector _states; + std::vector _V_states; + }; - } } -#endif //EDLIB_HUBBARDMODEL_H + +#endif diff --git a/include/edlib/Lanczos.h b/include/edlib/Lanczos.h index 010e359..3119360 100644 --- a/include/edlib/Lanczos.h +++ b/include/edlib/Lanczos.h @@ -1,228 +1,188 @@ -// -// Created by iskakoff on 01/08/16. -// - #ifndef EDLIB_LANCZOS_H #define EDLIB_LANCZOS_H -#include -#include -#include - #include - -namespace EDLib { - namespace gf { - template - class Lanczos { - protected: - using precision=typename Hamiltonian::ModelType::precision; - using Mesh=typename MeshFactory::MeshType; - using mesh_index=typename Mesh::index_type; - public: - Lanczos(alps::params &p, Hamiltonian &h, Args...args) : - ham(h), _omega(MeshFactory::createMesh(p, args...)),_Nl(p["lanc.NLANC"]), - alfalanc(p["lanc.NLANC"], 0.0), betalanc(int(p["lanc.NLANC"]) + 1, 0.0), det(p["lanc.NLANC"], 0), dl(p["lanc.NLANC"], 0.0), _beta(p["lanc.BETA"].as()) {} - - const Mesh &omega() const { - return _omega; - } - protected: - /** - * Lanczos basis construction - * @param v - initial vector - * @return number of Lanczos iteration - */ - int lanczos(std::vector < precision > &v) { - int nlanc = 0; - unsigned long size = v.size(); - std::vector < precision > w(size, precision(0.0)); - precision alf = 0, bet = 0; - ham.fill(); - if(v.size()!=0) { - ham.storage().prepare_work_arrays(v.data()); - for (int iter = 1; iter <= _Nl; ++iter) { - ++nlanc; - if (iter != 1) { - for (int j = 0; j < size; ++j) { - precision dummy = v[j]; - v[j] = w[j] / bet; - w[j] = -bet * dummy; - } - } - alf = 0.0; - bet = 0.0; - ham.storage().av(v.data(), w.data(), size, false); - alf = ham.storage().vv(v, w); - alfalanc[iter - 1] = alf; - for (int j = 0; j < size; ++j) { - w[j] -= alf * v[j]; - } - bet = ham.storage().vv(w, w); - bet = std::sqrt(bet); - - if (iter != _Nl) betalanc[iter] = bet; - if (std::abs(bet) < 1e-10 /*|| iter >= (2 * ham.model().symmetry().sector().size())*/) { - break; +#include +#include +#include +#include +#include + +#include "edlib/Gf.h" +#include "edlib/Mesh.h" +#include "edlib/Parameters.h" + +namespace edlib { + + /** + * Lanczos basis builder + continued fraction evaluator for spectral + * functions. Templated directly on the frequency mesh type (no MeshFactory + * layer): callers pass a mesh instance into the constructor. + */ + template + class Lanczos { + protected: + using precision = typename Hamiltonian::ModelType::precision; + + public: + Lanczos(const Parameters& p, Hamiltonian& h, Mesh omega) + : ham(h), + _omega(std::move(omega)), + _beta(static_cast(p.lanc_beta)), + _Nl(p.lanc_nlanc), + alfalanc(p.lanc_nlanc, precision(0)), + betalanc(p.lanc_nlanc + 1, precision(0)), + det(p.lanc_nlanc, std::complex(0)), + dl (p.lanc_nlanc, std::complex(0)) {} + + const Mesh& omega() const { return _omega; } + + protected: + int lanczos(std::vector& v) { + int nlanc = 0; + const std::size_t size = v.size(); + std::vector w(size, precision(0)); + precision alf = 0, bet = 0; + ham.fill(); + if (size != 0) { + ham.storage().prepare_work_arrays(v.data()); + for (int iter = 1; iter <= _Nl; ++iter) { + ++nlanc; + if (iter != 1) { + for (std::size_t j = 0; j < size; ++j) { + precision dummy = v[j]; + v[j] = w[j] / bet; + w[j] = -bet * dummy; } } - hamiltonian().storage().finalize(0, false); - } else { - hamiltonian().storage().finalize(0, false); + alf = 0; + bet = 0; + ham.storage().av(v.data(), w.data(), size, false); + alf = ham.storage().vv(v, w); + alfalanc[iter - 1] = alf; + for (std::size_t j = 0; j < size; ++j) w[j] -= alf * v[j]; + bet = ham.storage().vv(w, w); + bet = std::sqrt(bet); + if (iter != _Nl) betalanc[iter] = bet; + if (std::abs(bet) < precision(1e-10)) break; } + hamiltonian().storage().finalize(0, false); + } else { + hamiltonian().storage().finalize(0, false); + } #ifdef USE_MPI - MPI_Barrier(hamiltonian().comm()); + MPI_Barrier(hamiltonian().comm()); #endif - return nlanc; + return nlanc; + } + + /// Continued-fraction evaluation into a 3-index GF (frequency × orbital × spin). + void compute_continued_fraction(double expectation_value, double excited_state, + double groundstate, int nlanc, int isign, + GF3& gf, int site, int spin) { + double expb = (_beta * (excited_state - groundstate) > 25) + ? 0.0 + : std::exp(-_beta * (excited_state - groundstate)); + for (int iomega = 0; iomega < _omega.extent(); ++iomega) { + std::complex ener = freq_point(iomega) + excited_state * isign; + double shift = 1.0; + std::complex swp = get_frac_point(expectation_value, nlanc, isign, expb, shift, ener); + gf(iomega, site, spin) += swp; } - - /** - * Compute lanczos continues fraction - */ - template - void compute_continued_fraction(double expectation_value, double excited_state, double groundstate, int nlanc, int isign, GF_TYPE &gf, - const alps::gf::index_mesh::index_type &site, const alps::gf::index_mesh::index_type &spin) { - double expb = 0; - double shift; - if (_beta * (excited_state - groundstate) > 25) - expb = 0; - else - expb = exp(-_beta * (excited_state - groundstate)); - const std::vector < double > &freqs = _omega.points(); - for (int iomega = 0; iomega < _omega.extent(); ++iomega) { - shift = 1.0; - std::complex swp = 0.0; - std::complex < double > ener = freq_point(iomega) + (excited_state) * isign; - swp = get_frac_point(expectation_value, nlanc, isign, expb, shift, ener); - - gf(mesh_index(iomega), site, spin) += swp; - } + } + + /// Continued-fraction evaluation into a 2-index GF (frequency × orbital), symmetric variant. + void compute_sym_continued_fraction(double expectation_value, double excited_state, + double groundstate, int nlanc, int isign, + GF2& gf, int site) { + double expb = (_beta * (excited_state - groundstate) > 25) + ? 0.0 + : std::exp(-_beta * (excited_state - groundstate)); + update_static(gf, site, expectation_value, expb); + for (int iomega = zero_freq(); iomega < _omega.extent(); ++iomega) { + std::complex ener = freq_point(iomega) + excited_state * isign; + std::complex ener2 = -freq_point(iomega) + excited_state * isign; + double shift = 1.0; + std::complex swp = get_frac_point(expectation_value, nlanc, isign, expb, shift, ener); + swp += get_frac_point(expectation_value, nlanc, isign, expb, shift, ener2); + gf(iomega, site) += swp; } + } - /** - * Compute symmetrized lanczos continues fraction - */ - template - void compute_sym_continued_fraction(double expectation_value, double excited_state, double groundstate, int nlanc, int isign, GF_TYPE &gf, - const alps::gf::index_mesh::index_type &site) { - double expb = 0; - double shift; - if (_beta * (excited_state - groundstate) > 25) - expb = 0; - else - expb = exp(-_beta * (excited_state - groundstate)); - const std::vector < double > &freqs = _omega.points(); - update_static(gf, site, expectation_value, expb); - for (int iomega = zero_freq(); iomega < _omega.extent(); ++iomega) { - shift = 1.0; - std::complex < double > ener = freq_point(iomega) + (excited_state) * isign; - std::complex < double > ener2 = -freq_point(iomega) + (excited_state) * isign; - std::complex swp = get_frac_point(expectation_value, nlanc, isign, expb, shift, ener); - swp += get_frac_point(expectation_value, nlanc, isign, expb, shift, ener2); - gf(mesh_index(iomega), site) += swp; - } - } + Hamiltonian& hamiltonian() { return ham; } + const Hamiltonian& hamiltonian() const { return ham; } - const Hamiltonian &hamiltonian() const { - return ham; - }; + public: + /// Inverse temperature used by the spectral evaluation. + precision beta() const { return _beta; } - Hamiltonian &hamiltonian() { - return ham; - }; + protected: - precision beta() const { - return _beta; + std::complex freq_point(int index) const { + if constexpr (std::is_same_v) { + return std::complex(_omega.points()[index], M_PI / _beta); + } else { + return std::complex(0.0, _omega.points()[index]); } + } - /** - * Computes complex value for frequency. - * For Matsubara frequecy z = i*omega_n. For real frequency add small imaginary temperature dependent broadering z = omega_n + i\delta - * - * @tparam M - mesh type - * @param index frequency index - * @return proper complex representation for current frequency - */ - std::complex freq_point(int index) { - return std::is_base_of::value ? std::complex(_omega.points()[index], M_PI/_beta) : std::complex(0.0, _omega.points()[index]); - }; - - std::string suffix() { - return std::is_base_of::value ? "_r" : ""; - }; - - /** - * Compute smallest index for frequency. Since Lanczos continued fraction can not compute zero Matsubara frequency bosonic Green's function - * we should compute it from first non-zero Matsubara and treat zero frequency separately. - * - * @tparam M - mesh type - * @return valid smallest index for frequency - */ - int zero_freq() { - return std::is_base_of::value ? 1 : 0; - }; - - template - void update_static(GF_TYPE& gf, const alps::gf::index_mesh::index_type &site, double expectation_value, double expb) { - if(std::is_base_of::value) - gf(mesh_index(0), site) -= expectation_value*_beta*expb; - }; - - private: - /// frequency mesh - Mesh _omega; - /// inverse temperature - precision _beta; - - /// maximum number of Lanczos iteration - int _Nl; - /// Hamiltonain object - Hamiltonian &ham; - - /// Lanczos tridiagonal matrix - std::vector < precision > alfalanc; - std::vector < precision > betalanc; - - /// continued fraction arrays - std::vector < std::complex < double > > det; - std::vector < std::complex < double > > dl; - - /** - * Computes continued fraction for specific frequency - * - * @param expectation_value - * @param nlanc - number of Lanczos operation have been performed - * @param isign - sign - * @param expb - Boltzman exponent value - * @param shift - overflow avoiding shift - * @param ener - first denominator in fraction (w - E) - * @return GF value for specific energy point. - */ - std::complex < double > get_frac_point(double expectation_value, int nlanc, int isign, double expb, double shift, const std::complex < double > &ener) { - std::complex < double > swp = 0.0; - det.assign(nlanc, 0.0); - for (int i = 0; i < nlanc; ++i) { - dl[i] = ener - ((double) (alfalanc[i]) * isign); - } - if (nlanc == 1) { - det[0] = dl[0]; - swp += expectation_value * expb / det[0]; - } else { - det[nlanc - 1] = dl[nlanc - 1]; - det[nlanc - 2] = dl[nlanc - 2] * dl[nlanc - 1] - std::pow(betalanc[nlanc - 1], 2); - for (int i = nlanc - 3; i >= 0; --i) { - det[i] = (dl[i] * det[i + 1] - std::pow(betalanc[i + 1], 2) * det[i + 2]); - if(abs(det[i]) > (std::numeric_limits::max() / 2.0) && i != 0) { - shift = 1.0/(std::numeric_limits::max() / 1000.0); - det[i] *=shift; - det[i + 1] *=shift; - } + std::string suffix() const { + if constexpr (std::is_same_v) return "_r"; + else return ""; + } + + int zero_freq() const { + if constexpr (std::is_same_v) return 1; + else return 0; + } + + void update_static(GF2& gf, int site, double expectation_value, double expb) { + if constexpr (std::is_same_v) { + gf(0, site) -= std::complex(expectation_value * _beta * expb, 0.0); + } + } + + private: + Hamiltonian& ham; + Mesh _omega; + precision _beta; + int _Nl; + + std::vector alfalanc; + std::vector betalanc; + + std::vector> det; + std::vector> dl; + + std::complex get_frac_point(double expectation_value, int nlanc, int isign, + double expb, double shift, + const std::complex& ener) { + std::complex swp(0, 0); + det.assign(nlanc, std::complex(0)); + for (int i = 0; i < nlanc; ++i) { + dl[i] = ener - (double(alfalanc[i]) * isign); + } + if (nlanc == 1) { + det[0] = dl[0]; + swp += expectation_value * expb / det[0]; + } else { + det[nlanc - 1] = dl[nlanc - 1]; + det[nlanc - 2] = dl[nlanc - 2] * dl[nlanc - 1] - std::pow(betalanc[nlanc - 1], 2); + for (int i = nlanc - 3; i >= 0; --i) { + det[i] = dl[i] * det[i + 1] - std::pow(betalanc[i + 1], 2) * det[i + 2]; + // Avoid overflow when intermediate determinants get huge. + if (std::abs(det[i]) > (std::numeric_limits::max() / 2.0) && i != 0) { + shift = 1.0 / (std::numeric_limits::max() / 1000.0); + det[i] *= shift; + det[i + 1] *= shift; } - swp += expectation_value * expb * det[1] / det[0]; } - return swp; + swp += expectation_value * expb * det[1] / det[0]; } - }; - } + return swp; + } + }; + } -#endif //EDLIB_LANCZOS_H + +#endif diff --git a/include/edlib/Mesh.h b/include/edlib/Mesh.h new file mode 100644 index 0000000..cdac51c --- /dev/null +++ b/include/edlib/Mesh.h @@ -0,0 +1,72 @@ +#ifndef EDLIB_MESH_H +#define EDLIB_MESH_H + +#include +#include +#include + +namespace edlib { + + enum class Statistics { Fermionic, Bosonic }; + + class MatsubaraMesh { + public: + MatsubaraMesh() = default; + MatsubaraMesh(double beta, int n, Statistics stat) + : _beta(beta), _n(n), _stat(stat) {} + + double beta() const { return _beta; } + int extent() const { return _n; } + Statistics statistics() const { return _stat; } + + std::vector points() const { + if (_beta <= 0.0) { + throw std::invalid_argument("MatsubaraMesh: beta must be positive"); + } + std::vector w(_n); + const double pi = 3.14159265358979323846; + const int shift = (_stat == Statistics::Fermionic) ? 1 : 0; + for (int i = 0; i < _n; ++i) { + w[i] = pi * (2 * i + shift) / _beta; + } + return w; + } + + private: + double _beta = 0.0; + int _n = 0; + Statistics _stat = Statistics::Fermionic; + }; + + class RealFreqMesh { + public: + RealFreqMesh() = default; + RealFreqMesh(double emin, double emax, int n) + : _emin(emin), _emax(emax), _n(n) {} + + double emin() const { return _emin; } + double emax() const { return _emax; } + int extent() const { return _n; } + + std::vector points() const { + std::vector w(_n); + if (_n == 1) { + w[0] = _emin; + return w; + } + const double step = (_emax - _emin) / static_cast(_n - 1); + for (int i = 0; i < _n; ++i) { + w[i] = _emin + step * i; + } + return w; + } + + private: + double _emin = 0.0; + double _emax = 0.0; + int _n = 0; + }; + +} + +#endif diff --git a/include/edlib/MeshFactory.h b/include/edlib/MeshFactory.h index c1f9fdf..076d1a1 100644 --- a/include/edlib/MeshFactory.h +++ b/include/edlib/MeshFactory.h @@ -1,37 +1,29 @@ -// -// Created by iskakoff on 17/01/17. -// +#ifndef EDLIB_MESHFACTORY_H +#define EDLIB_MESHFACTORY_H -#ifndef EDLIB_MESHFACTORY_HPP -#define EDLIB_MESHFACTORY_HPP +#include "edlib/Mesh.h" +#include "edlib/Parameters.h" +namespace edlib { -#include -#include - -/** - * @brief Factory class for frequency mesh initialization. Creates proper Mesh-object based on the Mesh type . - * - * @author iskakoff - */ -namespace EDLib { - - class MatsubaraMeshFactory { - public: - using MeshType = alps::gf::matsubara_positive_mesh; - static MeshType createMesh(alps::params &p, alps::gf::statistics::statistics_type type) { - return std::move(alps::gf::matsubara_positive_mesh(p["lanc.BETA"], p["lanc.NOMEGA"], type)); + /** + * Convenience factories. Most call sites can construct meshes directly; + * these mirror the legacy API for symmetry. + */ + struct MatsubaraMeshFactory { + using MeshType = MatsubaraMesh; + static MatsubaraMesh createMesh(const Parameters& p, Statistics stat) { + return MatsubaraMesh(p.lanc_beta, p.lanc_nomega, stat); } }; - class RealFreqMeshFactory { - public: - using MeshType = alps::gf::real_frequency_mesh; - static MeshType createMesh(alps::params &p) { - alps::gf::grid::linear_real_frequency_grid g(p["lanc.EMIN"], p["lanc.EMAX"], p["lanc.NOMEGA"]); - return std::move(alps::gf::real_frequency_mesh(g)); + struct RealFreqMeshFactory { + using MeshType = RealFreqMesh; + static RealFreqMesh createMesh(const Parameters& p) { + return RealFreqMesh(p.lanc_emin, p.lanc_emax, p.lanc_nomega); } }; + } -#endif //EDLIB_MESHFACTORY_HPP +#endif diff --git a/include/edlib/MpiTypes.h b/include/edlib/MpiTypes.h new file mode 100644 index 0000000..05a8934 --- /dev/null +++ b/include/edlib/MpiTypes.h @@ -0,0 +1,32 @@ +#ifndef EDLIB_MPITYPES_H +#define EDLIB_MPITYPES_H + +#ifdef USE_MPI +#include +#include +#include + +namespace edlib { + + /** + * MPI datatype lookup for the small set of arithmetic types EDLib reduces + * over. Replaces alps::mpi::detail::mpi_type(). + */ + template + MPI_Datatype mpi_type() = delete; + + template <> inline MPI_Datatype mpi_type() { return MPI_FLOAT; } + template <> inline MPI_Datatype mpi_type() { return MPI_DOUBLE; } + template <> inline MPI_Datatype mpi_type() { return MPI_INT; } + template <> inline MPI_Datatype mpi_type() { return MPI_LONG; } + template <> inline MPI_Datatype mpi_type() { return MPI_LONG_LONG; } + template <> inline MPI_Datatype mpi_type() { return MPI_UNSIGNED; } + template <> inline MPI_Datatype mpi_type() { return MPI_UNSIGNED_LONG; } + template <> inline MPI_Datatype mpi_type() { return MPI_UNSIGNED_LONG_LONG; } + template <> inline MPI_Datatype mpi_type>() { return MPI_C_FLOAT_COMPLEX; } + template <> inline MPI_Datatype mpi_type>() { return MPI_C_DOUBLE_COMPLEX; } + +} +#endif // USE_MPI + +#endif diff --git a/include/edlib/NSymmetry.h b/include/edlib/NSymmetry.h index c6f99a6..6a6a6a7 100644 --- a/include/edlib/NSymmetry.h +++ b/include/edlib/NSymmetry.h @@ -1,171 +1,147 @@ -// -// Created by iskakoff on 21/08/16. -// - #ifndef EDLIB_NSYMMETRY_H #define EDLIB_NSYMMETRY_H - +#include +#include +#include #include -#include -#include "Symmetry.h" -#include "Combination.h" -namespace EDLib { - namespace Symmetry { - class NSymmetry : public Symmetry { +#include + +#include "edlib/Combination.h" +#include "edlib/Parameters.h" +#include "edlib/Symmetry.h" + +namespace edlib { + + /** + * Total-N conserving symmetry: sectors indexed by a single integer n. + * + * sector_list (default empty = "all sectors") restricts iteration to the + * supplied n values. _N is 2 * nsites (spinful), matching legacy semantics. + */ + class NSymmetry : public Symmetry { + public: + class Sector { public: - class Sector { - public: - friend class NSymmetry; - - friend std::ostream &operator<<(std::ostream &o, const NSymmetry::Sector &c) { return o << " (nup+ndown: " << c._n << ") size: " << c._size; } - - Sector(int n, size_t size) : _n(n), _size(size) {}; - - bool operator<(const Sector & s) const{ - return _size < s._size || (_size == s._size && _n < s._n); - } - - bool operator>(const Sector & s) const{ - return s < *this; - } - - public: - int n() const { return _n; } + friend class NSymmetry; - size_t size() const { return _size; } - - void print(std::ostream & out) const { - out << _n; - } - - void print() const {print(std::cout);} - - private: - int _n; - size_t _size; - }; - - NSymmetry(int N) : Symmetry(), _N(N), _totstate(N, 0.0), _current_sector(-1, 0), _comb(N) {} - - NSymmetry(alps::params &p) : Symmetry(), _N(2 * int(p["NSITES"])), _totstate(2 * p["NSITES"].as(), 0.0), _current_sector(-1, 0), - _comb(2 * p["NSITES"].as()) { - std::vector sectors; - if (p.exists("arpack.SECTOR") && bool(p["arpack.SECTOR"])) { - std::vector < std::vector < int > > sectors_list; - std::string input = p["INPUT_FILE"]; - alps::hdf5::archive input_file(input, "r"); - input_file >> alps::make_pvp("sectors/values", sectors_list); - input_file.close(); - for (int kkk = 0; kkk < sectors_list.size(); ++kkk) { - sectors.push_back(NSymmetry::Sector(sectors_list[kkk][0], (size_t) (_comb.c_n_k(_N, sectors_list[kkk][0])))); - } - } else { - for (int i = 0; i <= _N; ++i) { - sectors.push_back(NSymmetry::Sector(i, (size_t) (_comb.c_n_k(_N, i)))); - } - } - std::sort(sectors.begin(), sectors.end(), std::less()); - for(auto const& e : sectors) { - _sectors.push(e); - } + friend std::ostream& operator<<(std::ostream& o, const Sector& c) { + return o << " (nup+ndown: " << c._n << ") size: " << c._size; } - void set_sector(const NSymmetry::Sector §or) { - _current_sector = sector; - init(); - } + Sector(int n, std::size_t size) : _n(n), _size(size) {} - const Sector §or() const { - return _current_sector; - } + int n() const { return _n; } + std::size_t size() const { return _size; } - virtual bool next_state() override { - long long res = 0; - if (_ind >= _current_sector.size()) { - return false; - } - res = next_basis(_N, _current_sector.n(), _totstate); - _ind++; - state() = res; - return true; - } + void print(std::ostream& out) const { out << _n; } + void print() const { print(std::cout); } - virtual int index(long long st) override { - return _comb.c_n_k(_N, _current_sector.n()) - num(st, _N, _current_sector.n()) - 1; + bool operator<(const Sector& s) const { + return _size < s._size || (_size == s._size && _n < s._n); } + bool operator>(const Sector& s) const { return s < *this; } - virtual void reset() override { - state() = 0ll; - _first = true; - _ind = 0; - } - - virtual void init() override { - reset(); - _comb.init_state(_current_sector.n(), _totstate); - } - - virtual bool next_sector() override { - if (_sectors.empty()) - return false; - _current_sector = _sectors.front(); - _sectors.pop(); - return true; - } - - const Combination &comb() const { - return _comb; - } - - std::queue §ors() { - return _sectors; - } - - bool can_create_particle(int spin) override { - return _current_sector.n() < _N - 1; - } - - bool can_destroy_particle(int spin) override { - return _current_sector.n() > 0; - } + private: + int _n; + std::size_t _size; + }; - protected: - int _ind; - std::vector < int > _totstate; - int _N; - bool _first; - Combination _comb; - - int next_basis(int n, int k, std::vector < int > &old) { - int res = 0; - if (_first) { - _comb.init_state(k, old); - _first = false; - //pass - } else { - _comb.next_combination(n, k, old); + explicit NSymmetry(int N, const std::vector& sector_list = {}) + : Symmetry(), _N(N), _totstate(N, 0), _current_sector(-1, 0), _comb(N) { + populate_sectors(sector_list); + } + + NSymmetry(const Parameters& p, const std::vector& sector_list = {}) + : NSymmetry(2 * p.nsites, sector_list) {} + + void set_sector(const Sector& s) { _current_sector = s; init(); } + const Sector& sector() const { return _current_sector; } + + bool next_state() override { + if (_ind >= static_cast(_current_sector.size())) return false; + long long res = next_basis(_N, _current_sector.n(), _totstate); + ++_ind; + state() = res; + return true; + } + + int index(long long st) override { + return _comb.c_n_k(_N, _current_sector.n()) - num(st, _N, _current_sector.n()) - 1; + } + + void reset() override { + state() = 0ll; + _first = true; + _ind = 0; + } + + void init() override { + reset(); + _comb.init_state(_current_sector.n(), _totstate); + } + + bool next_sector() override { + if (_sectors.empty()) return false; + _current_sector = _sectors.front(); + _sectors.pop(); + return true; + } + + const Combination& comb() const { return _comb; } + std::queue& sectors() { return _sectors; } + + bool can_create_particle(int /*spin*/) override { + return _current_sector.n() < _N - 1; + } + bool can_destroy_particle(int /*spin*/) override { + return _current_sector.n() > 0; + } + + protected: + int _ind = 0; + std::vector _totstate; + int _N; + bool _first = true; + Combination _comb; + + int next_basis(int n, int k, std::vector& old) { + int res = 0; + if (_first) { _comb.init_state(k, old); _first = false; } + else { _comb.next_combination(n, k, old); } + for (int i = 0; i < k; ++i) res += (1 << old[i]); + return res; + } + + inline int num(long long b, int n, int m) const { + int res = 0; + if (((b & (1ll << (_N - n))) == 0) && ((n - 1) > 0) && (m > 0) && (m < n)) + res = num(b, n - 1, m); + else if (((n - 1) > 0) && (m > 0) && (m < n)) + res = _comb.c_n_k(n - 1, m) + num(b, n - 1, m - 1); + return res; + } + + private: + void populate_sectors(const std::vector& sector_list) { + std::vector sectors; + if (!sector_list.empty()) { + for (int n : sector_list) { + sectors.emplace_back(n, static_cast(_comb.c_n_k(_N, n))); } - for (int i = 0; i < k; i++) { - res += (1 << old[i]); + } else { + for (int i = 0; i <= _N; ++i) { + sectors.emplace_back(i, static_cast(_comb.c_n_k(_N, i))); } - return res; } + std::sort(sectors.begin(), sectors.end(), std::less()); + for (const auto& e : sectors) _sectors.push(e); + } - inline int num(long long b, int n, int m) const { - int res = 0; - if (((b & (1ll << (_N - n))) == 0) and ((n - 1) > 0) and (m > 0) and (m < n)) - res = num(b, n - 1, m); - else if (((n - 1) > 0) and (m > 0) and (m < n)) - res = _comb.c_n_k(n - 1, m) + num(b, n - 1, m - 1); - return res; - } + Sector _current_sector; + std::queue _sectors; + }; - private: - NSymmetry::Sector _current_sector; - std::queue < NSymmetry::Sector > _sectors; - }; - } } -#endif //EDLIB_NSYMMETRY_H +#endif diff --git a/include/edlib/Parameters.h b/include/edlib/Parameters.h new file mode 100644 index 0000000..189564b --- /dev/null +++ b/include/edlib/Parameters.h @@ -0,0 +1,34 @@ +#ifndef EDLIB_PARAMETERS_H +#define EDLIB_PARAMETERS_H + +#include +#include + +namespace edlib { + + struct Parameters { + int nsites = 4; + int nspins = 2; + + bool arpack_sector = false; + int arpack_nev = 2; + int arpack_ncv = 0; + + std::size_t storage_max_dim = 5000; + std::size_t storage_max_size = 70000; + bool eigenvalues_only = false; + int spinstorage_orbital_number = 1; + + int lanc_nomega = 32; + double lanc_emin = -3.0; + double lanc_emax = 3.0; + int lanc_nlanc = 100; + double lanc_beta = 10.0; + double lanc_boltzmann_cutoff = 1e-12; + + int siam_norbitals = 1; + }; + +} + +#endif diff --git a/include/edlib/SOCRSStorage.h b/include/edlib/SOCRSStorage.h index 10122ad..9720360 100644 --- a/include/edlib/SOCRSStorage.h +++ b/include/edlib/SOCRSStorage.h @@ -1,331 +1,268 @@ -// -// Created by iskakoff on 28/07/16. -// - #ifndef EDLIB_SOCRSSTORAGE_H #define EDLIB_SOCRSSTORAGE_H -#include +#include +#include #include +#include +#include +#include +#include + #ifdef _OPENMP #include #endif -#include "Storage.h" -namespace EDLib { - namespace Storage { +#include "edlib/Parameters.h" +#include "edlib/Storage.h" + +namespace edlib { + + /** + * Spin-orbital-coupled CRS storage: holds diagonal explicitly and stores + * off-diagonal column indices + signs in a compact bitmap. Matrix-vector + * product re-evaluates state values on the fly. + */ + template + class SOCRSStorage : public Storage { + public: + using Model = ModelType; + using prec = typename ModelType::precision; + using Storage::n; + using Storage::ntot; - template - class SOCRSStorage : public Storage < typename ModelType::precision > { - typedef typename ModelType::precision prec; - public: - typedef ModelType Model; - using Storage < prec >::n; - using Storage < prec >::ntot; #ifdef USE_MPI - SOCRSStorage(alps::params &p, Model &m, alps::mpi::communicator &comm) : Storage < prec >(p, comm), + SOCRSStorage(const Parameters& p, Model& m, MPI_Comm comm) + : Storage(p, comm), #else - SOCRSStorage(alps::params &p, Model &m) : Storage < prec >(p), + SOCRSStorage(const Parameters& p, Model& m) + : Storage(p), #endif - _max_size(p["storage.MAX_SIZE"]), + _max_size(p.storage_max_size), + _max_dim (p.storage_max_dim), #ifdef _OPENMP - _nthreads(omp_get_max_threads()), + _nthreads(omp_get_max_threads()), #else - _nthreads(1), + _nthreads(1), #endif - _row_offset(_nthreads + 1), _vind_offset(_nthreads + 1), - _vind(_nthreads), _vind_byte(_nthreads), _vind_bit(_nthreads), _vind_start(_nthreads), - _max_dim(p["storage.MAX_DIM"]), _model(m) { - /** init what you need from parameters*/ - col_ind.assign(_max_size, 0); - // XXX I don't trust myself about this one: - signs.assign(std::ceil(_max_size / sizeof(char)), 1); - dvalues.assign(_max_dim, prec(0.0)); - }; + _row_offset (_nthreads + 1), + _vind_offset(_nthreads + 1), + _vind (_nthreads), + _vind_byte (_nthreads), + _vind_bit (_nthreads), + _vind_start (_nthreads), + _model(m) { + col_ind.assign(_max_size, 0); + signs .assign(static_cast(std::ceil(double(_max_size) / sizeof(char))), 1); + dvalues.assign(_max_dim, prec(0)); + } - virtual void av(prec *v, prec *w, int n, bool clear = true) { - _model.symmetry().init(); + void av(prec* v, prec* w, int n_local, bool clear = true) override { + _model.symmetry().init(); #ifdef _OPENMP #pragma omp parallel - { - int myid = omp_get_thread_num(); + { + int myid = omp_get_thread_num(); #else - int myid = 0; + int myid = 0; #endif - size_t _vind = _vind_offset[myid]; - size_t _vind_byte = _vind / sizeof(char); - size_t _vind_bit = _vind % sizeof(char); - // Iteration over rows. - for(int i = _row_offset[myid]; (i < _row_offset[myid + 1]) && (i < n); ++i){ - long long nst = _model.symmetry().state_by_index(i); - // Diagonal contribution. - w[i] = dvalues[i] * v[i] + (clear ? 0.0 : w[i]); - // Offdiagonal contribution. - // Iteration over columns(unordered). - for (int kkk = 0; kkk < _model.T_states().size(); ++kkk) { - int test = _model.valid(_model.T_states()[kkk], nst); - // If transition between states corresponding to row and column is possible, calculate the offdiagonal element. - w[i] += test * _model.T_states()[kkk].value() * (1 - 2 * ((signs[_vind_byte] >> _vind_bit) & 1)) * v[col_ind[_vind]]; - _vind_bit += test; - _vind_byte += _vind_bit / sizeof(char); - _vind_bit %= sizeof(char); - _vind += test; - } - for (int kkk = 0; kkk < _model.V_states().size(); ++kkk) { - int test = _model.valid(_model.V_states()[kkk], nst); - // If transition between states corresponding to row and column is possible, calculate the offdiagonal element. - w[i] += test * _model.V_states()[kkk].value() * (1 - 2 * ((signs[_vind_byte] >> _vind_bit) & 1)) * v[col_ind[_vind]]; - _vind_bit += test; - _vind_byte += _vind_bit / sizeof(char); - _vind_bit %= sizeof(char); - _vind += test; - } + std::size_t vind = _vind_offset[myid]; + std::size_t vind_byte = vind / sizeof(char); + std::size_t vind_bit = vind % sizeof(char); + for (int i = _row_offset[myid]; (i < (int)_row_offset[myid + 1]) && (i < n_local); ++i) { + long long nst = _model.symmetry().state_by_index(i); + w[i] = dvalues[i] * v[i] + (clear ? prec(0) : w[i]); + // T_states + for (std::size_t kkk = 0; kkk < _model.T_states().size(); ++kkk) { + int test = _model.valid(_model.T_states()[kkk], nst); + w[i] += test * _model.T_states()[kkk].value() + * (1 - 2 * ((signs[vind_byte] >> vind_bit) & 1)) * v[col_ind[vind]]; + vind_bit += test; + vind_byte += vind_bit / sizeof(char); + vind_bit %= sizeof(char); + vind += test; + } + // V_states + for (std::size_t kkk = 0; kkk < _model.V_states().size(); ++kkk) { + int test = _model.valid(_model.V_states()[kkk], nst); + w[i] += test * _model.V_states()[kkk].value() + * (1 - 2 * ((signs[vind_byte] >> vind_bit) & 1)) * v[col_ind[vind]]; + vind_bit += test; + vind_byte += vind_bit / sizeof(char); + vind_bit %= sizeof(char); + vind += test; } -#ifdef _OPENMP } -#endif +#ifdef _OPENMP } +#endif + } - void init() { - _model.symmetry().init(); + void init() { _model.symmetry().init(); } + + void reset() { + _model.symmetry().init(); + const std::size_t sector_size = _model.symmetry().sector().size(); + if (sector_size > _max_dim) { + std::stringstream s; + s << "SOCRSStorage: sector requests more memory than allocated. " + "Increase storage.MAX_DIM. Requested " << sector_size + << ", allocated " << _max_dim << "."; + throw std::runtime_error(s.str()); } - void reset() { - _model.symmetry().init(); - size_t sector_size = _model.symmetry().sector().size(); - if (sector_size > _max_dim) { - std::stringstream s; - s << "New sector request more memory than allocated. Increase MAX_DIM parameter. Requested " << sector_size << ", allocated " << _max_dim << "."; - throw std::runtime_error(s.str().c_str()); - } - if (sector_size * (_model.T_states().size() + _model.V_states().size()) > _max_size) { - std::stringstream s; - s << "New sector request more memory than allocated. Increase MAX_SIZE parameter. Requested " << sector_size * (_model.T_states().size() + _model.V_states().size()) << ", allocated " << _max_size << "."; - throw std::runtime_error(s.str().c_str()); - } - for(int myid = 0; myid < _nthreads; ++myid){ - _vind[myid] = 0; - _vind_byte[myid] = 0; - _vind_bit[myid] = 0; - } - ntot() = sector_size; - n() = ntot(); + const std::size_t nnz_estimate = sector_size + * (_model.T_states().size() + _model.V_states().size()); + if (nnz_estimate > _max_size) { + std::stringstream s; + s << "SOCRSStorage: sector requests more memory than allocated. " + "Increase storage.MAX_SIZE. Requested " << nnz_estimate + << ", allocated " << _max_size << "."; + throw std::runtime_error(s.str()); + } + for (int t = 0; t < _nthreads; ++t) { + _vind[t] = 0; + _vind_byte[t] = 0; + _vind_bit[t] = 0; } + ntot() = static_cast(sector_size); + n() = ntot(); + } + + void fill() { + reset(); + const std::size_t sector_size = _model.symmetry().sector().size(); + const int step = static_cast(std::floor(double(sector_size) / _nthreads)); + for (int i = 0; i <= _nthreads; ++i) _row_offset[i] = step * i; + int more = static_cast(sector_size) - static_cast(_row_offset[_nthreads]); + for (int i = 0; i < more; ++i) _row_offset[i] += i; + for (int i = more; i <= _nthreads; ++i) _row_offset[i] += more; + for (int t = 0; t <= _nthreads; ++t) + _vind_offset[t] = (_model.T_states().size() + _model.V_states().size()) * _row_offset[t]; - void fill() { - reset(); - int i = 0; - long long k = 0; - int isign = 0; - // Size chunks equally. - int step = (int)std::floor(_model.symmetry().sector().size() / _nthreads); - for (int i = 0; i <= _nthreads; i++){ - _row_offset[i] = step * i; - } - // Put the rest into some of the first threads. - int more = _model.symmetry().sector().size() - _row_offset[_nthreads]; - for (int i = 0; i < more; i++){ - _row_offset[i] += i; - } - for (int i = more; i <= _nthreads; i++){ - _row_offset[i] += more; - } - for(int myid = 0; myid <= _nthreads; ++myid){ - _vind_offset[myid] = (_model.T_states().size() + _model.V_states().size()) * _row_offset[myid]; - } #ifdef _OPENMP #pragma omp parallel - { - int myid = omp_get_thread_num(); + { + int myid = omp_get_thread_num(); #else - int myid = 0; + int myid = 0; #endif -// Variant: serial, but more compact. -// for (int myid = 0; myid < _nthreads; ++myid){ - _vind[myid] = _vind_offset[myid]; - _vind_byte[myid] = _vind[myid] / sizeof(char); - _vind_bit[myid] = _vind[myid] % sizeof(char); - for (int i = _row_offset[myid]; i < _row_offset[myid + 1]; ++i) { - long long nst = _model.symmetry().state_by_index(i); - // Compute diagonal element for current i state - addDiagonal(i, _model.diagonal(nst), myid); - // non-diagonal terms calculation - off_diagonal < decltype(_model.T_states()) >(nst, i, _model.T_states(), myid); - off_diagonal < decltype(_model.V_states()) >(nst, i, _model.V_states(), myid); - } -// _vind_offset[myid + 1] = _vind[myid]; -#ifdef _OPENMP - } -#endif -// } - } - - void print() { - // See: av(). - // Each row of the matrix is first restored from the arrays. - std::vector < prec > line(n(), prec(0.0)); - _model.symmetry().init(); - std::cout << std::setprecision(2) << std::fixed; - std::cout << "["; - for (int myid = 0; myid < _nthreads; ++myid) { - size_t _vind = _vind_offset[myid]; - size_t _vind_byte = _vind / sizeof(char); - size_t _vind_bit = _vind % sizeof(char); - for (int i = _row_offset[myid]; i < _row_offset[myid + 1]; ++i) { - _model.symmetry().next_state(); - long long nst = _model.symmetry().state(); - std::fill(line.begin(), line.end(), prec(0.0)); - line[i] = dvalues[i]; - for (int kkk = 0; kkk < _model.T_states().size(); ++kkk) { - int test = _model.valid(_model.T_states()[kkk], nst); - line[col_ind[_vind]] += test * _model.T_states()[kkk].value() * (1 - 2 * ((signs[_vind_byte] >> _vind_bit) & 1)); - _vind_bit += test; - _vind_byte += _vind_bit / sizeof(char); - _vind_bit %= sizeof(char); - _vind += test; - } - for (int kkk = 0; kkk < _model.V_states().size(); ++kkk) { - int test = _model.valid(_model.V_states()[kkk], nst); - line[col_ind[_vind]] += test * _model.V_states()[kkk].value() * (1 - 2 * ((signs[_vind_byte] >> _vind_bit) & 1)); - _vind_bit += test; - _vind_byte += _vind_bit / sizeof(char); - _vind_bit %= sizeof(char); - _vind += test; - } - std::cout << "["; - for (int j = 0; j < n(); ++j) { - std::cout << std::setw(6) << line[j] << (j == n() - 1 ? "" : ", "); - } - std::cout << "]" << (i == n() - 1 ? "" : ", \n"); - } - std::cout << "]" << std::endl; + _vind[myid] = _vind_offset[myid]; + _vind_byte[myid] = _vind[myid] / sizeof(char); + _vind_bit[myid] = _vind[myid] % sizeof(char); + for (int i = _row_offset[myid]; i < (int)_row_offset[myid + 1]; ++i) { + long long nst = _model.symmetry().state_by_index(i); + addDiagonal(i, _model.diagonal(nst), myid); + off_diagonal(nst, i, _model.T_states(), myid); + off_diagonal(nst, i, _model.V_states(), myid); } +#ifdef _OPENMP } +#endif + } - virtual void zero_eigenapair() { - Storage < prec >::eigenvalues().resize(1); - Storage < prec >::eigenvalues()[0] = dvalues[0]; - Storage < prec >::eigenvectors().assign(1, std::vector < prec >(1, prec(1.0))); - } + void zero_eigenapair() override { + this->eigenvalues().resize(1); + this->eigenvalues()[0] = dvalues[0]; + this->eigenvectors().assign(1, std::vector(1, prec(1))); + } - size_t vector_size(typename Model::Sector sector) { - return sector.size(); - } + std::size_t vector_size(typename Model::Sector sector) const { return sector.size(); } #ifdef USE_MPI - prec vv(const std::vector & v, const std::vector & w, MPI_Comm com) { - return vv(v, w); - } + prec vv(const std::vector& v, const std::vector& w, MPI_Comm /*com*/) const { + return vv(v, w); + } #endif + prec vv(const std::vector& v, const std::vector& w) const { + prec alf = prec(0); + for (std::size_t k = 0; k < v.size(); ++k) alf += w[k] * v[k]; + return alf; + } - prec vv(const std::vector & v, const std::vector & w) { - prec alf = prec(0.0); - for (int k = 0; k < v.size(); ++k) { - alf += w[k] * v[k]; + void a_adag(int iii, const std::vector& invec, std::vector& outvec, + const typename Model::Sector& next_sec, bool a) { + long long k; + int sign; + int i = 0; + while (_model.symmetry().next_state()) { + long long nst = _model.symmetry().state(); + if (_model.checkState(nst, iii, _model.max_total_electrons()) == (a ? 1 : 0)) { + if (a) _model.a (iii, nst, k, sign); + else _model.adag(iii, nst, k, sign); + int i1 = _model.symmetry().index(k, next_sec); + outvec[i1] = sign * invec[i]; } - return alf; - } - - void a_adag(int iii, const std::vector < prec > &invec, std::vector < prec > &outvec, const typename Model::Sector& next_sec, bool a) { - long long k; - int sign; - int i = 0; - while (_model.symmetry().next_state()) { - long long nst = _model.symmetry().state(); - if (_model.checkState(nst, iii, _model.max_total_electrons()) == (a ? 1 : 0)) { - if(a) _model.a(iii, nst, k, sign); - else _model.adag(iii, nst, k, sign); - int i1 = _model.symmetry().index(k, next_sec); - outvec[i1] = sign * invec[i]; - } - ++i; - }; + ++i; } + } #ifdef _OPENMP - int &nprocs() { return _nthreads; } + int& nprocs() { return _nthreads; } #endif - void constant_shift(prec shift) { - std::transform( dvalues.begin(), dvalues.end(), dvalues.begin(), [shift](prec x) { return x + shift; }); - } - - private: - // Internal storage structure - std::vector < prec > dvalues; - std::vector < int > col_ind; - std::vector < char > signs; - - // the maximum sizes of all the objects - size_t _max_size; - size_t _max_dim; - - // number of OMP threads, "1" for serial mode. - int _nthreads; - // OMP chunks offset - std::vector < size_t > _row_offset; - std::vector < size_t > _vind_offset; - // internal indicies, only used for filling - std::vector < size_t > _vind; - // start of current row, used for checks - std::vector < size_t > _vind_start; - // bit and byte of the bitmap corresponding to CRS index - std::vector < size_t > _vind_bit; - std::vector < size_t > _vind_byte; - - // Hubbard model parameters - Model &_model; + void constant_shift(prec shift) { + std::transform(dvalues.begin(), dvalues.end(), dvalues.begin(), + [shift](prec x) { return x + shift; }); + } + private: + void addDiagonal(int i, prec v, int chunk) { + dvalues[i] = v; + _vind_start[chunk] = _vind[chunk]; + } - template - inline void off_diagonal(long long nst, int i, T_states& states, int chunk) { - long long k = 0; - int isign = 0; - for (int kkk = 0; kkk < states.size(); ++kkk) { - if (_model.valid(states[kkk], nst)) { - _model.set(states[kkk], nst, k, isign); - int k_index = _model.symmetry().index(k); - addElement(i, k_index, states[kkk].value(), isign, chunk); - } + void addElement(int i, int j, prec /*t*/, int sign, int chunk) { + if (i == j) { + throw std::logic_error("SOCRSStorage::addElement: cannot add diagonal; use addDiagonal"); + } + for (std::size_t k = _vind_start[chunk]; k < _vind[chunk]; ++k) { + if (col_ind[k] == j) { + throw std::logic_error("SOCRSStorage::addElement: column collision"); } - }; - - /** - * Add diagonal H(i,i) element with value v. - */ - void inline addDiagonal(int i, prec v, int chunk) { - dvalues[i] = v; - _vind_start[chunk] = _vind[chunk]; } + if (_vind[chunk] >= _vind_offset[chunk + 1]) { + throw std::runtime_error("SOCRSStorage: sector requests more memory than allocated. Increase storage.MAX_SIZE."); + } + col_ind[_vind[chunk]] = j; + signs[_vind_byte[chunk]] &= ~(1ll << _vind_bit[chunk]); + signs[_vind_byte[chunk]] |= (sign < 0) ? (1ll << _vind_bit[chunk]) : 0; + ++_vind_bit[chunk]; + ++_vind[chunk]; + _vind_byte[chunk] += _vind_bit[chunk] / sizeof(char); + _vind_bit [chunk] %= sizeof(char); + } - /** - * Add off-diagonal H(i,j) element with value t (discarded here, restored in av) and Fermi sign. - */ - void inline addElement(const int &i, int j, prec t, int sign, int chunk) { - if (i == j) { - throw std::logic_error("Attempt to use addElement() to add diagonal element. Use addDiagonal() instead!"); - } - // It is an error to add the element (i, j) twice. - for (size_t iii = _vind_start[chunk]; iii < _vind[chunk]; iii++) { - if (col_ind[iii] == j) { - throw std::logic_error("Collision. Check a, adag, numState, ninv_value!"); - } - } - if (_vind[chunk] >= _vind_offset[chunk+1]) { - std::stringstream s; - s << "Current sector request more memory than allocated. Increase MAX_SIZE parameter."; - throw std::runtime_error(s.str().c_str()); + template + void off_diagonal(long long nst, int i, const TStates& states, int chunk) { + long long k = 0; + int isign = 0; + for (std::size_t kkk = 0; kkk < states.size(); ++kkk) { + if (_model.valid(states[kkk], nst)) { + _model.set(states[kkk], nst, k, isign); + int k_index = _model.symmetry().index(k); + addElement(i, k_index, states[kkk].value(), isign, chunk); } - // Store sign in CRS-like array, one bit per sign. - col_ind[_vind[chunk]] = j; - signs[_vind_byte[chunk]] &= ~(1ll << _vind_bit[chunk]); - signs[_vind_byte[chunk]] |= sign < 0 ? 1ll << _vind_bit[chunk] : 0; - ++_vind_bit[chunk]; - ++_vind[chunk]; - _vind_byte[chunk] += _vind_bit[chunk] / sizeof(char); - _vind_bit[chunk] %= sizeof(char); } + } + + std::vector dvalues; + std::vector col_ind; + std::vector signs; + + std::size_t _max_size; + std::size_t _max_dim; + + int _nthreads; + std::vector _row_offset; + std::vector _vind_offset; + std::vector _vind; + std::vector _vind_byte; + std::vector _vind_bit; + std::vector _vind_start; + + Model& _model; + }; - }; - } } -#endif //EDLIB_SOCRSSTORAGE_H + +#endif diff --git a/include/edlib/SingleImpurityAndersonModel.h b/include/edlib/SingleImpurityAndersonModel.h index 3c0aaf8..c7a70e2 100644 --- a/include/edlib/SingleImpurityAndersonModel.h +++ b/include/edlib/SingleImpurityAndersonModel.h @@ -1,271 +1,228 @@ -// -// Created by iskakoff on 23/08/16. -// - #ifndef EDLIB_SINGLEIMPURITYANDERSONMODEL_H #define EDLIB_SINGLEIMPURITYANDERSONMODEL_H -#include -#include -#include "FermionicModel.h" -#include "CommonUtils.h" - -namespace EDLib { - namespace Model { - namespace SingleImpurityAnderson { - template - class InnerState { - public: - virtual int valid(long long, int) const {return 0;}; - virtual void set(long long,long long&, int&, int) const {}; - int inline checkState(long long nst, int im, int Ns) const { - return (int)((nst & (1ll << (2*Ns - 1 - im))) >> (2*Ns - 1 - im)); - } +#include +#include +#include +#include +#include +#include - /** - * Anihilate particle - * \param i [in] - site to anihilate particle - * \param jold [in] - current state - * \param k [out] - resulting state - * \param isign [out] - fermionic sign - * \param Ip [in] - number of fermionic sites - */ - void inline a(int i, long long jold, long long &k, int &isign, int Ip) const { - long long sign = 0; - for (int ll = 0; ll < i; ++ll) { - sign += ((jold & (1ll << (Ip - ll - 1))) != 0) ? 1 : 0; - } - isign = (sign % 2) == 0 ? 1 : -1; - k = jold - (1ll << (Ip - i - 1)); - } +#include "edlib/CommonUtils.h" +#include "edlib/FermionicModel.h" +#include "edlib/Gf.h" +#include "edlib/Mesh.h" +#include "edlib/Parameters.h" +#include "edlib/SzSymmetry.h" - /** - * Create particle - * \param i [in] - site to create particle - * \param jold [in] - current state - * \param k [out] - resulting state - * \param isign [out] - fermionic sign - * \param Ip [in] - number of fermionic sites - */ - void inline adag(int i, long long jold, long long &k, int &isign, int Ip) const { - long long sign = 0; - for (int ll = 0; ll < i; ++ll) { - sign += ((jold & (1ll << (Ip - ll - 1))) != 0) ? 1 : 0; - } - isign = (sign % 2) == 0 ? 1 : -1; - k = jold + (1ll << (Ip - i - 1)); - } +namespace edlib { - virtual inline prec value() const { return 0.0; } - }; - template - class InnerHybridizationState : public InnerState { - using InnerState::checkState; - using InnerState::a; - using InnerState::adag; - public: - InnerHybridizationState(int ii, int jj, int spin, prec val) : _indicies(ii, jj), _spin(spin), _value(val) {}; + namespace siam { - const inline std::pair < int, int > &indicies() const { return _indicies; } + /** + * Base inner state. Concrete derived states encode either a hopping / + * hybridisation transition (HybridisationInnerState) or a 4-operator + * interaction term (InteractionInnerState). + */ + template + class InnerState { + public: + virtual ~InnerState() = default; + // Non-pure defaults to mirror legacy alpscore-based hierarchy. Concrete + // derived states (HybridisationInnerState / InteractionInnerState) override + // them; extensions that add their own dispatch (HolsteinAnderson etc.) can + // ignore the 4-arg form and provide additional overloads of their own. + virtual int valid(long long, int) const { return 0; } + virtual void set (long long, long long&, int&, int) const {} + virtual Prec value() const { return Prec(0); } - virtual inline prec value() const { return _value; } + protected: + static int checkState(long long nst, int im, int Ns) { + return static_cast((nst & (1ll << (2 * Ns - 1 - im))) >> (2 * Ns - 1 - im)); + } + static void a(int i, long long jold, long long& k, int& isign, int Ip) { + long long sign = 0; + for (int ll = 0; ll < i; ++ll) sign += ((jold & (1ll << (Ip - ll - 1))) != 0) ? 1 : 0; + isign = (sign % 2) == 0 ? 1 : -1; + k = jold - (1ll << (Ip - i - 1)); + } + static void adag(int i, long long jold, long long& k, int& isign, int Ip) { + long long sign = 0; + for (int ll = 0; ll < i; ++ll) sign += ((jold & (1ll << (Ip - ll - 1))) != 0) ? 1 : 0; + isign = (sign % 2) == 0 ? 1 : -1; + k = jold + (1ll << (Ip - i - 1)); + } + }; - inline int spin() const { return _spin; } - virtual int valid(long long nst, int Ns) const { - return (checkState(nst, _indicies.first + _spin * Ns, Ns) * (1 - checkState(nst, _indicies.second + _spin * Ns, Ns))); - } - virtual void set(long long nst,long long&k, int&sign, int Ns) const { - long long k1, k2; - int isign1, isign2; - a(_indicies.first + _spin * Ns, nst, k1, isign1, 2*Ns); - adag(_indicies.second + _spin * Ns, k1, k2, isign2, 2*Ns); - k = k2; - sign = isign1 * isign2; - } + template + class HybridisationInnerState : public InnerState { + public: + HybridisationInnerState(int ii, int jj, int spin, Prec val) + : _indicies(ii, jj), _spin(spin), _value(val) {} - private: - std::pair < int, int > _indicies; - int _spin; - prec _value; - }; - template - class InnerInteractionState : public InnerState { - using InnerState::checkState; - using InnerState::a; - using InnerState::adag; - public: - InnerInteractionState(int i, int j, int k, int l, int sigma, int sigmaprime, prec U) : - _i(i), _j(j), _k(k), _l(l), _sigma(sigma), _sigmaprime(sigmaprime), _U(U) {} - - int i() const { - return _i; - } + const std::pair& indicies() const { return _indicies; } + int spin() const { return _spin; } + Prec value() const override { return _value; } - int j() const { - return _j; - } + int valid(long long nst, int Ns) const override { + return InnerState::checkState(nst, _indicies.first + _spin * Ns, Ns) + * (1 - InnerState::checkState(nst, _indicies.second + _spin * Ns, Ns)); + } + using InnerState::set; + void set(long long nst, long long& k, int& sign, int Ns) const override { + long long k1, k2; + int isign1, isign2; + InnerState::a (_indicies.first + _spin * Ns, nst, k1, isign1, 2 * Ns); + InnerState::adag(_indicies.second + _spin * Ns, k1, k2, isign2, 2 * Ns); + k = k2; + sign = isign1 * isign2; + } - int k() const { - return _k; - } + private: + std::pair _indicies; + int _spin; + Prec _value; + }; - int l() const { - return _l; - } + template + class InteractionInnerState : public InnerState { + public: + InteractionInnerState(int i, int j, int k, int l, int sigma, int sigma_prime, Prec U) + : _i(i), _j(j), _k(k), _l(l), + _sigma(sigma), _sigmaprime(sigma_prime), _U(U) {} - prec U() const { - return _U; - } - /** - * @brief Check the possible transition - * - * Evaluate the following four operators product: - * a^*_i a^*_j a_l a_k | nst> - * - * @param nst - current state - * @param Ns - number of fermionic sites - * @return One if transition is possible - */ - virtual int valid(long long nst, int Ns) const { - int Ip = 2*Ns; - if(checkState(nst, _k + _sigma * Ns, Ns) != 0) { - long long k3 = nst - (1ll << (Ip - 1 - _k - _sigma * Ns)); - if (checkState(k3, _l + _sigmaprime * Ns, Ns) != 0) { - long long k4 = k3 - (1ll << (Ip - 1 - _l - _sigmaprime * Ns)); - if (checkState(k4, _j + _sigmaprime * Ns, Ns) == 0) { - long long k2 = k4 | (1ll << (Ip - 1 - _j - _sigmaprime * Ns)); - return (1-checkState(k2, _i + _sigma * Ns, Ns)); - } + int i() const { return _i; } + int j() const { return _j; } + int k() const { return _k; } + int l() const { return _l; } + Prec U() const { return _U; } + + Prec value() const override { return Prec(0.5) * _U; } + + using InnerState::set; + int valid(long long nst, int Ns) const override { + int Ip = 2 * Ns; + if (InnerState::checkState(nst, _k + _sigma * Ns, Ns) != 0) { + long long k3 = nst - (1ll << (Ip - 1 - _k - _sigma * Ns)); + if (InnerState::checkState(k3, _l + _sigmaprime * Ns, Ns) != 0) { + long long k4 = k3 - (1ll << (Ip - 1 - _l - _sigmaprime * Ns)); + if (InnerState::checkState(k4, _j + _sigmaprime * Ns, Ns) == 0) { + long long k2 = k4 | (1ll << (Ip - 1 - _j - _sigmaprime * Ns)); + return (1 - InnerState::checkState(k2, _i + _sigma * Ns, Ns)); } } - return 0; - } - /** - * @brief Computes the new state for inter-orbital Coulomb transition - * - * |k> = a^*_i a^*_j a_l a_k | nst> - * - * @param nst - current state - * @param k - next state - * @param sign - sign of transition - * @param Ns - number of fermionic sites - */ - virtual void set(long long nst,long long&k, int&sign, int Ns) const { - long long k1, k2, k3, k4; - int isign1, isign2, isign3, isign4; - a(_k + _sigma * Ns, nst, k3, isign1, 2*Ns); - a(_l + _sigmaprime * Ns, k3, k4, isign2, 2*Ns); - adag(_j + _sigmaprime * Ns, k4, k2, isign3, 2*Ns); - adag(_i + _sigma * Ns, k2, k1, isign4, 2*Ns); - k = k1; - sign = isign1 * isign2*isign3*isign4; } + return 0; + } + void set(long long nst, long long& k, int& sign, int Ns) const override { + long long k1, k2, k3, k4; + int isign1, isign2, isign3, isign4; + InnerState::a (_k + _sigma * Ns, nst, k3, isign1, 2 * Ns); + InnerState::a (_l + _sigmaprime * Ns, k3, k4, isign2, 2 * Ns); + InnerState::adag(_j + _sigmaprime * Ns, k4, k2, isign3, 2 * Ns); + InnerState::adag(_i + _sigma * Ns, k2, k1, isign4, 2 * Ns); + k = k1; + sign = isign1 * isign2 * isign3 * isign4; + } + + private: + int _i, _j, _k, _l; + int _sigma, _sigmaprime; + Prec _U; + }; + + } + + template + class SingleImpurityAndersonModel : public FermionicModel { + public: + using precision = Prec; + using SYMMETRY = SzSymmetry; + using St = siam::InnerState; + using HSt = siam::HybridisationInnerState; + using USt = siam::InteractionInnerState; + using Sector = typename SzSymmetry::Sector; - /** - * @brief Return the interaction strength for current spin-orbital combination - * @return U_{ijkl} - */ - virtual inline prec value() const { return 0.5*_U; } - - private: - int _i; - int _j; - int _k; - int _l; - int _sigma; - int _sigmaprime; - prec _U; - }; - } /** - * Single multi-orbital Impurity Anderson Model class + * Caller-supplied bath / model data. Dimensions checked against + * Parameters::nsites, ::nspins and ::siam_norbitals at construction. + * + * ml = p.siam_norbitals (number of impurity orbitals) + * Nk = p.nsites - ml (number of bath levels) * - * @tparam prec - floating point precision + * Vk [ml][Nk][nspins] impurity-bath hybridisation + * H0 [ml][ml][nspins] non-interacting impurity Hamiltonian + * Epsk [Nk][nspins] bath energies + * U Gf shape {nspins, nspins, ml, ml, ml, ml} */ - template - class SingleImpurityAndersonModel : public FermionicModel { - public: - typedef prec precision; - typedef typename Symmetry::SzSymmetry SYMMETRY; - typedef typename SingleImpurityAnderson::InnerState St; - typedef typename SingleImpurityAnderson::InnerHybridizationState HSt; - typedef typename SingleImpurityAnderson::InnerInteractionState USt; - typedef typename Symmetry::SzSymmetry::Sector Sector; - - SingleImpurityAndersonModel(alps::params &p): FermionicModel(p), _symmetry(p), _ml(p["siam.NORBITALS"]), - _Vk(p["siam.NORBITALS"], std::vector >()), - _H0(p["siam.NORBITALS"], std::vector >(p["siam.NORBITALS"], std::vector(_ms, 0.0))), - _bath_ind(p["siam.NORBITALS"], 0) { - std::string input = p["INPUT_FILE"]; - alps::hdf5::archive input_data(input.c_str(), "r"); - if (_ml > _Ns) { - throw std::invalid_argument("Incorrect values for the total number of sites and the number of orbitals. Please check input file."); - } - if(_ms != 2) { - throw std::invalid_argument("Incorrect values for the number of spins. Please check input file."); - } - _Ip = _ms * _Ns; - input_data >> alps::make_pvp("Bath/Epsk/values", _Epsk); - if(_Epsk.size() != _Ns - _ml) { - throw std::invalid_argument("Total number of state does not equal to sum of the total number of bath levels and the number of impurity orbitals"); - } - for (int im = 0; im < _ml; ++im) { - std::stringstream s; - s<<"Bath/Vk_"<> alps::make_pvp(s.str().c_str(), _Vk[im]); - s.str(""); - s<<"H0_"<> alps::make_pvp(s.str().c_str(), _H0[im]); - } - input_data >> alps::make_pvp("mu", _xmu); - input_data >> alps::make_pvp("interaction/values", _U); - input_data.close(); - if(_U.shape()[2] != _ml) { - throw std::invalid_argument("Incorrect number of orbitals. Please check input file."); - } - for(int im = 0; im< _ml; ++im ){ - if(_H0[im].size()>_ml) { - throw std::invalid_argument("Inter orbital hoppings array dimension are bigger than number of impurity orbitals"); - } - } - // interorbital hoppings - for (int im = 0; im < _ml; ++im) { - for (int jm = 0; jm < im; ++jm) { - if(im == jm) {continue;} - for (int is = 0; is < _ms; ++is) { - if (std::abs(_H0[im][jm][is]) > 1e-10) { - _T_states.push_back(HSt(im, jm, is, _H0[im][jm][is])); - _T_states.push_back(HSt(jm, im, is, _H0[im][jm][is])); - } + struct ModelData { + std::vector>> Vk; + std::vector>> H0; + std::vector> Epsk; + Prec mu = Prec(0); + Gf U; + std::vector> sectors; + }; + + SingleImpurityAndersonModel(const Parameters& p, const ModelData& bath) + : FermionicModel(p), + _symmetry(p, bath.sectors), + _ml(p.siam_norbitals), + _Vk(bath.Vk), + _H0(bath.H0), + _Epsk(bath.Epsk), + _xmu(bath.mu), + _U(bath.U) { + if (p.nspins != 2) { + throw std::invalid_argument("SingleImpurityAndersonModel: NSPINS must be 2"); + } + if (_ml > _Ns) { + throw std::invalid_argument("SingleImpurityAndersonModel: siam.NORBITALS exceeds NSITES"); + } + const int Nk = _Ns - _ml; + if (static_cast(_Epsk.size()) != Nk) { + throw std::invalid_argument("SingleImpurityAndersonModel: Epsk size must equal nsites - siam.NORBITALS"); + } + if (_U.shape(2) != _ml || _U.shape(3) != _ml || _U.shape(4) != _ml || _U.shape(5) != _ml + || _U.shape(0) != p.nspins || _U.shape(1) != p.nspins) { + throw std::invalid_argument("SingleImpurityAndersonModel: U must have shape [nspins,nspins,ml,ml,ml,ml]"); + } + + // Inter-orbital hoppings within the impurity cluster + for (int im = 0; im < _ml; ++im) { + for (int jm = 0; jm < im; ++jm) { + for (int is = 0; is < _ms; ++is) { + if (std::abs(_H0[im][jm][is]) > 1e-10) { + _T_states.emplace_back(im, jm, is, _H0[im][jm][is]); + _T_states.emplace_back(jm, im, is, _H0[im][jm][is]); } } } - // fill hybridization part - for (int im = 0; im < _ml; ++im) { - for (int ik = 0; ik < _Vk[im].size(); ++ik) { - for (int is = 0; is < _ms; ++is) { - if (std::abs(_Vk[im][ik][is]) > 1e-10) { - int imk = ik + _ml; - _T_states.push_back(HSt(im, imk, is, _Vk[im][ik][is])); - _T_states.push_back(HSt(imk, im, is, _Vk[im][ik][is])); - } + } + + // Impurity-bath hybridisation + for (int im = 0; im < _ml; ++im) { + for (int ik = 0; ik < static_cast(_Vk[im].size()); ++ik) { + for (int is = 0; is < _ms; ++is) { + if (std::abs(_Vk[im][ik][is]) > 1e-10) { + int imk = ik + _ml; + _T_states.emplace_back(im, imk, is, _Vk[im][ik][is]); + _T_states.emplace_back(imk, im, is, _Vk[im][ik][is]); } } } - // fill off-diagonal interaction term - for (int is1 = 0; is1 < _ms; ++is1) { - for (int is2 = 0; is2 < _ms; ++is2) { - for (int i = 0; i < _ml; ++i) { - for (int j = 0; j < _ml; ++j) { - for (int k = 0; k < _ml; ++k) { - for (int l = 0; l < _ml; ++l) { - // skip density-density contribution - if ( ( (i == l) && (j == k) && (is1==is2) ) || ( (i == k) && (j == l) ) ) { - continue; - } - if(std::abs(_U(is1,is2,i,j,k,l)) != 0.0) { - _V_states.push_back(USt(i, j, k, l, is1, is2, _U(is1,is2,i,j,k,l))); - } + } + + // Off-diagonal interaction terms (skip density-density which is in diagonal) + for (int is1 = 0; is1 < _ms; ++is1) { + for (int is2 = 0; is2 < _ms; ++is2) { + for (int i = 0; i < _ml; ++i) { + for (int j = 0; j < _ml; ++j) { + for (int k = 0; k < _ml; ++k) { + for (int l = 0; l < _ml; ++l) { + if (((i == l) && (j == k) && (is1 == is2)) || ((i == k) && (j == l))) continue; + if (std::abs(_U(is1, is2, i, j, k, l)) != Prec(0)) { + _V_states.emplace_back(i, j, k, l, is1, is2, _U(is1, is2, i, j, k, l)); } } } @@ -273,149 +230,101 @@ namespace EDLib { } } } + } - /** - * computes diagonal contribution for the specific occupation basis state - * @param state - occupation basis state - * @return - */ - inline const precision diagonal(long long state) const { - precision xtemp = 0.0; + inline Prec diagonal(long long state) const { + Prec xtemp = Prec(0); + for (int is = 0; is < _ms; ++is) { + for (int ik = 0; ik < static_cast(_Epsk.size()); ++ik) { + int ikm = ik + _ml; + xtemp += _Epsk[ik][is] * checkState(state, ikm + is * _Ns, _Ip); + } + } + for (int im = 0; im < _ml; ++im) { for (int is = 0; is < _ms; ++is) { - for (int ik = 0; ik < _Epsk.size(); ++ik) { - int ikm = ik + _ml; - xtemp+=(_Epsk[ik][is] * checkState(state, ikm + is * _Ns, _Ip)); - } + xtemp += (_H0[im][im][is] - _xmu) * checkState(state, im + is * _Ns, _Ip); } - for (int im = 0; im < _ml; ++im) { - for (int is = 0; is < _ms; ++is) { - xtemp += (_H0[im][im][is] - _xmu) * checkState(state, im + is * _Ns, _Ip); - } + for (int is = 0; is < _ms; ++is) { + xtemp += Prec(0.5) * _U(is, is, im, im, im, im) + * checkState(state, im, _Ip) * checkState(state, im + _Ns, _Ip); + } + for (int jm = 0; jm < _ml; ++jm) { for (int is = 0; is < _ms; ++is) { - xtemp += 0.5*_U(is,is,im,im,im,im) * checkState(state, im, _Ip) * checkState(state, im + _Ns, _Ip); - } - for(int jm = 0; jm < _ml; ++jm) { - for(int is = 0; is< _ms; ++is) - if(im!=jm) { - xtemp += 0.5 * (_U(is,is,im,jm,im,jm) - _U(is,is,im,jm,jm,im)) * checkState(state, im + is*_Ns, _Ip) * checkState(state, jm + is*_Ns, _Ip); - xtemp += 0.5 * (_U(is,1-is,im,jm,im,jm)) * checkState(state, im + is*_Ns, _Ip) * checkState(state, jm + (1-is)*_Ns, _Ip); + if (im != jm) { + xtemp += Prec(0.5) * (_U(is, is, im, jm, im, jm) - _U(is, is, im, jm, jm, im)) + * checkState(state, im + is * _Ns, _Ip) * checkState(state, jm + is * _Ns, _Ip); + xtemp += Prec(0.5) * _U(is, 1 - is, im, jm, im, jm) + * checkState(state, im + is * _Ns, _Ip) * checkState(state, jm + (1 - is) * _Ns, _Ip); } } } - return xtemp; } + return xtemp; + } - /** - * @deprecated - */ - inline long long interacting_states(long long nst) { - long long up = 0; - for (int is = 0; is < _ms; ++is) { - up = nst >> (_Ip - _ml); - } - long long down = (nst & ((1ll<<_Ns) - 1))>>(_Ns-_ml); - return (up<<_ml) + down; - } + inline int valid(const St& state, long long nst) const { return state.valid(nst, _Ns); } + inline Prec set (const St& state, long long nst, long long& k, int& sign) const { + state.set(nst, k, sign, _Ns); + return state.value(); + } - /** - * Check that state describes valid transition for basis vector |nst> - * @param state - transition state - * @param nst - occupation basis vector - * @return 1 or 0 wheater the transition is possible or not respectively - */ - inline int valid(const St &state, long long nst) { - return state.valid(nst, _Ns); - } + /// @deprecated kept for API parity + inline long long interacting_states(long long nst) const { + long long up = nst >> (_Ip - _ml); + long long down = (nst & ((1ll << _Ns) - 1)) >> (_Ns - _ml); + return (up << _ml) + down; + } - /** - * Perform transition "state" from state |nst> to |k> - * @param state - * @param nst - * @param k - * @param sign - * @return contribution to off-diagonal Hamilonian element - */ - inline precision set(const St &state, long long nst, long long &k, int &sign) { - state.set(nst, k, sign, _Ns); - return state.value(); - } + SzSymmetry& symmetry() { return _symmetry; } + const SzSymmetry& symmetry() const { return _symmetry; } - /** - * Model symmetry type - */ - SYMMETRY &symmetry() { - return _symmetry; - } + int interacting_orbitals() const { return _ml; } - /** - * @return Hybridization transitions - */ - inline const std::vector& T_states() const { - return _T_states; - } - /** - * @return Off-diagonal Coulomb transitions - */ - inline const std::vector& V_states() const { - return _V_states; - } + const std::vector& T_states() const { return _T_states; } + const std::vector& V_states() const { return _V_states; } - /** - * Only impurity orbitals have Coulomb interaction. Bath is non-interacting. - * @return number of impurity orbitals - */ - int interacting_orbitals() const { - return _ml; - } + /// Read-only accessors used by legacy shims. + const std::vector>>& hybridisation() const { return _Vk; } + const std::vector>>& H0() const { return _H0; } + const std::vector>& bath_energies() const { return _Epsk; } + Prec chem_potential() const { return _xmu; } - /** - * Computes bare Green's function - * @tparam Mesh - Green's function frequency mesh - * @param bare_gf - Bare Green's function container - * @param beta - inverse temperature - */ - template - void bare_greens_function(alps::gf::three_index_gf, Mesh, alps::gf::index_mesh, alps::gf::index_mesh >& bare_gf, double beta) { - for(int iw = 0; iw< bare_gf.mesh1().points().size(); ++iw) { - typename Mesh::index_type w(iw); - for (int im: bare_gf.mesh2().points()) { - for (int is : bare_gf.mesh3().points()) { - std::complex delta = 0; - for(int ik = 0; ik< _Epsk.size(); ++ik) { - delta += _Vk[im][ik][is]*_Vk[im][ik][is]/(common::freq_point(iw, bare_gf.mesh1(), beta) - _Epsk[ik][is]); - } - bare_gf(w, alps::gf::index_mesh::index_type(im), alps::gf::index_mesh::index_type(is)) = 1.0/(common::freq_point(iw, bare_gf.mesh1(), beta) - _H0[im][im][is] - delta); + template + void bare_greens_function(Gf, 3>& bare_gf, + const Mesh& mesh, + double beta) const { + const int n_omega = bare_gf.shape(0); + const int n_orb = bare_gf.shape(1); + const int n_spin = bare_gf.shape(2); + for (int iw = 0; iw < n_omega; ++iw) { + std::complex z = freq_point(iw, mesh, beta); + for (int im = 0; im < n_orb; ++im) { + for (int is = 0; is < n_spin; ++is) { + std::complex delta(0.0, 0.0); + for (int ik = 0; ik < static_cast(_Epsk.size()); ++ik) { + delta += static_cast(_Vk[im][ik][is]) * static_cast(_Vk[im][ik][is]) + / (z - static_cast(_Epsk[ik][is])); } + bare_gf(iw, im, is) = 1.0 + / (z - static_cast(_H0[im][im][is]) - delta); } } } + } - private: - /// model symmetry - SYMMETRY _symmetry; - /// number of impurity orbitals - int _ml; - /// Coulomb interaction matrix - alps::numerics::tensor _U; - /// chemical potential - precision _xmu; - /// number of bath states - int _Nk; - /// Non-interacting impurity Hamiltonian - std::vector < std::vector < std::vector < precision > > > _H0; - /// Hybridization with bath - std::vector < std::vector < std::vector < precision > > > _Vk; - /// Bath energy levels - std::vector < std::vector < precision > > _Epsk; - /// indices for bath - std::vector _bath_ind; - - /// Kinetic part of the off-diagonal Hamiltonian elements - std::vector < HSt > _T_states; - /// Interaction part of the off-diagonal Hamiltonian elements - std::vector < USt > _V_states; - }; + private: + SzSymmetry _symmetry; + int _ml; + std::vector>> _Vk; + std::vector>> _H0; + std::vector> _Epsk; + Prec _xmu; + Gf _U; + + std::vector _T_states; + std::vector _V_states; + }; - } } -#endif //EDLIB_SINGLEIMPURITYANDERSONMODEL_H + +#endif diff --git a/include/edlib/SpinResolvedStorage.h b/include/edlib/SpinResolvedStorage.h index 5d2dcfc..f2ae513 100644 --- a/include/edlib/SpinResolvedStorage.h +++ b/include/edlib/SpinResolvedStorage.h @@ -1,109 +1,115 @@ -// -// Created by iskakoff on 23/08/16. -// - #ifndef EDLIB_SPINRESOLVEDSTORAGE_H #define EDLIB_SPINRESOLVEDSTORAGE_H +#include #include +#include +#include #include #include - -#include "Storage.h" -#include "SzSymmetry.h" -#include "NSymmetry.h" -#include "CRSMatrix.h" - -namespace EDLib { - namespace Storage { - - template - class SpinResolvedStorage : public Storage < typename ModelType::precision > { - static_assert(std::is_base_of::value, "Model have wrong symmetry."); - public: - typedef ModelType Model; - typedef typename Model::precision prec; - public: - using Storage < prec >::n; - using Storage < prec >::ntot; +#include + +#include "edlib/CRSMatrix.h" +#include "edlib/MpiTypes.h" +#include "edlib/NSymmetry.h" +#include "edlib/Parameters.h" +#include "edlib/Storage.h" +#include "edlib/SzSymmetry.h" + +namespace edlib { + + /** + * Spin-resolved storage: keeps hopping matrices for each spin channel + * separately, and the (smaller) off-diagonal interaction part. MPI-aware + * via RMA on the spin-up channel. + */ + template + class SpinResolvedStorage : public Storage { + static_assert(std::is_base_of::value, + "SpinResolvedStorage: model must use SzSymmetry"); + public: + using Model = ModelType; + using prec = typename ModelType::precision; + using Matrix = CRSMatrix; + using Storage::n; + using Storage::ntot; #ifdef USE_MPI - using Storage < prec >::comm; - using Storage < prec >::broadcast_evals; + using Storage::comm; + using Storage::broadcast_evals; #endif - using Storage < prec >::prepare_work_arrays; - using Storage < prec >::finalize; - - - - typedef CRSMatrix < prec > Matrix; + using Storage::prepare_work_arrays; + using Storage::finalize; #ifdef USE_MPI - SpinResolvedStorage(alps::params &p, Model &m, MPI_Comm comm) : Storage < prec >(p, comm), _comm(comm), _run_comm(MPI_COMM_NULL), _win(MPI_WIN_NULL), _model(m),_interaction_size(m.interacting_orbitals()), - _Ns(p["NSITES"].as()), _ms(p["NSPINS"].as()), _up_symmetry(p["NSITES"].as()), - _down_symmetry(p["NSITES"].as()) { - MPI_Comm_size(_comm, &_nprocs); - MPI_Comm_rank(_comm, &_myid); - } + SpinResolvedStorage(const Parameters& p, Model& m, MPI_Comm comm) + : Storage(p, comm), + _model(m), + _up_symmetry (p.nsites), + _down_symmetry(p.nsites), + _interaction_size(m.interacting_orbitals()), + _Ns(p.nsites), _ms(p.nspins), + _comm(comm), _run_comm(MPI_COMM_NULL), _win(MPI_WIN_NULL) { + MPI_Comm_size(_comm, &_nprocs); + MPI_Comm_rank(_comm, &_myid); + } #else - SpinResolvedStorage(alps::params &p, Model &m) : Storage < prec >(p), _model(m), _interaction_size(m.interacting_orbitals()), - _Ns(p["NSITES"]), _ms(p["NSPINS"]), _up_symmetry(int(p["NSITES"])), _down_symmetry(int(p["NSITES"])) {} + SpinResolvedStorage(const Parameters& p, Model& m) + : Storage(p), + _model(m), + _up_symmetry (p.nsites), + _down_symmetry(p.nsites), + _interaction_size(m.interacting_orbitals()), + _Ns(p.nsites), _ms(p.nspins) {} #endif - virtual void zero_eigenapair() { - Storage < prec >::eigenvalues().resize(1); - Storage < prec >::eigenvalues()[0] = _diagonal[0]; - Storage < prec >::eigenvectors().assign(1, std::vector < prec >(1, prec(1.0))); - } + void zero_eigenapair() override { + this->eigenvalues().resize(1); + this->eigenvalues()[0] = _diagonal[0]; + this->eigenvectors().assign(1, std::vector(1, prec(1))); + } - virtual void av(prec *v, prec *w, int n, bool clear = true) { + void av(prec* v, prec* w, int n_local, bool clear = true) override { #ifdef USE_MPI - // Initialize inter-processor communications - // we collect all data from the remote processes into _vecval array - MPI_Win_fence(MPI_MODE_NOPRECEDE, _win); - for(int i = 0; i<_procs.size(); ++i) { - if(_procs[i]!=0) - MPI_Get(&_vecval[_proc_offset[i]], _proc_size[i], alps::mpi::detail::mpi_type(), i, _loc_min[i], _proc_size[i], alps::mpi::detail::mpi_type(), _win); + MPI_Win_fence(MPI_MODE_NOPRECEDE, _win); + for (std::size_t i = 0; i < _procs.size(); ++i) { + if (_procs[i] != 0) { + MPI_Get(&_vecval[_proc_offset[i]], _proc_size[i], mpi_type(), + static_cast(i), _loc_min[i], _proc_size[i], mpi_type(), _win); } + } #endif - // Iteration over diagonal contribution. - for (int i = 0; i < n; ++i) { - // Diagonal contribution. - w[i] = _diagonal[i] * v[i] + (clear ? 0.0 : w[i]); - } - // Off-diagonal contribution. Process spin-down contribution for each spin-up block - // iterate over spin-up blocks - for (int k = 0; k < _up_size; ++k) { - // Iteration over rows. - for (int i = 0; i < _down_symmetry.sector().size(); ++i) { - // Iteration over columns. - for (int j = H_down.row_ptr()[i]; j < H_down.row_ptr()[i + 1]; ++j) { - w[i + k * _down_symmetry.sector().size()] += H_down.values()[j] * v[H_down.col_ind()[j] + k * _down_symmetry.sector().size()]; - } + for (int i = 0; i < n_local; ++i) { + w[i] = _diagonal[i] * v[i] + (clear ? prec(0) : w[i]); + } + // spin-down hopping + for (int k = 0; k < (int)_up_size; ++k) { + for (int i = 0; i < (int)_down_symmetry.sector().size(); ++i) { + for (int j = H_down.row_ptr()[i]; j < H_down.row_ptr()[i + 1]; ++j) { + w[i + k * _down_symmetry.sector().size()] += + H_down.values()[j] * v[H_down.col_ind()[j] + k * _down_symmetry.sector().size()]; } } + } #ifdef USE_MPI - // Waiting for the data to be received - MPI_Win_fence(MPI_MODE_NOSUCCEED | MPI_MODE_NOSTORE, _win); + MPI_Win_fence(MPI_MODE_NOSUCCEED | MPI_MODE_NOSTORE, _win); #endif - // Process spin-up hopping contribution - // Iteration over rows. - for (int i = 0; i < _up_size; ++i) { - // Iteration over columns. - for (int j = H_up.row_ptr()[i+_up_shift]; j < H_up.row_ptr()[i + _up_shift + 1]; ++j) { - for (int k = 0; k < _down_symmetry.sector().size(); ++k) { + // spin-up hopping + for (int i = 0; i < (int)_up_size; ++i) { + for (int j = H_up.row_ptr()[i + _up_shift]; j < H_up.row_ptr()[i + _up_shift + 1]; ++j) { + for (int k = 0; k < (int)_down_symmetry.sector().size(); ++k) { #ifdef USE_MPI - w[i * _down_symmetry.sector().size() + k] += H_up.values()[j] * _vecval[H_up.col_ind()[j] * _down_symmetry.sector().size() + k]; + w[i * _down_symmetry.sector().size() + k] += + H_up.values()[j] * _vecval[H_up.col_ind()[j] * _down_symmetry.sector().size() + k]; #else - w[i * _down_symmetry.sector().size() + k] += H_up.values()[j] * v[H_up.col_ind()[j] * _down_symmetry.sector().size() + k]; + w[i * _down_symmetry.sector().size() + k] += + H_up.values()[j] * v[H_up.col_ind()[j] * _down_symmetry.sector().size() + k]; #endif - } } } - - // Off-diagonal interaction contribution - // Check that we have off-diagonal interaction elements - if(H_loc.row_ptr().size()!=0) - for (size_t i = _int_start; i < n; ++i) { + } + // off-diagonal interaction + if (!H_loc.row_ptr().empty()) { + for (std::size_t i = _int_start; i < (std::size_t)n_local; ++i) { for (int j = H_loc.row_ptr()[i]; j < H_loc.row_ptr()[i + 1]; ++j) { #ifdef USE_MPI w[i] += H_loc.values()[j] * _vecval[H_loc.col_ind()[j]]; @@ -113,524 +119,354 @@ namespace EDLib { } } } - - /** - * Fill Hamiltonain matrix for current symmetry sector - */ - void fill() { - reset(); - if(n()==0) { - // Do nothing if the matrix size is zero; - return; - } - // Hopping term - // fill off-diagonal matrix for each spin - fill_spin(_up_symmetry, _Ns, H_up); - fill_spin(_down_symmetry, 0, H_down); - // fill local part; - int isign; - long long k; - _int_start = _locsize; - for(size_t i =0; i<_locsize; ++i) { - _model.symmetry().next_state(); - long long nst = _model.symmetry().state(); - // add diagonal contribution - _diagonal[i] = _model.diagonal(nst); - // Add off-diagonal contribution from interaction term - if(_model.V_states().size() > 0) { - for (int kkk = 0; kkk < _model.V_states().size(); ++kkk) { - if (_model.valid(_model.V_states()[kkk], nst)) { - _int_start = std::min(i, _int_start); - _model.set(_model.V_states()[kkk], nst, k, isign); - int j = _model.symmetry().index(k); - H_loc.addElement(i, j, _model.V_states()[kkk].value(), isign); - } + } + + void fill() { + reset(); + if (n() == 0) return; + + fill_spin(_up_symmetry, _Ns, H_up); + fill_spin(_down_symmetry, 0, H_down); + + int isign; + long long k; + _int_start = _locsize; + for (std::size_t i = 0; i < _locsize; ++i) { + _model.symmetry().next_state(); + long long nst = _model.symmetry().state(); + _diagonal[i] = _model.diagonal(nst); + if (_model.V_states().size() > 0) { + for (std::size_t kkk = 0; kkk < _model.V_states().size(); ++kkk) { + if (_model.valid(_model.V_states()[kkk], nst)) { + _int_start = std::min(i, _int_start); + _model.set(_model.V_states()[kkk], nst, k, isign); + int j = _model.symmetry().index(k); + H_loc.addElement(i, j, _model.V_states()[kkk].value(), isign); } - H_loc.endLine(i); } + H_loc.endLine(i); } + } #ifdef USE_MPI - find_neighbours(); + find_neighbours(); #endif - } - - void print() { - // nothing to do - } + } - void init() { - _model.symmetry().init(); + void init() { + _model.symmetry().init(); #ifdef USE_MPI - _model.symmetry().set_offset(_offset); + _model.symmetry().set_offset(_offset); #endif - } - - /** - * Reset storage and symmetry object for the current symmetry sector. - * Update MPI communicator if necessary, set local dimensions size, setup working arrays size. - */ - void reset(int t = 0) { - _model.symmetry().init(); - // For spin-resolved storage we should guarantee that model is spin-symmetric. - const Symmetry::SzSymmetry &symmetry = static_cast(_model.symmetry()); - // get current symmetry sector - const Symmetry::SzSymmetry::Sector §or = symmetry.sector(); - // get symmetries for each spin - _up_symmetry.set_sector(Symmetry::NSymmetry::Sector(sector.nup(), symmetry.comb().c_n_k(_Ns, sector.nup()))); - _down_symmetry.set_sector(Symmetry::NSymmetry::Sector(sector.ndown(), symmetry.comb().c_n_k(_Ns, sector.ndown()))); - // get each spin dimensions - size_t up_size = _up_symmetry.sector().size(); - size_t down_size = _down_symmetry.sector().size(); - // init hopping Hamiltonians for each spin - H_up.init(up_size, 100); - H_down.init(down_size, 100); + } + + void reset(int /*t*/ = 0) { + _model.symmetry().init(); + const SzSymmetry& symmetry = static_cast(_model.symmetry()); + const typename SzSymmetry::Sector& sector = symmetry.sector(); + _up_symmetry .set_sector(NSymmetry::Sector(sector.nup(), symmetry.comb().c_n_k(_Ns, sector.nup()))); + _down_symmetry.set_sector(NSymmetry::Sector(sector.ndown(), symmetry.comb().c_n_k(_Ns, sector.ndown()))); + const std::size_t up_size = _up_symmetry .sector().size(); + const std::size_t down_size = _down_symmetry.sector().size(); + H_up .init(up_size, 100); + H_down.init(down_size, 100); #ifdef USE_MPI - // release communicator if needed - if(_comm != _run_comm && _run_comm != MPI_COMM_NULL) { - MPI_Comm_free(&_run_comm); - }; - // communicator to be used during diagonalization - // check that there is data for the current CPU - int color = _myid < up_size ? 1 : 0;//MPI_UNDEFINED; - // Create new MPI communicator for the processors with defined color - MPI_Comm_split(_comm, color, (color == 1 ? _myid : 0), &_run_comm); - // update working communicator - if(color == 1) { - // there is data for current CPU - // get CPU rank and size for recently created working communicator - int myid; - MPI_Comm_rank(_run_comm,&myid); - int size; - MPI_Comm_size(_run_comm,&size); - // compute the size of local arrays and the offset from the beginning - int locsize = up_size / size; - if ((up_size % size) > myid) { - locsize += 1; - _offset = myid * locsize* _down_symmetry.sector().size(); - } else { - _offset = (myid* locsize + (up_size % size))* _down_symmetry.sector().size(); - } - // local dimension for the spin-up hopping Hamiltonian matrix - _up_size = locsize; - // offet in the spin-up channel - _up_shift = _offset / down_size; - // local dimension for the whole Hamiltonian matrix - _locsize = locsize * down_size; - // apply offset to the symmetry object to generate proper configuration state - _model.symmetry().set_offset(_offset); - // inter processor communactions - // array with flags for each processor - _procs.assign(size, 0); - // offset of each processor in the vecval array - _proc_offset.assign(size, 0); - // data amount to be received - _proc_size.assign(size, 0); - // index of the first element to be received - _loc_min.assign(size, 0); + if (_comm != _run_comm && _run_comm != MPI_COMM_NULL) MPI_Comm_free(&_run_comm); + int color = _myid < (int)up_size ? 1 : 0; + MPI_Comm_split(_comm, color, (color == 1 ? _myid : 0), &_run_comm); + if (color == 1) { + int myid; MPI_Comm_rank(_run_comm, &myid); + int size; MPI_Comm_size(_run_comm, &size); + std::size_t locsize = up_size / size; + if ((up_size % size) > (std::size_t)myid) { + locsize += 1; + _offset = myid * locsize * _down_symmetry.sector().size(); } else { - _up_size = 0; - _locsize=0; + _offset = (myid * locsize + (up_size % size)) * _down_symmetry.sector().size(); } + _up_size = locsize; + _up_shift = _offset / down_size; + _locsize = locsize * down_size; + _model.symmetry().set_offset(_offset); + _procs .assign(size, 0); + _proc_offset .assign(size, 0); + _proc_size .assign(size, 0); + _loc_min .assign(size, 0); + } else { + _up_size = 0; + _locsize = 0; + } #else - _locsize = up_size*down_size; - _up_size = up_size; - _up_shift = 0; + _locsize = up_size * down_size; + _up_size = up_size; + _up_shift = 0; #endif - // allocate memory for local Hamiltonian - // density-density contribution - _diagonal.assign(_locsize, prec(0.0)); - // off-diagonal contribution - if(_model.V_states().size()>0) { - H_loc.init(_locsize, 3); - } - // local dimension of the Hamiltonian matrix - n() = _locsize; - // total dimension of the Hamiltonian matrix - ntot() = sector.size(); - } - - /** - * Compute local dimension for the specific sector - * @param sector -- symmetry sector to compute dimension - * @return size of local vector for the specific symmetry sector - */ - size_t vector_size(typename Model::Sector sector) { - // get the total dimension for the current symmetry sector - size_t sector_size = sector.size(); + _diagonal.assign(_locsize, prec(0)); + if (_model.V_states().size() > 0) H_loc.init(_locsize, 3); + n() = static_cast(_locsize); + ntot() = static_cast(sector.size()); + } + + std::size_t vector_size(typename Model::Sector sector) const { + const std::size_t sector_size = sector.size(); #ifdef USE_MPI - int myid,size; - // get rank and size for current CPU in the global communicator - MPI_Comm_rank(_comm,&myid); - MPI_Comm_size(_comm,&size); - size_t up_size = _model.symmetry().comb().c_n_k(_Ns, sector.nup()); - size_t down_size = sector_size / up_size; - size = up_size>size ? size : up_size; - // there is no data for current CPU - if(myid >= size) { - return 0; - } - // compute local dimension for the spin-up channel - size_t locsize = up_size / size; - if ((up_size % size) > myid) { - locsize += 1; - } - // compute and return the total dimension - return locsize * down_size; + int myid, size; + MPI_Comm_rank(_comm, &myid); + MPI_Comm_size(_comm, &size); + std::size_t up_size = _model.symmetry().comb().c_n_k(_Ns, sector.nup()); + std::size_t down_size = sector_size / up_size; + size = (int)(up_size > (std::size_t)size ? size : up_size); + if (myid >= size) return 0; + std::size_t locsize = up_size / size; + if ((up_size % size) > (std::size_t)myid) locsize += 1; + return locsize * down_size; #else - return sector_size; + return sector_size; #endif - } - - /** - * Perform a_i or a_i^* operation on |invec> - * - * @param i -- position to destroy(create) electron - * @param invec -- input vector - * @param outvec -- output vector - * @param next_sec -- the resulting Symmetry sector - * @param a -- destroy particle if true, create otherwise - */ - void a_adag(int i, const std::vector < prec > &invec, std::vector < prec > &outvec, const typename Model::Sector& next_sec, bool a) { - // local dimension of current vector - size_t locsize = invec.size(); - // maximal local dimension of current vector - size_t locsize_max = locsize; - // dimension of the resulting symmetry sector - size_t next_size = next_sec.size(); - // dimension of spin-up channel Hamiltonian matrix - size_t up_size = _model.symmetry().comb().c_n_k(_Ns, next_sec.nup()); - // dimension of spin-down channel Hamiltonian matrix - size_t down_size = next_size / up_size; - long long k; - int sign; + } + + void a_adag(int i, const std::vector& invec, std::vector& outvec, + const typename Model::Sector& next_sec, bool a) { + std::size_t locsize = invec.size(); + std::size_t locsize_max = locsize; + std::size_t next_size = next_sec.size(); + std::size_t up_size = _model.symmetry().comb().c_n_k(_Ns, next_sec.nup()); + std::size_t down_size = next_size / up_size; + long long k; + int sign; #ifdef USE_MPI - MPI_Allreduce(MPI_IN_PLACE, &locsize_max, 1, alps::mpi::detail::mpi_type(), MPI_MAX, _comm); - int ci; - int cid; - int myid; - MPI_Comm_rank(_comm,&myid); - int size; - MPI_Comm_size(_comm,&size); - int t = 0; - // synchronization flag - bool fence = false; - // adjust maximal local dimension - if(_up_symmetry.sector().size()%size != 0) { - locsize_max+= _down_symmetry.sector().size(); - } - // communication buffer - std::vector buff(1000, 0.0); - // communication window - int rank; - MPI_Comm_rank(_run_comm, &rank); - MPI_Comm_size(_run_comm, &size); - size = outvec.size() > 0 ? 1 : 0; - MPI_Allreduce(MPI_IN_PLACE, &size, 1, alps::mpi::detail::mpi_type(), MPI_SUM, _comm); - MPI_Win eigwin; - MPI_Win_create(outvec.data(), sizeof(prec) * outvec.size(), sizeof(prec), MPI_INFO_NULL, _comm, &eigwin); - MPI_Win_fence(MPI_MODE_NOPRECEDE,eigwin); + MPI_Allreduce(MPI_IN_PLACE, &locsize_max, 1, mpi_type(), MPI_MAX, _comm); + int ci, cid; + int myid; MPI_Comm_rank(_comm, &myid); + int size; MPI_Comm_size(_comm, &size); + int t = 0; + bool fence = false; + if (_up_symmetry.sector().size() % size != 0) locsize_max += _down_symmetry.sector().size(); + std::vector buff(1000, prec(0)); + int rank; + MPI_Comm_rank(_run_comm, &rank); + MPI_Comm_size(_run_comm, &size); + size = outvec.size() > 0 ? 1 : 0; + MPI_Allreduce(MPI_IN_PLACE, &size, 1, mpi_type(), MPI_SUM, _comm); + MPI_Win eigwin; + MPI_Win_create(outvec.data(), sizeof(prec) * outvec.size(), sizeof(prec), + MPI_INFO_NULL, _comm, &eigwin); + MPI_Win_fence(MPI_MODE_NOPRECEDE, eigwin); #endif - // iterate over local part of vector - for (int ind = 0; ind < locsize_max; ++ind) { + for (std::size_t ind = 0; ind < locsize_max; ++ind) { #ifdef USE_MPI - // perfrom one-sided communication - if(fence) - MPI_Win_fence(MPI_MODE_NOPRECEDE,eigwin); - fence=false; + if (fence) MPI_Win_fence(MPI_MODE_NOPRECEDE, eigwin); + fence = false; #endif - // try to destroy (create) particle if index is within boundary - if(ind(),cid,ci,1,alps::mpi::detail::mpi_type(), eigwin); - } + calcIndex(ci, cid, i1, up_size, down_size, size); + if (myid == cid) { + outvec[ci] = sign * invec[ind]; + } else { + buff[t] = sign * invec[ind]; + MPI_Put(&buff[t], 1, mpi_type(), cid, ci, 1, mpi_type(), eigwin); + } #else - /// update array - outvec[i1] = sign * invec[ind]; + outvec[i1] = sign * invec[ind]; #endif - - } } -#ifdef USE_MPI - // check buffer boundary - if((++t)==buff.size()){fence=true;t=0;} - // synchronize if necessary - if(fence) - MPI_Win_fence(MPI_MODE_NOSUCCEED | MPI_MODE_NOSTORE,eigwin); -#endif } #ifdef USE_MPI - if(!fence) MPI_Win_fence(MPI_MODE_NOSUCCEED | MPI_MODE_NOSTORE, eigwin); - MPI_Win_free(&eigwin); + if ((++t) == (int)buff.size()) { fence = true; t = 0; } + if (fence) MPI_Win_fence(MPI_MODE_NOSUCCEED | MPI_MODE_NOSTORE, eigwin); #endif } +#ifdef USE_MPI + if (!fence) MPI_Win_fence(MPI_MODE_NOSUCCEED | MPI_MODE_NOSTORE, eigwin); + MPI_Win_free(&eigwin); +#endif + } - - /** - * Compute product - * @param v - bra-state - * @param w - ket-state - * @return product - */ - prec vv(const std::vector & v, const std::vector & w) { + prec vv(const std::vector& v, const std::vector& w) const { #ifdef USE_MPI - return vv(v, w, comm()); + return vv(v, w, const_cast(this)->comm()); #else - prec alf = prec(0.0); - for (int k = 0; k < v.size(); ++k) { - alf += w[k] * v[k]; - } - return alf; + prec alf = prec(0); + for (std::size_t k = 0; k < v.size(); ++k) alf += w[k] * v[k]; + return alf; #endif - } + } #ifdef USE_MPI - prec vv(const std::vector & v, const std::vector & w, MPI_Comm com) { - prec alf = prec(0.0); - prec temp = prec(0.0); - for (int k = 0; k < v.size(); ++k) { - temp += w[k] * v[k]; - } - MPI_Allreduce(&temp, &alf, 1, alps::mpi::detail::mpi_type(), MPI_SUM, com); - return alf; - } - - /** - * Initialize the communication window for the *data object from the specific offset - * @param data -- input array - * @param shift -- offset in the input array - */ - virtual void prepare_work_arrays(prec * data, size_t shift = 0) { - MPI_Win_create(&data[shift], (MPI_Aint)(n() * sizeof(prec)), - (int)sizeof(prec), MPI_INFO_NULL, _run_comm, &_win); - } - - /** - * - * @return current working communicator - */ - virtual MPI_Comm comm() { - return _run_comm; - } - - /** - * Finalize current MPI execution. Broadcast eigenvalues if necessary. Release window and communicator. - * Set the global MPI communicator as the current MPI communicator. - */ - virtual int finalize(int info, bool bcast = true, bool empty = true) { - MPI_Bcast(&info,1, MPI_INT, 0, Storage::comm()); - if(info>=0) { - if(bcast) broadcast_evals(empty); - } - if(ntot() > 1 && n() > 0) { - MPI_Win_free(&_win); - } - if(_run_comm != MPI_COMM_WORLD && _run_comm != MPI_COMM_NULL) { - MPI_Comm_free(&_run_comm); - } - _run_comm = Storage < prec >::comm(); - return info; - } - - /** - * @return offset from 0 for current CPU - */ - size_t offset(){ - return _offset; - } + prec vv(const std::vector& v, const std::vector& w, MPI_Comm com) const { + prec temp = prec(0), alf = prec(0); + for (std::size_t k = 0; k < v.size(); ++k) temp += w[k] * v[k]; + MPI_Allreduce(&temp, &alf, 1, mpi_type(), MPI_SUM, com); + return alf; + } + + void prepare_work_arrays(prec* data, std::size_t shift = 0) override { + MPI_Win_create(&data[shift], (MPI_Aint)(n() * sizeof(prec)), + (int)sizeof(prec), MPI_INFO_NULL, _run_comm, &_win); + } + + MPI_Comm comm() override { return _run_comm; } + + int finalize(int info, bool bcast = true, bool empty = true) override { + MPI_Bcast(&info, 1, MPI_INT, 0, Storage::comm()); + if (info >= 0 && bcast) broadcast_evals(empty); + if (ntot() > 1 && n() > 0) MPI_Win_free(&_win); + if (_run_comm != MPI_COMM_WORLD && _run_comm != MPI_COMM_NULL) MPI_Comm_free(&_run_comm); + _run_comm = Storage::comm(); + return info; + } + + std::size_t offset() const { return _offset; } #endif - void constant_shift(prec shift) { - std::transform( _diagonal.begin(), _diagonal.end(), _diagonal.begin(), [shift](prec x){ return x + shift; }); + void constant_shift(prec shift) { + std::transform(_diagonal.begin(), _diagonal.end(), _diagonal.begin(), + [shift](prec x) { return x + shift; }); + } + + private: + void fill_spin(NSymmetry& spin_symmetry, int shift, Matrix& spin_matrix) { + long long k = 0; + int isign = 0; + int i = 0; + while (spin_symmetry.next_state()) { + long long nst = spin_symmetry.state(); + for (std::size_t kkk = 0; kkk < _model.T_states().size(); ++kkk) { + if (_model.valid(_model.T_states()[kkk], nst << shift)) { + _model.set(_model.T_states()[kkk], nst << shift, k, isign); + int j = spin_symmetry.index(k >> shift); + spin_matrix.addElement(i, j, _model.T_states()[kkk].value(), isign); + } + } + spin_matrix.endLine(i); + ++i; } - - private: - /// Current model - Model &_model; - /// Off-diagonal part of local Hamiltonian - Matrix H_loc; - /// Spin-up hopping - Matrix H_up; - /// Spin-down hopping - Matrix H_down; - - /// diagonal part - std::vector < prec > _diagonal; - /// array to store remote processes communication data - std::vector < prec > _vecval; - - /// symmetries for each spin - Symmetry::NSymmetry _up_symmetry; - Symmetry::NSymmetry _down_symmetry; - - /// - int _interaction_size; - /// The total number of electon - int _Ns; - /// Total number of spins - int _ms; - - /// size of H_up - size_t _up_size; - /// offset in spin_up channel - size_t _up_shift; - /// local size on each CPU - size_t _locsize; - /// staring index in interaction Hamiltonian - size_t _int_start; + } #ifdef USE_MPI - /// global communicator - MPI_Comm _comm; - /// current working communicator - MPI_Comm _run_comm; - /// offset for the current CPU - size_t _offset; - int _myid; - int _nprocs; - /// Inter-process communication auxiliary arrays - std::vector _proc_offset; - std::vector _procs; - std::vector _loc_min; - std::vector _proc_size; - /// MPI communication window - MPI_Win _win; - - /** - * Find neighbour CPUs for the current Hamiltonian matrix - */ - void find_neighbours() { - int ci, cid; - // size of the working communicator - int nprocs; - MPI_Comm_size(_run_comm, &nprocs); - std::vector loc_offset(nprocs, 0); - // Find smallest and largest index in the current Hamiltonian - std::vector l_loc_max(_loc_min.size(), INT_MIN); - std::vector l_loc_min(_loc_min.size(), INT_MAX); - // For the spin-up channel - for(int i = 0; i< _up_size; ++ i) { - for (int j = H_up.row_ptr()[i+_up_shift]; j < H_up.row_ptr()[i + _up_shift + 1]; ++j) { - calcIndex(ci, cid, H_up.col_ind()[j]*_down_symmetry.sector().size(), _up_symmetry.sector().size(), _down_symmetry.sector().size(), nprocs); + void find_neighbours() { + int ci, cid; + int nprocs; MPI_Comm_size(_run_comm, &nprocs); + std::vector loc_offset(nprocs, 0); + std::vector l_loc_max(_loc_min.size(), INT_MIN); + std::vector l_loc_min(_loc_min.size(), INT_MAX); + for (int i = 0; i < (int)_up_size; ++i) { + for (int j = H_up.row_ptr()[i + _up_shift]; j < H_up.row_ptr()[i + _up_shift + 1]; ++j) { + calcIndex(ci, cid, + H_up.col_ind()[j] * _down_symmetry.sector().size(), + _up_symmetry.sector().size(), _down_symmetry.sector().size(), nprocs); + l_loc_max[cid] = std::max(ci, l_loc_max[cid]); + l_loc_min[cid] = std::min(ci, l_loc_min[cid]); + if (_procs[cid] == 0) _procs[cid] = 1; + } + } + if (!H_loc.row_ptr().empty()) { + for (std::size_t i = _int_start; i < _locsize; ++i) { + for (int j = H_loc.row_ptr()[i]; j < H_loc.row_ptr()[i + 1]; ++j) { + calcIndex(ci, cid, + _down_symmetry.sector().size() * + (H_loc.col_ind()[j] / _down_symmetry.sector().size()), + _up_symmetry.sector().size(), _down_symmetry.sector().size(), nprocs); l_loc_max[cid] = std::max(ci, l_loc_max[cid]); l_loc_min[cid] = std::min(ci, l_loc_min[cid]); - if(_procs[cid]==0) {_procs[cid]=1;} - } - } - // For the off-diagonal interaction term - if(H_loc.row_ptr().size()!=0) { - for (size_t i = _int_start; i < _locsize; ++i) { - for (int j = H_loc.row_ptr()[i]; j < H_loc.row_ptr()[i + 1]; ++j) { - calcIndex(ci, cid, _down_symmetry.sector().size()*(H_loc.col_ind()[j]/_down_symmetry.sector().size()), _up_symmetry.sector().size(), _down_symmetry.sector().size(), nprocs); - l_loc_max[cid] = std::max(ci, l_loc_max[cid]); - l_loc_min[cid] = std::min(ci, l_loc_min[cid]); - if(_procs[cid]==0) {_procs[cid]=1;} - } + if (_procs[cid] == 0) _procs[cid] = 1; } } - int oset = 0; - for(int i=0; i < nprocs; i++) { - if(_procs[i]) { - _procs[i]=1; - // calculate offset for i-th CPU - _proc_offset[i]=oset * _down_symmetry.sector().size() + l_loc_min[i]; - // The index of the first element of the vector to be received from i-th CPU - _loc_min[i] = l_loc_min[i]; - int ls=_up_symmetry.sector().size()/nprocs; - if((_up_symmetry.sector().size()% nprocs) > i) { - ls++; - loc_offset[i] = (i * ls) - oset; - }else{ - loc_offset[i] = i * ls + (_up_symmetry.sector().size() % nprocs) - oset; - } - // number of elements to be received from the i-th CPU - _proc_size[i]= l_loc_max[i] - l_loc_min[i] + _down_symmetry.sector().size(); - oset+=ls; + } + int oset = 0; + for (int i = 0; i < nprocs; ++i) { + if (_procs[i]) { + _procs[i] = 1; + _proc_offset[i] = oset * _down_symmetry.sector().size() + l_loc_min[i]; + _loc_min[i] = l_loc_min[i]; + int ls = _up_symmetry.sector().size() / nprocs; + if ((_up_symmetry.sector().size() % nprocs) > (std::size_t)i) { + ls++; + loc_offset[i] = (i * ls) - oset; + } else { + loc_offset[i] = i * ls + (_up_symmetry.sector().size() % nprocs) - oset; } + _proc_size[i] = l_loc_max[i] - l_loc_min[i] + _down_symmetry.sector().size(); + oset += ls; } - // alloacte memory for the working array - _vecval.assign(oset * _down_symmetry.sector().size(), prec(0.0)); - // adjust indexes - for (int i = 0; i < _up_size; ++i) { - for (int j = H_up.row_ptr()[i + _up_shift]; j < H_up.row_ptr()[i + _up_shift + 1]; ++j) { - calcIndex(ci, cid, H_up.col_ind()[j]*_down_symmetry.sector().size(), _up_symmetry.sector().size(), _down_symmetry.sector().size(), nprocs); - H_up.col_ind()[j] -= loc_offset[cid]; - } + } + _vecval.assign(oset * _down_symmetry.sector().size(), prec(0)); + for (int i = 0; i < (int)_up_size; ++i) { + for (int j = H_up.row_ptr()[i + _up_shift]; j < H_up.row_ptr()[i + _up_shift + 1]; ++j) { + calcIndex(ci, cid, + H_up.col_ind()[j] * _down_symmetry.sector().size(), + _up_symmetry.sector().size(), _down_symmetry.sector().size(), nprocs); + H_up.col_ind()[j] -= loc_offset[cid]; } - if(H_loc.row_ptr().size()!=0) { - for (size_t i = _int_start; i < _locsize; ++i) { - for (int j = H_loc.row_ptr()[i]; j < H_loc.row_ptr()[i + 1]; ++j) { - calcIndex(ci, cid, H_loc.col_ind()[j], _up_symmetry.sector().size(), _down_symmetry.sector().size(), nprocs); - H_loc.col_ind()[j] -= loc_offset[cid]*_down_symmetry.sector().size(); - } + } + if (!H_loc.row_ptr().empty()) { + for (std::size_t i = _int_start; i < _locsize; ++i) { + for (int j = H_loc.row_ptr()[i]; j < H_loc.row_ptr()[i + 1]; ++j) { + calcIndex(ci, cid, H_loc.col_ind()[j], + _up_symmetry.sector().size(), _down_symmetry.sector().size(), nprocs); + H_loc.col_ind()[j] -= loc_offset[cid] * _down_symmetry.sector().size(); } } } - - /// Calculate local index, ci, and CPU id, cid, for the global index i - void calcIndex(int &ci, int &cid, int i) { - int size; - MPI_Comm_size(_run_comm, &size); - calcIndex(ci, cid, i*_down_symmetry.sector().size(), _up_symmetry.sector().size(), _down_symmetry.sector().size(), size); - } - void calcIndex(int &ci, int &cid, int i, size_t u_s, size_t d_s, int nprocs) { - // local variables - int tmp1, tmp2, tmp3, tmp4; - int i_rest = i % d_s; - int i_up = i / d_s; - tmp1 = u_s / nprocs + 1; - tmp2 = u_s % nprocs; - tmp3 = u_s / nprocs; - tmp4 = (i_up) - (tmp1 * tmp2); - if (i_up > (tmp1 * tmp2)) { - ci = ((tmp4%tmp3))*d_s + i_rest; - cid = (i_up - tmp2) / tmp3; - } else { - ci = (i_up%(tmp3 + 1))*d_s + i_rest; - cid = (i_up)/ (tmp3 + 1); - } + } + + void calcIndex(int& ci, int& cid, int i, std::size_t u_s, std::size_t d_s, int nprocs) const { + int i_rest = i % d_s; + int i_up = i / d_s; + int tmp1 = u_s / nprocs + 1; + int tmp2 = u_s % nprocs; + int tmp3 = u_s / nprocs; + int tmp4 = i_up - (tmp1 * tmp2); + if (i_up > (tmp1 * tmp2)) { + ci = (tmp4 % tmp3) * d_s + i_rest; + cid = (i_up - tmp2) / tmp3; + } else { + ci = (i_up % (tmp3 + 1)) * d_s + i_rest; + cid = i_up / (tmp3 + 1); } + } #endif - /** - * Fill the Hamiltonian matrix for the specific spin - * @param spin_symmetry -- current - * @param shift -- spin shift in the configuration state (0 for spin-up, and Ns for spin-down) - * @param spin_matrix -- matrix to be filled - */ - void fill_spin(Symmetry::NSymmetry &spin_symmetry, int shift, Matrix &spin_matrix) { - long long k = 0; - int isign = 0; - int i = 0; - while (spin_symmetry.next_state()) { - long long nst = spin_symmetry.state(); - for (int kkk = 0; kkk < _model.T_states().size(); ++kkk) { - if (_model.valid(_model.T_states()[kkk], nst << shift)) { - _model.set(_model.T_states()[kkk], nst << shift, k, isign); - int j = spin_symmetry.index(k >> shift); - spin_matrix.addElement(i, j, _model.T_states()[kkk].value(), isign); - } - } - spin_matrix.endLine(i); - ++i; - } - } - }; - } + Model& _model; + Matrix H_loc; + Matrix H_up; + Matrix H_down; + + std::vector _diagonal; + std::vector _vecval; + + NSymmetry _up_symmetry; + NSymmetry _down_symmetry; + + int _interaction_size; + int _Ns; + int _ms; + + std::size_t _up_size = 0; + std::size_t _up_shift = 0; + std::size_t _locsize = 0; + std::size_t _int_start = 0; + +#ifdef USE_MPI + MPI_Comm _comm; + MPI_Comm _run_comm; + std::size_t _offset = 0; + int _myid = 0; + int _nprocs = 0; + std::vector _proc_offset; + std::vector _procs; + std::vector _loc_min; + std::vector _proc_size; + MPI_Win _win; +#endif + }; + } -#endif //EDLIB_SPINRESOLVEDSTORAGE_H + +#endif diff --git a/include/edlib/StaticObservables.h b/include/edlib/StaticObservables.h index 8f657e4..d5dc69f 100644 --- a/include/edlib/StaticObservables.h +++ b/include/edlib/StaticObservables.h @@ -1,386 +1,291 @@ #ifndef EDLIB_STATICOBSERVABLES_H #define EDLIB_STATICOBSERVABLES_H -#include "EigenPair.h" - -#include - -#include -#include +#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "edlib/EigenPair.h" +#include "edlib/MpiTypes.h" +#include "edlib/Parameters.h" + +namespace edlib { -namespace EDLib { /** - * Class for calculation of the static observables. - * - * - * - * @tparam Hamiltonian - type of Hamiltonian object + * Compute thermal-averaged static observables (N, N_up, N_dn, M, D_occ, + * M_i M_j, N_eff, E) from a diagonalized Hamiltonian. */ - template + template class StaticObservables { - protected: - typedef typename Hamiltonian::ModelType::precision precision; - typedef typename Hamiltonian::ModelType::Sector sector; - public: + using precision = typename Hamiltonian::ModelType::precision; + using sector = typename Hamiltonian::ModelType::Sector; - const static std::string _N_; - const static std::string _N_UP_; - const static std::string _N_DN_; - const static std::string _M_; - const static std::string _D_OCC_; - const static std::string _N_EFF_; - const static std::string _MI_MJ_; - const static std::string _E_; + static constexpr const char* _N_ = "N"; + static constexpr const char* _N_UP_ = "N_up"; + static constexpr const char* _N_DN_ = "N_dn"; + static constexpr const char* _M_ = "M"; + static constexpr const char* _D_OCC_ = "D_occ"; + static constexpr const char* _N_EFF_ = "N_eff"; + static constexpr const char* _MI_MJ_ = "M_i M_j"; + static constexpr const char* _E_ = "E"; - /** - * Construct an object of the static observables class - * - * @param p - AlpsCore parameter object - */ - StaticObservables(alps::params &p) : - _beta(p["lanc.BETA"].as()), - _cutoff(p["lanc.BOLTZMANN_CUTOFF"]) - { - if(p["storage.EIGENVALUES_ONLY"] == 1) { - throw std::logic_error("Eigenvectors have not been computed. StaticObservables can not continue."); + explicit StaticObservables(const Parameters& p) + : _beta(static_cast(p.lanc_beta)), + _cutoff(static_cast(p.lanc_boltzmann_cutoff)) { + if (p.eigenvalues_only) { + throw std::logic_error( + "StaticObservables: eigenvectors were not computed (eigenvalues_only=true)."); } #ifdef USE_MPI - const int nitems=2; - int blocklengths[nitems] = {1, 1}; - MPI_Datatype types[nitems] = {alps::mpi::detail::mpi_type(), alps::mpi::detail::mpi_type()}; - MPI_Aint offsets[nitems]; + const int nitems = 2; + int blocklengths[nitems] = {1, 1}; + MPI_Datatype types[nitems] = { mpi_type(), mpi_type() }; + MPI_Aint offsets[nitems]; offsets[0] = offsetof(Element, ind); offsets[1] = offsetof(Element, val); MPI_Type_create_struct(nitems, blocklengths, offsets, types, &mpi_Element); MPI_Type_commit(&mpi_Element); #endif - }; + } - /** - * @brief Print static observables. - * - * @param ham - the Hamiltonian - * - * Prints all the parameters returned by calculate_static_observables(). - */ - void print_static_observables(Hamiltonian& ham, std::ostream & out){ - std::map> obs = calculate_static_observables(ham); + void print_static_observables(Hamiltonian& ham, std::ostream& out) { + auto obs = calculate_static_observables(ham); #ifdef USE_MPI - int myid; - MPI_Comm_rank(ham.comm(), &myid); - if(!myid) + int myid; MPI_Comm_rank(ham.comm(), &myid); + if (myid != 0) return; #endif - { - for(auto ivar = obs.begin(); ivar != obs.end(); ++ivar){ - out << "<" << (*ivar).first << "> = {"; - for(int i = 0; i < (*ivar).second.size(); ++i){ - if(i){ - out << ", "; - } - out << (*ivar).second[i]; - } - out << "}" << std::endl; + for (const auto& kv : obs) { + out << "<" << kv.first << "> = {"; + for (std::size_t i = 0; i < kv.second.size(); ++i) { + if (i) out << ", "; + out << kv.second[i]; } + out << "}\n"; } } - /** - * @brief Compute static observables. - * - * @param ham - the Hamiltonian - * - * Returns the following parameters for all sites: - * n: average number of electrons; - * n_up: average number of electrons with the spin up; - * n_down: average number of electrons with the spin down; - * m: average magnetic moment; - * m_i m_j: product of magnetic moments on the i-th and j-th sites; - * d_occ: average double occupancy; - * N_eff: average effective dimension of Hilbert space; - * E: total grand-canonical energy - */ - std::map> calculate_static_observables(Hamiltonian& ham){ + std::map> + calculate_static_observables(Hamiltonian& ham) { #ifdef USE_MPI - int myid; - MPI_Comm_rank(ham.storage().comm(), &myid); + int myid; MPI_Comm_rank(ham.storage().comm(), &myid); #endif + const int n_orb = ham.model().interacting_orbitals(); std::map> avg = { - {_N_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_N_UP_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_N_DN_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_M_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_D_OCC_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_MI_MJ_, std::vector(ham.model().interacting_orbitals() * ham.model().interacting_orbitals(), 0.0)}, - {_N_EFF_, std::vector(1, 0.0)}, - {_E_, std::vector(1, 0.0)} + {_N_, std::vector(n_orb, precision(0))}, + {_N_UP_, std::vector(n_orb, precision(0))}, + {_N_DN_, std::vector(n_orb, precision(0))}, + {_M_, std::vector(n_orb, precision(0))}, + {_D_OCC_, std::vector(n_orb, precision(0))}, + {_MI_MJ_, std::vector(n_orb * n_orb, precision(0))}, + {_N_EFF_, std::vector(1, precision(0))}, + {_E_, std::vector(1, precision(0))}, }; - precision sum = 0.0; - const EigenPair &groundstate = *ham.eigenpairs().begin(); - // Loop over all eigenpairs. - for(auto ipair = ham.eigenpairs().begin(); ipair != ham.eigenpairs().end(); ++ipair){ - const EigenPair& pair = *ipair; - // Calculate Boltzmann factor, skip the states with trivial contribution. - precision boltzmann_f = std::exp( - -(pair.eigenvalue() - groundstate.eigenvalue()) * _beta - ); - if (std::abs(_cutoff - boltzmann_f) > std::numeric_limits::epsilon() && boltzmann_f < _cutoff ) { -// std::cout<<"Skipped by Boltzmann factor."< std::numeric_limits::epsilon() + && boltzmann_f < _cutoff) continue; #ifdef USE_MPI - if(!myid) + if (myid == 0) #endif - std::cout << "Compute static observables contribution for eigenvalue E=" << pair.eigenvalue() << " with Boltzmann factor = " << boltzmann_f << "; for sector" << pair.sector() << std::endl; - - std::map> contrib = calculate_static_observables_eigenvector(ham, pair); - for(auto ivar = contrib.begin(); ivar != contrib.end(); ++ivar){ - for(int i = 0; i < (*ivar).second.size(); ++i){ - avg[(*ivar).first][i] += (*ivar).second[i] * boltzmann_f; + std::cout << "Compute static observables contribution for eigenvalue E=" + << pair.eigenvalue() << " with Boltzmann factor = " + << boltzmann_f << "; for sector" << pair.sector() << std::endl; + auto contrib = calculate_static_observables_eigenvector(ham, pair); + for (auto& kv : contrib) { + for (std::size_t i = 0; i < kv.second.size(); ++i) { + avg[kv.first][i] += kv.second[i] * boltzmann_f; } } sum += boltzmann_f; } #ifdef USE_MPI - if(!myid) + if (myid == 0) #endif - std::cout << "Statsum: " << sum << std::endl; - for(auto ob = avg.begin(); ob != avg.end(); ++ob){ - for(int i = 0; i < (*ob).second.size(); ++i){ - (*ob).second[i] /= sum; - } - } - return std::move(avg); + std::cout << "Statsum: " << sum << std::endl; + for (auto& kv : avg) for (auto& v : kv.second) v /= sum; + return avg; } - /** - * @brief Find largest (by magnitude) coefficients in an eigenvector. - * - * @param ham - the Hamiltonian - * @param pair - the eigenpair - * @param nmax - maximum number of coefficients to be returned; - * @param trivial - skip the coefficients smaller than this number. - * - * Returns a vector of pairs sorted starting from largest magnitude: - * first: symmetry state; - * second: coefficient; - */ - std::vector> find_largest_coefficients(Hamiltonian& ham, const EigenPair& pair, size_t nmax, precision trivial){ + std::vector> + find_largest_coefficients(Hamiltonian& ham, + const EigenPair& pair, + std::size_t nmax, precision /*trivial*/) { ham.model().symmetry().set_sector(pair.sector()); ham.storage().reset(); - int count = std::min(nmax, pair.eigenvector().size()); - std::vector largest = std::vector(pair.eigenvector().size()); + int count = static_cast(std::min(nmax, pair.eigenvector().size())); + std::vector largest(pair.eigenvector().size()); #ifdef USE_MPI - int myid; - int nprocs; + int myid, nprocs; MPI_Comm_rank(ham.comm(), &myid); MPI_Comm_size(ham.comm(), &nprocs); std::vector counts(nprocs); std::vector displs(nprocs + 1); MPI_Gather(&count, 1, MPI_INT, counts.data(), 1, MPI_INT, 0, ham.comm()); - if(!myid){ + if (myid == 0) { displs[0] = 0; - for(size_t i = 0; i < nprocs; i++){ - displs[i + 1] = displs[i] + counts[i]; - } + for (int i = 0; i < nprocs; ++i) displs[i + 1] = displs[i] + counts[i]; } #endif - for(size_t i = 0; i < largest.size(); ++i){ - largest[i] = i; - } - std::partial_sort(largest.begin(), largest.begin()+count, largest.end(), [&pair] (int a, int b) -> bool { - return ((std::abs(pair.eigenvector()[a]) > std::abs(pair.eigenvector()[b])) - || ((std::abs(pair.eigenvector()[a]) == std::abs(pair.eigenvector()[b])) && (a < b)) - ); - }); + for (std::size_t i = 0; i < largest.size(); ++i) largest[i] = i; + std::partial_sort(largest.begin(), largest.begin() + count, largest.end(), + [&pair](std::size_t a, std::size_t b) { + return std::abs(pair.eigenvector()[a]) > std::abs(pair.eigenvector()[b]) + || (std::abs(pair.eigenvector()[a]) == std::abs(pair.eigenvector()[b]) + && a < b); + }); #ifdef USE_MPI std::vector send(count); - for(size_t i = 0; i < count; ++i){ - send[i] = Element(largest[i] + ham.storage().offset(), pair.eigenvector()[largest[i]]); + for (int i = 0; i < count; ++i) { + send[i] = Element(largest[i] + ham.storage().offset(), + pair.eigenvector()[largest[i]]); } std::vector all(displs[nprocs]); - MPI_Gatherv(send.data(), count, mpi_Element, all.data(), counts.data(), displs.data(), mpi_Element, 0, ham.comm()); - if (!myid) { + MPI_Gatherv(send.data(), count, mpi_Element, + all.data(), counts.data(), displs.data(), mpi_Element, + 0, ham.comm()); + if (myid == 0) { nmax = std::min(nmax, all.size()); - std::partial_sort(all.begin(), all.begin()+nmax, all.end(), [] (Element a, Element b) -> bool {return (a > b);}); + std::partial_sort(all.begin(), all.begin() + nmax, all.end(), + [](const Element& a, const Element& b) { return a > b; }); std::vector> ret(nmax); - for(size_t i = 0; i < nmax; ++i){ + for (std::size_t i = 0; i < nmax; ++i) { long long nst = ham.model().symmetry().state_by_index(all[i].ind); - ret[i] = std::pair(nst, all[i].val); + ret[i] = {nst, static_cast(all[i].val)}; } return ret; - } else { - std::vector> ret(0); - return ret; } + return {}; #else std::vector> ret(count); - for(size_t i = 0; i < count; ++i){ + for (int i = 0; i < count; ++i) { long long nst = ham.model().symmetry().state_by_index(largest[i]); - ret[i] = std::pair(nst, pair.eigenvector()[largest[i]]); + ret[i] = {nst, pair.eigenvector()[largest[i]]}; } return ret; #endif } - /** - * @brief Calculate contribution of the states with the same coefficient (classes) to the eigenvector. - * - * @param ham - the Hamiltonian - * @param pair - the eigenpair - * @param nmax - maximum number of coefficients to be processed; - * @param trivial - skip the coefficients smaller than this number; - * @param cumulative - calculate cumulative contribution: this class and all previous classes. - * - * Returns a vector of pairs: - * first: index of the first state in the class, as returned by find_largest_coefficients(); - * second: contribution of the class; - * The vector is sorted the largest coefficient of the class. - */ - std::vector> calculate_class_contrib(Hamiltonian& ham, const EigenPair& pair, size_t nmax, precision trivial, bool cumulative){ - std::vector> coeffs = find_largest_coefficients(ham, pair, nmax, trivial); - std::vector> contribs(0); + std::vector> + calculate_class_contrib(Hamiltonian& ham, const EigenPair& pair, + std::size_t nmax, precision trivial, bool cumulative) { + auto coeffs = find_largest_coefficients(ham, pair, nmax, trivial); + std::vector> contribs; #ifdef USE_MPI - int myid; - MPI_Comm_rank(ham.comm(), &myid); - if(!myid) + int myid; MPI_Comm_rank(ham.comm(), &myid); + if (myid != 0) return contribs; #endif - { - for(size_t i = 0; i < coeffs.size(); ++i){ - // Add up squares of coefficients within each class. - if(!i || std::abs(coeffs[i - 1].second - coeffs[i].second) > trivial){ - contribs.push_back(std::pair(i, coeffs[i].second * coeffs[i].second)); - if(i){ - contribs.back().second += (cumulative ? contribs[contribs.size() - 2].second : 0.0); - } - }else{ - contribs.back().second += coeffs[i].second * coeffs[i].second; - } + for (std::size_t i = 0; i < coeffs.size(); ++i) { + if (i == 0 || std::abs(coeffs[i - 1].second - coeffs[i].second) > trivial) { + contribs.push_back({i, coeffs[i].second * coeffs[i].second}); + if (i && cumulative) contribs.back().second += contribs[contribs.size() - 2].second; + } else { + contribs.back().second += coeffs[i].second * coeffs[i].second; } - // The last contribs may be truncated, remove. - contribs.pop_back(); } + if (!contribs.empty()) contribs.pop_back(); return contribs; } - /** - * @brief Print largest coefficients of an eigenvector in decreasing order of magnitude. - * - * @param ham - the Hamiltonian - * @param pair - the eigenpair - * @param nmax - maximum number of coefficients to be processed; - * @param trivial - skip the coefficients smaller than this number. - */ - void print_major_electronic_configuration(Hamiltonian& ham, const EigenPair& pair, size_t nmax, precision trivial, std::ostream & out){ - std::vector> coeffs = find_largest_coefficients(ham, pair, nmax, trivial); + void print_major_electronic_configuration(Hamiltonian& ham, + const EigenPair& pair, + std::size_t nmax, precision trivial, + std::ostream& out) { + auto coeffs = find_largest_coefficients(ham, pair, nmax, trivial); #ifdef USE_MPI - int myid; - MPI_Comm_rank(ham.comm(), &myid); - if(!myid) + int myid; MPI_Comm_rank(ham.comm(), &myid); + if (myid != 0) return; #endif - { - out << "Eigenvector components for eigenvalue " << pair.eigenvalue() << " "; - pair.sector().print(out); - out << std::endl; - for(size_t i = 0; i < coeffs.size(); ++i){ - out << coeffs[i].second << " * |"; - std::string spin_down = std::bitset< 64 >( coeffs[i].first ).to_string().substr(64- ham.model().orbitals(), ham.model().orbitals()); - std::string spin_up = std::bitset< 64 >( coeffs[i].first ).to_string().substr(64-2*ham.model().orbitals(), ham.model().orbitals()); - out<" << std::endl; - } + out << "Eigenvector components for eigenvalue " << pair.eigenvalue() << " "; + pair.sector().print(out); + out << std::endl; + for (const auto& c : coeffs) { + out << c.second << " * |"; + std::string spin_down = std::bitset<64>(c.first).to_string().substr( + 64 - ham.model().orbitals(), ham.model().orbitals()); + std::string spin_up = std::bitset<64>(c.first).to_string().substr( + 64 - 2 * ham.model().orbitals(), ham.model().orbitals()); + out << spin_up << "|" << spin_down << ">\n"; } } - /** - * @brief Print contribution of the states with the same coefficient (classes) to the eigenvector. - * - * @param ham - the Hamiltonian - * @param pair - the eigenpair - * @param nmax - maximum number of coefficients to be processed; - * @param trivial - skip the coefficients smaller than this number. - * @param cumulative - calculate cumulative contribution: this class and all previous classes. - */ - void print_class_contrib(Hamiltonian& ham, const EigenPair& pair, size_t nmax, precision trivial, bool cumulative, std::ostream & out){ - std::vector> contribs = calculate_class_contrib(ham, pair, nmax, trivial, cumulative); + void print_class_contrib(Hamiltonian& ham, const EigenPair& pair, + std::size_t nmax, precision trivial, bool cumulative, + std::ostream& out) { + auto contribs = calculate_class_contrib(ham, pair, nmax, trivial, cumulative); #ifdef USE_MPI - int myid; - MPI_Comm_rank(ham.comm(), &myid); - if(!myid) + int myid; MPI_Comm_rank(ham.comm(), &myid); + if (myid != 0) return; #endif - { - out << "Contributions of eigenvector component classes for eigenvalue " << pair.eigenvalue() << " "; - pair.sector().print(out); - out << std::endl; - for(size_t i = 0; i < contribs.size(); ++i){ - out << contribs[i].first << "\t" << contribs[i].second << std::endl; - } - } + out << "Contributions of eigenvector component classes for eigenvalue " + << pair.eigenvalue() << " "; + pair.sector().print(out); + out << std::endl; + for (const auto& c : contribs) out << c.first << "\t" << c.second << "\n"; } #ifdef USE_MPI - struct Element{ - Element() {}; - Element(size_t _ind, double _val) : ind(_ind), val(_val) {} - size_t ind; - double val; - - bool operator>(const Element &el) const { - return ((std::abs(val) > std::abs(el.val)) - || ((std::abs(val) == std::abs(el.val)) && (ind < el.ind)) - ); - }; - - bool operator<(const Element &el) const { - return ((std::abs(val) < std::abs(el.val)) - || ((std::abs(val) == std::abs(el.val)) && (ind > el.ind)) - ); - }; + struct Element { + Element() = default; + Element(std::size_t i, double v) : ind(i), val(v) {} + std::size_t ind; + double val; + bool operator>(const Element& e) const { + return std::abs(val) > std::abs(e.val) + || (std::abs(val) == std::abs(e.val) && ind < e.ind); + } + bool operator<(const Element& e) const { + return std::abs(val) < std::abs(e.val) + || (std::abs(val) == std::abs(e.val) && ind > e.ind); + } }; - - MPI_Datatype mpi_Element; + MPI_Datatype mpi_Element; #endif private: - - /** - * @brief Compute static observables for one eigenvector. - * - * @param ham - the Hamiltonian - * @param pair - the eigenpair - */ - std::map> calculate_static_observables_eigenvector(Hamiltonian& ham, const EigenPair& pair){ - std::vector n(ham.model().interacting_orbitals(), 0.0); - std::vector n_up(ham.model().interacting_orbitals(), 0.0); - std::vector n_down(ham.model().interacting_orbitals(), 0.0); - std::vector m(ham.model().interacting_orbitals(), 0.0); - std::vector mimj(ham.model().interacting_orbitals() * ham.model().interacting_orbitals(), 0.0); - std::vector d_occ(ham.model().interacting_orbitals(), 0.0); - precision inverse_N_eff = 0.0; + std::map> + calculate_static_observables_eigenvector(Hamiltonian& ham, + const EigenPair& pair) { + const int n_orb = ham.model().interacting_orbitals(); + std::vector n (n_orb, precision(0)); + std::vector n_up (n_orb, precision(0)); + std::vector n_down(n_orb, precision(0)); + std::vector m (n_orb, precision(0)); + std::vector mimj (n_orb * n_orb, precision(0)); + std::vector d_occ (n_orb, precision(0)); + precision inverse_N_eff = precision(0); ham.model().symmetry().set_sector(pair.sector()); ham.storage().reset(); - // Loop over basis vectors. - for(int i = 0; i < pair.eigenvector().size(); ++i){ + for (std::size_t i = 0; i < pair.eigenvector().size(); ++i) { precision weight = pair.eigenvector()[i] * pair.eigenvector()[i]; - // Calculate static variables for each orbital. ham.model().symmetry().next_state(); long long nst = ham.model().symmetry().state(); - for(int orb = 0; orb < ham.model().interacting_orbitals(); ++orb){ - int el_up = ham.model().checkState(nst, orb, ham.model().max_total_electrons()); - int el_down = ham.model().checkState(nst, orb + ham.model().orbitals(), ham.model().max_total_electrons()); - n[orb] += (el_up + el_down) * weight; - n_up[orb] += el_up * weight; + for (int orb = 0; orb < n_orb; ++orb) { + int el_up = ham.model().checkState(nst, orb, ham.model().max_total_electrons()); + int el_down = ham.model().checkState(nst, orb + ham.model().orbitals(), + ham.model().max_total_electrons()); + n [orb] += (el_up + el_down) * weight; + n_up [orb] += el_up * weight; n_down[orb] += el_down * weight; - m[orb] += (el_up - el_down) * weight; - for(int orb2 = 0; orb2 < ham.model().interacting_orbitals(); ++orb2){ - int el_up2 = ham.model().checkState(nst, orb2, ham.model().max_total_electrons()); - int el_down2 = ham.model().checkState(nst, orb2 + ham.model().interacting_orbitals(), ham.model().max_total_electrons()); - mimj[ham.model().interacting_orbitals() * orb + orb2] += (el_up - el_down) * (el_up2 - el_down2) * weight; + m [orb] += (el_up - el_down) * weight; + for (int orb2 = 0; orb2 < n_orb; ++orb2) { + int el_up2 = ham.model().checkState(nst, orb2, ham.model().max_total_electrons()); + int el_down2 = ham.model().checkState(nst, orb2 + n_orb, + ham.model().max_total_electrons()); + mimj[n_orb * orb + orb2] += (el_up - el_down) * (el_up2 - el_down2) * weight; } d_occ[orb] += el_up * el_down * weight; } @@ -388,62 +293,41 @@ namespace EDLib { } std::map> result = { - {_N_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_N_UP_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_N_DN_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_M_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_D_OCC_, std::vector(ham.model().interacting_orbitals(), 0.0)}, - {_MI_MJ_, std::vector(ham.model().interacting_orbitals() * ham.model().interacting_orbitals(), 0.0)}, - {_N_EFF_, std::vector(1, 0.0)}, - {_E_, std::vector(1, 0.0)} + {_N_, std::vector(n_orb, precision(0))}, + {_N_UP_, std::vector(n_orb, precision(0))}, + {_N_DN_, std::vector(n_orb, precision(0))}, + {_M_, std::vector(n_orb, precision(0))}, + {_D_OCC_, std::vector(n_orb, precision(0))}, + {_MI_MJ_, std::vector(n_orb * n_orb, precision(0))}, + {_N_EFF_, std::vector(1, precision(0))}, + {_E_, std::vector(1, precision(0))}, }; #ifdef USE_MPI - // Add the sums from all processes. - MPI_Reduce(n.data(), result[_N_].data(), n.size(), alps::mpi::detail::mpi_type(), MPI_SUM, 0, ham.comm()); - MPI_Reduce(n_up.data(), result[_N_UP_].data(), n_up.size(), alps::mpi::detail::mpi_type(), MPI_SUM, 0, ham.comm()); - MPI_Reduce(n_down.data(), result[_N_DN_].data(), n_down.size(), alps::mpi::detail::mpi_type(), MPI_SUM, 0, ham.comm()); - MPI_Reduce(m.data(), result[_M_].data(), m.size(), alps::mpi::detail::mpi_type(), MPI_SUM, 0, ham.comm()); - MPI_Reduce(d_occ.data(), result[_D_OCC_].data(), d_occ.size(), alps::mpi::detail::mpi_type(), MPI_SUM, 0, ham.comm()); - MPI_Reduce(mimj.data(), result[_MI_MJ_].data(), mimj.size(), alps::mpi::detail::mpi_type(), MPI_SUM, 0, ham.comm()); - MPI_Reduce(&inverse_N_eff, &result[_N_EFF_][0], 1, alps::mpi::detail::mpi_type(), MPI_SUM, 0, ham.comm()); + MPI_Reduce(n .data(), result[_N_] .data(), n .size(), mpi_type(), MPI_SUM, 0, ham.comm()); + MPI_Reduce(n_up .data(), result[_N_UP_] .data(), n_up .size(), mpi_type(), MPI_SUM, 0, ham.comm()); + MPI_Reduce(n_down.data(), result[_N_DN_] .data(), n_down.size(), mpi_type(), MPI_SUM, 0, ham.comm()); + MPI_Reduce(m .data(), result[_M_] .data(), m .size(), mpi_type(), MPI_SUM, 0, ham.comm()); + MPI_Reduce(d_occ .data(), result[_D_OCC_].data(), d_occ .size(), mpi_type(), MPI_SUM, 0, ham.comm()); + MPI_Reduce(mimj .data(), result[_MI_MJ_].data(), mimj .size(), mpi_type(), MPI_SUM, 0, ham.comm()); + MPI_Reduce(&inverse_N_eff, &result[_N_EFF_][0], 1, mpi_type(), MPI_SUM, 0, ham.comm()); #else - result[_N_] = n; - result[_N_UP_] = n_up; - result[_N_DN_] = n_down; - result[_M_] = m; + result[_N_] = n; + result[_N_UP_] = n_up; + result[_N_DN_] = n_down; + result[_M_] = m; result[_D_OCC_] = d_occ; result[_MI_MJ_] = mimj; result[_N_EFF_][0] = inverse_N_eff; #endif result[_E_][0] = pair.eigenvalue(); - result[_N_EFF_][0] = 1 / result[_N_EFF_][0]; + result[_N_EFF_][0] = precision(1) / result[_N_EFF_][0]; return result; } - /// Inverse temperature precision _beta; - /// Boltzmann-factor cutoff precision _cutoff; - }; - template - const std::string StaticObservables::_N_ = "N"; - template - const std::string StaticObservables::_N_UP_ = "N_up"; - template - const std::string StaticObservables::_N_DN_ = "N_dn"; - template - const std::string StaticObservables::_M_ = "M"; - template - const std::string StaticObservables::_D_OCC_ = "D_occ"; - template - const std::string StaticObservables::_N_EFF_ = "N_eff"; - template - const std::string StaticObservables::_MI_MJ_ = "M_i M_j"; - template - const std::string StaticObservables::_E_ = "E"; - } #endif diff --git a/include/edlib/Storage.h b/include/edlib/Storage.h index 5f155c5..dd07937 100644 --- a/include/edlib/Storage.h +++ b/include/edlib/Storage.h @@ -1,221 +1,171 @@ -// -// Created by iskakoff on 21/07/16. -// - #ifndef EDLIB_STORAGE_H #define EDLIB_STORAGE_H +#include +#include #include -#include +#include #include #ifdef USE_MPI #include #endif -namespace EDLib { - namespace Storage { +#include "edlib/MpiTypes.h" +#include "edlib/Parameters.h" + +namespace edlib { - template - class Storage { - public: + /** + * Abstract base storage. Concrete derived storages implement matrix-vector + * product (av) and sector fill (fill); diag() drives the cpp-arnoldi + * symmetric solver against av(). + */ + template + class Storage { + public: #ifdef USE_MPI - Storage(alps::params &p, MPI_Comm comm) : _comm(comm), _nev(p["arpack.NEV"]), _eval_only(p["storage.EIGENVALUES_ONLY"]) { + Storage(const Parameters& p, MPI_Comm comm) + : _nev(p.arpack_nev), + _ncv(p.arpack_ncv > 0 ? p.arpack_ncv : 2 * p.arpack_nev + 3), + _eval_only(p.eigenvalues_only ? 1 : 0), + _comm(comm) {} #else - Storage(alps::params &p) : _nev(p["arpack.NEV"]), _eval_only(p["storage.EIGENVALUES_ONLY"]) { + explicit Storage(const Parameters& p) + : _nev(p.arpack_nev), + _ncv(p.arpack_ncv > 0 ? p.arpack_ncv : 2 * p.arpack_nev + 3), + _eval_only(p.eigenvalues_only ? 1 : 0) {} #endif - if (p.exists("arpack.NCV")) { - _ncv = p["arpack.NCV"]; - } else { - _ncv = 2 * _nev + 3; - } - } - /** - * For matrix size equals to 1 we do not need to perform diagonalization. - * Eigenvalue = A(0,0) - * Eigenvector = [1.0] - */ - virtual void zero_eigenapair() = 0; - - /** - * Diagonalize current Hamiltonian sector using cpp-arnoldi (callback-based - * C++ port of the ARPACK symmetric driver). - */ - int diag() { - if (_n == 0) { - return finalize(0, true, true); - } - if (_ntot == 1) { - zero_eigenapair(); - return finalize(0); - } + virtual ~Storage() = default; + + /// For a 1x1 matrix the eigenpair is trivial; concrete storages set it. + virtual void zero_eigenapair() = 0; + + /// Matrix-vector product. Implemented by concrete storage. + virtual void av(Prec* v, Prec* w, int n, bool clear = true) = 0; + + /// Optional hook before the solve loop (used by SpinResolvedStorage for + /// MPI_Win setup). Default is no-op. + virtual void prepare_work_arrays(Prec* /*w*/, std::size_t /*shift*/ = 0) {} - const int ncv = std::min(_ncv, _ntot); - const int nev = std::min(_nev, ncv - 1); + /// Optional finalisation hook called after diag() completes. + virtual int finalize(int info, bool /*bcast*/ = true, bool /*empty*/ = false) { return info; } + + int diag() { + if (_n == 0) return finalize(0, true, true); + if (_ntot == 1) { + zero_eigenapair(); + return finalize(0); + } + + const int ncv = std::min(_ncv, _ntot); + const int nev = std::min(_nev, ncv - 1); #ifdef USE_MPI - arnoldi::MPIComm arnoldi_comm(comm()); - arnoldi::Arnoldi - solver("I", _n, "SA", nev, ncv, arnoldi_comm); + arnoldi::MPIComm arnoldi_comm(comm()); + arnoldi::Arnoldi + solver("I", _n, "SA", nev, ncv, arnoldi_comm); #else - arnoldi::Arnoldi - solver("I", _n, "SA", nev, ncv); + arnoldi::Arnoldi + solver("I", _n, "SA", nev, ncv); #endif - solver.tol(prec(1e-14)).maxiter(1000).mode(1).ishift(1); - - // Open MPI_Win on the solver's workd buffer (SpinResolvedStorage), - // or no-op for serial backends (CRSStorage, SOCRSStorage). - // Shift 2*n matches the existing convention in prepare_work_arrays. - prepare_work_arrays(solver.workd(), size_t(2 * _n)); - - solver.solve([this](const prec* x, prec* y) { - this->av(const_cast(x), y, _n, /*clear=*/true); - }); - - const int info = solver.info(); - if (info < 0) { - std::cout << "' '" << std::endl; - std::cout << "' Error with saupd, info = ' " << info << std::endl; - std::cout << "' '" << std::endl; - return finalize(info); - } + solver.tol(Prec(1e-14)).maxiter(1000).mode(1).ishift(1); - const int nconv = solver.num_converged(); - auto r = solver.eigenpairs(/*compute_vectors=*/_eval_only == 0, prec(0)); - - evals.assign(r.values.begin(), r.values.begin() + nconv); - if (_eval_only == 0) { - evecs.assign(nconv, std::vector(_n, prec(0.0))); - for (int i = 0; i < nconv; ++i) { - std::memcpy(evecs[i].data(), - r.vectors.data() + size_t(i) * _n, - _n * sizeof(prec)); - } - } else { - evecs.assign(nconv, std::vector(1, prec(0.0))); - } + prepare_work_arrays(solver.workd(), std::size_t(2 * _n)); -#ifdef USE_MPI - int myid; - MPI_Comm_rank(comm(), &myid); - if (myid == 0) { -#endif - std::cout << "Here is eigenvalues" << std::endl; - for (int j = 0; j < (int)evals.size(); ++j) { - std::cout << evals[j] << std::endl << std::flush; - } - if (info == 1) { - std::cout << "Maximum number of iterations reached." << std::endl; - } else if (info == 3) { - std::cout << " No shifts could be applied during implicit Arnoldi update, try increasing NCV." << std::endl; - } - std::cout << " ========================= " << std::endl; - std::cout << " Size of the matrix is " << _ntot << std::endl; - std::cout << " The number of Ritz values requested is: " << nev << std::endl; - std::cout << " The number of Arnoldi vectors generated: " << ncv << std::endl; - std::cout << " What portion of the spectrum: SA" << std::endl; - std::cout << " The number of converged Ritz values is: " << nconv << std::endl; - std::cout << " The number of Implicit Arnoldi update iterations taken is: " << solver.num_iterations() << std::endl; - std::cout << " The number of OP*x is: " << solver.num_op_applies() << std::endl; - std::cout << " The convergence criterion is: " << prec(1e-14) << std::endl; - std::cout << " ========================= " << std::endl; -#ifdef USE_MPI - } -#endif + solver.solve([this](const Prec* x, Prec* y) { + this->av(const_cast(x), y, _n, /*clear=*/true); + }); + + const int info = solver.info(); + if (info < 0) { + std::cout << "' '\n' Error with saupd, info = ' " << info << "\n' '" << std::endl; return finalize(info); } - /** - * @return eigen-values - */ - const std::vector < prec > &eigenvalues() const { - return evals; - } + const int nconv = solver.num_converged(); + auto r = solver.eigenpairs(/*compute_vectors=*/_eval_only == 0, Prec(0)); - std::vector < prec > &eigenvalues() { - return evals; - } - - /** - * @return eigen-vectors - */ - const std::vector < std::vector < prec > > &eigenvectors() const { - return evecs; + evals.assign(r.values.begin(), r.values.begin() + nconv); + if (_eval_only == 0) { + evecs.assign(nconv, std::vector(_n, Prec(0))); + for (int i = 0; i < nconv; ++i) { + std::memcpy(evecs[i].data(), + r.vectors.data() + std::size_t(i) * _n, + _n * sizeof(Prec)); + } + } else { + evecs.assign(nconv, std::vector(1, Prec(0))); } - std::vector < std::vector < prec > > &eigenvectors() { - return evecs; +#ifdef USE_MPI + int myid; + MPI_Comm_rank(comm(), &myid); + if (myid == 0) { +#endif + std::cout << "Here is eigenvalues" << std::endl; + for (auto e : evals) std::cout << e << "\n"; + if (info == 1) std::cout << "Maximum number of iterations reached.\n"; + else if (info == 3) std::cout << "No shifts could be applied during implicit Arnoldi update, try increasing NCV.\n"; + std::cout << " ========================= \n" + << " Size of the matrix is " << _ntot << "\n" + << " The number of Ritz values requested is: " << nev << "\n" + << " The number of Arnoldi vectors generated: " << ncv << "\n" + << " What portion of the spectrum: SA\n" + << " The number of converged Ritz values is: " << nconv << "\n" + << " The number of Implicit Arnoldi update iterations taken is: " << solver.num_iterations() << "\n" + << " The number of OP*x is: " << solver.num_op_applies() << "\n" + << " The convergence criterion is: " << Prec(1e-14) << "\n" + << " ========================= " << std::endl; +#ifdef USE_MPI } +#endif + return finalize(info); + } - /** - * Matrix-Vector product. - * Must be implemented by each concrete storage type. - */ - virtual void av(prec *v, prec *w, int n, bool clear = true) = 0; - - /** - * Perform additional setup on the solver's working array before the solve - * loop begins. Used by SpinResolvedStorage to open the MPI_Win. - * @param w - pointer to the solver's workd buffer - * @param shift - offset within w (convention: 2*n) - */ - virtual void prepare_work_arrays(prec *w, size_t shift = 0){}; - - /** - * Finalize diagonalization for the current Hamiltonian matrix sector. - * @param info - result code - * @param bcast - broadcast eigenvalues across MPI ranks - * @param empty - arrays are empty on this CPU - * @return result code - */ - virtual int finalize(int info, bool bcast = true, bool empty = false){return info;}; + const std::vector& eigenvalues() const { return evals; } + std::vector& eigenvalues() { return evals; } + const std::vector>& eigenvectors() const { return evecs; } + std::vector>& eigenvectors() { return evecs; } #ifdef USE_MPI - virtual MPI_Comm comm() { - return _comm; - } + virtual MPI_Comm comm() { return _comm; } #endif - protected: - /// local CPU dimension - int &n() { return _n; } - /// total matrix dimension - int &ntot() { return _ntot; } + + protected: + int& n() { return _n; } + int& ntot() { return _ntot; } #ifdef USE_MPI - void broadcast_evals(bool empty = false) { - MPI_Barrier(_comm); - int nconv = evals.size(); - MPI_Bcast(&nconv, 1, MPI_INT, 0, _comm); - int rank; - MPI_Comm_rank(_comm, &rank); - if(rank != 0) { - evals.resize(nconv); - if(empty) { - evecs.assign(nconv, std::vector(0, prec(0.0))); - } - } - MPI_Bcast(evals.data(), nconv, alps::mpi::detail::mpi_type(), 0, _comm); + void broadcast_evals(bool empty = false) { + MPI_Barrier(_comm); + int nconv = static_cast(evals.size()); + MPI_Bcast(&nconv, 1, MPI_INT, 0, _comm); + int rank; + MPI_Comm_rank(_comm, &rank); + if (rank != 0) { + evals.resize(nconv); + if (empty) evecs.assign(nconv, std::vector(0, Prec(0))); } + MPI_Bcast(evals.data(), nconv, mpi_type(), 0, _comm); + } #endif - private: - int _ntot; - int _n; - /// number of eigenvalues to be computed - int _nev; - /// number of Arnoldi vectors (Krylov subspace size) - int _ncv; - /// compute only eigenvalues to reduce memory requirements - int _eval_only; - - std::vector < prec > evals; - std::vector < std::vector < prec > > evecs; + private: + int _ntot = 0; + int _n = 0; + int _nev; + int _ncv; + int _eval_only; + + std::vector evals; + std::vector> evecs; #ifdef USE_MPI - MPI_Comm _comm; + MPI_Comm _comm; #endif - }; + }; - } } -#endif //EDLIB_STORAGE_H + +#endif diff --git a/include/edlib/Symmetry.h b/include/edlib/Symmetry.h index 3676d66..a0c1474 100644 --- a/include/edlib/Symmetry.h +++ b/include/edlib/Symmetry.h @@ -1,73 +1,37 @@ -// -// Created by iskakoff on 19/07/16. -// - #ifndef EDLIB_SYMMETRY_H #define EDLIB_SYMMETRY_H -#include -#include - -namespace EDLib { - namespace Symmetry { -/** - * Base class for symmetries - */ - class Symmetry { - public: - Symmetry() : _state(0) {}; - - virtual ~Symmetry() {}; +namespace edlib { - /** - * Check and, if possible, change basis state to the next state - * @return true if there was next basis state - */ - virtual bool next_state() = 0; + /** + * Base class for symmetry / sector machinery. + * + * Stateless across alpscore: derived classes own the sector queue and + * accept their sector restrictions as plain STL containers at construction + * time. + */ + class Symmetry { + public: + Symmetry() : _state(0) {} + virtual ~Symmetry() = default; - /** - * @param spin - * @return true if we can create/destroy particle for specific spin - */ - virtual bool can_create_particle(int spin) = 0; - virtual bool can_destroy_particle(int spin) = 0; + virtual bool next_state() = 0; - /** - * @return current basis state - */ - long long state() const { - return _state; - }; - long long &state() { - return _state; - }; + virtual bool can_create_particle(int spin) = 0; + virtual bool can_destroy_particle(int spin) = 0; - /** - * @param state - basis state - * @return index in the ordered basis for specific basis state - */ - virtual int index(long long state) = 0; + long long state() const { return _state; } + long long& state() { return _state; } - /** - * Reset stateful object ot the initial state - */ - virtual void reset() = 0; + virtual int index(long long state) = 0; + virtual void reset() = 0; + virtual void init() = 0; + virtual bool next_sector() = 0; - /** - * init symmetry stateful object - */ - virtual void init() = 0; + private: + long long _state; + }; - /** - * check that there is the next simmetry sector. if exist set current sector to the next available - * @return true if there is next state - */ - virtual bool next_sector() = 0; - - private: - /// basis state - long long _state; - }; - } } -#endif //EDLIB_SYMMETRY_H + +#endif diff --git a/include/edlib/SzSymmetry.h b/include/edlib/SzSymmetry.h index 3290b72..1b94897 100644 --- a/include/edlib/SzSymmetry.h +++ b/include/edlib/SzSymmetry.h @@ -1,238 +1,207 @@ -// -// Created by iskakoff on 19/07/16. -// - -#ifndef EDLIB_SZCOMBINATION_H -#define EDLIB_SZCOMBINATION_H +#ifndef EDLIB_SZSYMMETRY_H +#define EDLIB_SZSYMMETRY_H +#include +#include +#include +#include #include - -#include "Symmetry.h" -#include "Combination.h" -#include "NSymmetry.h" - -namespace EDLib { - namespace Symmetry { -/** - * Sz symmetry class - */ - class SzSymmetry : public Symmetry { +#include + +#include "edlib/Combination.h" +#include "edlib/Parameters.h" +#include "edlib/Symmetry.h" + +namespace edlib { + + /** + * Sz-conserving symmetry: sectors indexed by (n_up, n_down). + * + * Sector restrictions, if any, are passed as an array of {n_up, n_down} + * pairs at construction. An empty list (default) means "all sectors". + */ + class SzSymmetry : public Symmetry { + public: + class Sector { public: - class Sector { - public: - friend class SzSymmetry; - - friend std::ostream &operator<<(std::ostream &o, const SzSymmetry::Sector &c) { return o << " (nup: " << c._nup << " ndown: " << c._ndown << ") size: " << c._size; } - - Sector(int up, int down, size_t size) : _nup(up), _ndown(down), _size(size) {}; - - /** - * @return number of electrons with spin-up - */ - int nup() const { return _nup; } - /** - * @return number of electrons with spin-down - */ - int ndown() const { return _ndown; } - - /** - * @return sector dimension - */ - size_t size() const { return _size; } - - void print() const { - print(std::cout); - } - - void print(std::ostream & out) const { - out << _nup << " " << _ndown; - } - - bool operator<(const Sector & s) const{ - return _size < s._size || (_size == s._size && _nup < s._nup && _ndown < s._ndown ) - || (_size == s._size && _nup == s._nup && _ndown < s._ndown ); - } - - bool operator>(const Sector & s) const{ - return s < *this; - } - - private: - int _nup; - int _ndown; - size_t _size; - }; - - SzSymmetry(int N) :Symmetry(), _current_sector(-1, -1, 0), _Ns(N), upstate(N + 1), dostate(N + 1), - _comb(N), basis(N + 1), ninv(N + 1, std::vector < int >(1 << N, 0)), - _first(true) { - initial_fill(); - }; - - SzSymmetry(alps::params &p) : Symmetry(), _current_sector(-1, -1, 0), _Ns(p["NSITES"]), upstate(_Ns + 1), dostate(_Ns + 1), - _comb(_Ns), basis(_Ns + 1), ninv(_Ns + 1, std::vector < int >(1 << _Ns, 0)), - _first(true) { - initial_fill(); - std::vector sectors; - if (p.exists("arpack.SECTOR") && bool(p["arpack.SECTOR"])) { - std::vector < std::vector < int > > sectors_list; - std::string input = p["INPUT_FILE"]; - alps::hdf5::archive input_file(input, "r"); - input_file >> alps::make_pvp("sectors/values", sectors_list); - input_file.close(); - for (int i = 0; i < sectors_list.size(); ++i) { - sectors.push_back( SzSymmetry::Sector(sectors_list[i][0], sectors_list[i][1], - (size_t) (_comb.c_n_k(_Ns, sectors_list[i][0]) * _comb.c_n_k(_Ns, sectors_list[i][1])))); - } - } else { - for (int i = 0; i <= _Ns; ++i) { - for (int j = 0; j <= _Ns; ++j) { - sectors.push_back(SzSymmetry::Sector(i, j, (size_t) (_comb.c_n_k(_Ns, i) * _comb.c_n_k(_Ns, j)))); - } - } - } - std::sort(sectors.begin(), sectors.end(), std::less()); - for(auto const& e : sectors) { - _sectors.push(e); - } - } - - virtual ~SzSymmetry() {}; - - virtual bool next_state() override { - if (_first) { - _first = false; - } - if (_ind >= _current_sector.size()) { - return false; - } - state() = state_by_index(_ind); - _ind++; - return true; - } - - inline long long state_by_index(int ind) { - int u = ind / _comb.c_n_k(_Ns, _current_sector.ndown()); - int d = ind % _comb.c_n_k(_Ns, _current_sector.ndown()); - long long res = basis[_current_sector.nup()][u]; - res <<= _Ns; - res += basis[_current_sector.ndown()][d]; - return res; - } - - virtual int index(long long state, const SzSymmetry::Sector §or) { - long long up = state >> _Ns; - long long down = state & ((1ll << _Ns) - 1); - int cup = _comb.c_n_k(_Ns, sector.nup()); - int cdo = _comb.c_n_k(_Ns, sector.ndown()); - return ninv[sector.nup()][(int) up] * (cdo) + ninv[sector.ndown()][(int) down]; - } - - virtual int index(long long state) override{ - return index(state, _current_sector); - } - - virtual void reset() override{ - state() = 0ll; - _first = true; - _ind = 0; - } - - virtual void init() override { - // TODO: Decide what we should have to init - reset(); - _comb.init_state(_current_sector.nup(), upstate); - _comb.init_state(_current_sector.ndown(), dostate); - }; - - virtual bool next_sector() override { - if (_sectors.empty()) - return false; - _current_sector = _sectors.front(); - _sectors.pop(); - return true; - } + friend class SzSymmetry; - void set_sector(const SzSymmetry::Sector §or) { - _current_sector = sector; - init(); + friend std::ostream& operator<<(std::ostream& o, const Sector& c) { + return o << " (nup: " << c._nup << " ndown: " << c._ndown + << ") size: " << c._size; } - const SzSymmetry::Sector §or() const { - return _current_sector; - } + Sector(int up, int down, std::size_t size) + : _nup(up), _ndown(down), _size(size) {} - inline const Combination &comb() const { - return _comb; - } + int nup() const { return _nup; } + int ndown() const { return _ndown; } + std::size_t size() const { return _size; } - bool can_create_particle(int spin) override { - return spin == 0 ? _current_sector.nup() < _Ns : _current_sector.ndown() < _Ns; - } + void print(std::ostream& out) const { out << _nup << " " << _ndown; } + void print() const { print(std::cout); } - bool can_destroy_particle(int spin) override { - return spin == 0 ? _current_sector.nup() > 0 : _current_sector.ndown() > 0; + bool operator<(const Sector& s) const { + return _size < s._size + || (_size == s._size && _nup < s._nup && _ndown < s._ndown) + || (_size == s._size && _nup == s._nup && _ndown < s._ndown); } + bool operator>(const Sector& s) const { return s < *this; } - SzSymmetry::Sector destroy_particle(int spin) { - return Sector(_current_sector.nup() - (1 - spin), _current_sector.ndown() - spin, - _comb.c_n_k(_Ns, _current_sector.nup() - (1 - spin)) * _comb.c_n_k(_Ns, _current_sector.ndown() - spin)); - } + private: + int _nup; + int _ndown; + std::size_t _size; + }; - SzSymmetry::Sector create_particle(int spin) { - return Sector(_current_sector.nup() + (1 - spin), _current_sector.ndown() + spin, - _comb.c_n_k(_Ns, _current_sector.nup() + (1 - spin)) * _comb.c_n_k(_Ns, _current_sector.ndown() + spin)); - } + explicit SzSymmetry(int N, + const std::vector>& sector_list = {}) + : Symmetry(), _current_sector(-1, -1, 0), _Ns(N), + upstate(N + 1), dostate(N + 1), + _comb(N), basis(N + 1), + ninv(N + 1, std::vector(1 << N, 0)), + _first(true) { + initial_fill(); + populate_sectors(sector_list); + } + + SzSymmetry(const Parameters& p, + const std::vector>& sector_list = {}) + : SzSymmetry(p.nsites, sector_list) {} + + bool next_state() override { + if (_first) _first = false; + if (_ind >= static_cast(_current_sector.size())) return false; + state() = state_by_index(_ind); + ++_ind; + return true; + } + + inline long long state_by_index(int ind) { + int u = ind / _comb.c_n_k(_Ns, _current_sector.ndown()); + int d = ind % _comb.c_n_k(_Ns, _current_sector.ndown()); + long long res = basis[_current_sector.nup()][u]; + res <<= _Ns; + res += basis[_current_sector.ndown()][d]; + return res; + } + + int index(long long state, const Sector& sector) { + long long up = state >> _Ns; + long long down = state & ((1ll << _Ns) - 1); + int cdo = _comb.c_n_k(_Ns, sector.ndown()); + return ninv[sector.nup()][static_cast(up)] * cdo + + ninv[sector.ndown()][static_cast(down)]; + } + + int index(long long state) override { return index(state, _current_sector); } + + void reset() override { + state() = 0ll; + _first = true; + _ind = 0; + } + + void init() override { + reset(); + _comb.init_state(_current_sector.nup(), upstate); + _comb.init_state(_current_sector.ndown(), dostate); + } + + bool next_sector() override { + if (_sectors.empty()) return false; + _current_sector = _sectors.front(); + _sectors.pop(); + return true; + } + + void set_sector(const Sector& s) { _current_sector = s; init(); } + const Sector& sector() const { return _current_sector; } + + inline const Combination& comb() const { return _comb; } + + bool can_create_particle(int spin) override { + return spin == 0 ? _current_sector.nup() < _Ns + : _current_sector.ndown() < _Ns; + } + bool can_destroy_particle(int spin) override { + return spin == 0 ? _current_sector.nup() > 0 + : _current_sector.ndown() > 0; + } + + Sector destroy_particle(int spin) { + return Sector(_current_sector.nup() - (1 - spin), + _current_sector.ndown() - spin, + _comb.c_n_k(_Ns, _current_sector.nup() - (1 - spin)) + * _comb.c_n_k(_Ns, _current_sector.ndown() - spin)); + } + Sector create_particle(int spin) { + return Sector(_current_sector.nup() + (1 - spin), + _current_sector.ndown() + spin, + _comb.c_n_k(_Ns, _current_sector.nup() + (1 - spin)) + * _comb.c_n_k(_Ns, _current_sector.ndown() + spin)); + } + + std::queue& sectors() { return _sectors; } #ifdef USE_MPI - void set_offset(size_t offset) {_ind += offset;} + void set_offset(std::size_t offset) { _ind += offset; } #endif - private: - void initial_fill() { - _Ip = 2 * _Ns; - _ind = 0; + + private: + void populate_sectors(const std::vector>& sector_list) { + std::vector sectors; + if (!sector_list.empty()) { + for (const auto& s : sector_list) { + sectors.emplace_back(s[0], s[1], + static_cast(_comb.c_n_k(_Ns, s[0]) * _comb.c_n_k(_Ns, s[1]))); + } + } else { for (int i = 0; i <= _Ns; ++i) { - int cnk = _comb.c_n_k(_Ns, i); - basis[i].resize(cnk); - for (int k = 0; k < cnk; ++k) { - basis[i][k] = next_basis(_Ns, i, upstate, k == 0); - ninv[i][basis[i][k]] = k; + for (int j = 0; j <= _Ns; ++j) { + sectors.emplace_back(i, j, + static_cast(_comb.c_n_k(_Ns, i) * _comb.c_n_k(_Ns, j))); } } - }; - - int next_basis(int n, int k, std::vector < int > &old, bool start) { - int res = 0; - if (start) { - _comb.init_state(k, old); - //pass - } else { - _comb.next_combination(n, k, old); - } - for (int i = 0; i < k; i++) { - res += (1 << old[i]); + } + std::sort(sectors.begin(), sectors.end(), std::less()); + for (const auto& e : sectors) _sectors.push(e); + } + + void initial_fill() { + _Ip = 2 * _Ns; + _ind = 0; + for (int i = 0; i <= _Ns; ++i) { + int cnk = _comb.c_n_k(_Ns, i); + basis[i].resize(cnk); + for (int k = 0; k < cnk; ++k) { + basis[i][k] = next_basis(_Ns, i, upstate, k == 0); + ninv[i][basis[i][k]] = k; } - return res; } + } + + int next_basis(int n, int k, std::vector& old, bool start) { + int res = 0; + if (start) _comb.init_state(k, old); + else _comb.next_combination(n, k, old); + for (int i = 0; i < k; ++i) res += (1 << old[i]); + return res; + } + + Sector _current_sector; + std::queue _sectors; + int _Ns; + int _Ip = 0; + int _ind = 0; + std::vector upstate; + std::vector dostate; + std::vector> basis; + std::vector> ninv; + Combination _comb; + bool _first; + }; - SzSymmetry::Sector _current_sector; - std::queue < SzSymmetry::Sector > _sectors; - int _Ns; - int _Ip; - int _ind; - std::vector < int > upstate; - std::vector < int > dostate; - std::vector < std::vector < int > > basis; - std::vector < std::vector < int > > ninv; - Combination _comb; - bool _first; - protected: - public: - std::queue §ors() { - return _sectors; - } - }; - } } -#endif //EDLIB_SZCOMBINATION_H +#endif diff --git a/include/ext/CMakeLists.txt b/include/ext/CMakeLists.txt index f0114bd..4279a7a 100644 --- a/include/ext/CMakeLists.txt +++ b/include/ext/CMakeLists.txt @@ -1,9 +1,5 @@ - project(extlib CXX) -add_custom_target(extlib SOURCES - HolsteinAndersonModel.h - SzSymmetryWithBoson.h - FermiBosonStorage.h - HolsteinAndersonParameter.h -) +file(GLOB EXT_HEADERS *.h) + +add_custom_target(extlib SOURCES ${EXT_HEADERS}) diff --git a/include/ext/FermiBosonStorage.h b/include/ext/FermiBosonStorage.h deleted file mode 100644 index 8499ed4..0000000 --- a/include/ext/FermiBosonStorage.h +++ /dev/null @@ -1,93 +0,0 @@ -// -// Created by iskakoff on 20/04/17. -// - -#ifndef EDLIB_FERMIBOSONSTORAGE_H -#define EDLIB_FERMIBOSONSTORAGE_H - - -#include -#include -#include -#include "SzSymmetryWithBoson.h" - -namespace EDLib { - namespace Storage { - - /** - * @brief FermiBosonStorage class - * - * @author iskakoff - */ - template - class FermiBosonStorage : public Storage < typename ModelType::precision > { - typedef typename ModelType::precision prec; - public: - typedef ModelType Model; -#ifdef USE_MPI - FermiBosonStorage(alps::params &p, Model &m, MPI_Comm comm) : Storage(p, comm), _model(m), _el_symmetry(p[""].as()) {} -#else - FermiBosonStorage(alps::params &p) : Storage(p) {} -#endif - - void fill() { - //fill diagonal part - - long long k = 0; - int isign = 0; - int i = 0; - // Fill electronic part of Hamiltonian - while (_el_symmetry.next_state()) { - long long nst = _el_symmetry.state(); - for (int kkk = 0; kkk < _model.T_states().size(); ++kkk) { - if (_model.valid(_model.T_states()[kkk], nst)) { - _model.set(_model.T_states()[kkk], nst, k, isign); - int j = _el_symmetry.index(k); - _Hel.addElement(i, j, _model.T_states()[kkk].value(), isign); - } - } - _Hel.endLine(i); - ++i; - } - - int max_bos = _model.symmetry().maximum_bosons(); - for(int i = 0; i> max_bos; ++i) { - - } - } - - void reset() { - _model.symmetry().init(); - const Symmetry::SzSymmetryWithBoson &symmetry = static_cast(_model.symmetry()); - const Symmetry::SzSymmetryWithBoson::Sector §or = symmetry.sector(); - _el_symmetry.set_sector(Symmetry::NSymmetry::Sector(sector.n(), symmetry.comb().c_n_k(_Ns, sector.n()))); - H_up.init(up_size, 100); - } - - void zero_eigenapair() override { - - } - - void av(prec *v, prec *w, int n, bool clear) override { - - } - - private: - CRSMatrix _Hel; - CRSMatrix _Hbos; - Symmetry::NSymmetry _el_symmetry; - Model& _model; - - /// - int _interaction_size; - /// The total number of electons - int _Ns; - /// Total number of spins - int _ms; - /// Total number of bosons - int _Nb; - }; - - } -} -#endif //EDLIB_FERMIBOSONSTORAGE_H diff --git a/include/ext/HolsteinAndersonModel.h b/include/ext/HolsteinAndersonModel.h index 28ac323..a1a8e56 100644 --- a/include/ext/HolsteinAndersonModel.h +++ b/include/ext/HolsteinAndersonModel.h @@ -1,355 +1,336 @@ -// -// Created by iskakoff on 20/04/17. -// - -#ifndef EDLIB_HOLSTEINANDERSONMODEL_H -#define EDLIB_HOLSTEINANDERSONMODEL_H - - -#include -#include -#include -#include -#include "SzSymmetryWithBoson.h" - -namespace EDLib { - namespace Ext { - namespace Model { - namespace HolsteinAnderson { - template - class InnerState : public EDLib::Model::SingleImpurityAnderson::InnerState < prec > { - public: - using EDLib::Model::SingleImpurityAnderson::InnerState::a; - using EDLib::Model::SingleImpurityAnderson::InnerState::adag; - using EDLib::Model::SingleImpurityAnderson::InnerState::checkState; - virtual int valid(long long, int, int) const { return 0; }; - - virtual prec set(long long, long long &, int &, int, int) const {return 0.0;}; - }; - - template - class HybridisationInnerState : public InnerState < prec > { - - public: - HybridisationInnerState(int i1, int i2, int is, prec value) : _indicies(i1, i2), _spin(is), _value(value) {} - - const inline std::pair < int, int > &indicies() const { return _indicies; } - - virtual inline prec value() const { return _value; } - - inline int spin() const { return _spin; } - - virtual int valid(long long nst, int Ns, int Nb) const { - return (this->checkState(nst >> Nb, _indicies.first + _spin * Ns, Ns) * (1 - this->checkState(nst >> Nb, _indicies.second + _spin * Ns, Ns))); - } - - virtual prec set(long long nst, long long &k, int &sign, int Ns, int Nb) const { - long long k1, k2; - int isign1, isign2; - long long fnst = nst >> Nb; - long long bnst = nst & ((1 << Nb) - 1); - this->a(_indicies.first + _spin * Ns, fnst, k1, isign1, 2 * Ns); - this->adag(_indicies.second + _spin * Ns, k1, k2, isign2, 2 * Ns); - k = (k2 << Nb) + bnst; - sign = isign1 * isign2; - return _value; - } - - private: - std::pair < int, int > _indicies; - int _spin; - prec _value; - }; +#ifndef EDLIB_EXT_HOLSTEINANDERSONMODEL_H +#define EDLIB_EXT_HOLSTEINANDERSONMODEL_H + +#include +#include +#include +#include +#include +#include + +#include "edlib/FermionicModel.h" +#include "edlib/Gf.h" +#include "edlib/Parameters.h" +#include "edlib/SingleImpurityAndersonModel.h" +#include "ext/SzSymmetryWithBoson.h" + +namespace edlib { namespace ext { + + /** + * Extra parameters for Holstein-Anderson model. Mirrors the legacy + * EDLib::Ext::define_parameters() NBBITS/NBLEVEL keys plus the impurity + * orbital count (legacy: NORBITALS). Embedded in the model's ModelData struct + * so the model fits the standard (Parameters, ModelData) ctor signature. + */ + struct HolsteinAndersonParameters { + int nbbits = 3; ///< bits per bosonic mode + int nblevel = 1; ///< number of bosonic modes + int ml = 1; ///< number of impurity orbitals + double avg = 0; ///< average orbital occupation reference + }; + + namespace holstein { + + template + class InnerState : public edlib::siam::InnerState { + public: + using edlib::siam::InnerState::a; + using edlib::siam::InnerState::adag; + using edlib::siam::InnerState::checkState; + + int valid(long long, int) const override { return 0; } + void set(long long, long long&, int&, int) const override {} + Prec value() const override { return Prec(0); } + + virtual int valid(long long, int, int) const { return 0; } + virtual Prec set (long long, long long&, int&, int, int) const { return Prec(0); } + }; + + template + class HybridisationInnerState : public InnerState { + public: + HybridisationInnerState(int i1, int i2, int is, Prec val) + : _indicies(i1, i2), _spin(is), _value(val) {} + + const std::pair& indicies() const { return _indicies; } + Prec value() const override { return _value; } + int spin() const { return _spin; } + + int valid(long long nst, int Ns, int Nb) const override { + return this->checkState(nst >> Nb, _indicies.first + _spin * Ns, Ns) + * (1 - this->checkState(nst >> Nb, _indicies.second + _spin * Ns, Ns)); + } + Prec set(long long nst, long long& k, int& sign, int Ns, int Nb) const override { + long long k1, k2; + int isign1, isign2; + long long fnst = nst >> Nb; + long long bnst = nst & ((1ll << Nb) - 1); + this->a (_indicies.first + _spin * Ns, fnst, k1, isign1, 2 * Ns); + this->adag(_indicies.second + _spin * Ns, k1, k2, isign2, 2 * Ns); + k = (k2 << Nb) + bnst; + sign = isign1 * isign2; + return _value; + } - template - class BosonInnerState : public InnerState < prec > { + private: + std::pair _indicies; + int _spin; + Prec _value; + }; + + template + class BosonInnerState : public InnerState { + public: + BosonInnerState(int ib, int i, Prec value, int bit_cutoff, Prec avg, bool dag) + : _b(ib), _i(i), + _bit_cutoff(bit_cutoff), + _cutoff((1ll << bit_cutoff) - 1), + _dag(dag), _avg(avg), _value(value) {} + + int valid(long long nst, int Ns, int Nb) const override { + long long bnst = nst & ((1ll << Nb) - 1); + long long cbos = (bnst >> (_bit_cutoff * _b)) & _cutoff; + return (std::abs(this->checkState(nst >> Nb, _i, Ns) + + this->checkState(nst >> Nb, Ns + _i, Ns) + - _avg) > Prec(1e-9)) + * (_dag ? cbos < _cutoff : cbos > 0); + } + Prec set(long long nst, long long& k, int& sign, int Ns, int Nb) const override { + Prec N = (this->checkState(nst >> Nb, _i, Ns) + + this->checkState(nst >> Nb, Ns + _i, Ns) + - _avg); + long long bnst = nst & ((1ll << Nb) - 1); + long long cbos = (bnst >> (_bit_cutoff * _b)) & _cutoff; + if (_dag) { + k = nst + (1ll << (_bit_cutoff * _b)); + sign = 1; + return N * _value * static_cast(std::sqrt(static_cast(cbos + 1))); + } else { + k = nst - (1ll << (_bit_cutoff * _b)); + sign = 1; + return N * _value * static_cast(std::sqrt(static_cast(cbos))); + } + } - public: - BosonInnerState(int ib, int i, prec value, int cutoff, prec avg, bool dag) : _b(ib), _i(i), _value(value), _bit_cutoff(cutoff), _cutoff((1 << cutoff) - 1), _avg(avg), _dag(dag) {} + private: + int _b; + int _i; + int _bit_cutoff; + long long _cutoff; + bool _dag; + Prec _avg; + Prec _value; + }; - virtual int valid(long long nst, int Ns, int Nb) const { - // extract bosonic part of state - long long int bnst = nst & ((1 << Nb) - 1); - // extract current boson from N-dimensional representation - long long cbos = ((bnst >> (_bit_cutoff * _b)) & _cutoff); - return (std::abs(this->checkState(nst >> Nb, _i, Ns) + this->checkState(nst >> Nb, Ns+_i, Ns) - _avg) >1e-9) * (_dag ? (cbos) < _cutoff : cbos > 0); - } + } - virtual prec set(long long nst, long long &k, int &sign, int Ns, int Nb) const { - double N = (this->checkState(nst>>Nb, _i, Ns) + this->checkState(nst>>Nb, Ns+_i, Ns) - _avg); - long long int bnst = nst & ((1 << Nb) - 1); - long long cbos = ((bnst >> (_bit_cutoff * _b)) & _cutoff); - if (_dag) { - // create boson - k = nst + (1 << (_bit_cutoff * _b)); - sign = 1; - return N*_value * std::sqrt(cbos + 1); - } else { - // destroy boson - k = nst - (1 << (_bit_cutoff * _b)); - sign = 1; - return N * _value * std::sqrt(cbos); - } - } - private: - int _b; - int _i; - // number of bits per boson - int _bit_cutoff; - long long _cutoff; - bool _dag; - prec _avg; - prec _value; - }; + template + class HolsteinAndersonModel : public edlib::FermionicModel { + public: + using precision = Prec; + using SYMMETRY = SzSymmetryWithBoson; + using St = holstein::InnerState; + using HSt = holstein::HybridisationInnerState; + using BSt = holstein::BosonInnerState; + using Sector = typename SzSymmetryWithBoson::Sector; + + /** + * Caller-supplied Holstein-Anderson bath / model data. + * + * ml = ep.ml (impurity orbitals) + * Nk = p.nsites - ml (fermionic bath levels) + * nblevel = ep.nblevel (bosonic modes per orbital) + * + * tk [ml][ml] intracluster hopping + * U [ml][ml] on-site/inter-orbital interaction + * Eps [ml][nspins] impurity site energies + * Vk [ml][Nk][nspins] fermionic hybridisation + * Epsk [Nk][nspins] fermionic bath levels + * w0 [nblevel] bosonic mode frequencies + * W [ml][nblevel] bosonic couplings + */ + struct ModelData { + HolsteinAndersonParameters ep; + std::vector> tk; + std::vector> U; + std::vector> Eps; + std::vector>> Vk; + std::vector> Epsk; + std::vector w0; + std::vector> W; + Prec mu = Prec(0); + std::vector> sectors; + }; + + HolsteinAndersonModel(const Parameters& p, const ModelData& bath) + : HolsteinAndersonModel(p, bath.ep, bath) {} + + HolsteinAndersonModel(const Parameters& p, + const HolsteinAndersonParameters& ep, + const ModelData& bath) + : edlib::FermionicModel(p), + _symmetry(p, ep.nbbits * ep.nblevel, bath.sectors), + _Nb(ep.nbbits * ep.nblevel), + _ml(ep.ml), + _xmu(bath.mu), + _U(bath.U), + _Eps(bath.Eps), + _tk(bath.tk), + _Vk(bath.Vk), + _Epsk(bath.Epsk), + _w0(bath.w0), + _W(bath.W) { + if (p.nspins != 2) { + throw std::invalid_argument("HolsteinAndersonModel: NSPINS must be 2"); } - /** - * @brief HolsteinAndersonModel class - * - * @author iskakoff - */ - template - class HolsteinAndersonModel : public EDLib::Model::FermionicModel { - public: - typedef prec precision; - typedef typename Symmetry::SzSymmetryWithBoson SYMMETRY; - typedef typename HolsteinAnderson::InnerState < precision > St; - typedef typename HolsteinAnderson::HybridisationInnerState < precision > HSt; - typedef typename HolsteinAnderson::BosonInnerState < precision > BSt; - typedef typename Symmetry::SzSymmetryWithBoson::Sector Sector; - - - HolsteinAndersonModel(alps::params &p) : FermionicModel(p), _symmetry(p), _Nb(p["NBBITS"].as() * p["NBLEVEL"].as()), _ml(p["NORBITALS"].as()), - _U(p["NORBITALS"].as(), std::vector(p["NORBITALS"].as(), 0.0) ) { - std::string input = p["INPUT_FILE"]; - alps::hdf5::archive input_data(input.c_str(), "r"); - if (_ms != 2) { - throw std::invalid_argument("Incorrect values for the number of spins. Please check input file."); - } - _Ip = _ms * _Ns; - std::stringstream s; - s << "Bath/Vk" << "/values"; - input_data >> alps::make_pvp(s.str().c_str(), _Vk); - s.str(""); - s << "Bath/Epsk" << "/values"; - input_data >> alps::make_pvp(s.str().c_str(), _Epsk); - s.str(""); - s << "Bath/w0" << "/values"; - input_data >> alps::make_pvp(s.str().c_str(), _w0); - s.str(""); - s << "Bath/W" << "/values"; - input_data >> alps::make_pvp(s.str().c_str(), _W); - s.str(""); - s<<"tk/values"; - input_data >> alps::make_pvp(s.str().c_str(), _tk); - input_data >> alps::make_pvp("Eps0/values", _Eps); - input_data >> alps::make_pvp("mu", _xmu); - precision U; - input_data >> alps::make_pvp("U", _U); - - std::cout<<"ml:"<<_ml<<"\n"; - precision avg; - input_data >> alps::make_pvp("AVG", avg); - - int cutoff = p["NBBITS"].as(); - input_data.close(); - for (int i = 0; i< _ml; ++i) { - if (_Vk[i].size() != _Epsk.size()) { - throw std::invalid_argument("Incorrect bosonic bath. Please check input file."); - } - if (_w0.size() != _W[i].size()) { - throw std::invalid_argument("Incorrect bosonic bath. Please check input file."); - } - } - if(_w0.size() != p["NBLEVEL"].as()) { - throw std::invalid_argument("Incorrect bosonic bath. Number of bosonic levels in input file inconsistent with parameters. Please check input file."); - } - // Fermionic bath - // interorbital hoppings - for (int im = 0; im < _ml; ++im) { - for (int jm = 0; jm < im; ++jm) { - for(int is = 0; is< _ms; ++is) { - if (std::abs(_tk[im][jm]) > 1e-10) { - _F_states.push_back(HSt(im, jm, is, _tk[im][jm])); - _F_states.push_back(HSt(jm, im, is, _tk[im][jm])); - } - std::cout<<"hopping\n"; - } - } - } - for (int i = 0; i< _ml; ++i) { - for (int ik = 0; ik < _Vk[i].size(); ++ik) { - for (int is = 0; is < _ms; ++is) { - if (std::abs(_Vk[i][ik][is]) > 1e-10) { - int imk = _ml + ik; - // hoppings - // from impurity to bath - _F_states.push_back(HSt(i, imk, is, _Vk[i][ik][is])); - // from bath to impurity - _F_states.push_back(HSt(imk, i, is, _Vk[i][ik][is])); - } - } - } - // Bosonic bath - for (int ib = 0; ib < _W[i].size(); ++ib) { - std::cout<<"Wb["< 1e-10) { - // destroy boson - _B_states.push_back(BSt(ib, i, _W[i][ib], cutoff, avg, false)); - // create boson - _B_states.push_back(BSt(ib, i, _W[i][ib], cutoff, avg, true)); - } - } - } + if (static_cast(_w0.size()) != ep.nblevel) { + throw std::invalid_argument("HolsteinAndersonModel: w0.size() must equal nblevel"); + } + for (int i = 0; i < _ml; ++i) { + if (_Vk[i].size() != _Epsk.size()) { + throw std::invalid_argument("HolsteinAndersonModel: Vk[i].size() must equal Nk = Epsk.size()"); + } + if (_W[i].size() != _w0.size()) { + throw std::invalid_argument("HolsteinAndersonModel: W[i].size() must equal nblevel = w0.size()"); } + } - inline const precision diagonal(long long full_state) const { - precision xtemp = 0.0; - long long bosons = full_state & ((1 << _Nb) - 1); - // density term + // Fermionic bath - intracluster hopping + for (int im = 0; im < _ml; ++im) { + for (int jm = 0; jm < im; ++jm) { for (int is = 0; is < _ms; ++is) { - for (int ik = 0; ik < _Epsk.size(); ++ik) { - int ikm = _ml + ik; - xtemp += (_Epsk[ik][is] * checkState(full_state, ikm + is * _Ns, _Ip)); - } - for( int i = 0; i < _ml; ++i) { - xtemp += (_Eps[i][is] - _xmu) * checkState(full_state, i + is * _Ns, _Ip); + if (std::abs(_tk[im][jm]) > 1e-10) { + _F_states.emplace_back(im, jm, is, _tk[im][jm]); + _F_states.emplace_back(jm, im, is, _tk[im][jm]); } } - // interaction term - for( int i = 0; i < _ml; ++i) { - xtemp += _U[i][i] * checkState(full_state, i, _Ip) * checkState(full_state, _Ns + i, _Ip); - for( int j = 0; j < _ml; ++j) { - if(i!=j) { - xtemp += 0.5*(_U[i][j] * checkState(full_state, i, _Ip) * checkState(full_state, _Ns + j, _Ip) + - _U[i][j] * checkState(full_state, _Ns + i, _Ip) * checkState(full_state, j, _Ip) + - _U[i][j] * checkState(full_state, i, _Ip) * checkState(full_state, j, _Ip) + - _U[i][j] * checkState(full_state, _Ns + i, _Ip) * checkState(full_state, _Ns + j, _Ip) ); - } + } + } + // Impurity-bath hybridisation + for (int i = 0; i < _ml; ++i) { + for (int ik = 0; ik < static_cast(_Vk[i].size()); ++ik) { + for (int is = 0; is < _ms; ++is) { + if (std::abs(_Vk[i][ik][is]) > 1e-10) { + int imk = _ml + ik; + _F_states.emplace_back(i, imk, is, _Vk[i][ik][is]); + _F_states.emplace_back(imk, i, is, _Vk[i][ik][is]); } } - - int bit_cutoff = _Nb > 0 ? (_Nb / _w0.size()) :0; - long long cutoff = (1 << bit_cutoff) - 1; - for (int i = 0; i < _w0.size(); ++i) { - long long cbos = ((bosons >> (bit_cutoff * i)) & cutoff); - xtemp += cbos * (_w0[i]); - } - return xtemp; } - - inline int valid(const St &state, long long nst) { - return state.valid(nst, _Ns, _Nb); + // Bosonic bath + for (int ib = 0; ib < static_cast(_W[i].size()); ++ib) { + if (std::abs(_W[i][ib]) > 1e-10) { + _B_states.emplace_back(ib, i, _W[i][ib], ep.nbbits, ep.avg, false); + _B_states.emplace_back(ib, i, _W[i][ib], ep.nbbits, ep.avg, true); + } } + } + } - inline prec set(const St &state, long long nst, long long &k, int &sign) { - return state.set(nst, k, sign, _Ns, _Nb); - } + inline Prec diagonal(long long full_state) const { + Prec xtemp = Prec(0); + long long bosons = full_state & ((1ll << _Nb) - 1); - int inline checkState(long long nst, const int im, int Ip) const { - return (int) (((nst>>_Nb) & (1ll << (Ip - 1 - im))) >> (Ip - 1 - im)); + for (int is = 0; is < _ms; ++is) { + for (int ik = 0; ik < static_cast(_Epsk.size()); ++ik) { + int ikm = _ml + ik; + xtemp += _Epsk[ik][is] * checkState(full_state, ikm + is * _Ns, _Ip); } - - /** - * @brief Anihilate particle - * @param i [in] - site to anihilate particle - * @param jold [in] - current state - * @param k [out] - resulting state - * @param isign [out] - fermionic sign - */ - void inline a(int i, long long jold, long long &k, int &isign) { - long long sign = 0; - long long bos = jold & ((1<<_Nb) - 1); - jold >>= _Nb; - for (int ll = 0; ll < i; ++ll) { - sign += ((jold & (1ll << (_Ip - ll - 1))) != 0) ? 1 : 0; - } - isign = (sign % 2) == 0 ? 1 : -1; - k = ((jold - (1ll << (_Ip - i - 1))) << _Nb) + bos; + for (int i = 0; i < _ml; ++i) { + xtemp += (_Eps[i][is] - _xmu) * checkState(full_state, i + is * _Ns, _Ip); } + } - /** - * @brief Create particle - * \param i [in] - site to create particle - * \param jold [in] - current state - * \param k [out] - resulting state - * \param isign [out] - fermionic sign - */ - void inline adag(int i, long long jold, long long &k, int &isign) { - long long sign = 0; - long long bos = jold & ((1<<_Nb) - 1); - jold >>= _Nb; - for (int ll = 0; ll < i; ++ll) { - sign += ((jold & (1ll << (_Ip - ll - 1))) != 0) ? 1 : 0; + for (int i = 0; i < _ml; ++i) { + xtemp += _U[i][i] * checkState(full_state, i, _Ip) * checkState(full_state, _Ns + i, _Ip); + for (int j = 0; j < _ml; ++j) { + if (i != j) { + xtemp += Prec(0.5) + * (_U[i][j] * checkState(full_state, i, _Ip) * checkState(full_state, _Ns + j, _Ip) + + _U[i][j] * checkState(full_state, _Ns + i, _Ip) * checkState(full_state, j, _Ip) + + _U[i][j] * checkState(full_state, i, _Ip) * checkState(full_state, j, _Ip) + + _U[i][j] * checkState(full_state, _Ns + i, _Ip) * checkState(full_state, _Ns + j, _Ip)); } - isign = (sign % 2) == 0 ? 1 : -1; - k = ((jold + (1ll << (_Ip - i - 1))) << _Nb) + bos; } + } - size_t bos_dim() const { - return _w0.size(); - } + int bit_cutoff = _Nb > 0 ? (_Nb / static_cast(_w0.size())) : 0; + long long cutoff = (1ll << bit_cutoff) - 1; + for (int i = 0; i < static_cast(_w0.size()); ++i) { + long long cbos = (bosons >> (bit_cutoff * i)) & cutoff; + xtemp += static_cast(cbos) * _w0[i]; + } + return xtemp; + } - int number_of_bosons(long long nst, int orb) const { - long long bosons = nst & ((1 << _Nb) - 1); - int bit_cutoff = _Nb > 0 ? (_Nb / _w0.size()) :0; - long long cutoff = (1 << bit_cutoff) - 1; - long long cbos = ((bosons >> (bit_cutoff * orb)) & cutoff); - return int(cbos); - } + inline int valid(const St& state, long long nst) const { return state.valid(nst, _Ns, _Nb); } + inline Prec set (const St& state, long long nst, long long& k, int& sign) const { + return state.set(nst, k, sign, _Ns, _Nb); + } - inline const std::vector < HSt > &T_states() const { - return _F_states; - } + inline int checkState(long long nst, int im, int Ip) const { + return static_cast(((nst >> _Nb) & (1ll << (Ip - 1 - im))) >> (Ip - 1 - im)); + } - inline const std::vector < BSt > &V_states() const { - return _B_states; - } + inline void a(int i, long long jold, long long& k, int& isign) const { + long long sign = 0; + long long bos = jold & ((1ll << _Nb) - 1); + jold >>= _Nb; + for (int ll = 0; ll < i; ++ll) sign += ((jold & (1ll << (_Ip - ll - 1))) != 0) ? 1 : 0; + isign = (sign % 2) == 0 ? 1 : -1; + k = ((jold - (1ll << (_Ip - i - 1))) << _Nb) + bos; + } + inline void adag(int i, long long jold, long long& k, int& isign) const { + long long sign = 0; + long long bos = jold & ((1ll << _Nb) - 1); + jold >>= _Nb; + for (int ll = 0; ll < i; ++ll) sign += ((jold & (1ll << (_Ip - ll - 1))) != 0) ? 1 : 0; + isign = (sign % 2) == 0 ? 1 : -1; + k = ((jold + (1ll << (_Ip - i - 1))) << _Nb) + bos; + } - int interacting_orbitals() const { - return _ml; - } + std::size_t bos_dim() const { return _w0.size(); } - template - void bare_greens_function(alps::gf::three_index_gf < std::complex < double >, Mesh, alps::gf::index_mesh, alps::gf::index_mesh > &bare_gf, double beta) { - } + int number_of_bosons(long long nst, int orb) const { + long long bosons = nst & ((1ll << _Nb) - 1); + int bit_cutoff = _Nb > 0 ? (_Nb / static_cast(_w0.size())) : 0; + long long cutoff = (1ll << bit_cutoff) - 1; + long long cbos = (bosons >> (bit_cutoff * orb)) & cutoff; + return static_cast(cbos); + } - inline const Symmetry::SzSymmetryWithBoson &symmetry() const { - return _symmetry; - } + const std::vector& T_states() const { return _F_states; } + const std::vector& V_states() const { return _B_states; } - inline Symmetry::SzSymmetryWithBoson &symmetry() { - return _symmetry; - } + int interacting_orbitals() const { return _ml; } - private: - Symmetry::SzSymmetryWithBoson _symmetry; - int _Nb; - // impurity parameters - // number of sites in cluster impurity - int _ml; - // chemical potential - precision _xmu; - // local interaction - std::vector< std::vector< precision > > _U; - std::vector< std::vector< precision > > _Eps; - // intracluster hoppings - std::vector< std::vector < precision > > _tk; - // fermionic bath - std::vector< std::vector< std::vector < precision > > > _Vk; - std::vector< std::vector < precision > > _Epsk; - // bosonic bath - std::vector < precision > _w0; - std::vector< std::vector < precision > > _W; - - - // Fermionic non-digaonal part of Hamiltonian - std::vector < HSt > _F_states; - // Bosonic non-digaonal part of Hamiltonian - std::vector < BSt > _B_states; - }; + SzSymmetryWithBoson& symmetry() { return _symmetry; } + const SzSymmetryWithBoson& symmetry() const { return _symmetry; } + template + void bare_greens_function(edlib::Gf, 3>& /*bare_gf*/, + const Mesh& /*mesh*/, + double /*beta*/) const { + // Mirror of legacy: stub. To be implemented when needed. } - } -} -#endif //EDLIB_HOLSTEINANDERSONMODEL_H + private: + SzSymmetryWithBoson _symmetry; + int _Nb; + int _ml; + Prec _xmu; + std::vector> _U; + std::vector> _Eps; + std::vector> _tk; + std::vector>> _Vk; + std::vector> _Epsk; + std::vector _w0; + std::vector> _W; + + std::vector _F_states; + std::vector _B_states; + }; + +}} // namespace edlib::ext + +#endif diff --git a/include/ext/HolsteinAndersonParameter.h b/include/ext/HolsteinAndersonParameter.h deleted file mode 100644 index b2a08b4..0000000 --- a/include/ext/HolsteinAndersonParameter.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by iskakoff on 01/06/17. -// - -#ifndef EDLIB_HOLSTEINANDERSONPARAMETER_H -#define EDLIB_HOLSTEINANDERSONPARAMETER_H - -namespace EDLib { - namespace Ext { - /** - * Defines additional parameters for Holstein Anderson impurity model - * @param params - ALPSCore parameters container - */ - void define_parameters(alps::params ¶ms) { - params.define < int >("NBBITS", 3, "Total maximum number of bits per bosonic level in the system."); - params.define < int >("NBLEVEL", 1, "Total maximum number of bosonic levels in the system."); - } - } -} - -#endif //EDLIB_HOLSTEINANDERSONPARAMETER_H diff --git a/include/ext/SzSymmetryWithBoson.h b/include/ext/SzSymmetryWithBoson.h index 269c39b..aadb3e4 100644 --- a/include/ext/SzSymmetryWithBoson.h +++ b/include/ext/SzSymmetryWithBoson.h @@ -1,235 +1,224 @@ -// -// Created by iskakoff on 20/04/17. -// +#ifndef EDLIB_EXT_SZSYMMETRYWITHBOSON_H +#define EDLIB_EXT_SZSYMMETRYWITHBOSON_H -#ifndef EDLIB_NSYMMETRYWITHBOSON_H -#define EDLIB_NSYMMETRYWITHBOSON_H - -#include -#include +#include +#include +#include +#include #include -#include - -namespace EDLib { - namespace Symmetry { - - /** - * @brief SzSymmetryWithBoson class - * - * @author iskakoff - */ - class SzSymmetryWithBoson : public Symmetry { +#include + +#include "edlib/Combination.h" +#include "edlib/Parameters.h" +#include "edlib/Symmetry.h" + +namespace edlib { namespace ext { + + /** + * Sz-conserving fermionic symmetry augmented with a bosonic state encoded + * in the low `boson_bits_count` bits of each basis state. Sector + * dimensions are size_fermion(nup, ndown) * 2^boson_bits_count. + */ + class SzSymmetryWithBoson : public Symmetry { + public: + class Sector { public: - class Sector { - public: - friend class SzSymmetryWithBoson; - - friend std::ostream &operator<<(std::ostream &o, const SzSymmetryWithBoson::Sector &c) { return o << " (nup: " << c._nup << " ndown: " << c._ndown<<" bosons cutoff: "<< (1<() * p["NBLEVEL"].as()), - _maximum_bosons((1<<(p["NBBITS"].as() * p["NBLEVEL"].as())) - 1), - _current_sector(-1, -1, 0, 0), _Ns(p["NSITES"]), upstate(_Ns + 1), dostate(_Ns + 1), - _comb(_Ns), basis(_Ns + 1), ninv(_Ns + 1, std::vector < int >(1 << _Ns, 0)), - _first(true) { - initial_fill(); - if (p.exists("arpack.SECTOR") && bool(p["arpack.SECTOR"])) { - std::vector < std::vector < int > > sectors; - std::string input = p["INPUT_FILE"]; - alps::hdf5::archive input_file(input, "r"); - input_file >> alps::make_pvp("sectors/values", sectors); - input_file.close(); - for (int i = 0; i < sectors.size(); ++i) { - _sectors.push(SzSymmetryWithBoson::Sector(sectors[i][0], sectors[i][1], _boson_bits_count, (size_t) (_comb.c_n_k(_Ns, sectors[i][0]) * _comb.c_n_k(_Ns, sectors[i][1]) * (_maximum_bosons+1)))); - } - } else { - for (int i = 0; i <= _Ns; ++i) { - for (int j = 0; j <= _Ns; ++j) { - _sectors.push(SzSymmetryWithBoson::Sector(i, j, _boson_bits_count, (size_t) (_comb.c_n_k(_Ns, i) * _comb.c_n_k(_Ns, j) * (_maximum_bosons + 1)))); - } - } - } - } - SzSymmetryWithBoson(int N, int Nb) : _current_sector(-1, -1, 0, 0), _Ns(N), upstate(N + 1), dostate(N + 1), - _comb(N), basis(N + 1), ninv(N + 1, std::vector < int >(1 << N, 0)), - _first(true) { - _boson_bits_count = Nb; - _maximum_bosons = (1<>& sector_list = {}) + : Symmetry(), + _current_sector(-1, -1, boson_bits_count, 0), + _Ns(N), + _boson_bits_count(boson_bits_count), + _maximum_bosons((1 << boson_bits_count) - 1), + upstate(N + 1), dostate(N + 1), + _comb(N), basis(N + 1), + ninv(N + 1, std::vector(1 << N, 0)), + _first(true) { + initial_fill(); + populate_sectors(sector_list); + } - virtual int index(long long state, const SzSymmetryWithBoson::Sector §or) { - long long fnst = state >> _boson_bits_count; - long long up = fnst >> _Ns; - long long down = fnst & ((1ll << _Ns) - 1); - int cup = _comb.c_n_k(_Ns, sector.nup()); - int cdo = _comb.c_n_k(_Ns, sector.ndown()); - int fermions_index = ninv[sector.nup()][(int) up] * (cdo) + ninv[sector.ndown()][(int) down]; - int bosons = state & (_maximum_bosons); - return sector.fsize() * bosons + fermions_index; - } + /** + * @param p Core parameters (uses p.nsites). + * @param boson_bits_count total bits reserved for the bosonic state + * (legacy: NBBITS * NBLEVEL). + * @param sector_list optional sector restriction; empty = all sectors. + */ + SzSymmetryWithBoson(const Parameters& p, + int boson_bits_count, + const std::vector>& sector_list = {}) + : SzSymmetryWithBoson(p.nsites, boson_bits_count, sector_list) {} + + bool next_state() override { + int u = (_ind % _current_sector.fsize()) / _comb.c_n_k(_Ns, _current_sector.ndown()); + int d = (_ind % _current_sector.fsize()) % _comb.c_n_k(_Ns, _current_sector.ndown()); + long long st = basis[_current_sector.nup()][u]; + st <<= _Ns; + st += basis[_current_sector.ndown()][d]; + _bosons = _ind / _current_sector.fsize(); + st <<= _boson_bits_count; + st += _bosons; + state() = st; + ++_ind; + return _ind <= static_cast(_current_sector.size()); + } - virtual void reset() override { - state() = 0ll; - _ind = 0; - _bosons = 0; - } + int index(long long st) override { return index(st, _current_sector); } + + int index(long long state, const Sector& sector) { + long long fnst = state >> _boson_bits_count; + long long up = fnst >> _Ns; + long long down = fnst & ((1ll << _Ns) - 1); + int cdo = _comb.c_n_k(_Ns, sector.ndown()); + int fermions_index = ninv[sector.nup()][static_cast(up)] * cdo + + ninv[sector.ndown()][static_cast(down)]; + int bosons = state & _maximum_bosons; + return sector.fsize() * bosons + fermions_index; + } - virtual void init() override { - reset(); - _comb.init_state(_current_sector.nup(), upstate); - _comb.init_state(_current_sector.ndown(), dostate); - }; - - virtual bool next_sector() override { - if (_sectors.empty()) - return false; - _current_sector = _sectors.front(); - _sectors.pop(); - return true; - } + void reset() override { + state() = 0ll; + _ind = 0; + _bosons = 0; + } - void set_sector(const SzSymmetryWithBoson::Sector §or) { - _current_sector = sector; - init(); - } + void init() override { + reset(); + _comb.init_state(_current_sector.nup(), upstate); + _comb.init_state(_current_sector.ndown(), dostate); + } - const SzSymmetryWithBoson::Sector §or() const { - return _current_sector; - } + bool next_sector() override { + if (_sectors.empty()) return false; + _current_sector = _sectors.front(); + _sectors.pop(); + return true; + } - inline const Combination &comb() const { - return _comb; - } -#ifdef USE_MPI - void set_offset(size_t offset) {_ind += offset;} -#endif + void set_sector(const Sector& s) { _current_sector = s; init(); } + const Sector& sector() const { return _current_sector; } - int maximum_bosons() const { - return _maximum_bosons; - } + int maximum_bosons() const { return _maximum_bosons; } - bool can_create_particle(int spin) override { - return spin == 0 ? _current_sector.nup() < _Ns - 1 : _current_sector.ndown() < _Ns - 1; - } + bool can_create_particle(int spin) override { + return spin == 0 ? _current_sector.nup() < _Ns - 1 + : _current_sector.ndown() < _Ns - 1; + } + bool can_destroy_particle(int spin) override { + return spin == 0 ? _current_sector.nup() > 0 + : _current_sector.ndown() > 0; + } - bool can_destroy_particle(int spin) override { - return spin == 0 ? _current_sector.nup() > 0 : _current_sector.ndown() > 0; - } + Sector destroy_particle(int spin) { + return Sector(_current_sector.nup() - (1 - spin), + _current_sector.ndown() - spin, + _boson_bits_count, + _comb.c_n_k(_Ns, _current_sector.nup() - (1 - spin)) + * _comb.c_n_k(_Ns, _current_sector.ndown() - spin) + * (_maximum_bosons + 1)); + } + Sector create_particle(int spin) { + return Sector(_current_sector.nup() + (1 - spin), + _current_sector.ndown() + spin, + _boson_bits_count, + _comb.c_n_k(_Ns, _current_sector.nup() + (1 - spin)) + * _comb.c_n_k(_Ns, _current_sector.ndown() + spin) + * (_maximum_bosons + 1)); + } - SzSymmetryWithBoson::Sector destroy_particle(int spin) { - return Sector(_current_sector.nup() - (1 - spin), _current_sector.ndown() - spin, _boson_bits_count, - _comb.c_n_k(_Ns, _current_sector.nup() - (1 - spin)) * _comb.c_n_k(_Ns, _current_sector.ndown() - spin) * (_maximum_bosons + 1)); - } + const Combination& comb() const { return _comb; } + std::queue& sectors() { return _sectors; } - SzSymmetryWithBoson::Sector create_particle(int spin) { - return Sector(_current_sector.nup() + (1 - spin), _current_sector.ndown() + spin, _boson_bits_count, - _comb.c_n_k(_Ns, _current_sector.nup() + (1 - spin)) * _comb.c_n_k(_Ns, _current_sector.ndown() + spin)* (_maximum_bosons + 1)); - } +#ifdef USE_MPI + void set_offset(std::size_t offset) { _ind += offset; } +#endif - private: - void initial_fill() { - _Ip = 2 * _Ns; - _ind = 0; + private: + void populate_sectors(const std::vector>& sector_list) { + if (!sector_list.empty()) { + for (const auto& s : sector_list) { + _sectors.push(Sector(s[0], s[1], _boson_bits_count, + static_cast(_comb.c_n_k(_Ns, s[0]) + * _comb.c_n_k(_Ns, s[1]) + * (_maximum_bosons + 1)))); + } + } else { for (int i = 0; i <= _Ns; ++i) { - int cnk = _comb.c_n_k(_Ns, i); - basis[i].reserve(cnk); - for (int k = 0; k < cnk; ++k) { - basis[i][k] = next_basis(_Ns, i, upstate, k == 0); - ninv[i][basis[i][k]] = k; + for (int j = 0; j <= _Ns; ++j) { + _sectors.push(Sector(i, j, _boson_bits_count, + static_cast(_comb.c_n_k(_Ns, i) + * _comb.c_n_k(_Ns, j) + * (_maximum_bosons + 1)))); } } - }; - - int next_basis(int n, int k, std::vector < int > &old, bool start) { - int res = 0; - if (start) { - _comb.init_state(k, old); - //pass - } else { - _comb.next_combination(n, k, old); - } - for (int i = 0; i < k; i++) { - res += (1 << old[i]); - } - return res; } + } - SzSymmetryWithBoson::Sector _current_sector; - std::queue < SzSymmetryWithBoson::Sector > _sectors; - int _Ns; - int _Ip; - int _ind; - int _bosons; - int _boson_bits_count; - int _maximum_bosons; - - std::vector < int > upstate; - std::vector < int > dostate; - std::vector < std::vector < int > > basis; - std::vector < std::vector < int > > ninv; - Combination _comb; - bool _first; - protected: - public: - std::queue §ors() { - return _sectors; + void initial_fill() { + _Ip = 2 * _Ns; + _ind = 0; + for (int i = 0; i <= _Ns; ++i) { + int cnk = _comb.c_n_k(_Ns, i); + basis[i].resize(cnk); + for (int k = 0; k < cnk; ++k) { + basis[i][k] = next_basis(_Ns, i, upstate, k == 0); + ninv[i][basis[i][k]] = k; + } } - }; - - } + } - namespace hdf5 { - template<> - void EDLib::hdf5::HDF5Utils::save(const typename EDLib::Symmetry::SzSymmetryWithBoson::Sector& s, alps::hdf5::archive & ar, const std::string& path) { - ar[path + "/nup"]<& old, bool start) { + int res = 0; + if (start) _comb.init_state(k, old); + else _comb.next_combination(n, k, old); + for (int i = 0; i < k; ++i) res += (1 << old[i]); + return res; } - } -} + Sector _current_sector; + std::queue _sectors; + int _Ns; + int _Ip = 0; + int _ind = 0; + int _bosons = 0; + int _boson_bits_count; + int _maximum_bosons; + + std::vector upstate; + std::vector dostate; + std::vector> basis; + std::vector> ninv; + Combination _comb; + bool _first; + }; + +}} // namespace edlib::ext -#endif //EDLIB_NSYMMETRYWITHBOSON_H +#endif diff --git a/main.cpp b/main.cpp index ce59b09..d49fffd 100644 --- a/main.cpp +++ b/main.cpp @@ -1,17 +1,17 @@ #include -#include -#include "edlib/Hamiltonian.h" -#include "edlib/SzSymmetry.h" -#include "edlib/SOCRSStorage.h" -#include "edlib/CRSStorage.h" -#include "edlib/HubbardModel.h" -#include "edlib/GreensFunction.h" -#include "edlib/ChiLoc.h" -#include "edlib/HDF5Utils.h" -#include "edlib/SpinResolvedStorage.h" -#include "edlib/StaticObservables.h" -#include "edlib/MeshFactory.h" +#include +#include "edlib/alpscore/Hamiltonian.h" +#include "edlib/alpscore/SzSymmetry.h" +#include "edlib/alpscore/SOCRSStorage.h" +#include "edlib/alpscore/CRSStorage.h" +#include "edlib/alpscore/HubbardModel.h" +#include "edlib/alpscore/GreensFunction.h" +#include "edlib/alpscore/ChiLoc.h" +#include "edlib/alpscore/HDF5Utils.h" +#include "edlib/alpscore/SpinResolvedStorage.h" +#include "edlib/alpscore/StaticObservables.h" +#include "edlib/alpscore/MeshFactory.h" // freq, I, J, spin template diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cd49e22..5e4e40d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,33 +1,31 @@ - +add_executable(HubbardTest Hubbard_Test.cpp) +add_executable(LanczosTest Lanczos_Test.cpp) add_executable(SzSymmetryTest SzSymmetry_Test.cpp) -add_executable(SzSymmetryWithBosonTest SzSymmetryWithBoson_Test.cpp) -add_executable(NSymmetryTest NSymmetry_Test.cpp) -add_executable(HubbardModelTest HubbardModel_Test.cpp) -add_executable(SpinResolvedStorage SRS.cpp SpinResolvedStorage_Test.cpp) -add_executable(StaticObservablesTest StaticObservables_Test.cpp) -add_executable(StatisticsTest Statistics_Test.cpp) -add_executable(LanczosTest Lanczos_Test.cpp) - +add_executable(NSymmetryTest NSymmetry_Test.cpp) +if(USE_MPI) + add_executable(MpiTest Mpi_Test.cpp) +endif() +set(core_test_libs + EDLib + arnoldi::arnoldi + ${BLAS_LIBRARIES} + ${LAPACK_LIBRARIES} + ${GTEST_LIBRARY} + ${GTEST_MAIN_LIBRARY}) -target_link_libraries(SzSymmetryTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) -target_link_libraries(SzSymmetryWithBosonTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) -target_link_libraries(NSymmetryTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) -target_link_libraries(HubbardModelTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) -target_link_libraries(SpinResolvedStorage EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) -target_link_libraries(StaticObservablesTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) -target_link_libraries(StatisticsTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) -target_link_libraries(LanczosTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) +target_link_libraries(HubbardTest ${core_test_libs}) +target_link_libraries(LanczosTest ${core_test_libs}) +target_link_libraries(SzSymmetryTest ${core_test_libs}) +target_link_libraries(NSymmetryTest ${core_test_libs}) -if (USE_MPI) +if(USE_MPI) + target_link_libraries(HubbardTest ${parlibs}) + target_link_libraries(LanczosTest ${parlibs}) target_link_libraries(SzSymmetryTest ${parlibs}) - target_link_libraries(SzSymmetryWithBosonTest ${parlibs}) - target_link_libraries(NSymmetryTest ${parlibs}) - target_link_libraries(HubbardModelTest ${parlibs}) - target_link_libraries(SpinResolvedStorage ${parlibs}) - target_link_libraries(StaticObservablesTest ${parlibs}) - target_link_libraries(StatisticsTest ${parlibs}) - target_link_libraries(LanczosTest ${parlibs}) -endif(USE_MPI) - + target_link_libraries(NSymmetryTest ${parlibs}) + target_link_libraries(MpiTest ${parlibs} ${core_test_libs}) +endif() -file(COPY input DESTINATION ${CMAKE_BINARY_DIR}/test) +# LanczosTest reads test/input/GF_Chi/*.dat from CWD; copy it in. +file(COPY ${PROJECT_SOURCE_DIR}/test/input + DESTINATION ${CMAKE_BINARY_DIR}/test) diff --git a/test/Hubbard_Test.cpp b/test/Hubbard_Test.cpp new file mode 100644 index 0000000..5d77f08 --- /dev/null +++ b/test/Hubbard_Test.cpp @@ -0,0 +1,87 @@ +// Core-only Hubbard test. Mirrors the legacy HubbardModelTest 4-ring case +// using hand-built bath data (no HDF5). + +#include +#include + +#include + +#ifdef USE_MPI +#include +#endif + +namespace { + +edlib::Parameters make_params() { + edlib::Parameters p; + p.nsites = 4; + p.nspins = 2; + p.arpack_nev = 1; + p.storage_max_size = 576; + p.storage_max_dim = 36; + p.lanc_beta = 10.0; + p.lanc_boltzmann_cutoff = 1e-12; + return p; +} + +edlib::HubbardModel::ModelData make_4ring_bath() { + edlib::HubbardModel::ModelData b; + b.hopping = { + { 0.0, -1.0, 0.0, -1.0}, + {-1.0, 0.0, -1.0, 0.0}, + { 0.0, -1.0, 0.0, -1.0}, + {-1.0, 0.0, -1.0, 0.0} + }; + b.U = {5.0, 5.0, 5.0, 5.0}; + b.mu = {2.5, 2.5, 2.5, 2.5}; + b.magnetic_field = {0.01, 0.01, 0.01, 0.01}; + return b; +} + +} + +TEST(HubbardCore, ReferenceTest) { + auto p = make_params(); + auto bath = make_4ring_bath(); + +#ifdef USE_MPI + using HamType = edlib::SRSHubbardHamiltonian; + HamType ham(p, bath, MPI_COMM_WORLD); +#else + using HamType = edlib::SOCSRHubbardHamiltonian; + HamType ham(p, bath); +#endif + + ham.diag(); + + // Ground state reference from arXiv:cond-mat/0101476 + ASSERT_NEAR(ham.eigenpairs().begin()->eigenvalue(), -11.8443, 1e-4); + ASSERT_EQ(ham.eigenpairs().begin()->sector().nup(), 2); + ASSERT_EQ(ham.eigenpairs().begin()->sector().ndown(), 2); + + edlib::StaticObservables so(p); + auto obs = so.calculate_static_observables(ham); +#ifdef USE_MPI + for (auto& kv : obs) + MPI_Bcast(kv.second.data(), kv.second.size(), MPI_DOUBLE, 0, ham.comm()); +#endif + + for (int orb = 0; orb < ham.model().interacting_orbitals(); ++orb) { + ASSERT_NEAR(obs[edlib::StaticObservables::_N_][orb], 1.0, 1e-8); + ASSERT_GT(obs[edlib::StaticObservables::_N_UP_][orb], + obs[edlib::StaticObservables::_N_DN_][orb]); + ASSERT_GT(obs[edlib::StaticObservables::_M_][orb], 0.0); + } +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); +#ifdef USE_MPI + MPI_Init(&argc, &argv); +#endif + int res = RUN_ALL_TESTS(); +#ifdef USE_MPI + MPI_Finalize(); +#endif + return res; +} diff --git a/test/Lanczos_Test.cpp b/test/Lanczos_Test.cpp index ede6176..0539b0e 100644 --- a/test/Lanczos_Test.cpp +++ b/test/Lanczos_Test.cpp @@ -1,142 +1,147 @@ -// -// Created by iskakoff on 22/08/16. -// +// Core-only Lanczos / GF / Chi test. Mirrors legacy LanczosTest's reference +// comparison against test/input/GF_Chi/{gom1,xiats,xiatd}.dat using only the +// new edlib:: API. -#include -#include "edlib/Hamiltonian.h" -#include "edlib/HubbardModel.h" -#include "edlib/Storage.h" -#include "edlib/EDParams.h" -#include "edlib/StaticObservables.h" -#include "edlib/GreensFunction.h" -#include "edlib/ChiLoc.h" -#include "edlib/MeshFactory.h" +#include +#include +#include +#include +#include -#ifdef USE_MPI +#include -class HubbardModelTestEnv : public ::testing::Environment { - protected: +#include +#include +#include +#include - ~HubbardModelTestEnv(){}; +#ifdef USE_MPI +#include +#endif -}; +namespace { -::testing::Environment* const foo_env = AddGlobalTestEnvironment(new HubbardModelTestEnv); +double frob_diff(const std::vector>& a, + const std::vector>& b) { + double s = 0.0; + for (std::size_t i = 0; i < a.size(); ++i) { + auto d = a[i] - b[i]; + s += d.real() * d.real() + d.imag() * d.imag(); + } + return std::sqrt(s); +} -#endif +} +TEST(LanczosCore, ReferenceGFAndChi) { + edlib::Parameters p; + p.nsites = 4; + p.nspins = 2; + p.arpack_nev = 100; + p.storage_max_size = 864; + p.storage_max_dim = 36; + p.lanc_beta = 20.0; + p.lanc_nomega = 1000; + p.lanc_nlanc = 100; + p.lanc_emin = -4.0; + p.lanc_emax = 4.0; + p.lanc_boltzmann_cutoff = 1e-12; -TEST(HubbardModelTest, ReferenceTest) { - alps::params p; - EDLib::define_parameters(p); - p["NSITES"]=4; - p["NSPINS"]=2; - p["INPUT_FILE"]="test/input/GF_Chi/input.h5"; - p["arpack.SECTOR"]=false; - p["storage.MAX_SIZE"]=864; - p["storage.MAX_DIM"]=36; - p["storage.EIGENVALUES_ONLY"]=0; - p["storage.ORBITAL_NUMBER"]=1; - p["arpack.NEV"]=100; - p["lanc.BETA"]=20; - p["lanc.EMIN"]=-4.0; - p["lanc.EMAX"]=4.0; - p["lanc.NOMEGA"]=1000; + edlib::HubbardModel::ModelData bath; + bath.hopping = { + { 0.0, 1.0, 1.0, -0.3}, + { 1.0, 0.0, -0.3, 1.0}, + { 1.0, -0.3, 0.0, 1.0}, + {-0.3, 1.0, 1.0, 0.0} + }; + bath.U = {3.0, 3.0, 3.0, 3.0}; + bath.mu = {1.0, 1.0, 1.0, 1.0}; #ifdef USE_MPI - typedef EDLib::SRSHubbardHamiltonian HamType; + using HamType = edlib::SRSHubbardHamiltonian; + HamType ham(p, bath, MPI_COMM_WORLD); #else - typedef EDLib::CSRHubbardHamiltonian HamType; -#endif - HamType ham(p -#ifdef USE_MPI - , MPI_COMM_WORLD + using HamType = edlib::CSRHubbardHamiltonian; + HamType ham(p, bath); #endif - ); - ham.diag(); - // Compute our GFs for the reference model. - EDLib::gf::GreensFunction < HamType, EDLib::MatsubaraMeshFactory, alps::gf::statistics::statistics_type> greensFunction(p, ham,alps::gf::statistics::statistics_type::FERMIONIC); - greensFunction.compute(); - auto G = greensFunction.G(); - EDLib::StaticObservables so(p); - std::map> observables = so.calculate_static_observables(ham); + // Single-particle GF on orbital 0 + edlib::MatsubaraMesh fmesh(p.lanc_beta, p.lanc_nomega, edlib::Statistics::Fermionic); + edlib::GreensFunction gf(p, ham, fmesh, {{0, 0}}); + gf.compute(); + + edlib::StaticObservables so(p); + auto obs = so.calculate_static_observables(ham); #ifdef USE_MPI - // StaticObservables reduces to rank 0; broadcast so the averages below - // are identical on every rank before being fed into susc.compute(). - for(auto& kv : observables){ + for (auto& kv : obs) MPI_Bcast(kv.second.data(), kv.second.size(), MPI_DOUBLE, 0, ham.comm()); - } #endif - EDLib::gf::ChiLoc susc(p, ham, alps::gf::statistics::statistics_type::BOSONIC); - // compute average magnetic moment - double avg = 0.0; - for(auto x : observables[so._M_]) { - avg += x / (2.0*observables[so._M_].size()); - } - // compute spin susceptibility - susc.compute>(&avg); - auto ChiSz = susc.G(); - // compute average occupancy moment - avg = 0.0; - for(auto x : observables[so._N_]) { - avg += x / double(observables[so._N_].size()); - } - // Compute sharge susceptibility - susc.compute>(&avg); - auto ChiN = susc.G(); - - // Read in the reference GFs. - // FIXME Desperate kludges. Must take the indextypes from GFs instead. - auto G_file = G; - std::ifstream infile("test/input/GF_Chi/gom1.dat"); - for(size_t ii = 0; ii < 200; ++ii){ - double omega, real, imag; - infile >> omega >> real >> imag; - for(size_t is = 0; is < ham.model().spins(); ++is){ - G_file(alps::gf::generic_index>(ii), alps::gf::generic_index(0), alps::gf::generic_index(is)) = std::complex(real, imag); - } - } - infile.close(); - auto ChiSz_file = ChiSz; - infile.open("test/input/GF_Chi/xiats.dat"); - for(size_t ii = 0; ii < 200; ++ii){ - double omega, real, imag; - infile >> omega >> real >> imag; - // S_z = 0.5 M, = 0.25 - ChiSz_file(alps::gf::generic_index>(ii), alps::gf::generic_index(0)) = std::complex(-0.25 * real, 0.0); + + double avg_M = 0; + for (double m : obs[edlib::StaticObservables::_M_]) { + avg_M += m / (2.0 * obs[edlib::StaticObservables::_M_].size()); } - infile.close(); - auto ChiN_file = ChiN; - infile.open("test/input/GF_Chi/xiatd.dat"); - for(size_t ii = 0; ii < 200; ++ii){ - double omega, real, imag; - infile >> omega >> real >> imag; - ChiN_file(alps::gf::generic_index>(ii), alps::gf::generic_index(0)) = std::complex(-real, 0.0); + double avg_N = 0; + for (double n : obs[edlib::StaticObservables::_N_]) { + avg_N += n / obs[edlib::StaticObservables::_N_].size(); } - infile.close(); + + edlib::MatsubaraMesh bmesh(p.lanc_beta, p.lanc_nomega, edlib::Statistics::Bosonic); + edlib::ChiLoc susc(p, ham, bmesh, {{0, 0}}); + susc.compute>(&avg_M); + auto chiSz = susc.G(); + susc.compute>(&avg_N); + auto chiN = susc.G(); #ifdef USE_MPI - // GreensFunction / ChiLoc accumulate into _G only on rank 0 - // (see GreensFunction.h::local_contribution etc.). Compare against the - // reference only there; other ranks would see zero-filled buffers. - int _rk = 0; - MPI_Comm_rank(ham.comm(), &_rk); - if(_rk == 0) + int rank; MPI_Comm_rank(ham.comm(), &rank); + if (rank != 0) return; #endif + + const std::string root = "test/input/GF_Chi/"; + + // Compare 200 frequencies × 2 spins for G + std::vector> G_ours(200 * 2), G_ref(200 * 2); + { + std::ifstream f(root + "gom1.dat"); + ASSERT_TRUE(f.is_open()) << "Cannot open " << root << "gom1.dat"; + for (int ii = 0; ii < 200; ++ii) { + double w, r, i; f >> w >> r >> i; + for (int is = 0; is < 2; ++is) { + G_ref [ii * 2 + is] = {r, i}; + G_ours[ii * 2 + is] = gf.G()(ii, 0, is); + } + } + } + EXPECT_LT(frob_diff(G_ours, G_ref), 5e-10); + + std::vector> Cs_ours(200), Cs_ref(200); + { + std::ifstream f(root + "xiats.dat"); + ASSERT_TRUE(f.is_open()); + for (int ii = 0; ii < 200; ++ii) { + double w, r, i; f >> w >> r >> i; + Cs_ref [ii] = {-0.25 * r, 0.0}; // legacy convention = 0.25 + Cs_ours[ii] = chiSz(ii, 0); + } + } + EXPECT_LT(frob_diff(Cs_ours, Cs_ref), 1e-9); + + std::vector> Cn_ours(200), Cn_ref(200); { - // Subtract the reference GF from our result, the norm() is then the largest diff. - G -= G_file; - ASSERT_NEAR(G.norm(), 0.0, 1e-10); - ChiSz -= ChiSz_file; - ASSERT_NEAR(ChiSz.norm(), 0.0, 1e-9); - ChiN -= ChiN_file; - ASSERT_NEAR(ChiN.norm(), 0.0, 1e-9); + std::ifstream f(root + "xiatd.dat"); + ASSERT_TRUE(f.is_open()); + for (int ii = 0; ii < 200; ++ii) { + double w, r, i; f >> w >> r >> i; + Cn_ref [ii] = {-r, 0.0}; + Cn_ours[ii] = chiN(ii, 0); + } } + EXPECT_LT(frob_diff(Cn_ours, Cn_ref), 1e-9); } -int main(int argc, char **argv) { +int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); #ifdef USE_MPI MPI_Init(&argc, &argv); diff --git a/test/Mpi_Test.cpp b/test/Mpi_Test.cpp new file mode 100644 index 0000000..67083e3 --- /dev/null +++ b/test/Mpi_Test.cpp @@ -0,0 +1,101 @@ +// Core-only MPI test. Exercises the alpscore-free edlib:: API on the +// MPI-distributed SpinResolvedStorage Hamiltonian and asserts the parallel +// result is consistent across all ranks and matches the serial reference. +// +// Only meaningful when built with USE_MPI and launched under mpirun with +// >= 1 rank; with 2+ ranks it catches storage-distribution / reduction bugs +// that a single-process run cannot. + +#include + +#include + +#ifdef USE_MPI +#include + +namespace { + +edlib::Parameters make_params() { + edlib::Parameters p; + p.nsites = 4; + p.nspins = 2; + p.arpack_nev = 1; + p.storage_max_size = 576; + p.storage_max_dim = 36; + p.lanc_beta = 10.0; + p.lanc_boltzmann_cutoff = 1e-12; + return p; +} + +edlib::HubbardModel::ModelData make_4ring_bath() { + edlib::HubbardModel::ModelData b; + b.hopping = { + { 0.0, -1.0, 0.0, -1.0}, + {-1.0, 0.0, -1.0, 0.0}, + { 0.0, -1.0, 0.0, -1.0}, + {-1.0, 0.0, -1.0, 0.0} + }; + b.U = {5.0, 5.0, 5.0, 5.0}; + b.mu = {2.5, 2.5, 2.5, 2.5}; + b.magnetic_field = {0.01, 0.01, 0.01, 0.01}; + return b; +} + +} // namespace + +// Distributed diagonalization must give every rank the same ground state, +// equal to the serial reference (arXiv:cond-mat/0101476, 4-ring, U=5). +TEST(MpiCore, DistributedGroundStateIsRankConsistent) { + auto p = make_params(); + auto bath = make_4ring_bath(); + + edlib::SRSHubbardHamiltonian ham(p, bath, MPI_COMM_WORLD); + ham.diag(); + + ASSERT_FALSE(ham.eigenpairs().empty()); + const auto& gp = *ham.eigenpairs().begin(); + + int size = 0; + MPI_Comm_size(ham.comm(), &size); + + // 1. Reference value, checked locally on every rank. + EXPECT_NEAR(gp.eigenvalue(), -11.8443, 1e-4); + EXPECT_EQ(gp.sector().nup(), 2); + EXPECT_EQ(gp.sector().ndown(), 2); + + // 2. Cross-rank consistency: the spread of the ground-state energy across + // all ranks must be zero (bit-for-bit; the reduced scalar is replicated). + const double local_gs = gp.eigenvalue(); + double gs_min = 0.0, gs_max = 0.0; + MPI_Allreduce(&local_gs, &gs_min, 1, MPI_DOUBLE, MPI_MIN, ham.comm()); + MPI_Allreduce(&local_gs, &gs_max, 1, MPI_DOUBLE, MPI_MAX, ham.comm()); + EXPECT_DOUBLE_EQ(gs_min, gs_max) + << "ground-state energy differs across " << size << " ranks"; + + // 3. Sector identification must also agree on every rank. + const int local_nup = gp.sector().nup(); + int nup_min = 0, nup_max = 0; + MPI_Allreduce(&local_nup, &nup_min, 1, MPI_INT, MPI_MIN, ham.comm()); + MPI_Allreduce(&local_nup, &nup_max, 1, MPI_INT, MPI_MAX, ham.comm()); + EXPECT_EQ(nup_min, nup_max); +} + +#else // !USE_MPI + +TEST(MpiCore, SkippedWithoutMpi) { + GTEST_SKIP() << "built without USE_MPI"; +} + +#endif + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); +#ifdef USE_MPI + MPI_Init(&argc, &argv); +#endif + int res = RUN_ALL_TESTS(); +#ifdef USE_MPI + MPI_Finalize(); +#endif + return res; +} diff --git a/test/NSymmetry_Test.cpp b/test/NSymmetry_Test.cpp index 0e062e4..4fa16a8 100644 --- a/test/NSymmetry_Test.cpp +++ b/test/NSymmetry_Test.cpp @@ -1,17 +1,14 @@ -// -// Created by iskakoff on 21/08/16. -// +// Core-only NSymmetry parity test. -#include "gtest/gtest.h" +#include +#include -#include "edlib/NSymmetry.h" -#include "edlib/EDParams.h" +#include -TEST(NSymmetryTest, Initialization) { - alps::params p; - EDLib::define_parameters(p); - EDLib::Symmetry::NSymmetry sym(p); - while(sym.next_sector()) { +TEST(NSymmetryCore, IndexMatchesIteration) { + edlib::Parameters p; + edlib::NSymmetry sym(p); + while (sym.next_sector()) { sym.init(); int i = 0; while (sym.next_state()) { diff --git a/test/SzSymmetry_Test.cpp b/test/SzSymmetry_Test.cpp index e62f87a..6d45deb 100644 --- a/test/SzSymmetry_Test.cpp +++ b/test/SzSymmetry_Test.cpp @@ -1,35 +1,27 @@ -// -// Created by iskakoff on 22/07/16. -// +// Core-only SzSymmetry parity tests. -#include "gtest/gtest.h" +#include +#include -#include "edlib/SzSymmetry.h" -#include "edlib/EDParams.h" +#include - -TEST(SzSymmetryTest, Combinatorics) { - alps::params p; - EDLib::define_parameters(p); - EDLib::Symmetry::SzSymmetry sym(p); +TEST(SzSymmetryCore, Combinatorics) { + edlib::Parameters p; + edlib::SzSymmetry sym(p); sym.init(); - ASSERT_EQ(sym.comb().c_n_k(3, 2), 3); } - -TEST(SzSymmetryTest, States) { - alps::params p; - EDLib::define_parameters(p); - EDLib::Symmetry::SzSymmetry sym(p); +TEST(SzSymmetryCore, Construct) { + edlib::Parameters p; + edlib::SzSymmetry sym(p); sym.init(); } -TEST(SzSymmetryTest, Initialization) { - alps::params p; - EDLib::define_parameters(p); - EDLib::Symmetry::SzSymmetry sym(p); - while(sym.next_sector()) { +TEST(SzSymmetryCore, IndexMatchesIteration) { + edlib::Parameters p; + edlib::SzSymmetry sym(p); + while (sym.next_sector()) { sym.init(); int i = 0; while (sym.next_state()) { diff --git a/test/alpscore/CMakeLists.txt b/test/alpscore/CMakeLists.txt new file mode 100644 index 0000000..24ab0fd --- /dev/null +++ b/test/alpscore/CMakeLists.txt @@ -0,0 +1,33 @@ + +add_executable(SzSymmetryAlpsCoreTest SzSymmetry_Test.cpp) +add_executable(SzSymmetryWithBosonAlpsCoreTest SzSymmetryWithBoson_Test.cpp) +add_executable(NSymmetryAlpsCoreTest NSymmetry_Test.cpp) +add_executable(HubbardModelAlpsCoreTest HubbardModel_Test.cpp) +add_executable(SpinResolvedStorageAlpsCoreTest SRS.cpp SpinResolvedStorage_Test.cpp) +add_executable(StaticObservablesAlpsCoreTest StaticObservables_Test.cpp) +add_executable(StatisticsAlpsCoreTest Statistics_Test.cpp) +add_executable(LanczosAlpsCoreTest Lanczos_Test.cpp) + + +target_link_libraries(SzSymmetryAlpsCoreTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) +target_link_libraries(SzSymmetryWithBosonAlpsCoreTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) +target_link_libraries(NSymmetryAlpsCoreTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) +target_link_libraries(HubbardModelAlpsCoreTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) +target_link_libraries(SpinResolvedStorageAlpsCoreTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) +target_link_libraries(StaticObservablesAlpsCoreTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) +target_link_libraries(StatisticsAlpsCoreTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) +target_link_libraries(LanczosAlpsCoreTest EDLib ${extlibs} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) + +if (USE_MPI) + target_link_libraries(SzSymmetryAlpsCoreTest ${parlibs}) + target_link_libraries(SzSymmetryWithBosonAlpsCoreTest ${parlibs}) + target_link_libraries(NSymmetryAlpsCoreTest ${parlibs}) + target_link_libraries(HubbardModelAlpsCoreTest ${parlibs}) + target_link_libraries(SpinResolvedStorageAlpsCoreTest ${parlibs}) + target_link_libraries(StaticObservablesAlpsCoreTest ${parlibs}) + target_link_libraries(StatisticsAlpsCoreTest ${parlibs}) + target_link_libraries(LanczosAlpsCoreTest ${parlibs}) +endif(USE_MPI) + + +file(COPY ../input DESTINATION ${CMAKE_BINARY_DIR}/test) diff --git a/test/HubbardModel_Test.cpp b/test/alpscore/HubbardModel_Test.cpp similarity index 90% rename from test/HubbardModel_Test.cpp rename to test/alpscore/HubbardModel_Test.cpp index 5926447..8b159f4 100644 --- a/test/HubbardModel_Test.cpp +++ b/test/alpscore/HubbardModel_Test.cpp @@ -3,11 +3,11 @@ // #include -#include "edlib/Hamiltonian.h" -#include "edlib/HubbardModel.h" -#include "edlib/Storage.h" -#include "edlib/EDParams.h" -#include "edlib/StaticObservables.h" +#include "edlib/alpscore/Hamiltonian.h" +#include "edlib/alpscore/HubbardModel.h" +#include "edlib/alpscore/Storage.h" +#include "edlib/alpscore/EDParams.h" +#include "edlib/alpscore/StaticObservables.h" #ifdef USE_MPI diff --git a/test/alpscore/Lanczos_Test.cpp b/test/alpscore/Lanczos_Test.cpp new file mode 100644 index 0000000..a07aab3 --- /dev/null +++ b/test/alpscore/Lanczos_Test.cpp @@ -0,0 +1,149 @@ +// +// Created by iskakoff on 22/08/16. +// + +#include +#include "edlib/alpscore/Hamiltonian.h" +#include "edlib/alpscore/HubbardModel.h" +#include "edlib/alpscore/Storage.h" +#include "edlib/alpscore/EDParams.h" +#include "edlib/alpscore/StaticObservables.h" +#include "edlib/alpscore/GreensFunction.h" +#include "edlib/alpscore/ChiLoc.h" +#include "edlib/alpscore/MeshFactory.h" + +#ifdef USE_MPI + +class HubbardModelTestEnv : public ::testing::Environment { + protected: + + ~HubbardModelTestEnv(){}; + +}; + +::testing::Environment* const foo_env = AddGlobalTestEnvironment(new HubbardModelTestEnv); + +#endif + + +TEST(HubbardModelTest, ReferenceTest) { + alps::params p; + EDLib::define_parameters(p); + p["NSITES"]=4; + p["NSPINS"]=2; + p["INPUT_FILE"]="test/input/GF_Chi/input.h5"; + p["arpack.SECTOR"]=false; + p["storage.MAX_SIZE"]=864; + p["storage.MAX_DIM"]=36; + p["storage.EIGENVALUES_ONLY"]=0; + p["storage.ORBITAL_NUMBER"]=1; + p["arpack.NEV"]=100; + p["lanc.BETA"]=20; + p["lanc.EMIN"]=-4.0; + p["lanc.EMAX"]=4.0; + p["lanc.NOMEGA"]=1000; + +#ifdef USE_MPI + typedef EDLib::SRSHubbardHamiltonian HamType; +#else + typedef EDLib::CSRHubbardHamiltonian HamType; +#endif + HamType ham(p +#ifdef USE_MPI + , MPI_COMM_WORLD +#endif + ); + + ham.diag(); + + // Compute our GFs for the reference model. + EDLib::gf::GreensFunction < HamType, EDLib::MatsubaraMeshFactory, alps::gf::statistics::statistics_type> greensFunction(p, ham,alps::gf::statistics::statistics_type::FERMIONIC); + greensFunction.compute(); + auto G = greensFunction.G(); + EDLib::StaticObservables so(p); + std::map> observables = so.calculate_static_observables(ham); +#ifdef USE_MPI + // StaticObservables reduces to rank 0; broadcast so the averages below + // are identical on every rank before being fed into susc.compute(). + for(auto& kv : observables){ + MPI_Bcast(kv.second.data(), kv.second.size(), MPI_DOUBLE, 0, ham.comm()); + } +#endif + EDLib::gf::ChiLoc susc(p, ham, alps::gf::statistics::statistics_type::BOSONIC); + // compute average magnetic moment + double avg = 0.0; + for(auto x : observables[so._M_]) { + avg += x / (2.0*observables[so._M_].size()); + } + // compute spin susceptibility + susc.compute>(&avg); + auto ChiSz = susc.G(); + // compute average occupancy moment + avg = 0.0; + for(auto x : observables[so._N_]) { + avg += x / double(observables[so._N_].size()); + } + // Compute sharge susceptibility + susc.compute>(&avg); + auto ChiN = susc.G(); + + // Read in the reference GFs. + // FIXME Desperate kludges. Must take the indextypes from GFs instead. + auto G_file = G; + std::ifstream infile("test/input/GF_Chi/gom1.dat"); + for(size_t ii = 0; ii < 200; ++ii){ + double omega, real, imag; + infile >> omega >> real >> imag; + for(size_t is = 0; is < ham.model().spins(); ++is){ + G_file(alps::gf::generic_index>(ii), alps::gf::generic_index(0), alps::gf::generic_index(is)) = std::complex(real, imag); + } + } + infile.close(); + auto ChiSz_file = ChiSz; + infile.open("test/input/GF_Chi/xiats.dat"); + for(size_t ii = 0; ii < 200; ++ii){ + double omega, real, imag; + infile >> omega >> real >> imag; + // S_z = 0.5 M, = 0.25 + ChiSz_file(alps::gf::generic_index>(ii), alps::gf::generic_index(0)) = std::complex(-0.25 * real, 0.0); + } + infile.close(); + auto ChiN_file = ChiN; + infile.open("test/input/GF_Chi/xiatd.dat"); + for(size_t ii = 0; ii < 200; ++ii){ + double omega, real, imag; + infile >> omega >> real >> imag; + ChiN_file(alps::gf::generic_index>(ii), alps::gf::generic_index(0)) = std::complex(-real, 0.0); + } + infile.close(); + +#ifdef USE_MPI + // GreensFunction / ChiLoc accumulate into _G only on rank 0 + // (see GreensFunction.h::local_contribution etc.). Compare against the + // reference only there; other ranks would see zero-filled buffers. + int _rk = 0; + MPI_Comm_rank(ham.comm(), &_rk); + if(_rk == 0) +#endif + { + // Subtract the reference GF from our result, the norm() is then the largest diff. + G -= G_file; + ASSERT_NEAR(G.norm(), 0.0, 1e-10); + ChiSz -= ChiSz_file; + ASSERT_NEAR(ChiSz.norm(), 0.0, 1e-9); + ChiN -= ChiN_file; + ASSERT_NEAR(ChiN.norm(), 0.0, 1e-9); + } +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); +#ifdef USE_MPI + MPI_Init(&argc, &argv); +#endif + int res = RUN_ALL_TESTS(); +#ifdef USE_MPI + MPI_Finalize(); +#endif + return res; +} diff --git a/test/alpscore/NSymmetry_Test.cpp b/test/alpscore/NSymmetry_Test.cpp new file mode 100644 index 0000000..1c24fd7 --- /dev/null +++ b/test/alpscore/NSymmetry_Test.cpp @@ -0,0 +1,22 @@ +// +// Created by iskakoff on 21/08/16. +// + +#include "gtest/gtest.h" + +#include "edlib/alpscore/NSymmetry.h" +#include "edlib/alpscore/EDParams.h" + +TEST(NSymmetryTest, Initialization) { + alps::params p; + EDLib::define_parameters(p); + EDLib::Symmetry::NSymmetry sym(p); + while(sym.next_sector()) { + sym.init(); + int i = 0; + while (sym.next_state()) { + ASSERT_EQ(i, sym.index(sym.state())); + ++i; + } + } +} diff --git a/test/SRS.cpp b/test/alpscore/SRS.cpp similarity index 100% rename from test/SRS.cpp rename to test/alpscore/SRS.cpp diff --git a/test/SpinResolvedStorage_Test.cpp b/test/alpscore/SpinResolvedStorage_Test.cpp similarity index 93% rename from test/SpinResolvedStorage_Test.cpp rename to test/alpscore/SpinResolvedStorage_Test.cpp index 2dc8156..f4a9d13 100644 --- a/test/SpinResolvedStorage_Test.cpp +++ b/test/alpscore/SpinResolvedStorage_Test.cpp @@ -3,12 +3,12 @@ // #include -#include "edlib/HubbardModel.h" +#include "edlib/alpscore/HubbardModel.h" -#include "edlib/SpinResolvedStorage.h" -#include "edlib/SingleImpurityAndersonModel.h" -#include "edlib/CRSStorage.h" -#include "edlib/EDParams.h" +#include "edlib/alpscore/SpinResolvedStorage.h" +#include "edlib/alpscore/SingleImpurityAndersonModel.h" +#include "edlib/alpscore/CRSStorage.h" +#include "edlib/alpscore/EDParams.h" #include #include diff --git a/test/StaticObservables_Test.cpp b/test/alpscore/StaticObservables_Test.cpp similarity index 91% rename from test/StaticObservables_Test.cpp rename to test/alpscore/StaticObservables_Test.cpp index 0bee015..639d3d2 100644 --- a/test/StaticObservables_Test.cpp +++ b/test/alpscore/StaticObservables_Test.cpp @@ -1,9 +1,9 @@ #include -#include "edlib/Hamiltonian.h" -#include "edlib/HubbardModel.h" -#include "edlib/Storage.h" -#include "edlib/EDParams.h" -#include "edlib/StaticObservables.h" +#include "edlib/alpscore/Hamiltonian.h" +#include "edlib/alpscore/HubbardModel.h" +#include "edlib/alpscore/Storage.h" +#include "edlib/alpscore/EDParams.h" +#include "edlib/alpscore/StaticObservables.h" #ifdef USE_MPI diff --git a/test/Statistics_Test.cpp b/test/alpscore/Statistics_Test.cpp similarity index 95% rename from test/Statistics_Test.cpp rename to test/alpscore/Statistics_Test.cpp index 0b9d94b..4ba6b26 100644 --- a/test/Statistics_Test.cpp +++ b/test/alpscore/Statistics_Test.cpp @@ -3,7 +3,7 @@ // #include -#include +#include TEST(Statistics, UpdateEventTest) { EDLib::common::statistics.registerEvent("test"); diff --git a/test/SzSymmetryWithBoson_Test.cpp b/test/alpscore/SzSymmetryWithBoson_Test.cpp similarity index 76% rename from test/SzSymmetryWithBoson_Test.cpp rename to test/alpscore/SzSymmetryWithBoson_Test.cpp index 3ff22db..2d28078 100644 --- a/test/SzSymmetryWithBoson_Test.cpp +++ b/test/alpscore/SzSymmetryWithBoson_Test.cpp @@ -5,9 +5,9 @@ #include #include -#include -#include "edlib/EDParams.h" -#include "ext/SzSymmetryWithBoson.h" +#include +#include "edlib/alpscore/EDParams.h" +#include "ext/alpscore/SzSymmetryWithBoson.h" TEST(SzSymmetryWithBosonTest, Indexing) { alps::params p; diff --git a/test/alpscore/SzSymmetry_Test.cpp b/test/alpscore/SzSymmetry_Test.cpp new file mode 100644 index 0000000..cc836b1 --- /dev/null +++ b/test/alpscore/SzSymmetry_Test.cpp @@ -0,0 +1,40 @@ +// +// Created by iskakoff on 22/07/16. +// + +#include "gtest/gtest.h" + +#include "edlib/alpscore/SzSymmetry.h" +#include "edlib/alpscore/EDParams.h" + + +TEST(SzSymmetryTest, Combinatorics) { + alps::params p; + EDLib::define_parameters(p); + EDLib::Symmetry::SzSymmetry sym(p); + sym.init(); + + ASSERT_EQ(sym.comb().c_n_k(3, 2), 3); +} + + +TEST(SzSymmetryTest, States) { + alps::params p; + EDLib::define_parameters(p); + EDLib::Symmetry::SzSymmetry sym(p); + sym.init(); +} + +TEST(SzSymmetryTest, Initialization) { + alps::params p; + EDLib::define_parameters(p); + EDLib::Symmetry::SzSymmetry sym(p); + while(sym.next_sector()) { + sym.init(); + int i = 0; + while (sym.next_state()) { + ASSERT_EQ(i, sym.index(sym.state())); + ++i; + } + } +}