# Build CP2K as a static library using its native Makefile

#  Copyright (C) 2017 The authors of Py-ChemShell
#
#  This file is part of Py-ChemShell.
#
#  Py-ChemShell is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as
#  published by the Free Software Foundation, either version 3 of the
#  License, or (at your option) any later version.
#
#  Py-ChemShell is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with Py-ChemShell.  If not, see
#  <http://www.gnu.org/licenses/>.

# you.lu@stfc.ac.uk
# June 2015

set(CP2K_LATEST_RELEASE_VERSION "2024.2")

include(ExternalProject)

# paths
set(CHEMSH_CP2K_LIB_DIR       ${CHEMSH_BUILD_DIR}/lib/cp2k)
set(CHEMSH_CP2K_INCLUDE_DIR   ${CHEMSH_BUILD_DIR}/include/cp2k)
set(CHEMSH_CP2K_TOOLCHAIN_DIR ${CHEMSH_CP2K_LIB_DIR}/toolchains)
file(MAKE_DIRECTORY ${CHEMSH_CP2K_LIB_DIR})
file(MAKE_DIRECTORY ${CHEMSH_CP2K_INCLUDE_DIR})
file(MAKE_DIRECTORY ${CHEMSH_CP2K_TOOLCHAIN_DIR})
set(CMAKE_Fortran_MODULE_DIRECTORY ${CHEMSH_BUILD_DIR}/modules/cp2k)
include_directories(${CHEMSH_BUILD_DIR}/modules
                    ${CHEMSH_BUILD_DIR}/modules/parallel
                    ${CHEMSH_BUILD_DIR}/modules/dl_py2f)

# without CP2K
if("${CP2K}" STREQUAL "")
    # compile libcp2k.so with dummy source files
    add_library(cp2k SHARED cp2k.f90 cp2k_dummies.f90 ${NOTLINKED})
    target_link_libraries(cp2k dl_py2f parallel)
    set_target_properties(cp2k PROPERTIES LINK_FLAGS "${CHEMSH_LINK_FLAGS}")
    return()
endif()

# TD 03/10/2025: Issue with cp2k init function using MPI_COMM_WORLD
#                I will try and fix upstream
if(NOT ${NWCHEM} STREQUAL "")
    if(${GA_ARMCI_NETWORK} STREQUAL "MPI-PR")
	    message(FATAL_ERROR "ERROR: Cannot compile GA in MPI-PR mode with cp2k in this version\nEither remove NWChem from the build or set --ga-armci-network to MPI-TS")
    endif()
endif()

get_filename_component(CP2K_ABSOLUTE "${CP2K}" ABSOLUTE)

# determine if serial or parallel build required
if(WITH_MPI)
    message("CP2K library will be compiled parallel with MPI")
    set(CP2K_BUILD_VERSION "psmp")
    set(CP2K_PARALLEL_MACROS "-D__parallel")
    set(CP2K_ScaLAPACK_FLAGS ${CHEMSH_ScaLAPACK_FLAGS})
    set(CP2K_BUILD_ARCH "chemsh-${CHEMSH_ARCH}-mpi")
else()
    message("CP2K library will be compiled serial. Specify --mpi to compile a parallel version")
    set(CP2K_BUILD_VERSION "ssmp")
    set(CP2K_BUILD_ARCH "chemsh-${CHEMSH_ARCH}")
endif()

# from user-specified location
if(EXISTS ${CP2K_ABSOLUTE})

    # if is directory
    if(IS_DIRECTORY ${CP2K})
        set(CP2K_DIR       ${CP2K})
        set(CP2K_SRC_DIR   ${CP2K_DIR}/src)
        set(CP2K_LIB_DIR   ${CP2K_DIR}/lib/${CP2K_BUILD_ARCH}/${CP2K_BUILD_VERSION})
        # currently not in use
        set(CP2K_TOOLS_DIR ${CP2K_DIR}/tools/toolchain/install)
    # if file
    else()
        set(CP2K_DIR     ${CMAKE_CURRENT_SOURCE_DIR})
        set(CP2K_SRC_DIR ${CP2K_DIR}/src)
    endif()
    set(CP2K_DATA_DIR ${CP2K_DIR}/data)

    # get the CP2K version number from the source file
    if("${CP2K_VERSION}" STREQUAL "")
        file(READ ${CP2K_SRC_DIR}/cp2k_info.F CP2K_INFO LIMIT 3000)
        # TWK 11/10/2022 This finds the version number line for CP2K versions < 9.1
        string(REGEX MATCH "version_nr = \"[+-]?([0-9]*[.])?[0-9]+" CP2K_VERSION ${CP2K_INFO})
        if("${CP2K_VERSION}" STREQUAL "")
            # TWK 11/10/2022 This finds the version number line for CP2K from 2022 onwards
            string(REGEX MATCH "cp2k_version = \"CP2K version [+-]?([0-9]*[.])?[0-9]+" CP2K_VERSION ${CP2K_INFO})
        endif()
        # TWK 11/10/2022 modified to ensure final number on the line is chosen
        string(REGEX MATCH "[+-]?([0-9]*[.])?[0-9]+$" CP2K_VERSION ${CP2K_VERSION})
        set(CP2K_NAME cp2k-${CP2K_VERSION})
    endif()
    if("${CP2K_VERSION}" VERSION_LESS 8.0)
        message(FATAL_ERROR " ERROR: We no longer support CP2K version earlier than 8.0. Please upgrade!")
    endif()

    set(CP2K_NAME cp2k-${CP2K_VERSION})
