
cmake_minimum_required(VERSION 3.20)

project(PIMD CXX C Fortran)



option(MKLUSE "Use MKL or not" OFF)
option(ACCUSE "Use MacOs Accelerate framework" OFF)

find_package(MPI    REQUIRED)

set(CMAKE_CXX_STANDARD 14)

include_directories(SYSTEM ${MPI_C_INCLUDE_PATH} ${MPI_CXX_INCLUDE_PATH})

# Find Package
#list(APPEND CMAKE_PREFIX_PATH ~/torch/libtorch)


set(SRC_DIR source)




include_directories(${fortran_dir})


# Setup Compiler Flags
# 1. Warnings
add_compile_options("$<$<COMPILE_LANG_AND_ID:Fortran,GNU>:-Wall;-fimplicit-none>")
add_compile_options("$<$<COMPILE_LANG_AND_ID:Fortran,Intel,IntelLLVM>:-Warn;all,noexternal,nointerfaces;-stand;f23>")
# 2. Integer Options
#add_compile_options("$<$<COMPILE_LANG_AND_ID:Fortran,GNU>:-fdefault-integer-8>")
add_compile_options("$<$<COMPILE_LANG_AND_ID:Fortran,GNU>:-fallow-argument-mismatch>")
add_compile_options("$<$<COMPILE_LANG_AND_ID:Fortran,Intel,IntelLLVM>:-i4>")
#set(CMAKE_Fortran_FLAGS "${CMAKE_FOrtran_FLAGS} -g -p")
# Options for the Fujitsu compiler 
# Note that this also includes math libraries
add_compile_options("$<$<COMPILE_LANG_AND_ID:Fortran,Fujitsu>:-Kfast;-SSL2>")

#
# Setup the common_variables.F90 as a library to help compiling external libraries
#

add_library(CommonVars OBJECT ${SRC_DIR}/common_variables.F90 ${SRC_DIR}/int_to_char.F90)


add_executable(pimd.mpi.x )
add_executable(pimd.x )
add_executable(polymers.x)
add_executable(prep_liquid.x)
add_executable(convert_tinker.x)

target_link_libraries(pimd.mpi.x CommonVars)
target_link_libraries(pimd.x CommonVars)
target_link_libraries(polymers.x CommonVars)
target_link_libraries(prep_liquid.x CommonVars)
target_link_libraries(convert_tinker.x CommonVars)

include(./cmake/pimd.cmake)
include(./cmake/pimd_mpi.cmake)
include(./cmake/polymers.cmake)
include(./cmake/prep_liquid.cmake)
include(./cmake/convert_tinker.cmake)

option(PME "PME" OFF)
option(AENET "aenet: neural network potential" OFF)
option(AENETPYTORCH "aenet-PyTorch: neural network potential" OFF)
option(QE "Quantum Espresso" OFF)
option(DFTB+ "DFTB+" OFF)
option(VASP6 "VASP6-Interface" OFF)
option(CP2K "CP2K-Interface" OFF)
option(N2P2 "N2P2-Interface" OFF)
option(QEGIT "Quantum Espresso (git)" OFF)

option(IOUTILS "ioutils for aenet" OFF)

#QE Git interface
if(QEGIT)
   target_compile_definitions(pimd.mpi.x PUBLIC "-Dqegit")
   target_compile_definitions(polymers.x PUBLIC "-Dqegit")
   
   include(FetchContent)
   message(STATUS "Downloading Q-E from Gitlab, this may take a few minutes")
   FetchContent_Declare(
      q-e
      GIT_REPOSITORY https://gitlab.com/QEF/q-e.git
      GIT_TAG develop
      SOURCE_DIR q-e
   )
   FetchContent_MakeAvailable(q-e)

   target_include_directories(pimd.mpi.x PRIVATE ${q-e_BINARY_DIR}/Modules/mod/qe_modules)   
   target_include_directories(polymers.x PRIVATE ${q-e_BINARY_DIR}/Modules/mod/qe_modules)   

   target_link_libraries(pimd.mpi.x qe_modules)
   target_link_libraries(polymers.x qe_modules)
   target_link_libraries(pimd.mpi.x qe_couple)
   target_link_libraries(polymers.x qe_couple)
   target_link_libraries(pimd.mpi.x qe_pw)
   target_link_libraries(polymers.x qe_pw)

#   set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
endif()


