From 378e22bc61c3d8513aca193e85c1a505d675d3dd Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 24 Jul 2025 16:16:31 +0200 Subject: [PATCH 1/6] added pyproject.toml --- pyproject.toml | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..e2b0cf67 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,62 @@ +[build-system] +requires = [ + "scikit-build-core>=0.8.0", + "numpy>=2.2", + "f90wrap", +] +build-backend = "scikit_build_core.build" + +[project] +name = "spec" +version = "0.0.3" +description = "Stepped Pressure Equilibrium Code MHD Equilibrium Solver" +authors = [ + {name = "Christopher Berg Smiet", email = "christopher.smiet@epfl.ch"}, + {name = "Stuart R. Hudson"}, + {name = "Joaquim Loizu", email = "joaquim.loizu@epfl.ch"}, + {name = "Bharat Medasani", email = "mbkumar@gmail.com"}, + {name = "Caoxiang Zhu"} +] +readme = "README.md" +license = {file = "LICENSE"} +requires-python = ">=3.8" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: GPL-3.0 License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Physics", +] +dependencies = [ + "numpy>=1.23.5", + "f90wrap", +] + + +[project.urls] +Homepage = "https://github.com/PrincetonUniversity/SPEC" +Documentation = "https://princetonuniversity.github.io/SPEC/" +Repository = "https://github.com/PrincetonUniversity/SPEC.git" +"Bug Tracker" = "https://github.com/PrincetonUniversity/SPEC/issues" + +[tool.scikit-build] +# Specify the CMake source directory +cmake.source-dir = "." +# Set minimum CMake version +cmake.version = ">=3.15" +# Build directory +build-dir = "build/{wheel_tag}" +# Configure install components +install.components = ["python_wrapper"] +# Ensure wheel includes the Python wrapper package +wheel.packages = ["spec"] + +[tool.scikit-build.cmake.define] +# Ensure SKBUILD is defined for conditional Python wrapper building +SKBUILD = "ON" + From c5725b835506b797d9cd109fd0b6bed1c885225e Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 24 Jul 2025 18:23:12 +0200 Subject: [PATCH 2/6] new and improved toml-based build --- Utilities/python_wrapper/CMakeLists.txt | 9 +++-- Utilities/python_wrapper/cmake/FindF2PY.cmake | 38 +++++++++++++++++++ .../python_wrapper/cmake/FindNumPy.cmake | 11 ++++++ .../cmake/FindPythonExtensions.cmake | 22 +++++++++++ Utilities/python_wrapper/spec/__init__.py | 12 +++--- pyproject.toml | 5 +++ src/CMakeLists.txt | 10 +++++ 7 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 Utilities/python_wrapper/cmake/FindF2PY.cmake create mode 100644 Utilities/python_wrapper/cmake/FindNumPy.cmake create mode 100644 Utilities/python_wrapper/cmake/FindPythonExtensions.cmake diff --git a/Utilities/python_wrapper/CMakeLists.txt b/Utilities/python_wrapper/CMakeLists.txt index 96658fe1..cf63b317 100644 --- a/Utilities/python_wrapper/CMakeLists.txt +++ b/Utilities/python_wrapper/CMakeLists.txt @@ -132,10 +132,11 @@ add_custom_command( #VERBATIM COMMAND_EXPAND_LISTS ) - -python_extension_module(${generated_module_file}) -install(FILES ${python_mod_file} ${generated_module_file} #${CMAKE_CURRENT_SOURCE_DIR}/__init__.py - DESTINATION Utilities/python_wrapper/spec +install(FILES ${python_mod_file} ${generated_module_file} + ${CMAKE_CURRENT_SOURCE_DIR}/spec/__init__.py + ${CMAKE_CURRENT_SOURCE_DIR}/spec/core.py + DESTINATION spec + COMPONENT python_wrapper ) #set(PYINIT_STR "import sys\nimport os.path\nsys.path.append(os.path.dirname(__file__))\n\nfrom .spec import *\n") diff --git a/Utilities/python_wrapper/cmake/FindF2PY.cmake b/Utilities/python_wrapper/cmake/FindF2PY.cmake new file mode 100644 index 00000000..ebb6230b --- /dev/null +++ b/Utilities/python_wrapper/cmake/FindF2PY.cmake @@ -0,0 +1,38 @@ +# FindF2PY.cmake +# Find f2py executable and include directories + +# Find Python first +find_package(Python COMPONENTS Interpreter Development NumPy REQUIRED) + +# Find f2py executable +execute_process( + COMMAND ${Python_EXECUTABLE} -c "import numpy.f2py; print(numpy.f2py.get_include())" + OUTPUT_VARIABLE F2PY_INCLUDE_DIRS + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +# Find f2py executable path +execute_process( + COMMAND ${Python_EXECUTABLE} -c "import sys; import numpy.f2py as f2py; print(f2py.main.__file__.replace('__main__.py', 'f2py.py') if hasattr(f2py, 'main') else sys.executable + ' -m numpy.f2py')" + OUTPUT_VARIABLE F2PY_EXECUTABLE_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +# Set F2PY_EXECUTABLE - use python -m numpy.f2py for reliability +set(F2PY_EXECUTABLE "${Python_EXECUTABLE}" "-m" "numpy.f2py") + +# Set include directories +if(NOT F2PY_INCLUDE_DIRS) + set(F2PY_INCLUDE_DIRS ${Python_NumPy_INCLUDE_DIRS}) +endif() + +# Mark as found if we have Python and NumPy +if(Python_FOUND AND Python_NumPy_FOUND) + set(F2PY_FOUND TRUE) +else() + set(F2PY_FOUND FALSE) +endif() + +mark_as_advanced(F2PY_EXECUTABLE F2PY_INCLUDE_DIRS) \ No newline at end of file diff --git a/Utilities/python_wrapper/cmake/FindNumPy.cmake b/Utilities/python_wrapper/cmake/FindNumPy.cmake new file mode 100644 index 00000000..946bf68e --- /dev/null +++ b/Utilities/python_wrapper/cmake/FindNumPy.cmake @@ -0,0 +1,11 @@ +# FindNumPy.cmake +# Find NumPy include directories + +# Find Python first +find_package(Python COMPONENTS Interpreter NumPy REQUIRED) + +# Set NumPy variables for compatibility +set(NumPy_INCLUDE_DIRS ${Python_NumPy_INCLUDE_DIRS}) +set(NumPy_FOUND ${Python_NumPy_FOUND}) + +mark_as_advanced(NumPy_INCLUDE_DIRS) \ No newline at end of file diff --git a/Utilities/python_wrapper/cmake/FindPythonExtensions.cmake b/Utilities/python_wrapper/cmake/FindPythonExtensions.cmake new file mode 100644 index 00000000..3a034a73 --- /dev/null +++ b/Utilities/python_wrapper/cmake/FindPythonExtensions.cmake @@ -0,0 +1,22 @@ +# FindPythonExtensions.cmake +# Provides functionality for building Python extension modules + +# Find Python +find_package(Python COMPONENTS Interpreter Development REQUIRED) + +# Get the extension suffix +execute_process( + COMMAND ${Python_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))" + OUTPUT_VARIABLE PYTHON_EXTENSION_MODULE_SUFFIX + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Function to mark a target as a Python extension module +function(python_extension_module target) + set_target_properties(${target} PROPERTIES + PREFIX "" + SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}" + ) +endfunction() + +mark_as_advanced(PYTHON_EXTENSION_MODULE_SUFFIX) \ No newline at end of file diff --git a/Utilities/python_wrapper/spec/__init__.py b/Utilities/python_wrapper/spec/__init__.py index 183e796b..56b6c816 100644 --- a/Utilities/python_wrapper/spec/__init__.py +++ b/Utilities/python_wrapper/spec/__init__.py @@ -1,10 +1,8 @@ import os.path -#print(__file__) -#print(os.path.dirname(__file__)) -path_to_spec_f90wrapped = os.path.dirname(__file__) - import sys -if not path_to_spec_f90wrapped in sys.path: - sys.path.append(path_to_spec_f90wrapped) -# import spec_f90wrapped as spec +# Add current directory to path if not already there +path_to_spec_f90wrapped = os.path.dirname(__file__) +if path_to_spec_f90wrapped not in sys.path: + sys.path.append(path_to_spec_f90wrapped) + diff --git a/pyproject.toml b/pyproject.toml index e2b0cf67..68db0b16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ classifiers = [ dependencies = [ "numpy>=1.23.5", "f90wrap", + "mpi4py", ] @@ -59,4 +60,8 @@ wheel.packages = ["spec"] [tool.scikit-build.cmake.define] # Ensure SKBUILD is defined for conditional Python wrapper building SKBUILD = "ON" +# Set compilers and BLAS vendor from cmake_config.json +CMAKE_C_COMPILER = "mpicc" +CMAKE_Fortran_COMPILER = "mpif90" +BLA_VENDOR = "OpenBLAS" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 387457d8..718c321b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -295,6 +295,16 @@ endif() add_executable(xspec ${XSPEC_OUT_FILE}) target_link_libraries(xspec PUBLIC spec) + +# Ensure MPI and OpenMP are linked to xspec executable +if(MPI_Fortran_FOUND) + target_link_libraries(xspec PUBLIC MPI::MPI_Fortran) +endif() + +if(OpenMP_Fortran_FOUND) + target_link_libraries(xspec PUBLIC OpenMP::OpenMP_Fortran) +endif() + set_target_properties(xspec PROPERTIES POSITION_INDEPENDENT_CODE ON) install(TARGETS xspec spec) From 69c512f40ab5b5b78063cf80b100fa7e3690466c Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 24 Jul 2025 18:24:56 +0200 Subject: [PATCH 3/6] remove hard-coding of compilers in toml file. on my system does not compile because usual compiler resolves paths to blas and lapack with library paths with strings '-pthread' instead of full file path. This then borks the f2py compilation that can only deal with filepaths. Not sure how to fix. --- pyproject.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 68db0b16..5cb8f4f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,8 +60,3 @@ wheel.packages = ["spec"] [tool.scikit-build.cmake.define] # Ensure SKBUILD is defined for conditional Python wrapper building SKBUILD = "ON" -# Set compilers and BLAS vendor from cmake_config.json -CMAKE_C_COMPILER = "mpicc" -CMAKE_Fortran_COMPILER = "mpif90" -BLA_VENDOR = "OpenBLAS" - From 8489a67feddf996a1535f4e9e0f7b992bae7c209 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 24 Jul 2025 18:28:03 +0200 Subject: [PATCH 4/6] remove machine-specific configs I cannot make them work with toml-based install. --- cmake_config.json | 1 - cmake_machines/centos7_default.json | 9 --------- cmake_machines/conda.json | 9 --------- cmake_machines/conda_debian.json | 7 ------- cmake_machines/gfortran_arch.json | 7 ------- cmake_machines/gfortran_debian.json | 7 ------- cmake_machines/gfortran_ubuntu.json | 9 --------- cmake_machines/intel_SUSE.json | 8 -------- 8 files changed, 57 deletions(-) delete mode 120000 cmake_config.json delete mode 100644 cmake_machines/centos7_default.json delete mode 100644 cmake_machines/conda.json delete mode 100644 cmake_machines/conda_debian.json delete mode 100644 cmake_machines/gfortran_arch.json delete mode 100644 cmake_machines/gfortran_debian.json delete mode 100644 cmake_machines/gfortran_ubuntu.json delete mode 100644 cmake_machines/intel_SUSE.json diff --git a/cmake_config.json b/cmake_config.json deleted file mode 120000 index 4f8229e4..00000000 --- a/cmake_config.json +++ /dev/null @@ -1 +0,0 @@ -cmake_machines/conda_debian.json \ No newline at end of file diff --git a/cmake_machines/centos7_default.json b/cmake_machines/centos7_default.json deleted file mode 100644 index acbe200d..00000000 --- a/cmake_machines/centos7_default.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "cmake_args": [ - "-DCMAKE_C_COMPILER=mpicc", - "-DCMAKE_CXX_COMPILER=mpicxx", - "-DCMAKE_Fortran_COMPILER=mpifort", - "-DBLA_VENDOR=OpenBLAS", - "-DHDF5_NO_FIND_PACKAGE_CONFIG_FILE=TRUE", - "-DHDF5_PREFER_PARALLEL=TRUE"] -} diff --git a/cmake_machines/conda.json b/cmake_machines/conda.json deleted file mode 100644 index 4f5c4e81..00000000 --- a/cmake_machines/conda.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "cmake_args": [ - "-DCMAKE_C_COMPILER=mpicc", - "-DCMAKE_CXX_COMPILER=mpicxx", - "-DCMAKE_Fortran_COMPILER=mpifort", - "-DBLA_VENDOR=OpenBLAS", - "-DHDF5_ROOT=~/opt/miniconda3/envs/simsopt/", - "-DHDF5_PREFER_PARALLEL=TRUE"] -} diff --git a/cmake_machines/conda_debian.json b/cmake_machines/conda_debian.json deleted file mode 100644 index db76bf76..00000000 --- a/cmake_machines/conda_debian.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "cmake_args": [ - "-DCMAKE_C_COMPILER=mpicc", - "-DCMAKE_Fortran_COMPILER=mpif90", - "-DBLA_VENDOR=OpenBLAS" - ] -} diff --git a/cmake_machines/gfortran_arch.json b/cmake_machines/gfortran_arch.json deleted file mode 100644 index 3cea61a1..00000000 --- a/cmake_machines/gfortran_arch.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "cmake_args": [ - "-DCMAKE_C_COMPILER=mpicc", - "-DCMAKE_CXX_COMPILER=mpicxx", - "-DCMAKE_Fortran_COMPILER=mpif90", - "-DHDF5_PREFER_PARALLEL=False"] -} diff --git a/cmake_machines/gfortran_debian.json b/cmake_machines/gfortran_debian.json deleted file mode 100644 index 1f35f7bb..00000000 --- a/cmake_machines/gfortran_debian.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "cmake_args": [ - "-DCMAKE_C_COMPILER=mpicc", - "-DCMAKE_Fortran_COMPILER=mpif90", - "-DHDF5_ROOT=/usr/lib/x86_64-linux-gnu/hdf5/serial" - ] -} diff --git a/cmake_machines/gfortran_ubuntu.json b/cmake_machines/gfortran_ubuntu.json deleted file mode 100644 index 80ec61e3..00000000 --- a/cmake_machines/gfortran_ubuntu.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "cmake_args": [ - "-DCMAKE_C_COMPILER=mpicc", - "-DCMAKE_CXX_COMPILER=mpicxx", - "-DCMAKE_Fortran_COMPILER=mpifort", - "-DBLA_VENDOR=OpenBLAS", - "-DHDF5_ROOT=/usr/lib/x86_64-linux-gnu/hdf5/serial", - "-DHDF5_PREFER_PARALLEL=False"] -} diff --git a/cmake_machines/intel_SUSE.json b/cmake_machines/intel_SUSE.json deleted file mode 100644 index 398c9534..00000000 --- a/cmake_machines/intel_SUSE.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "cmake_args": [ - "-DCMAKE_C_COMPILER=mpicc", - "-DCMAKE_CXX_COMPILER=mpicc", - "-DCMAKE_Fortran_COMPILER=mpif90", - "-DBLA_VENDOR=Intel10_64lp" - ] -} From d6cba74a7561124f4f7fa4788f0e365317ccf66a Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 24 Jul 2025 18:48:20 +0200 Subject: [PATCH 5/6] add default variables for compiler and BLA to avoid common pitfall and show users some common variables. --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5cb8f4f5..3f223529 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,6 @@ classifiers = [ dependencies = [ "numpy>=1.23.5", "f90wrap", - "mpi4py", ] @@ -60,3 +59,7 @@ wheel.packages = ["spec"] [tool.scikit-build.cmake.define] # Ensure SKBUILD is defined for conditional Python wrapper building SKBUILD = "ON" +# Set compilers and BLAS vendor from cmake_config.json +CMAKE_C_COMPILER = {env="CMAKE_ARGS_CMAKE_C_COMPILER", default="mpicc"} +CMAKE_Fortran_COMPILER = {env="CMAKE_ARGS_CMAKE_Fortran_COMPILER", default="mpif90"} +BLA_VENDOR = {env="CMAKE_ARGS_BLA_VENDOR", default="OpenBLAS"} From 410eebb2eddc2a312866938b63c59f80a1834714 Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 25 Jul 2025 13:04:00 +0200 Subject: [PATCH 6/6] add meson dependency for newer python versions, build in different dir than source files --- Utilities/python_wrapper/CMakeLists.txt | 6 ++++-- pyproject.toml | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Utilities/python_wrapper/CMakeLists.txt b/Utilities/python_wrapper/CMakeLists.txt index cf63b317..c6850852 100644 --- a/Utilities/python_wrapper/CMakeLists.txt +++ b/Utilities/python_wrapper/CMakeLists.txt @@ -109,11 +109,13 @@ add_custom_target(${f2py_module_name} ALL DEPENDS ${generated_module_file} spec ${f90wrap_output_files} ) +set(f2py_build_dir ${CMAKE_CURRENT_BINARY_DIR}/f2py_build) + add_custom_command( OUTPUT ${generated_module_file} COMMAND ${F2PY_EXECUTABLE} -m ${f2py_module_name} - --build-dir ${CMAKE_CURRENT_BINARY_DIR} + --build-dir ${f2py_build_dir} --f90exec=${CMAKE_Fortran_COMPILER} --f77exec=${CMAKE_Fortran_COMPILER} --f90flags="-fopenmp" @@ -123,7 +125,6 @@ add_custom_command( ${f90wrap_output_files} -I${CMAKE_BINARY_DIR}/build/modules/spec_modules -I${HDF5_Fortran_INCLUDE_DIRS} - --verbose ${CMAKE_BINARY_DIR}/build/lib/libspec.a ${SPEC_LINK_LIB} #IMPLICIT_DEPENDS Fortran ${f90wrap_output_files} @@ -132,6 +133,7 @@ add_custom_command( #VERBATIM COMMAND_EXPAND_LISTS ) + install(FILES ${python_mod_file} ${generated_module_file} ${CMAKE_CURRENT_SOURCE_DIR}/spec/__init__.py ${CMAKE_CURRENT_SOURCE_DIR}/spec/core.py diff --git a/pyproject.toml b/pyproject.toml index 3f223529..3be479d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,8 @@ requires = [ "scikit-build-core>=0.8.0", "numpy>=2.2", "f90wrap", + "meson;python_version>='3.12'", + "ninja" ] build-backend = "scikit_build_core.build"