# from the GitHub repository
else()
    message("${BoldWhite}CP2K${EndColour} repository URL: ${CP2K}")
    set(CP2K_VERSION   ${CP2K_LATEST_RELEASE_VERSION})
    set(CP2K_NAME      cp2k-${CP2K_VERSION})
    set(CP2K_GIT       ON)
    set(CP2K_GIT_REPO  ${CP2K})
    set(CP2K_GIT_TAG   "v${CP2K_VERSION}")
    set(CP2K_TARBALL   "")
    set(CP2K_DIR       ${CMAKE_CURRENT_LIST_DIR}/${CHEMSH_ARCH}/src/${CP2K_NAME})
    set(CP2K_SRC_DIR   ${CP2K_DIR}/src)
    set(CP2K_DATA_DIR  ${CP2K_DIR}/data)
    set(CP2K_LIB_DIR   ${CP2K_DIR}/lib/${CP2K_BUILD_ARCH}/${CP2K_BUILD_VERSION})
    # currently not in use
    set(CP2K_TOOLS_DIR ${CP2K_DIR}/tools/toolchain/install)
endif()

# to make static lib linkable from a shared lib
# always build CP2K 32-bit
set(CP2K_FFLAGS "-fPIC")

if( ("${CP2K_VERSION}" VERSION_GREATER_EQUAL 2024.1) OR ("${CP2K_VERSION}" STREQUAL "latest" ))
    set(CP2K_CFLAGS "-std=c11 -fPIC")
    set(CP2K_CXX_FLAGS "-std=c++14 -fPIC")
else()
    set(CP2K_CFLAGS "-std=c99 -fPIC")
endif()

# these variales are to be used by cp2k_arch.in
set(LD ${CMAKE_Fortran_COMPILER})