if(IOUTILS)
  target_compile_definitions(pimd.mpi.x PUBLIC "-Dioutils")
  target_sources(pimd.mpi.x PRIVATE ${SRC_DIR}/ioutils_s.c)
endif()

# N2P2 Interface
if(N2P2)
    find_package(Eigen3 REQUIRED)
    find_package(GSL REQUIRED)

    target_link_libraries(pimd.mpi.x GSL::gsl Eigen3::Eigen)
    target_link_libraries(polymers.x GSL::gsl Eigen3::Eigen)

    target_link_libraries(pimd.mpi.x ${PROJECT_SOURCE_DIR}/lib/libnnptrain.a ${PROJECT_SOURCE_DIR}/lib/libnnp.a)
    target_link_libraries(polymers.x ${PROJECT_SOURCE_DIR}/lib/libnnptrain.a ${PROJECT_SOURCE_DIR}/lib/libnnp.a)

    target_link_libraries(pimd.mpi.x stdc++ pthread)
    target_link_libraries(polymers.x stdc++ pthread)

    set(N2P2 -Dn2p2)
    target_compile_definitions(pimd.mpi.x PUBLIC ${N2P2})
    target_compile_definitions(polymers.x PUBLIC ${N2P2})
endif()

# CP2K Interface
option(CP2K_DIR "CP2K Directory for linking as library" "")
option(CP2K_VER "CP2K version for linking as library" "")
# CP2K will usually come with its own MKL/Lapack, so no need to link again
set(MATHLIBDONE FALSE)
if(CP2K)
  if(CP2K_DIR)
    if(CP2K_VER)

      # We need pkg-config to determine the variables below
      find_package(PkgConfig REQUIRED)
      # Set the compile flag to enable compilation of the CP2K dependent parts of PIMD
      target_compile_definitions(pimd.mpi.x PUBLIC "-Dlibcp2kmpi")

      # Run an external command to get the correct compiler and linker flags
      # The reason why we get three linker flags seperately is that the sorting of --libs will not be correct
      execute_process(COMMAND pkg-config --libs-only-other ${CP2K_DIR}/lib/${CP2K_VER}/psmp/pkgconfig/libcp2k.pc OUTPUT_VARIABLE CP2K_LIBS)
      execute_process(COMMAND pkg-config --libs-only-L ${CP2K_DIR}/lib/${CP2K_VER}/psmp/pkgconfig/libcp2k.pc OUTPUT_VARIABLE CP2K_LLIBS)
      execute_process(COMMAND pkg-config --libs-only-l ${CP2K_DIR}/lib/${CP2K_VER}/psmp/pkgconfig/libcp2k.pc OUTPUT_VARIABLE CP2K_LLLIBS)
      execute_process(COMMAND pkg-config --cflags ${CP2K_DIR}/lib/${CP2K_VER}/psmp/pkgconfig/libcp2k.pc OUTPUT_VARIABLE CP2K_CFLAGS)

      # Clean the strings
      string(STRIP ${CP2K_LIBS} CP2K_LIBS)
      string(STRIP ${CP2K_LLIBS} CP2K_LLIBS)
      string(STRIP ${CP2K_LLLIBS} CP2K_LLLIBS)
      string(STRIP ${CP2K_CFLAGS} CP2K_CFLAGS)

      string(REGEX REPLACE " " ";" CP2K_CFLAGS ${CP2K_CFLAGS})
     
      # Output the variables
      message(STATUS "CP2K_LIBS (only-other) : ${CP2K_LIBS}")
      message(STATUS "CP2K_LIBS (only-L) : ${CP2K_LLIBS}")
      message(STATUS "CP2K_LIBS (only-l) : ${CP2K_LLLIBS}")
      message(STATUS "CP2K_CFLAGS: ${CP2K_CFLAGS}")

      # Add the libs and flags to the compilation of PIMD
      target_compile_options(pimd.mpi.x PUBLIC ${CP2K_CFLAGS})
      target_link_libraries(pimd.mpi.x ${CP2K_LLLIBS})
      target_link_libraries(pimd.mpi.x ${CP2K_LLIBS})
      target_link_libraries(pimd.mpi.x ${CP2K_LIBS})

      # Add the directory containing the module files for CP2K
      target_include_directories(pimd.mpi.x PRIVATE "${CP2K_DIR}/obj/${CP2K_VER}/psmp")

      # MKL/LAPACK would have been linked by CP2K, so use that one
      set(MATHLIBDONE TRUE)

    else()
      message(FATAL_ERROR "Please specify the CP2K version with the -DCP2K_VER flag if you wish to link as a library to PIMD")
    endif()
  else()
    message(FATAL_ERROR "Please specify the CP2K directory through the -DCP2K_DIR flag if you wish to link as a library to PIMD")
  endif()
