pyvcell-odesolver 0.9.4__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from pyvcell_odesolver.sundials import __doc__, version, solve
4
+
5
+ __all__ = ["__doc__", "version", "solve"]
File without changes
@@ -0,0 +1,114 @@
1
+ import platform
2
+ import struct
3
+ import os
4
+ import platform
5
+
6
+ def get_library_archs(lib_path: str) -> tuple[str, list[str]]:
7
+ """Determine architecture based on file type and header."""
8
+ _, ext = os.path.splitext(lib_path)
9
+
10
+ # ELF (Linux .so files)
11
+ if ext == ".so" or ".so." in str(lib_path):
12
+ with open(lib_path, 'rb') as f:
13
+ f.seek(5)
14
+ endian = '<' if f.read(1)[0] == 1 else '>'
15
+ f.seek(18)
16
+ machine = struct.unpack(endian + 'H', f.read(2))[0]
17
+
18
+ arch_map = {
19
+ 0x03: 'i386',
20
+ 0x3E: 'x86_64',
21
+ 0xB7: 'aarch64',
22
+ 0x28: 'armv7',
23
+ }
24
+ return "Linux", [normalize_arch(arch_map.get(machine, f'unknown (0x{machine:x})'))]
25
+
26
+ # Mach-O (macOS .dylib files)
27
+ elif ext == '.dylib':
28
+ with open(lib_path, 'rb') as f:
29
+ # Check for fat binary magic
30
+ magic = f.read(4)
31
+ if magic == b'\xca\xfe\xba\xbe' or magic == b'\xbf\xba\xfe\xca':
32
+ # Fat binary - list all architectures
33
+ f.seek(4)
34
+ num_archs = struct.unpack('>I', f.read(4))[0]
35
+ archs = set()
36
+ for i in range(num_archs):
37
+ cpu_type = struct.unpack('>I', f.read(4))[0]
38
+ f.read(4) # Skip cpu_subtype
39
+ f.read(4) # Skip offset
40
+ f.read(4) # Skip size
41
+ f.read(4) # Skip align
42
+
43
+ arch_map = {
44
+ 0x00000007: 'i386',
45
+ 0x01000007: 'x86_64',
46
+ 0x0100000C: 'aarch64',
47
+ }
48
+ archs.add(normalize_arch(arch_map.get(cpu_type, f'unknown (0x{cpu_type:x})')))
49
+ return "Darwin", list(archs)
50
+ else:
51
+ # Single-arch Mach-O - determine endianness
52
+ f.seek(4)
53
+ cpu_type_bytes = f.read(4)
54
+ # Try little-endian first (most common)
55
+ cpu_type = struct.unpack('<I', cpu_type_bytes)[0]
56
+
57
+ arch_map = {
58
+ 0x00000007: 'i386',
59
+ 0x01000007: 'x86_64',
60
+ 0x0100000C: 'aarch64',
61
+ }
62
+ return "Darwin", [ normalize_arch(arch_map.get(cpu_type, f'unknown (0x{cpu_type:x})')) ]
63
+
64
+ # PE (Windows .dll files)
65
+ elif ext == '.dll':
66
+ with open(lib_path, 'rb') as f:
67
+ f.seek(0x3c)
68
+ pe_offset = struct.unpack('<I', f.read(4))[0]
69
+ f.seek(pe_offset + 4)
70
+ machine = struct.unpack('<H', f.read(2))[0]
71
+
72
+ arch_map = {
73
+ 0x014C: 'i386',
74
+ 0x8664: 'x86_64',
75
+ 0xAA64: 'aarch64',
76
+ }
77
+ return "Windows", [ normalize_arch(arch_map.get(machine, f'unknown (0x{machine:x})')) ]
78
+
79
+ return 'unknown', [ ]
80
+
81
+ def get_all_valid_libraries_from_dir(lib_dir: str):
82
+ dir_files = [str(full_file) for f in os.listdir(lib_dir) if os.path.isfile(full_file := os.path.join(lib_dir, f))]
83
+ return filter_all_valid_libraries(dir_files)
84
+
85
+
86
+ def filter_all_valid_libraries(lib_path_list: list[str]) -> list[str]:
87
+ if not lib_path_list:
88
+ raise ValueError("A list must be provided, `None` is not allowed.")
89
+
90
+ system_name = platform.system()
91
+ system_arch = normalize_arch(platform.machine())
92
+ valid_libraries = []
93
+ for lib_path in lib_path_list:
94
+ required_system_name, shared_library_archs = get_library_archs(lib_path)
95
+ if system_arch in shared_library_archs and required_system_name in system_name:
96
+ valid_libraries.append(lib_path)
97
+ return valid_libraries
98
+
99
+ def normalize_arch(machine_str=None):
100
+ if machine_str is None:
101
+ machine_str = platform.machine().lower()
102
+
103
+ # Normalize common variants
104
+ if machine_str in ('x86_64', 'amd64', 'x64'):
105
+ return 'x86_64'
106
+ elif machine_str in ('aarch64', 'arm64'):
107
+ return 'aarch64'
108
+ elif machine_str in ('i386', 'i486', 'i586', 'i686', 'x86'):
109
+ return 'i386'
110
+ elif machine_str.startswith('arm'):
111
+ return 'arm'
112
+ elif machine_str.lower() != machine_str:
113
+ return normalize_arch(machine_str.lower())
114
+ raise OSError(f"Unknown machine type detected: `{machine_str}`")
@@ -0,0 +1,53 @@
1
+ import ctypes
2
+ import logging
3
+ from pathlib import Path
4
+
5
+ from pydantic import BaseModel
6
+
7
+ from pyvcell_odesolver._internal.native_utils import VCellNativeLibraryLoader
8
+
9
+
10
+ class ReturnValue(BaseModel):
11
+ success: bool
12
+ message: str
13
+
14
+
15
+ class MutableString:
16
+ def __init__(self, value: str):
17
+ self.value: str = value
18
+
19
+
20
+ class ODENativeCalls:
21
+ def __init__(self) -> None:
22
+ self.loader = VCellNativeLibraryLoader()
23
+ self.lib: ctypes.CDLL = self.loader.lib
24
+
25
+ def call_version(self) -> ReturnValue:
26
+ try:
27
+ version_str: str = self.lib.version_ctypes()
28
+ if version_str is None:
29
+ error_msg = "Failed to collect and covert version information"
30
+ logging.error(error_msg)
31
+ return ReturnValue(success=False, message=error_msg)
32
+ return ReturnValue(success=True, message=version_str.decode("utf-8"))
33
+ except Exception as e:
34
+ logging.exception("Error in vcml_to_finite_volume_input()", exc_info=e)
35
+ raise
36
+
37
+ def call_solve(
38
+ self, input_file_path: str, output_file_path: str, task_id: int
39
+ ) -> ReturnValue:
40
+ try:
41
+ solver_return_code: int = self.lib.solve_ctypes(
42
+ ctypes.c_char_p(input_file_path.encode("utf-8")),
43
+ ctypes.c_char_p(output_file_path.encode("utf-8")),
44
+ ctypes.c_int(task_id),
45
+ )
46
+ if solver_return_code == 0:
47
+ return ReturnValue(success=True, message="Solver returned successfully.")
48
+ error_msg = "Solver failed with exit code {}".format(solver_return_code)
49
+ logging.error(error_msg)
50
+ return ReturnValue(success=False, message=error_msg)
51
+ except Exception as e:
52
+ logging.exception("Error in calling SundialsSolverInterface::solve_ctypes(...)", exc_info=e)
53
+ raise e
@@ -0,0 +1,56 @@
1
+ import ctypes
2
+ import platform
3
+ from _ctypes import byref
4
+ from importlib.resources import files
5
+ import os
6
+ import sys
7
+
8
+ import pyvcell_odesolver
9
+ from pyvcell_odesolver._internal import check_arch
10
+
11
+
12
+ class VCellNativeLibraryLoader:
13
+ lib: ctypes.CDLL
14
+
15
+ def __init__(self) -> None:
16
+ self.lib = self._load_library()
17
+ self._define_entry_points()
18
+
19
+ def _load_library(self) -> ctypes.CDLL:
20
+ system = platform.system()
21
+ lib_ext = {"Linux": ".so", "Darwin": ".dylib", "Windows": ".dll"}.get(system)
22
+ version_identifier = "VCell ODE solver (CVODE/IDA) v"
23
+
24
+ if lib_ext is None:
25
+ raise OSError(f"Unsupported operating system: {system}")
26
+
27
+ libs_dir = files(pyvcell_odesolver).joinpath("lib")
28
+ if not libs_dir.is_dir():
29
+ raise OSError(f"Could not find the shared library directory {libs_dir}")
30
+
31
+
32
+ valid_libraries = check_arch.get_all_valid_libraries_from_dir(str(libs_dir))
33
+ for file in valid_libraries:
34
+ file_str = str(file)
35
+ try:
36
+ lib = ctypes.CDLL(name=file_str)
37
+ lib.version_ctypes.restype = ctypes.c_char_p # signals ctypes to return as char-array
38
+ version_str: str = lib.version_ctypes().decode("utf-8")
39
+ if version_identifier not in version_str:
40
+ err_str = f"`{file_str}` isn't desired lib (`{version_identifier}` not in `{version_str}`)"
41
+ print(err_str, file=sys.stderr)
42
+ except AttributeError as e:
43
+ print(f"library `{file_str}` could not be loaded: {e}", file=sys.stderr)
44
+ continue # If we didn't get a version, we didn't get the correct lib
45
+ return lib
46
+
47
+ # Didn't find what we needed
48
+ files_checked = "\n - ".join(valid_libraries if len(valid_libraries) > 0 else os.listdir(str(libs_dir)))
49
+ raise OSError(f"Could not find the shared library; in `{str(libs_dir)}` checked: \n - {files_checked}")
50
+
51
+ def _define_entry_points(self) -> None:
52
+ self.lib.version_ctypes.restype = ctypes.c_char_p # technically, we did this above, but just in case
53
+ self.lib.version_ctypes.argtypes = []
54
+
55
+ self.lib.solve_ctypes.restype = ctypes.c_int
56
+ self.lib.solve_ctypes.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int]
Binary file
@@ -0,0 +1 @@
1
+ After building the IDAWin library, a copy of it should be placed here for packaging into a working python wheel.
@@ -0,0 +1,13 @@
1
+ from pyvcell_odesolver._internal.native_calls import ODENativeCalls
2
+
3
+ def version() -> str:
4
+ obj = ODENativeCalls()
5
+ return obj.call_version()
6
+
7
+ def solve(input_file_path: str, output_file_path: str, task_id: int = -1) -> int:
8
+ obj = ODENativeCalls()
9
+ # In python, True == 1; we want 0 == true (a.k.a.: process returned exit code 0)
10
+ return int(not obj.call_solve(input_file_path, output_file_path, task_id).success)
11
+
12
+ if __name__ == "__main__":
13
+ print(version())
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.3
2
+ Name: pyvcell_odesolver
3
+ Version: 0.9.4
4
+ Summary: Virtual Cell CVODE / IDA based ODE solver
5
+ Author: Jim Schaff, Logan Drescher
6
+ Author-email: Jim Schaff <schaff@uchc.edu>, Logan Drescher <drescher@uchc.edu>
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: pydantic>=2.13.4
16
+ Requires-Dist: build ; extra == 'build'
17
+ Requires-Dist: wheel ; extra == 'build'
18
+ Requires-Dist: pytest ; extra == 'test'
19
+ Requires-Python: >=3.10
20
+ Provides-Extra: build
21
+ Provides-Extra: test
22
+ Description-Content-Type: text/markdown
23
+
24
+ <p align="center" width="100%">
25
+ <a href="https://vcell.org">
26
+ <img width="10%" src="https://github.com/biosimulations/biosimulations/blob/dev/docs/src/assets/images/about/partners/vcell.svg">
27
+ </a>
28
+ </p>
29
+
30
+ ## The Virtual Cell Project
31
+ The Virtual Cell is a modeling and simulation framework for computational biology. For details see http://vcell.org and http://github.com/virtualcell.
32
+
33
+ ---
34
+ # vcell-ode
35
+ ![CI](https://github.com/virtualcell/vcell-ode/actions/workflows/cd.yml/badge.svg)
36
+
37
+ Virtual Cell ODE [virtualcell/vcell-ode](https://github.com/virtualcell/vcell-ode) is a collection of numerical
38
+ simulation libraries and protocols used to process ODEs in the Virtual Cell framework [virtualcell/vcell](https://github.com/virtualcell/vcell)).
39
+
40
+ ## Building VCell ODE
41
+ There are two ways to build VCell ODE, but both start out the same way
42
+ ### Step 1: Acquire the source-code
43
+ The source code can be found online at the GitHub repository.
44
+ `git clone https://github.com/virtualcell/vcell.git`
45
+
46
+ ### Step 2: Install Dependencies
47
+ You will need to acquire a unix-style C and C++ compiler suite in order to build VCell ODE. Traditionally, this project
48
+ uses CLang + Mold, but other compiler suites may work. Additionally, you'll need to install the other dependencies for
49
+ VCell ODE, which can be done in one of two ways:
50
+
51
+ #### Method 1: Conan Toolchain (Recommended)
52
+ The VCell ODE project uses the C++ dependency management system called ***conan*** to handle dependency management when
53
+ building VCell ODE. We provide a number of conan-profiles that will provide conan the information need to automatically
54
+ build the desired toolchain for cmake.
55
+
56
+ #### Method 2: Manual Dependency Installation
57
+ If you'd rather manually install all the dependencies build-tools to create VCell CLI, you'll need the following:
58
+ ##### Dependencies
59
+ * If you want live messaging while the solver runs: `libcurl` (add `-DOPTION_TARGET_MESSAGING` to cmake call below)
60
+ ##### Build Tools
61
+ * `cmake` to perform the build configuration
62
+ * `ninja` (or equivalent) to perform the actual build process
63
+
64
+ ### Step 3: Invoke the Build
65
+ In a shell with conan and/or the other build tools in path, navigate to the project's root directory and
66
+ run the following commands (tested in bash on unix and powershell on windows)
67
+
68
+ Note that if using `conan`, you'll need to define a profile. use `conan profile detect --force` to generate one automatically,
69
+ or use one of the provided ones in `<project_root>/conan-profiles` [link](https://docs.conan.io/2/reference/config_files/profiles.html)
70
+ to further reading on the official `conan` website.
71
+ #### Powershell
72
+ ```pwsh
73
+ mdkir build # Must do if not using conan
74
+ conan install . --output-folder build --build=missing # If building using conan's help
75
+ cd build
76
+ ./conanbuild.ps1 # If building using conan's help
77
+ cmake -B . -S .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release
78
+ cmake --build . --config Release
79
+ ```
80
+
81
+ ```bash
82
+ mdkir build # must do if not building conan
83
+ conan install . --output-folder build --build=missing # If building using conan's help
84
+ cd build
85
+ ./conanbuild.sh # If building using conan's help
86
+ cmake -B . -S .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release
87
+ cmake --build . --config Release
88
+ ```
89
+
90
+ ### Step 4: Manual user/system install
91
+ Currently, VCell ODE does not have an automated installation script. You will either need to move the resulting executables
92
+ in `<project_root>/build/bin` to an appropriate folder in path, or put said folder into path for your computer.
@@ -0,0 +1,11 @@
1
+ pyvcell_odesolver/__init__.py,sha256=tCVNXPq-mNEw3_Mgl2jY2plhJ47alNww0Ey9K7AbXbo,147
2
+ pyvcell_odesolver/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ pyvcell_odesolver/_internal/check_arch.py,sha256=SldYkqmGc9RqXidlzO9z_MKbgPCPJmLlClTHNdJgfpA,4352
4
+ pyvcell_odesolver/_internal/native_calls.py,sha256=pBrkg9ceWtHpjUiVHK7KK_FoZhw2SXIKEiszUziMwHE,1935
5
+ pyvcell_odesolver/_internal/native_utils.py,sha256=l78LJXZ4PPOoBGd2GuNrgt2uDonHdeXcz2SzQYmcYYo,2381
6
+ pyvcell_odesolver/lib/IDAWin.dll,sha256=uRmIDqFZj2pr-XeNk0_j0lDio9o2b8ClLRGJwC8uBuw,1033728
7
+ pyvcell_odesolver/lib/README.md,sha256=O3e3gN08qZWr85izOaKG1-kb8GMuDLUxDSA-ozMMWow,112
8
+ pyvcell_odesolver/sundials.py,sha256=Cxz85SKDxHxOfrToCViCMHOBIgFEPieVmOy5B7chLsU,490
9
+ pyvcell_odesolver-0.9.4.dist-info/WHEEL,sha256=1EjGHjH5dz_UZIF9e_7PpgY8tR3oVUD4yQEimBKjzys,88
10
+ pyvcell_odesolver-0.9.4.dist-info/METADATA,sha256=xvSfIP_SIAcd2bZYNpunQwHpKoJqrjHv3JuRnAPfaX4,4566
11
+ pyvcell_odesolver-0.9.4.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.28
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-win_amd64
5
+