if(${CHEMSH_ARCH} STREQUAL "intel")

  #if(NOT("${CMAKE_CUDA_COMPILER}" STREQUAL ""))
    #set(CP2K_ScaLAPACK_FLAGS "${CP2K_ScaLAPACK_FLAGS} -lmkl_gf_lp64")
    #set(CHEMSH_MKL_FLAGS "${CHEMSH_MKL_FLAGS} -lmkl_gf_lp64")
  #endif()
    # YL 05/03/2021: Intel 17 results in wrong
    if(${CMAKE_Fortran_COMPILER_VERSION} VERSION_LESS 18.0)
        message(FATAL_ERROR "\nCP2K v{CP2K_VERSION} requires Intel compilers of at least v18.0, the current v${CMAKE_Fortran_COMPILER_VERSION} results in wrong energy!\n")
    endif()

    # always use 32-bit integers
    string(REPLACE "-i8" "-i4" CP2K_FFLAGS ${CP2K_FFLAGS})

    # compile macros
    set(CP2K_MACROS "-D__MKL -D__INTEL -D__DATA_DIR=\\\"${CP2K_DATA_DIR}\\\"")

    # FFTW
    if(NOT WITH_FFTW)
        message(STATUS "\n Compiling CP2K without FFTW...\n")
        #set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -L${MKL_LIB} -lmkl_cdft_core")

    elseif(WITH_FFTW)
        #set(CP2K_MACROS "${CP2K_MACROS} -D__FFTW3_MKL -D__FFTW3_UNALIGNED")
        set(CP2K_MACROS "${CP2K_MACROS} -D__FFTW3")
        #set(CP2K_FFLAGS "${CP2K_FFLAGS} -I${FFTW_INCLUDE_DIR}") # explicitly provied paths/set at earlier stage
        #set(CP2K_CFLAGS "${CP2K_CFLAGS} -I${FFTW_INCLUDE_DIR}")
        set(CP2K_FFLAGS "${CP2K_FFLAGS} ${FFTW_COMPILE_FLAGS}")# explicitly provied paths/set at earlier stage
        set(CP2K_CFLAGS "${CP2K_CFLAGS} ${FFTW_COMPILE_FLAGS}")

        if(WITH_FFTW_MKL)

        #set(CP2K_MACROS "${CP2K_MACROS} -D__FFTW3_MKL -D__FFTW3_UNALIGNED")
        set(CP2K_MACROS "${CP2K_MACROS} -D__FFTW3_MKL")

        #cluster ffts for mpi, does't build without icc;hopefully no need to build.
        #set(CP2K_FFTW_STATICLINK_FLAGS "${CHEMSH_CP2K_LIB_DIR}/libfftw3xf.a ${CHEMSH_CP2K_LIB_DIR}/libfftw3x_cdft.a")
        set(CP2K_FFTW_LINK_FLAGS "-lmkl_cdft_core")
        set(CP2K_FFTW_STATICLINK_FLAGS "${CHEMSH_CP2K_LIB_DIR}/libfftw3xf.a")

        set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CP2K_FFTW_LINK_FLAGS}")
        set(CHEMSH_CP2K_STATIC_LIBS "${CHEMSH_CP2K_STATIC_LIBS} ${CP2K_FFTW_STATICLINK_FLAGS}")
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CP2K_FFTW_STATICLINK_FLAGS} ${CP2K_FFTW_LINK_FLAGS}")
        #set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CP2K_FFTW_LINK_FLAGS}")

	#fftw with mkl should be built.
        # build command needs to be revised, whenever intel modifies it.
        # do NOT use -j for multi-jobs building because when one thread deletes the dir containing the object files others are still compiling (bug in MKL makefile!)
        set(FFTW3_CFLAGS "-fPIC -Wall -Werror -wd167 -diag-disable=10441,10448")
        set(FFTW_INTERFACE_DIR2  "${FFTW_INTERFACE_DIR}./../fftw3x_cdft")

        set(FFTW3_BUILD_COMMAND make -j1 libintel64 INSTALL_DIR=${CHEMSH_CP2K_LIB_DIR} INSTALL_LIBNAME="libfftw3xf.a" INTERFACE=${MKL_SUFFIX} compiler=${CHEMSH_ARCH} _cflags_intel_diag=${FFTW3_CFLAGS})
        set(FFTW3_BUILD_COMMAND2 make -j1 libintel64 INSTALL_DIR=${CHEMSH_CP2K_LIB_DIR} INSTALL_LIBNAME="libfftw3x_cdft.a" interface=${MKL_SUFFIX} TARGET_ARCH=${FFTW3_CFLAGS})
        set(FFTW3_BUILD_COMMAND_STR "make -j1 libintel64 INSTALL_DIR=${CHEMSH_CP2K_LIB_DIR} INSTALL_LIBNAME="libfftw3xf.a" INTERFACE=${MKL_SUFFIX} compiler=${CHEMSH_ARCH} _cflags_intel_diag=${FFTW3_CFLAGS}")
        set(FFTW3_BUILD_COMMAND_STR2 "make -j1 libintel64 INSTALL_DIR=${CHEMSH_CP2K_LIB_DIR} INSTALL_LIBNAME="libfftw3x_cdft.a" interface=${MKL_SUFFIX} TARGET_ARCH=${FFTW3_CFLAGS}")
        else()
          if(FFTW_LINK_FLAGS)#to allow for explicit provision of link flags:not used now
            set(CP2K_FFTW_LINK_FLAGS ${FFTW_LINK_FLAGS})
          else()
            set(CP2K_FFTW_LINK_FLAGS "-L${FFTW_DIR} -Wl,rpath ${FFTW_DIR} -lfftw3 -lfftw3_threads -lfftw3_omp")
	    if(WITH_MPI)
            set(CP2K_FFTW_LINK_FLAGS "-L${FFTW_DIR} -Wl,-rpath ${FFTW_DIR} -lfftw3_mpi -lfftw3_threads -lfftw3_omp -lfftw3")
            endif()
          endif()
          set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CP2K_FFTW_LINK_FLAGS}")
          set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CP2K_FFTW_LINK_FLAGS}")
        endif()
    endif()

    # parallel version
    if(WITH_MPI)
      set(CP2K_MACROS "${CP2K_MACROS} ${CP2K_PARALLEL_MACROS}")
      if( ("${CP2K_VERSION}" VERSION_GREATER_EQUAL 2024.1) OR ("${CP2K_VERSION}" STREQUAL "latest" ))
       set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -L${MPI_LIB_PATH} -lmpi -lmpicxx")
      else()
       set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -L${MPI_LIB_PATH} -lmpi")
      endif()
      set(CP2K_FFLAGS "${CP2K_FFLAGS} -I${MPI_INCLUDE_PATH}") # these were not set?
      set(CP2K_CFLAGS "${CP2K_CFLAGS} -I${MPI_INCLUDE_PATH}")

    # ScaLAPACK
    #if(WITH_ScaLAPACK) # swith is not used for intel
      set(CP2K_MACROS "${CP2K_MACROS} -D__SCALAPACK")
      set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CP2K_ScaLAPACK_FLAGS}")
      set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CP2K_ScaLAPACK_FLAGS}")
    else()
    #If mpi, scalapack_flags is used, else mkl_flags for serial must be linked.
      set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CHEMSH_MKL_FLAGS}")
      set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CHEMSH_MKL_FLAGS}")
    endif()

    # debug
    if(DEBUG)
        set(CP2K_FFLAGS "${CP2K_FFLAGS} -fp-model=precise -g -traceback -xHost -O2 -qopenmp -funroll-loops -fpp -free")
        set(CP2K_CFLAGS "${CP2K_CFLAGS} -fp-model=precise -g -traceback -xHost -O2 -qopenmp -funroll-loops -wd167")
    else()
        set(CP2K_FFLAGS "${CP2K_FFLAGS} -fp-model=precise -g -traceback -xHost -O2 -qopenmp -funroll-loops -fpp -free -diag-disable=6843,8291,8293,10441,10448")
        set(CP2K_CFLAGS "${CP2K_CFLAGS} -fp-model=precise -g -traceback -xHost -O2 -qopenmp -funroll-loops -xHost  -wd167 -Wall -Werror -Wno-error=unused-function -Wno-error=unused-variable -diag-disable=10441,10448")
    endif()

    set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -qopenmp")
    set(CP2K_FFLAGS "${CP2K_FFLAGS} -shared -fpscomp logicals")
    set(CP2K_CFLAGS "${CP2K_CFLAGS} -mtune=native")

    # link MKL
    set(CP2K_FFLAGS "${CP2K_FFLAGS} ${MKL_COMPILE_FLAGS} -m64") #include-flags, defined in platform/setup
    set(CP2K_CFLAGS "${CP2K_CFLAGS}  ${MKL_COMPILE_FLAGS} -m64")
    #Raj;same as the chemsh_scalapack_flags:don't duplicate
    #set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CHEMSH_MKL_FLAGS}")
    #set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CP2K_ScaLAPACK_FLAGS}")
    #set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS}  ${CHEMSH_MKL_FLAGS}")


    # link flags
    if(${CMAKE_Fortran_COMPILER_VERSION} VERSION_GREATER_EQUAL 20.0)
        set(CP2K_C_LDFLAGS "-nofor-main")
    else()
        set(CP2K_C_LDFLAGS "-nofor_main")
    endif()
    if( ("${CP2K_VERSION}" VERSION_GREATER_EQUAL 2024.1) OR ("${CP2K_VERSION}" STREQUAL "latest" ))
        if(WITH_MPI)
          set(CP2K_MACROS "${CP2K_MACROS} -D__MPI_F08")
        endif()
        set(CP2K_FFLAGS "${CP2K_FFLAGS} -Wl,--enable-new-dtags")
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -lstdc++")
        set(CP2K_CXX_FLAGS "${CP2K_CXX_FLAGS} -fp-model=precise -g -traceback -xHost -O2 -funroll-loops -xHost -mtune=native -Wno-deprecated-declarations -qopenmp -diag-disable=10448 ${CP2K_MACROS}")
    endif()
    set(CP2K_CFLAGS "${CP2K_CFLAGS} ${CP2K_MACROS}")
    set(CP2K_FFLAGS "${CP2K_FFLAGS} ${CP2K_MACROS}")
    #set(FYPPFLAGS  "-n --line-marker-format=gfortran5")
    # cp2k recommends using sequential math libraries. still when compiler is intel, omp can be used.
    # string(REPLACE "-liomp5" "-lmkl_sequential" CP2K_LDFLAGS ${CP2K_LDFLAGS})
    # string(REPLACE "-lmkl_intel_thread" "" CP2K_LDFLAGS ${CP2K_LDFLAGS})


