Skip to content

Commit

Permalink
pybamm-team#3049 Add CMakeBuild steps to setup.py instead of import…
Browse files Browse the repository at this point in the history
…ing it
  • Loading branch information
agriyakhetarpal committed Aug 30, 2023
1 parent 3af5092 commit 8768ed7
Showing 1 changed file with 173 additions and 122 deletions.
295 changes: 173 additions & 122 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import sys
import glob
import logging
import subprocess
Expand All @@ -13,12 +14,173 @@
from distutils.core import setup, find_packages
from distutils.command.install import install

import CMakeBuild
# import CMakeBuild

# ---------- cmakebuild was integrated into setup.py directly --------------------------

try:
from setuptools.command.build_ext import build_ext
except ImportError:
from distutils.command.build_ext import build_ext

default_lib_dir = (
"" if system() == "Windows" else os.path.join(os.getenv("HOME"), ".local")
)


def set_vcpkg_environment_variables():
if not os.getenv("VCPKG_ROOT_DIR"):
raise EnvironmentError("Environment variable 'VCPKG_ROOT_DIR' is undefined.")
if not os.getenv("VCPKG_DEFAULT_TRIPLET"):
raise EnvironmentError(
"Environment variable 'VCPKG_DEFAULT_TRIPLET' is undefined."
)
if not os.getenv("VCPKG_FEATURE_FLAGS"):
raise EnvironmentError(
"Environment variable 'VCPKG_FEATURE_FLAGS' is undefined."
)
return (
os.getenv("VCPKG_ROOT_DIR"),
os.getenv("VCPKG_DEFAULT_TRIPLET"),
os.getenv("VCPKG_FEATURE_FLAGS"),
)


class CMakeBuild(build_ext):
user_options = build_ext.user_options + [
("suitesparse-root=", None, "suitesparse source location"),
("sundials-root=", None, "sundials source location"),
]

def initialize_options(self):
build_ext.initialize_options(self)
self.suitesparse_root = None
self.sundials_root = None

def finalize_options(self):
build_ext.finalize_options(self)
# Determine the calling command to get the
# undefined options from.
# If build_ext was called directly then this
# doesn't matter.
try:
self.get_finalized_command("install", create=0)
calling_cmd = "install"
except AttributeError:
calling_cmd = "bdist_wheel"
self.set_undefined_options(
calling_cmd,
("suitesparse_root", "suitesparse_root"),
("sundials_root", "sundials_root"),
)
if not self.suitesparse_root:
self.suitesparse_root = os.path.join(default_lib_dir)
if not self.sundials_root:
self.sundials_root = os.path.join(default_lib_dir)

def get_build_directory(self):
# distutils outputs object files in directory self.build_temp
# (typically build/temp.*). This is our CMake build directory.
# On Windows, distutils is too smart and appends "Release" or
# "Debug" to self.build_temp. So in this case we want the
# build directory to be the parent directory.
if system() == "Windows":
return Path(self.build_temp).parents[0]
return self.build_temp

def run(self):
if not self.extensions:
return

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

build_type = os.getenv("PYBAMM_CPP_BUILD_TYPE", "RELEASE")
cmake_args = [
"-DCMAKE_BUILD_TYPE={}".format(build_type),
"-DPYTHON_EXECUTABLE={}".format(sys.executable),
"-DUSE_PYTHON_CASADI={}".format("TRUE" if use_python_casadi else "FALSE"),
]
if self.suitesparse_root:
cmake_args.append(
"-DSuiteSparse_ROOT={}".format(os.path.abspath(self.suitesparse_root))
)
if self.sundials_root:
cmake_args.append(
"-DSUNDIALS_ROOT={}".format(os.path.abspath(self.sundials_root))
)

build_dir = self.get_build_directory()
if not os.path.exists(build_dir):
os.makedirs(build_dir)

# The CMakeError.log file is generated by cmake is the configure step
# encounters error. In the following the existence of this file is used
# to determine whether or not the cmake configure step went smoothly.
# So must make sure this file does not remain from a previous failed build.
if os.path.isfile(os.path.join(build_dir, "CMakeError.log")):
os.remove(os.path.join(build_dir, "CMakeError.log"))

