Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 24 additions & 61 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.13)
cmake_policy(SET CMP0074 NEW)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This policy is too old; it prevents us from using https://cmake.org/cmake/help/latest/module/FindPython.html as it gets disabled when this policy is enabled.

cmake_minimum_required(VERSION 3.26)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The earliest version of CMake to support the new FindPython mode is 3.26. We don't pin CMake in pyproject.toml, and our use of CMake isn't too heavy either, so the current 4.1.0 works fine. Please let me know if you'd like me to upper-pin this in pyproject.toml as well, though I think it shouldn't be needed.


set(CMAKE_VERBOSE_MAKEFILE ON)

if (DEFINED ENV{VCPKG_ROOT_DIR} AND NOT DEFINED VCPKG_ROOT_DIR)
Expand Down Expand Up @@ -32,6 +32,7 @@ endif ()
# casadi seems to compile without the newer versions of std::string
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)

set(PYBIND11_FINDPYTHON ON)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will help with WASM builds :)

find_package(pybind11 CONFIG REQUIRED)

# Check Casadi build flag
Expand Down Expand Up @@ -91,73 +92,35 @@ if (NOT DEFINED USE_PYTHON_CASADI)
set(USE_PYTHON_CASADI TRUE)
endif ()

if (${USE_PYTHON_CASADI})
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" -c
"import os; import sysconfig; print(os.path.join(sysconfig.get_path('purelib'), 'casadi', 'cmake'))"
OUTPUT_VARIABLE CASADI_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)

if (CASADI_DIR)
file(TO_CMAKE_PATH ${CASADI_DIR} CASADI_DIR)
message("Found Python casadi path: ${CASADI_DIR}")
else ()
message(FATAL_ERROR "Did not find casadi path")
endif ()

message("Trying to link against Python casadi package in ${CASADI_DIR}")
if (EXISTS "${CASADI_DIR}/casadiConfig.cmake" OR EXISTS "${CASADI_DIR}/casadi-config.cmake")
find_package(casadi CONFIG PATHS ${CASADI_DIR} NO_DEFAULT_PATH)
else ()
message(WARNING "CasADi CMake config not found in ${CASADI_DIR}. Proceeding without find_package; using include and library paths discovered from the Python package.")
endif ()

execute_process(
COMMAND "${PYTHON_EXECUTABLE}" -c
"import casadi; from pathlib import Path; print(Path(casadi.__file__).parent / 'include')"
OUTPUT_VARIABLE CASADI_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)