elseif(${CHEMSH_ARCH} STREQUAL "gnu")
    #always use 32-bit integers
    string(REPLACE "-fdefault-integer-8" "" CP2K_FFLAGS ${CP2K_FFLAGS})

    # compile macros
#    set(CP2K_MACROS "-D__DATA_DIR=\\\"${CP2K_DATA_DIR}\\\" -D__COMPILE_ARCH=\\\"${CP2K_BUILD_ARCH}\\\"")
    set(CP2K_MACROS "-D__DATA_DIR=\\\"${CP2K_DATA_DIR}\\\"")

    # parallel version
    if(WITH_MPI)
        set(CP2K_MACROS "${CP2K_MACROS} ${CP2K_PARALLEL_MACROS}")
        # ScaLAPACK
        # Scalapack only with MPI
        if(WITH_ScaLAPACK)
          set(CP2K_MACROS "${CP2K_MACROS} -D__SCALAPACK")
          set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CP2K_ScaLAPACK_FLAGS}")
          set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CP2K_ScaLAPACK_FLAGS}")
        endif()
    else()
      # we don't use these -U for Intel which issues tons of warnings
      set(CP2K_PARALLEL_MACROS "-U__parallel -U__SCALAPACK")
      set(CP2K_MACROS "${CP2K_MACROS} ${CP2K_PARALLEL_MACROS}")
    endif()

    # FFTW
    if(WITH_FFTW)
        if(FFTW_LINK_FLAGS)#to allow for explicit provision of link flags:not used now
            set(CP2K_FFTW_LINK_FLAGS ${FFTW_LINK_FLAGS})
        else()
          set(CP2K_FFTW_LINK_FLAGS "-L${FFTW_DIR} -lfftw3 -lfftw3_threads -lfftw3_omp")
          if(WITH_MPI)
          set(CP2K_FFTW_LINK_FLAGS "-L${FFTW_DIR} -lfftw3 -lfftw3_mpi -lfftw3_threads -lfftw3_omp")
          endif()
        endif()

        set(CP2K_MACROS "${CP2K_MACROS} -D__FFTW3")
        set(CP2K_FFLAGS "${CP2K_FFLAGS} ${FFTW_COMPILE_FLAGS}")
        set(CP2K_CFLAGS "${CP2K_CFLAGS} ${FFTW_COMPILE_FLAGS}")
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CP2K_FFTW_LINK_FLAGS}")
        set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CP2K_FFTW_LINK_FLAGS}")
    endif()

    set(CP2K_C_LDFLAGS "")

    if(DEBUG)
        set(CP2K_FFLAGS "${CP2K_FFLAGS} -cpp -ffree-form -ffree-line-length-none -fopenmp -fallow-argument-mismatch -fcheck=no-array-temps -Werror=unused-parameter -Werror=unused-label -fbacktrace -fimplicit-none")
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -cpp -ffree-form -ffree-line-length-none -fopenmp -fallow-argument-mismatch -fcheck=no-array-temps -fbacktrace -fimplicit-none")
        set(CP2K_CFLAGS "${CP2K_CFLAGS} -ftree-vectorize -fopenmp -mtune=native")
    else()
        set(CP2K_FFLAGS "${CP2K_FFLAGS} -cpp -ffree-form -ffree-line-length-none -fopenmp -fallow-argument-mismatch -fcheck=no-array-temps")
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -cpp -ffree-form -ffree-line-length-none -fopenmp -fallow-argument-mismatch -fcheck=no-array-temps")
        set(CP2K_CFLAGS "${CP2K_CFLAGS} -ftree-vectorize -fopenmp -mtune=native")
    endif()
    
    if( ("${CP2K_VERSION}" VERSION_GREATER_EQUAL 2024.1) OR ("${CP2K_VERSION}" STREQUAL "latest" ))
      if(WITH_MPI)
        set(CP2K_MACROS "${CP2K_MACROS} -D__MPI_F08")
      endif()
      set(CP2K_FFLAGS "${CP2K_FFLAGS} -std=f2008")
      set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -lstdc++ -Wl,--enable-new-dtags")
      set(CP2K_CXX_FLAGS "-O2 -fPIC -fno-omit-frame-pointer -fopenmp -g -march=native -mtune=native --std=c++14 -Wno-deprecated-declarations ${CP2K_MACROS}")
    endif()
    set(CP2K_CFLAGS "${CP2K_CFLAGS} ${CP2K_MACROS}")
    set(CP2K_FFLAGS "${CP2K_FFLAGS} ${CP2K_MACROS}")
    set(FYPPFLAGS  "-n --line-marker-format=gfortran5")

