|
| 1 | +cmake_minimum_required(VERSION 3.18...3.28) |
| 2 | +project(fidimag LANGUAGES C) |
| 3 | + |
| 4 | +# Policy settings for modern CMake |
| 5 | +if(POLICY CMP0167) |
| 6 | + cmake_policy(SET CMP0167 NEW) # FindBoost compatibility |
| 7 | +endif() |
| 8 | + |
| 9 | +# ============================================================================= |
| 10 | +# Build Configuration |
| 11 | +# ============================================================================= |
| 12 | + |
| 13 | +# Default to Release build for optimization |
| 14 | +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) |
| 15 | + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) |
| 16 | + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo") |
| 17 | +endif() |
| 18 | + |
| 19 | +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") |
| 20 | + |
| 21 | +# C/C++ standards |
| 22 | +set(CMAKE_C_STANDARD 99) |
| 23 | +set(CMAKE_C_STANDARD_REQUIRED ON) |
| 24 | + |
| 25 | +# Position independent code (required for Python extensions) |
| 26 | +set(CMAKE_POSITION_INDEPENDENT_CODE ON) |
| 27 | + |
| 28 | +# ============================================================================= |
| 29 | +# Add local library paths (for SUNDIALS/FFTW in ./local/) |
| 30 | +# ============================================================================= |
| 31 | + |
| 32 | +list(APPEND CMAKE_PREFIX_PATH |
| 33 | + "${PROJECT_SOURCE_DIR}/local" |
| 34 | + "${PROJECT_SOURCE_DIR}/local/lib/cmake" |
| 35 | + "${PROJECT_SOURCE_DIR}/local/lib64/cmake" |
| 36 | +) |
| 37 | + |
| 38 | +# Also add to module path for custom Find modules |
| 39 | +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") |
| 40 | + |
| 41 | +message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") |
| 42 | + |
| 43 | +# ============================================================================= |
| 44 | +# Find Dependencies |
| 45 | +# ============================================================================= |
| 46 | + |
| 47 | +# Python, NumPy, and Cython |
| 48 | +find_package(Python REQUIRED COMPONENTS Interpreter Development.Module NumPy) |
| 49 | +message(STATUS "Python: ${Python_VERSION} (${Python_EXECUTABLE})") |
| 50 | +message(STATUS "NumPy include: ${Python_NumPy_INCLUDE_DIRS}") |
| 51 | + |
| 52 | +# Cython |
| 53 | +find_program(CYTHON_EXECUTABLE NAMES cython cython3 REQUIRED) |
| 54 | +message(STATUS "Cython: ${CYTHON_EXECUTABLE}") |
| 55 | + |
| 56 | +# OpenMP (required for parallel computation) |
| 57 | +find_package(OpenMP REQUIRED COMPONENTS C) |
| 58 | +message(STATUS "OpenMP found: C ${OpenMP_C_FLAGS}") |
| 59 | + |
| 60 | +# BLAS and LAPACK (linear algebra) |
| 61 | +find_package(BLAS REQUIRED) |
| 62 | +find_package(LAPACK REQUIRED) |
| 63 | +message(STATUS "BLAS: ${BLAS_LIBRARIES}") |
| 64 | +message(STATUS "LAPACK: ${LAPACK_LIBRARIES}") |
| 65 | + |
| 66 | +# FFTW3 (Fast Fourier Transform) |
| 67 | +# Look for both regular and OpenMP versions |
| 68 | +find_library(FFTW3_LIBRARY NAMES fftw3 REQUIRED |
| 69 | + HINTS ${PROJECT_SOURCE_DIR}/local/lib ${PROJECT_SOURCE_DIR}/local/lib64 |
| 70 | + DOC "FFTW3 library" |
| 71 | +) |
| 72 | +find_library(FFTW3_OMP_LIBRARY NAMES fftw3_omp REQUIRED |
| 73 | + HINTS ${PROJECT_SOURCE_DIR}/local/lib ${PROJECT_SOURCE_DIR}/local/lib64 |
| 74 | + DOC "FFTW3 OpenMP library" |
| 75 | +) |
| 76 | +find_path(FFTW3_INCLUDE_DIR fftw3.h |
| 77 | + HINTS ${PROJECT_SOURCE_DIR}/local/include $ENV{FFTW_INC} |
| 78 | + DOC "FFTW3 include directory" |
| 79 | +) |
| 80 | +message(STATUS "FFTW3: ${FFTW3_LIBRARY}") |
| 81 | +message(STATUS "FFTW3 OpenMP: ${FFTW3_OMP_LIBRARY}") |
| 82 | +message(STATUS "FFTW3 include: ${FFTW3_INCLUDE_DIR}") |
| 83 | + |
| 84 | +# SUNDIALS (ODE/DAE solver) - Custom find module |
| 85 | +find_package(SUNDIALS 7.6 REQUIRED COMPONENTS cvodes nvecserial nvecopenmp) |
| 86 | +message(STATUS "SUNDIALS ${SUNDIALS_VERSION} found") |
| 87 | +message(STATUS " Include: ${SUNDIALS_INCLUDE_DIRS}") |
| 88 | +message(STATUS " Libraries: ${SUNDIALS_LIBRARIES}") |
| 89 | + |
| 90 | +# ============================================================================= |
| 91 | +# Common Compiler Flags |
| 92 | +# ============================================================================= |
| 93 | + |
| 94 | +# C compiler flags |
| 95 | +set(C_COMPILE_FLAGS |
| 96 | + -O3 |
| 97 | + -Wall |
| 98 | + -Wno-cpp |
| 99 | + -Wno-unused-function |
| 100 | +) |
| 101 | + |
| 102 | +# Common libraries for all extensions |
| 103 | +set(COMMON_LIBRARIES |
| 104 | + m # Math library |
| 105 | + ${FFTW3_OMP_LIBRARY} |
| 106 | + ${FFTW3_LIBRARY} |
| 107 | + ${SUNDIALS_LIBRARIES} |
| 108 | + ${BLAS_LIBRARIES} |
| 109 | + ${LAPACK_LIBRARIES} |
| 110 | + OpenMP::OpenMP_C |
| 111 | +) |
| 112 | + |
| 113 | +# Common include directories |
| 114 | +set(COMMON_INCLUDES |
| 115 | + ${Python_INCLUDE_DIRS} |
| 116 | + ${Python_NumPy_INCLUDE_DIRS} |
| 117 | + ${PROJECT_SOURCE_DIR}/local/include |
| 118 | + ${FFTW3_INCLUDE_DIR} |
| 119 | + ${SUNDIALS_INCLUDE_DIRS} |
| 120 | +) |
| 121 | + |
| 122 | +# Add environment variable paths if set |
| 123 | +if(DEFINED ENV{SUNDIALS_INC}) |
| 124 | + list(APPEND COMMON_INCLUDES $ENV{SUNDIALS_INC}) |
| 125 | + message(STATUS "Added SUNDIALS_INC: $ENV{SUNDIALS_INC}") |
| 126 | +endif() |
| 127 | + |
| 128 | +if(DEFINED ENV{FFTW_INC}) |
| 129 | + list(APPEND COMMON_INCLUDES $ENV{FFTW_INC}) |
| 130 | + message(STATUS "Added FFTW_INC: $ENV{FFTW_INC}") |
| 131 | +endif() |
| 132 | + |
| 133 | +# ============================================================================= |
| 134 | +# Helper Function: Create Cython Extension |
| 135 | +# ============================================================================= |
| 136 | + |
| 137 | +function(add_fidimag_extension MODULE_NAME SOURCE_FILE) |
| 138 | + # Get the module name without "fidimag.extensions." prefix for target name |
| 139 | + string(REPLACE "." "_" TARGET_NAME ${MODULE_NAME}) |
| 140 | + |
| 141 | + # Determine output paths |
| 142 | + get_filename_component(SOURCE_DIR ${SOURCE_FILE} DIRECTORY) |
| 143 | + get_filename_component(SOURCE_NAME ${SOURCE_FILE} NAME_WE) |
| 144 | + |
| 145 | + # Cython output file (always C) |
| 146 | + set(CYTHON_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_DIR}/${SOURCE_NAME}.c") |
| 147 | + |
| 148 | + # Find additional C/C++ source files in the same directory |
| 149 | + file(GLOB EXTRA_SOURCES |
| 150 | + "${PROJECT_SOURCE_DIR}/fidimag/${SOURCE_DIR}/*.c" |
| 151 | + "${PROJECT_SOURCE_DIR}/fidimag/${SOURCE_DIR}/*.cpp" |
| 152 | + ) |
| 153 | + |
| 154 | + # Remove the cythonized output if it's in the source dir |
| 155 | + list(FILTER EXTRA_SOURCES EXCLUDE REGEX ".*${SOURCE_NAME}\\.c$") |
| 156 | + list(FILTER EXTRA_SOURCES EXCLUDE REGEX ".*${SOURCE_NAME}\\.cpp$") |
| 157 | + |
| 158 | + message(STATUS "Extension ${MODULE_NAME}:") |
| 159 | + message(STATUS " Source: ${SOURCE_FILE}") |
| 160 | + message(STATUS " Cython output: ${CYTHON_OUTPUT}") |
| 161 | + message(STATUS " Extra sources: ${EXTRA_SOURCES}") |
| 162 | + message(STATUS " Language: C") |
| 163 | + |
| 164 | + # Ensure output directory exists |
| 165 | + get_filename_component(CYTHON_OUTPUT_DIR ${CYTHON_OUTPUT} DIRECTORY) |
| 166 | + file(MAKE_DIRECTORY ${CYTHON_OUTPUT_DIR}) |
| 167 | + |
| 168 | + # Cythonize the .pyx file |
| 169 | + add_custom_command( |
| 170 | + OUTPUT ${CYTHON_OUTPUT} |
| 171 | + COMMAND ${CYTHON_EXECUTABLE} |
| 172 | + -3 # Python 3 |
| 173 | + --directive linetrace=True |
| 174 | + --directive language_level=3 |
| 175 | + -o ${CYTHON_OUTPUT} |
| 176 | + ${PROJECT_SOURCE_DIR}/fidimag/${SOURCE_FILE} |
| 177 | + DEPENDS ${PROJECT_SOURCE_DIR}/fidimag/${SOURCE_FILE} |
| 178 | + COMMENT "Cythonizing ${SOURCE_FILE}" |
| 179 | + ) |
| 180 | + |
| 181 | + set_source_files_properties(${CYTHON_OUTPUT} PROPERTIES LANGUAGE C) |
| 182 | + set_source_files_properties(${EXTRA_SOURCES} PROPERTIES LANGUAGE C) |
| 183 | + |
| 184 | + # Create the Python extension module |
| 185 | + Python_add_library(${TARGET_NAME} MODULE |
| 186 | + ${CYTHON_OUTPUT} |
| 187 | + ${EXTRA_SOURCES} |
| 188 | + WITH_SOABI |
| 189 | + ) |
| 190 | + |
| 191 | + # Set target properties |
| 192 | + set_target_properties(${TARGET_NAME} PROPERTIES |
| 193 | + OUTPUT_NAME ${SOURCE_NAME} |
| 194 | + PREFIX "" # No lib prefix |
| 195 | + LINKER_LANGUAGE C |
| 196 | + ) |
| 197 | + |
| 198 | + # Include directories |
| 199 | + target_include_directories(${TARGET_NAME} PRIVATE |
| 200 | + ${COMMON_INCLUDES} |
| 201 | + ${PROJECT_SOURCE_DIR}/fidimag/${SOURCE_DIR} |
| 202 | + ) |
| 203 | + |
| 204 | + # Compiler flags |
| 205 | + target_compile_options(${TARGET_NAME} PRIVATE ${C_COMPILE_FLAGS}) |
| 206 | + |
| 207 | + # Link libraries |
| 208 | + target_link_libraries(${TARGET_NAME} PRIVATE ${COMMON_LIBRARIES}) |
| 209 | + |
| 210 | + # RPATH for finding local libraries at runtime |
| 211 | + set_target_properties(${TARGET_NAME} PROPERTIES |
| 212 | + INSTALL_RPATH "${PROJECT_SOURCE_DIR}/local/lib;${PROJECT_SOURCE_DIR}/local/lib64" |
| 213 | + BUILD_RPATH "${PROJECT_SOURCE_DIR}/local/lib;${PROJECT_SOURCE_DIR}/local/lib64" |
| 214 | + ) |
| 215 | + |
| 216 | + # Install the extension to the proper location in the fidimag package |
| 217 | + # Extract the Python module path from MODULE_NAME |
| 218 | + string(REPLACE "." "/" MODULE_PATH ${MODULE_NAME}) |
| 219 | + get_filename_component(INSTALL_DIR ${MODULE_PATH} DIRECTORY) |
| 220 | + |
| 221 | + install(TARGETS ${TARGET_NAME} |
| 222 | + LIBRARY DESTINATION ${INSTALL_DIR} |
| 223 | + COMPONENT python |
| 224 | + ) |
| 225 | +endfunction() |
| 226 | + |
| 227 | +# ============================================================================= |
| 228 | +# Define All Cython Extensions |
| 229 | +# ============================================================================= |
| 230 | + |
| 231 | +# Atomistic extensions |
| 232 | +add_fidimag_extension("fidimag.extensions.clib" "atomistic/lib/clib.pyx") |
| 233 | + |
| 234 | +# Micromagnetic extensions |
| 235 | +add_fidimag_extension("fidimag.extensions.micro_clib" "micro/lib/micro_clib.pyx") |
| 236 | +add_fidimag_extension("fidimag.extensions.baryakhtar_clib" "micro/lib/baryakhtar/baryakhtar_clib.pyx") |
| 237 | + |
| 238 | +# Common extensions |
| 239 | +add_fidimag_extension("fidimag.extensions.common_clib" "common/lib/common_clib.pyx") |
| 240 | +add_fidimag_extension("fidimag.extensions.cvode" "common/sundials/cvode.pyx") |
| 241 | +add_fidimag_extension("fidimag.extensions.dipolar" "common/dipolar/dipolar.pyx") |
| 242 | +add_fidimag_extension("fidimag.extensions.nebm_clib" "common/neb_method/nebm_clib.pyx") |
| 243 | + |
| 244 | +# ============================================================================= |
| 245 | +# Print Configuration Summary |
| 246 | +# ============================================================================= |
| 247 | + |
| 248 | +message(STATUS "") |
| 249 | +message(STATUS "========================================") |
| 250 | +message(STATUS "fidimag Build Configuration Summary") |
| 251 | +message(STATUS "========================================") |
| 252 | +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") |
| 253 | +message(STATUS "C compiler: ${CMAKE_C_COMPILER}") |
| 254 | +message(STATUS "Python: ${Python_VERSION}") |
| 255 | +message(STATUS "NumPy: ${Python_NumPy_VERSION}") |
| 256 | +message(STATUS "SUNDIALS: ${SUNDIALS_VERSION}") |
| 257 | +message(STATUS "OpenMP: Enabled") |
| 258 | +message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") |
| 259 | +message(STATUS "========================================") |
| 260 | +message(STATUS "") |
0 commit comments