if (CASADI_INCLUDE_DIR)
file(TO_CMAKE_PATH ${CASADI_INCLUDE_DIR} CASADI_INCLUDE_DIR)
message("Found Python CasADi include directory: ${CASADI_INCLUDE_DIR}")
target_include_directories(idaklu PRIVATE ${CASADI_INCLUDE_DIR})
else ()
message(FATAL_ERROR "Could not find CasADi include directory")
endif ()
execute_process(
COMMAND "${Python_EXECUTABLE}" -c
Copy link

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable Python_EXECUTABLE is not defined. Since pybind11's FindPython mode is enabled, this should be Python3_EXECUTABLE or you need to ensure Python_EXECUTABLE is properly set from the FindPython module.

Suggested change
COMMAND "${Python_EXECUTABLE}" -c
COMMAND "${Python3_EXECUTABLE}" -c

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect; FindPython defines Python_EXECUTABLE and not Python3_EXECUTABLE.

"import pathlib, casadi; print(pathlib.Path(next(iter(casadi.__path__))).joinpath('cmake'))"
OUTPUT_VARIABLE CASADI_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
Comment on lines +96 to +100
Copy link
Member Author

@agriyakhetarpal agriyakhetarpal Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that the previous version of this command, i.e.,

import os; import sysconfig; print(os.path.join(sysconfig.get_path('purelib'), 'casadi', 'cmake'))

thinks that it finds CasADi in something like /Users/agriyakhetarpal/Desktop/pybammsolvers/venv/lib/python3.12/site-packages/casadi/cmake, which will never exist – this is because it just constructs the path for where CasADi would be placed.

What we needed to do here is to import casadi – this is always guaranteed to work as we require casadi as a build-time dependency, and hence is available in sys.modules during build isolation. I switched to this, and we correctly get the build-time CasADi:

/private/var/folders/b3/2bq1m1_50bs4c7305j8vxcqr0000gn/T/pip-build-env-ihqwpmd7/overlay/lib/python3.12/site-packages/casadi/cmake


execute_process(
COMMAND "${PYTHON_EXECUTABLE}" -c
"import casadi; from pathlib import Path; import glob; lib_dir = Path(casadi.__file__).parent; lib_files = list(lib_dir.glob('*casadi*')); print(str(lib_dir) if lib_files else '')"
OUTPUT_VARIABLE CASADI_LIB_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
if (CASADI_DIR)
file(TO_CMAKE_PATH "${CASADI_DIR}" CASADI_DIR)
message("Found Python casadi path: ${CASADI_DIR}")
else ()
message(FATAL_ERROR "Did not find casadi path")
endif ()

if (CASADI_LIB_DIR)
file(TO_CMAKE_PATH ${CASADI_LIB_DIR} CASADI_LIB_DIR)
message("Found Python CasADi library directory: ${CASADI_LIB_DIR}")
target_link_directories(idaklu PRIVATE ${CASADI_LIB_DIR})

set_target_properties(
idaklu PROPERTIES
INSTALL_RPATH "${CASADI_LIB_DIR}"
INSTALL_RPATH_USE_LINK_PATH TRUE
)
# Attempt to link the casadi library directly if found
find_library(CASADI_LIBRARY NAMES casadi PATHS ${CASADI_LIB_DIR} NO_DEFAULT_PATH)
if (CASADI_LIBRARY)
message("Found CasADi library: ${CASADI_LIBRARY}")
target_link_libraries(idaklu PRIVATE ${CASADI_LIBRARY})
else ()
message(WARNING "CasADi library not found in ${CASADI_LIB_DIR}. The target will rely on transitive linkage via CMake config if available.")
endif ()
else ()
message(FATAL_ERROR "Could not find CasADi library directory")
endif ()
if (${USE_PYTHON_CASADI})
message("Trying to link against Python casadi package in ${CASADI_DIR}")
find_package(casadi CONFIG PATHS "${CASADI_DIR}" REQUIRED NO_DEFAULT_PATH)
else ()
message("Trying to link against any casadi package apart from the Python one")
set(CMAKE_IGNORE_PATH "${CASADI_DIR}/cmake")
find_package(casadi CONFIG REQUIRED)
endif ()

set_target_properties(
idaklu PROPERTIES
INSTALL_RPATH "${CASADI_DIR}"
INSTALL_RPATH_USE_LINK_PATH TRUE
)

# openmp
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
execute_process(
Expand Down
35 changes: 7 additions & 28 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@
"" if system() == "Windows" else str(Path(__file__).parent.resolve() / ".idaklu")
)

INCLUDE_DIRS = [str(Path(default_lib_dir) / "include"), pybind11.get_include()]

USE_PYTHON_CASADI = False if system() == "Windows" else True

# ---------- set environment variables for vcpkg on Windows ----------------------------


Expand All @@ -37,23 +33,6 @@ def set_vcpkg_environment_variables():
)


# ---------- find Python CasADi's include dirs (for Linux and macOS) -------------------


def get_casadi_python_include_dir():
import casadi

casadi_path = Path(casadi.__file__).parent
include_dir = casadi_path / "include"
assert include_dir.exists(), f"CasADi include directory not found at {include_dir}"
return str(include_dir)


if USE_PYTHON_CASADI:
casadi_include = get_casadi_python_include_dir()
INCLUDE_DIRS.append(casadi_include)
print(f"Adding CasADi include directory: {casadi_include}")

# ---------- CMakeBuild class (custom build_ext for IDAKLU target) ---------------------


Expand Down Expand Up @@ -107,6 +86,11 @@ def run(self):
# Build in parallel wherever possible
os.environ["CMAKE_BUILD_PARALLEL_LEVEL"] = str(cpu_count())

if system() == "Windows":
use_python_casadi = False
else:
use_python_casadi = True

build_type = os.getenv("PYBAMM_CPP_BUILD_TYPE", "Release")
idaklu_expr_casadi = os.getenv("PYBAMM_IDAKLU_EXPR_CASADI", "ON")

Expand All @@ -115,7 +99,7 @@ def run(self):
cmake_args = [
f"-DCMAKE_BUILD_TYPE={build_type}",
f"-DPYTHON_EXECUTABLE={sys.executable}",
"-DUSE_PYTHON_CASADI={}".format("TRUE" if USE_PYTHON_CASADI else "FALSE"),
"-DUSE_PYTHON_CASADI={}".format("TRUE" if use_python_casadi else "FALSE"),
f"-DPYBAMM_IDAKLU_EXPR_CASADI={idaklu_expr_casadi}",
f"-Dpybind11_DIR={pybind11_cmake_dir}",
]
Expand All @@ -126,11 +110,6 @@ def run(self):
if self.sundials_root:
cmake_args.append(f"-DSUNDIALS_ROOT={os.path.abspath(self.sundials_root)}")

if USE_PYTHON_CASADI:
casadi_include = get_casadi_python_include_dir()
cmake_args.append(f"-DCASADI_INCLUDE_DIR={casadi_include}")
print(f"Adding CasADi include directory to CMake: {casadi_include}")

build_dir = self.get_build_directory()
if not os.path.exists(build_dir):
os.makedirs(build_dir)
Expand Down Expand Up @@ -293,7 +272,7 @@ def run(self):
"src/pybammsolvers/idaklu_source/Options.cpp",
"src/pybammsolvers/idaklu.cpp",
],
include_dirs=INCLUDE_DIRS,
include_dirs=[str(Path(default_lib_dir) / "include"), pybind11.get_include()],
)
]

Expand Down
Loading