#Rajany: seperating cray-flags, for easing management individually for cray-compilers
elseif( ${CHEMSH_ARCH} STREQUAL "cpe-gnu")
    string(REPLACE "-fdefault-integer-8" "" CP2K_FFLAGS ${CP2K_FFLAGS})

    # compile macros
    set(CP2K_MACROS "-D__DATA_DIR=\\\"${CP2K_DATA_DIR}\\\"")

    # parallel version
    if(WITH_MPI)
        set(CP2K_MACROS "${CP2K_MACROS} ${CP2K_PARALLEL_MACROS}")
    else()
        set(CP2K_PARALLEL_MACROS "-U__parallel -U__SCALAPACK")
        set(CP2K_MACROS "${CP2K_MACROS} ${CP2K_PARALLEL_MACROS}")
    endif()

    # FFTW
    if(WITH_FFTW)
        if(FFTW_LINK_FLAGS)#to allow for explicit provision of link flags:not used now
            set(CP2K_FFTW_LINK_FLAGS ${FFTW_LINK_FLAGS})
        else()
          set(CP2K_FFTW_LINK_FLAGS "-L${FFTW_DIR} -lfftw3 -lfftw3_threads -lfftw3_omp")
          if(WITH_MPI)
            set(CP2K_FFTW_LINK_FLAGS "-L${FFTW_DIR} -lfftw3 -lfftw3_mpi -lfftw3_threads -lfftw3_omp")
          endif()
        endif()

        set(CP2K_MACROS "${CP2K_MACROS} -D__FFTW3")
        set(CP2K_FFLAGS "${CP2K_FFLAGS} ${FFTW_COMPILE_FLAGS}")
        set(CP2K_CFLAGS "${CP2K_CFLAGS} ${FFTW_COMPILE_FLAGS}")
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CP2K_FFTW_LINK_FLAGS}")
        set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CP2K_FFTW_LINK_FLAGS}")
    endif()

    # parallel version
    if(WITH_MPI)
      if( ("${CP2K_VERSION}" VERSION_GREATER_EQUAL 2024.1) OR ("${CP2K_VERSION}" STREQUAL "latest" ))
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -L${MPI_LIB_PATH} -lmpi -lmpicxx")
      else()
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -L${MPI_LIB_PATH} -lmpi")
      endif()
      set(CP2K_FFLAGS "${CP2K_FFLAGS} -I${MPI_INCLUDE_PATH}") # these were not set?
      set(CP2K_CFLAGS "${CP2K_CFLAGS} -I${MPI_INCLUDE_PATH}")

      # ScaLAPACK
      if(WITH_ScaLAPACK)
        set(CP2K_MACROS "${CP2K_MACROS} -D__SCALAPACK")
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${CP2K_ScaLAPACK_FLAGS}")
        set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} ${CP2K_ScaLAPACK_FLAGS}")
      endif()
    endif()

    if(WITH_LIBXC)
	    set(CP2K_MACROS "${CP2K_MACROS} -D__LIBXC")
            set(CP2K_CFLAGS "${CP2K_CFLAGS} -I${LIBXC_ROOT}/include")
    endif()

    set(CP2K_C_LDFLAGS "")

    set(CP2K_WFLAGS "${CP2K_WFLAGS} -Werror=aliasing -Werror=ampersand -Werror=c-binding-type -Werror=intrinsic-shadow -Werror=intrinsics-std -Werror=line-truncation -Werror=tabs -Werror=target-lifetime -Werror=underflow -Werror=unused-but-set-variable -Werror=unused-variable -Werror=unused-dummy-argument -Werror=unused-parameter -Werror=unused-label -Werror=conversion -Werror=zerotrip -Wno-maybe-uninitialized -Wuninitialized -Wuse-without-only")
    if(DEBUG)
        set(CP2K_FFLAGS "${CP2K_FFLAGS} -cpp -ffree-form -ffree-line-length-none -fopenmp -fallow-argument-mismatch -fcheck=no-array-temps -fno-omit-frame-pointer -g -O3 -funroll-loops -fbacktrace -fimplicit-none ${CP2K_WFLAGS}")
	set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -cpp -ffree-form -ffree-line-length-none -fopenmp -fallow-argument-mismatch -fcheck=no-array-temps -fno-omit-frame-pointer -g -O3 -funroll-loops -fbacktrace -fimplicit-none")
        set(CP2K_CFLAGS "${CP2K_CFLAGS} -ftree-vectorize -fopenmp -march=native -mtune=native -fno-omit-frame-pointer -g -O3 -funroll-loops")#-m64,not sure
    else()
        set(CP2K_FFLAGS "${CP2K_FFLAGS} -cpp -ffree-form -ffree-line-length-none -fopenmp -fallow-argument-mismatch -fcheck=no-array-temps -fno-omit-frame-pointer -g -O3 -funroll-loops")
	set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -cpp -ffree-form -ffree-line-length-none -fopenmp -fallow-argument-mismatch -fcheck=no-array-temps -fno-omit-frame-pointer -g -O3 -funroll-loops")
        set(CP2K_CFLAGS "${CP2K_CFLAGS} -ftree-vectorize -fopenmp -march=native -mtune=native -fno-omit-frame-pointer -g -O3 -funroll-loops")#-m64,notsure
    endif()
 
    set(CP2K_CFLAGS "${CP2K_CFLAGS} -Wall -Wextra -Werror -Wno-vla-parameter -Wno-deprecated-declarations")

    if( ("${CP2K_VERSION}" VERSION_GREATER_EQUAL 2024.1) OR ("${CP2K_VERSION}" STREQUAL "latest" ))
        #set(CP2K_MACROS "${CP2K_MACROS} -D__MPI_F08")
        set(CP2K_FFLAGS "${CP2K_FFLAGS} -std=f2008")
        set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -lstdc++ -Wl,--enable-new-dtags")
        set(CP2K_CXX_FLAGS "-O2 -fPIC -fno-omit-frame-pointer -fopenmp -g -march=native -mtune=native --std=c++14 -Wno-deprecated-declarations ${CP2K_MACROS}")
    endif()

    set(CP2K_LDFLAGS "${CP2K_LDFLAGS} -lgomp")
    set(CP2K_CFLAGS "${CP2K_CFLAGS} ${CP2K_MACROS}")
    set(CP2K_FFLAGS "${CP2K_FFLAGS} ${CP2K_MACROS}")
    set(FYPP_FLAGS  "-n --line-marker-format=gfortran5")

    # Cp2k arch file for archer2 uses mkl. For now, not using it.
    #set(CP2K_CFLAGS "${CP2K_CFLAGS} -I$(MKLROOT)/include -m64")
    #set(CP2K_MACROS "${CP2K_MACROS} -D__MKL")