endif()

# HDF5 - Sometimes linked with VASP
option(HDF5_INC "The include directory of HDF5, needed if VASP6 is compiled with support for this" "")

# VASP6 Interface
option(VASP6_DIR "VASP6 Directory for linking as library" "")
if(VASP6)
  if(VASP6_DIR)
    # Read the file into a variable
    file(READ "${VASP6_DIR}/build_info.inc" BUILD_INFO)

    # Find the start of the llibs definition
    string(REGEX MATCH "llibs  = '([^']*)" LLIBS_LINE ${BUILD_INFO})

    # Initialize LLIBS_VALUE with the first part of the definition
    string(REGEX REPLACE "llibs  = '([^']*)" "\\1" LLIBS_VALUE ${LLIBS_LINE})

    # Remove the processed part from BUILD_INFO
    string(REPLACE "${LLIBS_LINE}" "" BUILD_INFO ${BUILD_INFO})

    # Initialize a variable to indicate if we should continue reading lines
    set(CONTINUE_READING TRUE)

    # While the last line ended with an &, continue reading lines
    while(CONTINUE_READING)
      # Get the next line
      string(REGEX MATCH "^[^'\n]*" NEXT_LINE ${BUILD_INFO})
      
      # Check if the line ends with an &
      string(REGEX MATCH "&$" LINE_ENDS_WITH_AMP ${NEXT_LINE})

      if(LINE_ENDS_WITH_AMP)
        # If the line ends with an &, append it to LLIBS_VALUE and continue reading
        string(APPEND LLIBS_VALUE ${NEXT_LINE})
        # Remove the processed line from BUILD_INFO
        string(REPLACE "${NEXT_LINE}" "" BUILD_INFO ${BUILD_INFO})
      else()
        # If the line doesn't end with an &, append it to LLIBS_VALUE and stop reading
        set(CONTINUE_READING FALSE)
      endif()
    endwhile()

    # Remove line continuation characters and leading/trailing whitespaces
    string(REGEX REPLACE "&" "" LLIBS_VALUE ${LLIBS_VALUE})
    string(REGEX REPLACE "\n" "" LLIBS_VALUE ${LLIBS_VALUE})
    string(REGEX REPLACE " " ";" LLIBS_VALUE ${LLIBS_VALUE})
    
    set(VASP6 -Dvasp6)
    file(GLOB VASP_LIB_FILES "${VASP6_DIR}/*.o")
    list(FILTER VASP_LIB_FILES EXCLUDE REGEX "${VASP6_DIR}/main.o")
    
    target_compile_definitions(pimd.mpi.x PUBLIC ${VASP6})
    target_include_directories(pimd.mpi.x PRIVATE ${VASP6_DIR})
    
    target_link_libraries(pimd.mpi.x ${VASP_LIB_FILES})
    target_link_libraries(pimd.mpi.x "${VASP6_DIR}/parser/libparser.a")
    target_link_libraries(pimd.mpi.x "${VASP6_DIR}/lib/libdmy.a")
    
    target_link_libraries(pimd.mpi.x ${LLIBS_VALUE})

    target_compile_definitions(polymers.x PUBLIC ${VASP6})
    target_include_directories(polymers.x PRIVATE ${VASP6_DIR})

    target_link_libraries(polymers.x ${VASP_LIB_FILES})
    target_link_libraries(polymers.x "${VASP6_DIR}/parser/libparser.a")
    target_link_libraries(polymers.x "${VASP6_DIR}/lib/libdmy.a")
    
    target_link_libraries(polymers.x ${LLIBS_VALUE})
  
    if(HDF5_INC)
       target_include_directories(pimd.mpi.x PRIVATE ${HDF5_INC})
       target_include_directories(polymers.x PRIVATE ${HDF5_INC})
    endif()
  else()
    message(FATAL_ERROR "Please specify the VASP6 directory through the -DVASP6_DIR flag if you wish to link as a library to PIMD")
  endif()