build_env = os.environ
if os.getenv("PYBAMM_USE_VCPKG"):
(
vcpkg_root_dir,
vcpkg_default_triplet,
vcpkg_feature_flags,
) = set_vcpkg_environment_variables()
build_env["vcpkg_root_dir"] = vcpkg_root_dir
build_env["vcpkg_default_triplet"] = vcpkg_default_triplet
build_env["vcpkg_feature_flags"] = vcpkg_feature_flags

cmake_list_dir = os.path.abspath(os.path.dirname(__file__))
print("-" * 10, "Running CMake for idaklu solver", "-" * 40)
subprocess.run(
["cmake", cmake_list_dir] + cmake_args, cwd=build_dir, env=build_env
)

if os.path.isfile(os.path.join(build_dir, "CMakeError.log")):
msg = (
"cmake configuration steps encountered errors, and the idaklu module"
" could not be built. Make sure dependencies are correctly "
"installed. See "
"https://github.com/pybamm-team/PyBaMM/tree/develop"
"INSTALL-LINUX-MAC.md"
)
raise RuntimeError(msg)
else:
print("-" * 10, "Building idaklu module", "-" * 40)
subprocess.run(
["cmake", "--build", ".", "--config", "Release"],
cwd=build_dir,
env=build_env,
)

# Move from build temp to final position
for ext in self.extensions:
self.move_output(ext)

def move_output(self, ext):
# Copy built module to dist/ directory
build_temp = Path(self.build_temp).resolve()
# Get destination location
# self.get_ext_fullpath(ext.name) -->
# build/lib.linux-x86_64-3.5/idaklu.cpython-37m-x86_64-linux-gnu.so
# using resolve() with python < 3.6 will result in a FileNotFoundError
# since the location does not yet exists.
dest_path = Path(self.get_ext_fullpath(ext.name)).resolve()
source_path = build_temp / os.path.basename(self.get_ext_filename(ext.name))
dest_directory = dest_path.parents[0]
dest_directory.mkdir(parents=True, exist_ok=True)
self.copy_file(source_path, dest_path)

# ---------- end of cmakebuild steps ---------------------------------------------------

# default_lib_dir = (
# "" if system() == "Windows" else os.path.join(os.getenv("HOME"), ".local")
# )

log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
logger = logging.getLogger("PyBaMM setup")

Expand Down Expand Up @@ -123,6 +285,7 @@ def compile_KLU():

# Build the list of package data files to be included in the PyBaMM package.
# These are mainly the parameter files located in the input/parameters/ subdirectories.
# TODO: might be possible to include in pyproject.toml with data configuration values
pybamm_data = []
for file_ext in ["*.csv", "*.py", "*.md", "*.txt"]:
# Get all the files ending in file_ext in pybamm/input dir.
Expand Down Expand Up @@ -162,144 +325,32 @@ def compile_KLU():
ext_modules = [idaklu_ext] if compile_KLU() else []

# Defines __version__
# TODO: might not be needed anymore, because we define it in pyproject.toml
# and can therefore access it with importlib.metadata.version("pybamm") (python 3.8+)
# The version.py file can then be imported with attr: pybamm.__version__ dynamically
root = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(root, "pybamm", "version.py")) as f:
exec(f.read())

# Load text for description and license
# TODO: might not be needed anymore, because we define the description and license
# in pyproject.toml
# TODO: add long description there and remove it from setup()
with open("README.md", encoding="utf-8") as f:
readme = f.read()