endif()

#Rajany: 5/08/24 : GPU compile tested for gnu+cuda only
# Doing here explicitly rather than using find_package or findCUDAToolkit
if(GPU OR GPU_COMPILER OR GPU_ARCH)
 if(NOT(${CMAKE_CUDA_COMPILER} STREQUAL ""))

  set(ARCHLIST 35 37 60 70 80 86 90 K40 K80 P100 V100 A100 A40 H100)
  if(NOT("${GPU_ARCH}" STREQUAL "") AND (NOT("${GPU_ARCH}" IN_LIST ARCHLIST)) )
    message(WARNING "CUDA ARCH NUMBER IS NOT COMPATIBLE..CANNOT COMPILE CP2K WITH CUDA-SUPPORT.. Possible values are ${ARCHLIST}")
  elseif(NOT("${GPU_ARCH}" STREQUAL ""))
    if(${GPU_ARCH} STREQUAL 35)
      set(archname "K40")
      set(archnum 35)
    elseif(${GPU_ARCH} STREQUAL 37)
      set(archname "K80")
      set(archnum 37)
    elseif(${GPU_ARCH} STREQUAL 60)
      set(archname "P100")
      set(archnum 60)
    elseif(${GPU_ARCH} STREQUAL 70)
      set(archname "V100")
      set(archnum 70)
    elseif(${GPU_ARCH} STREQUAL 80)
      set(archname "A100")
      set(archnum 80)
    elseif(${GPU_ARCH} STREQUAL 86)
      set(archname "A40")
      set(archnum 86)
    elseif(${GPU_ARCH} STREQUAL 90)
      set(archname "H100")
      set(archnum 90)
    elseif(${GPU_ARCH} STREQUAL "K40")
      set(archname ${GPU_ARCH})
      set(archnum 35)
    elseif(${GPU_ARCH} STREQUAL "K80")
      set(archname ${GPU_ARCH})
      set(archnum 37)
    elseif(${GPU_ARCH} STREQUAL "P100")
      set(archname ${GPU_ARCH})
      set(archnum 60)
    elseif(${GPU_ARCH} STREQUAL "V100")
      set(archname ${GPU_ARCH})
      set(archnum 70)
    elseif(${GPU_ARCH} STREQUAL "A100")
      set(archname ${GPU_ARCH})
      set(archnum 80)
    elseif(${GPU_ARCH} STREQUAL "A40")
      set(archname ${GPU_ARCH})
      set(archnum 86)
    elseif(${GPU_ARCH} STREQUAL "H100")
      set(archname ${GPU_ARCH})
      set(archnum 90)
    endif()
  endif()

    set(GPU_ARCHNAME ${archname})
    set(GPU_ARCHNUM ${archnum})
    set(CP2K_GPU_COMPILER  "${CMAKE_CUDA_COMPILER}")
    #gpu is cuda now.not tested others like hip
    if(NOT("${GPU}" STREQUAL "" OR "${GPU}" STREQUAL "TRUE"))
      string(TOLOWER "${GPU}" GPU_TMP)
      set(CP2K_GPU_API ${GPU_TMP})
    else()
      set(CP2K_GPU_API "cuda")
    endif()

    set(GPU_MACROS "-D__OFFLOAD_CUDA -D__DBCSR_ACC")
    set(GPU_COMPILE_FLAGS "${CUDA_INCLUDE_FLAGS}")
    set(GPU_LDFLAGS "${CUDA_LINK_FLAGS} ${CUDA_MATH_LINK_FLAGS}")
    set(CP2K_GPU_FLAGS "-g -arch sm_${GPU_ARCHNUM} -O3 -allow-unsupported-compiler -Xcompiler='-fPIC -fopenmp -Wall -Wextra -Werror' --std=c++14 ${GPU_MACROS} ${GPU_COMPILE_FLAGS}")

    set(CP2K_LDFLAGS "${CP2K_LDFLAGS} ${GPU_LDFLAGS}")
    set(CP2K_CFLAGS "${CP2K_CFLAGS} ${GPU_COMPILE_FLAGS} ${GPU_MACROS}")
    set(CP2K_CXX_FLAGS "${CP2K_CXX_FLAGS} ${GPU_COMPILE_FLAGS} ${GPU_MACROS}")
    set(CP2K_FFLAGS "${CP2K_FFLAGS} ${GPU_MACROS} ${GPU_COMPILE_FLAGS}")
  endif()