endif()

if(DFTB+)
  if(DFTB+_INSTALLDIR)
     if(IS_DIRECTORY ${DFTB+_INSTALLDIR}/lib)
       set(DFTB+_INSTALLDIR ${DFTB+_INSTALLDIR}/lib)
     else()
       set(DFTB+_INSTALLDIR ${DFTB+_INSTALLDIR}/lib64)
     endif()
     file(GLOB children ABSOLUTE ${DFTB+_INSTALLDIR}/cmake/*)
     foreach(child ${children})
        if(IS_DIRECTORY ${child})
           list(APPEND CMAKE_PREFIX_PATH ${child})
        endif()
     endforeach()
  endif()

  message("CMAKE_PREFIX_PATH : ${CMAKE_PREFIX_PATH}")

  find_package(dftbplus REQUIRED)
  set(DFTB -Ddftblib)
  target_compile_definitions(pimd.mpi.x PUBLIC ${DFTB})
  target_compile_definitions(pimd.x PUBLIC ${DFTB})
  target_link_libraries(pimd.mpi.x DftbPlus::DftbPlus)
  target_link_libraries(pimd.x DftbPlus::DftbPlus)
endif()

link_directories(./lib)
include_directories(./lib)
target_link_directories(pimd.mpi.x PUBLIC  lib)
target_include_directories(pimd.mpi.x PUBLIC   lib)


#     compile PIMD with PME
# PME = -Dpme
#     compile PIMD without PME
# PME = -Dnopme

if(PME)
set(PME pme)
target_compile_definitions(pimd.mpi.x PUBLIC ${PME} )
else()
set(PME nopme)
target_compile_definitions(pimd.mpi.x PUBLIC ${PME} )
endif()

if(QE)
#LIBQE = -L../lib/qe/q-e-qe-6.2.1/qelib -lqe -L. -lfftw3
#     compile PIMD with QE
# QE = -Dqe
  set(QE qe)
  add_subdirectory(lib/qe)
  add_dependencies(qe_lib CommonVars)
  
  add_dependencies(pimd.mpi.x qe_lib)
  target_compile_definitions(pimd.mpi.x PUBLIC ${QE} )
  target_link_directories(pimd.mpi.x PUBLIC ${CMAKE_BINARY_DIR}/qe_lib/src/qe_lib/qelib/)
  target_link_libraries(pimd.mpi.x qe)
  add_dependencies(pimd.x qe_lib)
  target_compile_definitions(pimd.x PUBLIC ${QE} )
  target_link_directories(pimd.x PUBLIC ${CMAKE_BINARY_DIR}/qe_lib/src/qe_lib/qelib/)
  target_link_libraries(pimd.x qe)
  add_dependencies(polymers.x qe_lib)
  target_compile_definitions(polymers.x PUBLIC ${QE} )
  target_link_directories(polymers.x PUBLIC ${CMAKE_BINARY_DIR}/qe_lib/src/qe_lib/qelib/)
  target_link_libraries(polymers.x qe)
endif()

if(AENET)
  add_subdirectory(lib/aenet)
  target_link_libraries(pimd.mpi.x aenet lbfgsb)
  set(AENET aenet2)
  set(AENETFLAG nlist)
  target_compile_definitions(pimd.mpi.x PUBLIC ${AENET} ${AENETFLAG})
  target_link_directories(pimd.mpi.x PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/lib/aenet)
  target_include_directories(pimd.mpi.x PUBLIC  ${CMAKE_CURRENT_BINARY_DIR}/lib/aenet)
  #target_link_directories(pimd.mpi.x PUBLIC lib/aenet/src_modified)
  #target_include_directories(pimd.mpi.x PUBLIC  lib/aenet/src_modified)
  link_directories(./lib/aenet/src_modified)
  include_directories(./lib/aenet/src_modified)
  
  target_link_libraries(pimd.mpi.x aenet)
  target_link_libraries(pimd.mpi.x lbfgsb)
  #set(LIBAENET -L../lib/aenet/src_modified -laenet -I../lib/aenet/src_modified ./lib/liblbfgsb.a)

  target_link_libraries(polymers.x aenet lbfgsb)
  target_compile_definitions(polymers.x PUBLIC ${AENET} ${AENETFLAG})
  target_link_directories(polymers.x PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/lib/aenet)
  target_include_directories(polymers.x PUBLIC  ${CMAKE_CURRENT_BINARY_DIR}/lib/aenet)
  target_link_libraries(polymers.x aenet)
  target_link_libraries(polymers.x lbfgsb)
endif()


if(AENETPYTORCH)
  add_subdirectory(lib/aenetpytorch)
  target_link_libraries(pimd.mpi.x aenet lbfgsb)
  set(AENET aenet_pytorch)
  set(AENETFLAG nlist)
  target_compile_definitions(pimd.mpi.x PUBLIC ${AENET} ${AENETFLAG})
  target_link_directories(pimd.mpi.x PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/lib/aenetpytorch)
  target_include_directories(pimd.mpi.x PUBLIC  ${CMAKE_CURRENT_BINARY_DIR}/lib/aenetpytorch)
  link_directories(./lib/aenetpytorch/src_modified)
  include_directories(./lib/aenetpytorch/src_modified)
  
  target_link_libraries(pimd.mpi.x aenet)
  target_link_libraries(pimd.mpi.x lbfgsb)

  target_link_libraries(polymers.x aenet lbfgsb)
  target_compile_definitions(polymers.x PUBLIC ${AENET} ${AENETFLAG})
  target_link_directories(polymers.x PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/lib/aenetpytorch)
  target_include_directories(polymers.x PUBLIC  ${CMAKE_CURRENT_BINARY_DIR}/lib/aenetpytorch)
  target_link_libraries(polymers.x aenet)
  target_link_libraries(polymers.x lbfgsb)
endif()

#target_link_libraries(pimd.mpi.x PRIVATE ${MPI_LIBRARIES})
#target_include_directories(pimd.mpi.x PRIVATE ${MPI_INCLUDE_PATH})

target_link_libraries(pimd.mpi.x MPI::MPI_Fortran)


target_link_libraries(polymers.x MPI::MPI_Fortran)


#add_definitions(${MPI_Fortran_COMPILE_FLAGS})
#include_directories(${MPI_Fortran_INCLUDE_PATH})
#link_directories(${MPI_Fortran_LIBRARIES})
#target_link_libraries(pimd.mpi.x otherlibs ${MPI_Fortran_LIBRARIES})


if(MKLUSE)
   set(MKL_INTERFACE lp64)
   find_package(MKL REQUIRED)
   if(NOT MATHLIBDONE)
      target_link_libraries(pimd.mpi.x MKL::MKL)
   endif() 
   target_link_libraries(polymers.x MKL::MKL) 
   target_link_libraries(pimd.x MKL::MKL) 
elseif(ACCUSE)
   find_library(ACCELERATE_LIBRARY Accelerate)
   if(ACCELERATE_LIBRARY)
      if(NOT MATHLIBDONE)
         target_link_libraries(pimd.mpi.x "${ACCELERATE_LIBRARY}")
      endif()
      target_link_libraries(pimd.x "${ACCELERATE_LIBRARY}")
      target_link_libraries(polymers.x "${ACCELERATE_LIBRARY}")
      else()
         message(FATAL_ERROR "Accelerate framework not found")
      endif()
else()
   find_package(LAPACK REQUIRED)
   if(NOT MATHLIBDONE)
      target_link_libraries(pimd.mpi.x LAPACK::LAPACK) 
   endif()
   target_link_libraries(polymers.x LAPACK::LAPACK) 
   target_link_libraries(pimd.x LAPACK::LAPACK) 
endif()


if(MPI_COMPILE_FLAGS)
#  set_target_properties(pytorchtest PROPERTIES
#    COMPILE_FLAGS "${MPI_COMPILE_FLAGS}")
  set_target_properties(pimd.mpi.x  PROPERTIES
    COMPILE_FLAGS "${MPI_COMPILE_FLAGS}")
    set_target_properties(polymers.x  PROPERTIES
    COMPILE_FLAGS "${MPI_COMPILE_FLAGS}")
endif()

if(MPI_LINK_FLAGS)
  #set_target_properties(pytorchtest PROPERTIES
  #  LINK_FLAGS "${MPI_LINK_FLAGS}")
  set_target_properties(pimd.mpi.x  PROPERTIES
    LINK_FLAGS "${MPI_LINK_FLAGS}")
    set_target_properties(polymers.x  PROPERTIES
    LINK_FLAGS "${MPI_LINK_FLAGS}")    
endif()