# Project metadata was moved to pyproject.toml (which is read by pip).
# However, custom build commands and setuptools extension modules are still defined here
setup(
name="pybamm",
version=__version__, # noqa: F821
description="Python Battery Mathematical Modelling.",
long_description=readme,
long_description_content_type="text/markdown",
url="https://github.com/pybamm-team/PyBaMM",
packages=find_packages(include=("pybamm", "pybamm.*")),
ext_modules=ext_modules,
cmdclass={
"build_ext": CMakeBuild.CMakeBuild,
"build_ext": CMakeBuild,
"bdist_wheel": bdist_wheel,
"install": CustomInstall,
},
package_data={"pybamm": pybamm_data},
# Python version
python_requires=">=3.8,<3.12",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Scientific/Engineering",
],
# List of dependencies
install_requires=[
"numpy>=1.16",
"scipy>=1.3",
"casadi>=3.6.0",
"xarray",
],
extras_require={
"docs": [
"sphinx>=6",
"sphinx_rtd_theme>=0.5",
"pydata-sphinx-theme",
"sphinx_design",
"sphinx-copybutton",
"myst-parser",
"sphinx-inline-tabs",
"sphinxcontrib-bibtex",
"sphinx-autobuild",
"sphinx-last-updated-by-git",
"nbsphinx",
"ipykernel",
"ipywidgets",
"sphinx-gallery",
"sphinx-hoverxref",
"sphinx-docsearch",
], # For doc generation
"examples": [
"jupyter", # For example notebooks
],
"plot": [
"imageio>=2.9.0",
# Note: Matplotlib is loaded for debug plots, but to ensure pybamm runs
# on systems without an attached display, it should never be imported
# outside of plot() methods.
# Should not be imported
"matplotlib>=2.0",
],
"cite": [
"pybtex>=0.24.0",
],
"latexify": [
"sympy>=1.8",
],
"bpx": [
"bpx",
],
"tqdm": [
"tqdm",
],
"dev": [
"pre-commit", # For code style checking
"ruff", # For code style auto-formatting
"nox", # For running testing sessions
],
"pandas": [
"pandas>=0.24",
],
"jax": [
"jax==0.4.8",
"jaxlib==0.4.7",
],
"odes": ["scikits.odes"],
"all": [
"anytree>=2.4.3",
"autograd>=1.2",
"pandas>=0.24",
"scikit-fem>=0.2.0",
"imageio>=2.9.0",
"pybtex>=0.24.0",
"sympy>=1.8",
"bpx",
"tqdm",
"matplotlib>=2.0",
"jupyter",
],
},
entry_points={
"console_scripts": [
"pybamm_edit_parameter = pybamm.parameters_cli:edit_parameter",
"pybamm_add_parameter = pybamm.parameters_cli:add_parameter",
"pybamm_rm_parameter = pybamm.parameters_cli:remove_parameter",
"pybamm_install_odes = pybamm.install_odes:main",
"pybamm_install_jax = pybamm.util:install_jax",
],
"pybamm_parameter_sets": [
"Sulzer2019 = pybamm.input.parameters.lead_acid.Sulzer2019:get_parameter_values", # noqa: E501
"Ai2020 = pybamm.input.parameters.lithium_ion.Ai2020:get_parameter_values", # noqa: E501
"Chen2020 = pybamm.input.parameters.lithium_ion.Chen2020:get_parameter_values", # noqa: E501
"Chen2020_composite = pybamm.input.parameters.lithium_ion.Chen2020_composite:get_parameter_values", # noqa: E501
"Ecker2015 = pybamm.input.parameters.lithium_ion.Ecker2015:get_parameter_values", # noqa: E501
"Marquis2019 = pybamm.input.parameters.lithium_ion.Marquis2019:get_parameter_values", # noqa: E501
"Mohtat2020 = pybamm.input.parameters.lithium_ion.Mohtat2020:get_parameter_values", # noqa: E501
"NCA_Kim2011 = pybamm.input.parameters.lithium_ion.NCA_Kim2011:get_parameter_values", # noqa: E501
"OKane2022 = pybamm.input.parameters.lithium_ion.OKane2022:get_parameter_values", # noqa: E501
"ORegan2022 = pybamm.input.parameters.lithium_ion.ORegan2022:get_parameter_values", # noqa: E501
"Prada2013 = pybamm.input.parameters.lithium_ion.Prada2013:get_parameter_values", # noqa: E501
"Ramadass2004 = pybamm.input.parameters.lithium_ion.Ramadass2004:get_parameter_values", # noqa: E501
"Xu2019 = pybamm.input.parameters.lithium_ion.Xu2019:get_parameter_values", # noqa: E501
"ECM_Example = pybamm.input.parameters.ecm.example_set:get_parameter_values", # noqa: E501
],
},
)

0 comments on commit 8768ed7

Please sign in to comment.