endif()

    # Cp2k arch file for archer2 uses mkl. For now, not using it.
    #set(CP2K_CFLAGS "${CP2K_CFLAGS} -I$(MKLROOT)/include -m64")
    #set(CP2K_MACROS "${CP2K_MACROS} -D__MKL")

#list(APPEND CP2K_TOOLCHAIN_LIBS libfftw3xf_intel.a)
list(APPEND CHEMSH_CP2K_EXTRA_LIBS ${CP2K_LIB_DIR}/exts/dbcsr/libdbcsr.a)

# YL 23/02/2021: we support 8.1 now which allows building a single archive
list(APPEND CP2K_STATIC_LIBS libcp2k.a)

foreach(CP2K_STATIC_LIB ${CP2K_STATIC_LIBS})
    # a list of source static libs to copy: should NOT include quotation marks
    set(CP2K_STATIC_LIBS_FULLPATH ${CP2K_STATIC_LIBS_FULLPATH} ${CP2K_LIB_DIR}/${CP2K_STATIC_LIB})
    # note: by this point the libs have not yet been copied. this is only an assumed full list of them
    set(CHEMSH_CP2K_STATIC_LIBS "${CHEMSH_CP2K_STATIC_LIBS} ${CHEMSH_CP2K_LIB_DIR}/${CP2K_STATIC_LIB}")
endforeach(CP2K_STATIC_LIB)

needRebuild(CP2K_NEEDS_RECOMPILE CP2K_REINSTALL CP2K_STATIC_LIBS CP2K_LIB_DIR CHEMSH_CP2K_LIB_DIR)

# include
include_directories(${CP2K_DIR}/obj/${CP2K_BUILD_ARCH}/${CP2K_BUILD_VERSION}
                    ${CP2K_DIR}/obj/${CP2K_BUILD_ARCH}/${CP2K_BUILD_VERSION}/exts/dbcsr
                   )
if(NOT EXISTS ${CP2K_DIR}/obj/${CP2K_BUILD_ARCH}/${CP2K_BUILD_VERSION})
    message("No pre-compiled objects found. CP2K needs to be recompiled.")
    set(CP2K_NEEDS_RECOMPILE ON)
endif()

# get CP2K arch file and build command ready
set(CP2K_ARCH_FILE ${CP2K_BUILD_ARCH}.${CP2K_BUILD_VERSION})
configure_file(cp2k_arch.in ${CP2K_ARCH_FILE} @ONLY)

# NB: the flags are passed via the arch file rather than make command
set(CP2K_BUILD_COMMAND make -j ${CHEMSH_MAKE_NJOBS} ARCH=${CP2K_BUILD_ARCH} VERSION=${CP2K_BUILD_VERSION} libcp2k)
#message(FATAL_ERROR "CP2K_BUILD_COMMAND = make -j ${CHEMSH_MAKE_NJOBS} ARCH=${CP2K_BUILD_ARCH} VERSION=${CP2K_BUILD_VERSION} F77=${CMAKE_Fortran_COMPILER} FCFLAGS=${CP2K_FFLAGS} CFLAGS=${CP2K_CFLAGS} LDFLAGS=${CP2K_LDFLAGS}")

set(CP2K_INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CP2K_STATIC_LIBS_FULLPATH} ${CHEMSH_CP2K_LIB_DIR})

# forced to recompile and CP2K objects already exist
if(CP2K_RECOMPILE AND NOT CP2K_NEEDS_RECOMPILE)
    # CMake stupidly only accepts 0 or 1 in ExternalProject_Add()
    set(CP2K_CLEAN_COMMAND1 make clean)
    set(CP2K_CLEAN_COMMAND2 make distclean)
    file(REMOVE_RECURSE ${CHEMSH_ARCH}/stamp)
# forced to recompile but CP2K objects do not exist
elseif(CP2K_RECOMPILE AND CP2K_NEEDS_RECOMPILE)
# not forced to recompile but CP2K objects do not exist
elseif(NOT CP2K_RECOMPILE AND CP2K_NEEDS_RECOMPILE)
# not forced to recompile and CP2K objects already exist
else()
    set(FFTW3_BUILD_COMMAND    "")
    set(CP2K_CONFIGURE_COMMAND "")
    set(CP2K_BUILD_COMMAND     "")
    set(CP2K_INSTALL_COMMAND   "")
endif()

# compile CP2K as an external project using its original Makefile
ExternalProject_Add(
    ${CP2K_NAME}
    GIT_REPOSITORY      ${CP2K_GIT_REPO}
    GIT_TAG             ${CP2K_GIT_TAG}
    GIT_SHALLOW         1
    GIT_PROGRESS        ${DEBUG}
    GIT_CONFIG          advice.detachedHead=false
    UPDATE_DISCONNECTED 0
    UPDATE_COMMAND      pwd
    PREFIX              ${CHEMSH_ARCH}
    STAMP_DIR           ${CHEMSH_ARCH}/stamp
    TMP_DIR             ${CHEMSH_ARCH}/tmp
    BUILD_ALWAYS        ${CP2K_RECOMPILE}
    SOURCE_DIR          ${CP2K_DIR}
    CONFIGURE_COMMAND   ""
    BUILD_COMMAND       ${CMAKE_COMMAND} -E cmake_echo_color --switch=$(COLOR) --magenta --bold "Architecture for which '${CP2K_NAME}' is built: '${CHEMSH_ARCH}'"
    INSTALL_COMMAND     ""
)

