Skip to content

Commit 7b9eaff

Browse files
authored
Use submodules instead of downloads (#44)
* Update build script * Pre-commit changes * Update python version * Switch to submodules * Set correct verison * Pre-commit * Roll back python range, streamline if-else logic * Fix install order
1 parent ffec7b5 commit 7b9eaff

File tree

5 files changed

+32
-127
lines changed

5 files changed

+32
-127
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ __pycache__
99
.DS_Store
1010

1111
setup.log
12-
install_KLU_Sundials
12+
build_sundials
1313
.idaklu
1414
build
1515

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
[submodule "pybind11"]
22
path = pybind11
33
url = https://github.com/pybind/pybind11.git
4+
[submodule "sundials"]
5+
path = sundials
6+
url = git@github.com:LLNL/sundials.git
7+
[submodule "SuiteSparse"]
8+
path = SuiteSparse
9+
url = git@github.com:DrTimothyAldenDavis/SuiteSparse.git

SuiteSparse

Submodule SuiteSparse added at d3c4926

install_KLU_Sundials.py

Lines changed: 23 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,27 @@
11
import os
22
import subprocess
3-
import tarfile
43
import argparse
54
import platform
6-
import hashlib
75
import shutil
8-
import urllib.request
96
from os.path import join, isfile
10-
from urllib.parse import urlparse
11-
from concurrent.futures import ThreadPoolExecutor
127
from multiprocessing import cpu_count
138
import pathlib
149

1510

1611
def build_solvers():
17-
SUITESPARSE_VERSION = "7.8.3"
18-
SUITESPARSE_URL = f"https://github.com/DrTimothyAldenDavis/SuiteSparse/archive/v{SUITESPARSE_VERSION}.tar.gz"
19-
SUITESPARSE_CHECKSUM = (
20-
"ce39b28d4038a09c14f21e02c664401be73c0cb96a9198418d6a98a7db73a259"
21-
)
22-
SUNDIALS_VERSION = "7.3.0"
23-
SUNDIALS_URL = f"https://github.com/LLNL/sundials/releases/download/v{SUNDIALS_VERSION}/sundials-{SUNDIALS_VERSION}.tar.gz"
24-
SUNDIALS_CHECKSUM = (
25-
"697b7b0dbc229f149e39b293d1ab03d321d61adb6733ffb78c0ddbffaf73d839"
26-
)
2712
DEFAULT_INSTALL_DIR = str(pathlib.Path(__file__).parent.resolve() / ".idaklu")
2813

2914
def safe_remove_dir(path):
3015
if os.path.exists(path):
3116
shutil.rmtree(path)
3217

33-
def install_suitesparse(download_dir):
18+
def install_suitesparse():
3419
# The SuiteSparse KLU module has 4 dependencies:
3520
# - suitesparseconfig
3621
# - AMD
3722
# - COLAMD
3823
# - BTF
39-
suitesparse_dir = f"SuiteSparse-{SUITESPARSE_VERSION}"
40-
suitesparse_src = os.path.join(download_dir, suitesparse_dir)
24+
suitesparse_src = pathlib.Path("SuiteSparse")
4125
print("-" * 10, "Building SuiteSparse_config", "-" * 40)
4226
make_cmd = [
4327
"make",
@@ -61,28 +45,26 @@ def install_suitesparse(download_dir):
6145
# dylibs to find libomp.dylib when repairing the wheel
6246
if os.environ.get("CIBUILDWHEEL") == "1":
6347
env["CMAKE_OPTIONS"] = (
64-
f"-DCMAKE_INSTALL_PREFIX={install_dir} -DCMAKE_INSTALL_RPATH={install_dir}/lib"
48+
f"-DCMAKE_INSTALL_PREFIX={DEFAULT_INSTALL_DIR} -DCMAKE_INSTALL_RPATH={DEFAULT_INSTALL_DIR}/lib"
6549
)
6650
else:
67-
env["CMAKE_OPTIONS"] = f"-DCMAKE_INSTALL_PREFIX={install_dir}"
51+
env["CMAKE_OPTIONS"] = (
52+
f"-DCMAKE_INSTALL_PREFIX={DEFAULT_INSTALL_DIR}"
53+
)
6854
else:
6955
# For AMD, COLAMD, BTF and KLU; do not set a BUILD RPATH but use an
7056
# INSTALL RPATH in order to ensure that the dynamic libraries are found
7157
# at runtime just once. Otherwise, delocate complains about multiple
7258
# references to the SuiteSparse_config dynamic library (auditwheel does not).
7359
env["CMAKE_OPTIONS"] = (
74-
f"-DCMAKE_INSTALL_PREFIX={install_dir} -DCMAKE_INSTALL_RPATH={install_dir}/lib -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE -DCMAKE_BUILD_WITH_INSTALL_RPATH=FALSE"
60+
f"-DCMAKE_INSTALL_PREFIX={DEFAULT_INSTALL_DIR} -DCMAKE_INSTALL_RPATH={DEFAULT_INSTALL_DIR}/lib -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=FALSE -DCMAKE_BUILD_WITH_INSTALL_RPATH=FALSE"
7561
)
7662
subprocess.run(make_cmd, cwd=build_dir, env=env, shell=True, check=True)
7763
subprocess.run(install_cmd, cwd=build_dir, check=True)
7864

79-
def install_sundials(download_dir, install_dir):
80-
# Set install dir for SuiteSparse libs
81-
# Ex: if install_dir -> "/usr/local/" then
82-
# KLU_INCLUDE_DIR -> "/usr/local/include"
83-
# KLU_LIBRARY_DIR -> "/usr/local/lib"
84-
KLU_INCLUDE_DIR = os.path.join(install_dir, "include", "suitesparse")
85-
KLU_LIBRARY_DIR = os.path.join(install_dir, "lib")
65+
def install_sundials():
66+
KLU_INCLUDE_DIR = os.path.join(DEFAULT_INSTALL_DIR, "include", "suitesparse")
67+
KLU_LIBRARY_DIR = os.path.join(DEFAULT_INSTALL_DIR, "lib")
8668
cmake_args = [
8769
"-DENABLE_LAPACK=ON",
8870
"-DSUNDIALS_INDEX_SIZE=32",
@@ -93,7 +75,7 @@ def install_sundials(download_dir, install_dir):
9375
"-DENABLE_OPENMP=ON",
9476
f"-DKLU_INCLUDE_DIR={KLU_INCLUDE_DIR}",
9577
f"-DKLU_LIBRARY_DIR={KLU_LIBRARY_DIR}",
96-
"-DCMAKE_INSTALL_PREFIX=" + install_dir,
78+
"-DCMAKE_INSTALL_PREFIX=" + DEFAULT_INSTALL_DIR,
9779
# on macOS use fixed paths rather than rpath
9880
"-DCMAKE_INSTALL_NAME_DIR=" + KLU_LIBRARY_DIR,
9981
]
@@ -134,24 +116,22 @@ def install_sundials(download_dir, install_dir):
134116
"-DOpenMP_omp_LIBRARY=" + OpenMP_omp_LIBRARY,
135117
]
136118

137-
# SUNDIALS are built within download_dir 'build_sundials' in the PyBaMM root
138-
# download_dir
139-
build_dir = os.path.abspath(os.path.join(download_dir, "build_sundials"))
119+
build_dir = pathlib.Path("build_sundials")
140120
if not os.path.exists(build_dir):
141121
print("\n-" * 10, "Creating build dir", "-" * 40)
142122
os.makedirs(build_dir)
143123

144-
sundials_src = f"../sundials-{SUNDIALS_VERSION}"
124+
sundials_src = "../sundials"
145125
print("-" * 10, "Running CMake prepare", "-" * 40)
146126
subprocess.run(["cmake", sundials_src, *cmake_args], cwd=build_dir, check=True)
147127

148128
print("-" * 10, "Building SUNDIALS", "-" * 40)
149129
make_cmd = ["make", f"-j{cpu_count()}", "install"]
150130
subprocess.run(make_cmd, cwd=build_dir, check=True)
151131

152-
def check_libraries_installed(install_dir):
132+
def check_libraries_installed():
153133
# Define the directories to check for SUNDIALS and SuiteSparse libraries
154-
lib_dirs = [install_dir]
134+
lib_dirs = [DEFAULT_INSTALL_DIR]
155135

156136
sundials_files = [
157137
"libsundials_idas",
@@ -217,121 +197,38 @@ def check_libraries_installed(install_dir):
217197

218198
return sundials_lib_found, suitesparse_lib_found
219199

220-
def calculate_sha256(file_path):
221-
sha256_hash = hashlib.sha256()
222-
with open(file_path, "rb") as f:
223-
# Read and update hash in chunks of 4K
224-
for byte_block in iter(lambda: f.read(4096), b""):
225-
sha256_hash.update(byte_block)
226-
return sha256_hash.hexdigest()
227-
228-
def download_extract_library(url, expected_checksum, download_dir):
229-
file_name = url.split("/")[-1]
230-
file_path = os.path.join(download_dir, file_name)
231-
232-
# Check if file already exists and validate checksum
233-
if os.path.exists(file_path):
234-
print(f"Validating checksum for {file_name}...")
235-
actual_checksum = calculate_sha256(file_path)
236-
print(f"Found {actual_checksum} against {expected_checksum}")
237-
if actual_checksum == expected_checksum:
238-
print(f"Checksum valid. Skipping download for {file_name}.")
239-
# Extract the archive as the checksum is valid
240-
with tarfile.open(file_path) as tar:
241-
tar.extractall(download_dir)
242-
return
243-
else:
244-
print(f"Checksum invalid. Redownloading {file_name}.")
245-
246-
# Download and extract archive at url
247-
parsed_url = urlparse(url)
248-
if parsed_url.scheme not in ["http", "https"]:
249-
raise ValueError(
250-
f"Invalid URL scheme: {parsed_url.scheme}. Only HTTP and HTTPS are allowed."
251-
)
252-
with urllib.request.urlopen(url) as response:
253-
os.makedirs(download_dir, exist_ok=True)
254-
with open(file_path, "wb") as out_file:
255-
out_file.write(response.read())
256-
with tarfile.open(file_path) as tar:
257-
tar.extractall(download_dir)
258-
259-
def parallel_download(urls, download_dir):
260-
# Use 2 processes for parallel downloading
261-
with ThreadPoolExecutor(max_workers=len(urls)) as executor:
262-
futures = [
263-
executor.submit(
264-
download_extract_library, url, expected_checksum, download_dir
265-
)
266-
for (url, expected_checksum) in urls
267-
]
268-
for future in futures:
269-
future.result()
270-
271200
# First check requirements: make and cmake
272201
check_build_tools()
273202

274203
# Build in parallel wherever possible
275204
os.environ["CMAKE_BUILD_PARALLEL_LEVEL"] = str(cpu_count())
276205

277-
# Create download directory in PyBaMM dir
278-
pybamm_dir = pathlib.Path(__file__).parent.resolve()
279-
download_dir = str(pybamm_dir / "install_KLU_Sundials")
280-
if not os.path.exists(download_dir):
281-
os.makedirs(download_dir)
282-
283206
# Get installation location
284207
parser = argparse.ArgumentParser(
285-
description="Download, compile and install Sundials and SuiteSparse."
208+
description="Compile and install Sundials and SuiteSparse."
286209
)
287210
parser.add_argument(
288211
"--force",
289212
action="store_true",
290213
help="Force installation even if libraries are already found. This will overwrite the pre-existing files.",
291214
)
292-
parser.add_argument("--install-dir", type=str, default=DEFAULT_INSTALL_DIR)
293215
args = parser.parse_args()
294-
install_dir = (
295-
args.install_dir
296-
if os.path.isabs(args.install_dir)
297-
else os.path.join(pybamm_dir, args.install_dir)
298-
)
299216

300217
if args.force:
301218
print(
302219
"The '--force' option is activated: installation will be forced, ignoring any existing libraries."
303220
)
304-
safe_remove_dir(os.path.join(download_dir, "build_sundials"))
305-
safe_remove_dir(
306-
os.path.join(download_dir, f"SuiteSparse-{SUITESPARSE_VERSION}")
307-
)
308-
safe_remove_dir(os.path.join(download_dir, f"sundials-{SUNDIALS_VERSION}"))
221+
safe_remove_dir(pathlib.Path("build_sundials"))
309222
sundials_found, suitesparse_found = False, False
310223
else:
311224
# Check whether the libraries are installed
312-
sundials_found, suitesparse_found = check_libraries_installed(install_dir)
225+
sundials_found, suitesparse_found = check_libraries_installed()
313226

314-
# Determine which libraries to download based on whether they were found
315-
if not sundials_found and not suitesparse_found:
316-
# Both SUNDIALS and SuiteSparse are missing, download and install both
317-
parallel_download(
318-
[
319-
(SUITESPARSE_URL, SUITESPARSE_CHECKSUM),
320-
(SUNDIALS_URL, SUNDIALS_CHECKSUM),
321-
],
322-
download_dir,
323-
)
324-
install_suitesparse(download_dir)
325-
install_sundials(download_dir, install_dir)
326-
else:
327-
if not sundials_found:
328-
# Only SUNDIALS is missing, download and install it
329-
parallel_download([(SUNDIALS_URL, SUNDIALS_CHECKSUM)], download_dir)
330-
install_sundials(download_dir, install_dir)
331-
if not suitesparse_found:
332-
# Only SuiteSparse is missing, download and install it
333-
parallel_download([(SUITESPARSE_URL, SUITESPARSE_CHECKSUM)], download_dir)
334-
install_suitesparse(download_dir)
227+
# Determine which libraries to install
228+
if not suitesparse_found:
229+
install_suitesparse()
230+
if not sundials_found:
231+
install_sundials()
335232

336233

337234
def check_build_tools():

sundials

Submodule sundials added at e941546

0 commit comments

Comments
 (0)