ExternalProject_Add_Step(
    ${CP2K_NAME} CP2K_CONFIGURE
    COMMENT           "Cleaning up previous binaries for '${CP2K_NAME}'..."
    ALWAYS            ${CP2K_RECOMPILE}
    DEPENDEES         configure
    COMMAND           ${CP2K_CLEAN_COMMAND1} COMMAND ${CP2K_CLEAN_COMMAND2}
    WORKING_DIRECTORY ${CP2K_DIR}
)

ExternalProject_Add_Step(
    ${CP2K_NAME} CP2K_FFTW3_BUILD
    COMMENT           "Building FFTW3 library from ${FFTW_INTERFACE_DIR} with build command ${FFTW3_BUILD_COMMAND_STR}"
 #   ALWAYS            ${CP2K_RECOMPILE}
    DEPENDEES         CP2K_CONFIGURE
    COMMAND           ${FFTW3_BUILD_COMMAND}
    WORKING_DIRECTORY ${FFTW_INTERFACE_DIR}
)

#ExternalProject_Add_Step(
#    ${CP2K_NAME} CP2K_FFTW3_BUILD2
#    COMMENT           "Building FFTW3 library from ${FFTW_INTERFACE_DIR2} with build command ${FFTW3_BUILD_COMMAND_STR2}"
#    DEPENDEES         CP2K_FFTW3_BUILD
#    COMMAND           ${FFTW3_BUILD_COMMAND2}
#    WORKING_DIRECTORY ${FFTW_INTERFACE_DIR2}
#)

ExternalProject_Add_Step(
    ${CP2K_NAME} CP2K_ARCH
    COMMENT           "Installing CP2K architechture file to ${CP2K_DIR}/arch..."
    #ALWAYS            ${CP2K_RECOMPILE}
    DEPENDEES         CP2K_FFTW3_BUILD
    #DEPENDEES         CP2K_FFTW3_BUILD2
    COMMAND           ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/${CP2K_ARCH_FILE} ${CP2K_DIR}/arch
    WORKING_DIRECTORY ${CP2K_DIR}
)

ExternalProject_Add_Step(
    ${CP2K_NAME} CP2K_BUILD
    COMMENT           "Building static library for '${CP2K_NAME}'..."
    ALWAYS            ${CP2K_RECOMPILE}
    # always preceded by CP2K_CONFIGURE
    DEPENDEES         CP2K_ARCH
    COMMAND           ${CMAKE_COMMAND} -E cmake_echo_color --switch=$(COLOR) --white --bold "This may take a while. Please be patient..."
    COMMAND           ${CP2K_BUILD_COMMAND}
    WORKING_DIRECTORY ${CP2K_DIR}
)
ExternalProject_Add_Step(
    ${CP2K_NAME} CP2K_INSTALL
    COMMENT           "Installing static library to ${CHEMSH_CP2K_LIB_DIR}..."
    ALWAYS            ${COPY_CP2K_LIBS}
    # always preceded by CP2K_BUILD
    DEPENDEES         CP2K_BUILD
    COMMAND           ${CP2K_INSTALL_COMMAND}
    WORKING_DIRECTORY ${CP2K_DIR}
)

# cp2k_base_uses.f90
add_custom_target(cp2k_base_uses)
add_custom_command(TARGET cp2k_base_uses PRE_BUILD
                   COMMAND ${CMAKE_COMMAND} -E copy ${CP2K_SRC_DIR}/base/base_uses.f90 ${CMAKE_CURRENT_LIST_DIR}/cp2k_base_uses.f90)

# compile libcp2k.so (containing only Py-ChemShell interface files)
add_library(cp2k SHARED cp2k.f90 cp2k_routines.f90 ${LINKED})
# dependencies: cp2k -> cp2k_base_uses.f90 -> cp2k-x.x.x -> cp2k_arch_file
add_dependencies(cp2k ${CP2K_NAME} cp2k_base_uses)
add_dependencies(cp2k_base_uses ${CP2K_NAME})
#add_dependencies(${CP2K_NAME} cp2k_arch_target)

# link the static library agains libcp2k.so
target_link_libraries(cp2k dl_py2f parallel)
target_link_libraries(cp2k ${BLAS_LIBRARIES} ${LAPACK_LIBRARIES})
target_link_directories(cp2k PUBLIC ${CHEMSH_CP2K_LIB_DIR})
target_include_directories(cp2k PUBLIC ${CHEMSH_CP2K_INCLUDE_DIR})

if(${CHEMSH_ARCH} STREQUAL "intel")
    set_target_properties(cp2k PROPERTIES COMPILE_FLAGS "-fpp -qopenmp")
    set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} -qopenmp")
elseif(${CHEMSH_ARCH} STREQUAL "gnu")
    set_target_properties(cp2k PROPERTIES COMPILE_FLAGS "-cpp -ffree-line-length-none -fopenmp")
    set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} -fopenmp")
elseif(${CHEMSH_ARCH} STREQUAL "cpe-gnu")
    set_target_properties(cp2k PROPERTIES COMPILE_FLAGS "-cpp -ffree-line-length-none -fopenmp")
    set(CHEMSH_CP2K_LINK_FLAGS "${CHEMSH_CP2K_LINK_FLAGS} -fopenmp")
endif()

# CP2K uses the built-in ScaLAPACK provided by the toolchain utility
set_target_properties(cp2k PROPERTIES LINK_FLAGS "-Wl,--whole-archive                  \
                                                  ${CHEMSH_CP2K_STATIC_LIBS}           \
                                                  ${CHEMSH_CP2K_EXTRA_LIBS}            \
                                                  -Wl,--no-whole-archive               \
                                                  ${CHEMSH_CP2K_LINK_FLAGS}            \
                                                  -Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/export.map")
                                                  #-Wl,--allow-multiple-definition      \
                                                  #-Wl,--unresolved-symbols=ignore-all  \
