libvcell 0.0.17__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.
- libvcell/__init__.py +31 -0
- libvcell/_internal/__init__.py +0 -0
- libvcell/_internal/native_calls.py +228 -0
- libvcell/_internal/native_utils.py +102 -0
- libvcell/lib/.gitkeep +0 -0
- libvcell/lib/libvcell.dll +0 -0
- libvcell/model_utils.py +91 -0
- libvcell/py.typed +0 -0
- libvcell/solver_utils.py +56 -0
- libvcell-0.0.17.dist-info/METADATA +69 -0
- libvcell-0.0.17.dist-info/RECORD +13 -0
- libvcell-0.0.17.dist-info/WHEEL +5 -0
- libvcell-0.0.17.dist-info/licenses/LICENSE +21 -0
libvcell/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
from libvcell.model_utils import (
|
|
4
|
+
sbml_to_vcml,
|
|
5
|
+
vcell_infix_to_num_expr_infix,
|
|
6
|
+
vcell_infix_to_python_infix,
|
|
7
|
+
vcml_to_sbml,
|
|
8
|
+
vcml_to_vcml,
|
|
9
|
+
)
|
|
10
|
+
from libvcell.solver_utils import (
|
|
11
|
+
sbml_to_finite_volume_input,
|
|
12
|
+
vcml_to_finite_volume_input,
|
|
13
|
+
vcml_to_moving_boundary_input,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
__version__ = version("libvcell")
|
|
18
|
+
except PackageNotFoundError: # pragma: no cover - source tree, package not installed
|
|
19
|
+
__version__ = "0.0.0"
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"__version__",
|
|
23
|
+
"sbml_to_finite_volume_input",
|
|
24
|
+
"sbml_to_vcml",
|
|
25
|
+
"vcell_infix_to_num_expr_infix",
|
|
26
|
+
"vcell_infix_to_python_infix",
|
|
27
|
+
"vcml_to_finite_volume_input",
|
|
28
|
+
"vcml_to_moving_boundary_input",
|
|
29
|
+
"vcml_to_sbml",
|
|
30
|
+
"vcml_to_vcml",
|
|
31
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from libvcell._internal.native_utils import IsolateManager, 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 VCellNativeCalls:
|
|
21
|
+
def __init__(self) -> None:
|
|
22
|
+
self.loader = VCellNativeLibraryLoader()
|
|
23
|
+
self.lib = self.loader.lib
|
|
24
|
+
|
|
25
|
+
def vcml_to_finite_volume_input(
|
|
26
|
+
self, vcml_content: str, simulation_name: str, output_dir_path: Path
|
|
27
|
+
) -> ReturnValue:
|
|
28
|
+
try:
|
|
29
|
+
with IsolateManager(self.lib) as isolate_thread:
|
|
30
|
+
json_ptr: ctypes.c_char_p = self.lib.vcmlToFiniteVolumeInput(
|
|
31
|
+
isolate_thread,
|
|
32
|
+
ctypes.c_char_p(vcml_content.encode("utf-8")),
|
|
33
|
+
ctypes.c_char_p(simulation_name.encode("utf-8")),
|
|
34
|
+
ctypes.c_char_p(str(output_dir_path).encode("utf-8")),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
|
|
38
|
+
if value is None:
|
|
39
|
+
logging.error("Failed to convert vcml to finite volume input")
|
|
40
|
+
return ReturnValue(success=False, message="Failed to convert vcml to finite volume input")
|
|
41
|
+
json_str: str = value.decode("utf-8")
|
|
42
|
+
# self.lib.freeString(json_ptr)
|
|
43
|
+
return ReturnValue.model_validate_json(json_data=json_str)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
logging.exception("Error in vcml_to_finite_volume_input()", exc_info=e)
|
|
46
|
+
raise
|
|
47
|
+
|
|
48
|
+
def vcml_to_moving_boundary_input(
|
|
49
|
+
self, vcml_content: str, simulation_name: str, output_dir_path: Path
|
|
50
|
+
) -> ReturnValue:
|
|
51
|
+
try:
|
|
52
|
+
with IsolateManager(self.lib) as isolate_thread:
|
|
53
|
+
json_ptr: ctypes.c_char_p = self.lib.vcmlToMovingBoundaryInput(
|
|
54
|
+
isolate_thread,
|
|
55
|
+
ctypes.c_char_p(vcml_content.encode("utf-8")),
|
|
56
|
+
ctypes.c_char_p(simulation_name.encode("utf-8")),
|
|
57
|
+
ctypes.c_char_p(str(output_dir_path).encode("utf-8")),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
|
|
61
|
+
if value is None:
|
|
62
|
+
logging.error("Failed to convert vcml to moving boundary input")
|
|
63
|
+
return ReturnValue(success=False, message="Failed to convert vcml to moving boundary input")
|
|
64
|
+
json_str: str = value.decode("utf-8")
|
|
65
|
+
# self.lib.freeString(json_ptr)
|
|
66
|
+
return ReturnValue.model_validate_json(json_data=json_str)
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logging.exception("Error in vcml_to_moving_boundary_input()", exc_info=e)
|
|
69
|
+
raise
|
|
70
|
+
|
|
71
|
+
def sbml_to_finite_volume_input(self, sbml_content: str, output_dir_path: Path) -> ReturnValue:
|
|
72
|
+
try:
|
|
73
|
+
with IsolateManager(self.lib) as isolate_thread:
|
|
74
|
+
json_ptr: ctypes.c_char_p = self.lib.sbmlToFiniteVolumeInput(
|
|
75
|
+
isolate_thread,
|
|
76
|
+
ctypes.c_char_p(sbml_content.encode("utf-8")),
|
|
77
|
+
ctypes.c_char_p(str(output_dir_path).encode("utf-8")),
|
|
78
|
+
)
|
|
79
|
+
value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
|
|
80
|
+
if value is None:
|
|
81
|
+
logging.error("Failed to convert sbml to finite volume input")
|
|
82
|
+
return ReturnValue(success=False, message="Failed to convert sbml to finite volume input")
|
|
83
|
+
json_str: str = value.decode("utf-8")
|
|
84
|
+
# self.lib.freeString(json_ptr)
|
|
85
|
+
return ReturnValue.model_validate_json(json_data=json_str)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logging.exception("Error in sbml_to_finite_volume_input()", exc_info=e)
|
|
88
|
+
raise
|
|
89
|
+
|
|
90
|
+
def vcml_to_sbml(
|
|
91
|
+
self, vcml_content: str, application_name: str, sbml_file_path: Path, round_trip_validation: bool
|
|
92
|
+
) -> ReturnValue:
|
|
93
|
+
try:
|
|
94
|
+
with IsolateManager(self.lib) as isolate_thread:
|
|
95
|
+
json_ptr: ctypes.c_char_p = self.lib.vcmlToSbml(
|
|
96
|
+
isolate_thread,
|
|
97
|
+
ctypes.c_char_p(vcml_content.encode("utf-8")),
|
|
98
|
+
ctypes.c_char_p(application_name.encode("utf-8")),
|
|
99
|
+
ctypes.c_char_p(str(sbml_file_path).encode("utf-8")),
|
|
100
|
+
ctypes.c_int(int(round_trip_validation)),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
|
|
104
|
+
if value is None:
|
|
105
|
+
logging.error("Failed to convert vcml application to sbml")
|
|
106
|
+
return ReturnValue(success=False, message="Failed to convert vcml to sbml")
|
|
107
|
+
json_str: str = value.decode("utf-8")
|
|
108
|
+
# self.lib.freeString(json_ptr)
|
|
109
|
+
return ReturnValue.model_validate_json(json_data=json_str)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logging.exception("Error in vcml_to_sbml()", exc_info=e)
|
|
112
|
+
raise
|
|
113
|
+
|
|
114
|
+
def sbml_to_vcml(self, sbml_content: str, vcml_file_path: Path) -> ReturnValue:
|
|
115
|
+
try:
|
|
116
|
+
with IsolateManager(self.lib) as isolate_thread:
|
|
117
|
+
json_ptr: ctypes.c_char_p = self.lib.sbmlToVcml(
|
|
118
|
+
isolate_thread,
|
|
119
|
+
ctypes.c_char_p(sbml_content.encode("utf-8")),
|
|
120
|
+
ctypes.c_char_p(str(vcml_file_path).encode("utf-8")),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
|
|
124
|
+
if value is None:
|
|
125
|
+
logging.error("Failed to convert sbml to vcml")
|
|
126
|
+
return ReturnValue(success=False, message="Failed to convert sbml to vcml")
|
|
127
|
+
json_str: str = value.decode("utf-8")
|
|
128
|
+
# self.lib.freeString(json_ptr)
|
|
129
|
+
return ReturnValue.model_validate_json(json_data=json_str)
|
|
130
|
+
except Exception as e:
|
|
131
|
+
logging.exception("Error in sbml_to_vcml()", exc_info=e)
|
|
132
|
+
raise
|
|
133
|
+
|
|
134
|
+
def vcml_to_vcml(self, vcml_content: str, vcml_file_path: Path) -> ReturnValue:
|
|
135
|
+
try:
|
|
136
|
+
with IsolateManager(self.lib) as isolate_thread:
|
|
137
|
+
json_ptr: ctypes.c_char_p = self.lib.vcmlToVcml(
|
|
138
|
+
isolate_thread,
|
|
139
|
+
ctypes.c_char_p(vcml_content.encode("utf-8")),
|
|
140
|
+
ctypes.c_char_p(str(vcml_file_path).encode("utf-8")),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
|
|
144
|
+
if value is None:
|
|
145
|
+
logging.error("Failed to regenerate vcml")
|
|
146
|
+
return ReturnValue(success=False, message="Failed to regenerate vcml")
|
|
147
|
+
json_str: str = value.decode("utf-8")
|
|
148
|
+
# self.lib.freeString(json_ptr)
|
|
149
|
+
return ReturnValue.model_validate_json(json_data=json_str)
|
|
150
|
+
except Exception as e:
|
|
151
|
+
logging.exception("Error in vcml_to_vcml()", exc_info=e)
|
|
152
|
+
raise
|
|
153
|
+
|
|
154
|
+
def vcell_infix_to_python_infix(
|
|
155
|
+
self, vcell_infix: str, target_python_infix: MutableString, buffer_size: int | None = None
|
|
156
|
+
) -> ReturnValue:
|
|
157
|
+
try:
|
|
158
|
+
needed_buffer_size = int(1.5 * len(vcell_infix)) if buffer_size is None else buffer_size
|
|
159
|
+
buff = ctypes.create_string_buffer(needed_buffer_size)
|
|
160
|
+
with IsolateManager(self.lib) as isolate_thread:
|
|
161
|
+
json_ptr = self.lib.vcellInfixToPythonInfix(
|
|
162
|
+
isolate_thread, ctypes.c_char_p(vcell_infix.encode("utf-8")), buff, needed_buffer_size
|
|
163
|
+
)
|
|
164
|
+
value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
|
|
165
|
+
if value is None:
|
|
166
|
+
logging.error("Failed to regenerate vcml")
|
|
167
|
+
return ReturnValue(success=False, message="Failed to generate python infix")
|
|
168
|
+
json_str = value.decode("utf-8")
|
|
169
|
+
if "not enough room, need: `" in json_str:
|
|
170
|
+
if buffer_size is not None:
|
|
171
|
+
logging.error("Failed to identify correct buffer size reported by previous error")
|
|
172
|
+
return ReturnValue(
|
|
173
|
+
success=False, message="Failed to identify correct buffer size reported by previous error"
|
|
174
|
+
)
|
|
175
|
+
# get the size from the error
|
|
176
|
+
index = json_str.find("not enough room, need: `") + len("not enough room, need: `")
|
|
177
|
+
end_index = json_str.find("`", index)
|
|
178
|
+
size_as_string: str = json_str[index:end_index]
|
|
179
|
+
if not size_as_string.isnumeric():
|
|
180
|
+
logging.error("Buffer size reported by previous error is not an integer!")
|
|
181
|
+
return ReturnValue(
|
|
182
|
+
success=False, message="Buffer size reported by previous error is not an integer!"
|
|
183
|
+
)
|
|
184
|
+
return self.vcell_infix_to_python_infix(vcell_infix, target_python_infix, int(size_as_string))
|
|
185
|
+
# self.lib.freeString(json_ptr)
|
|
186
|
+
target_python_infix.value = buff.value.decode("utf-8")
|
|
187
|
+
return ReturnValue.model_validate_json(json_data=json_str)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
logging.exception("Error in vcell_infix_to_python_infix()", exc_info=e)
|
|
190
|
+
raise
|
|
191
|
+
|
|
192
|
+
def vcell_infix_to_num_expr_infix(
|
|
193
|
+
self, vcell_infix: str, target_num_expr_infix: MutableString, buffer_size: int | None = None
|
|
194
|
+
) -> ReturnValue:
|
|
195
|
+
try:
|
|
196
|
+
needed_buffer_size = int(1.5 * len(vcell_infix)) if buffer_size is None else buffer_size
|
|
197
|
+
buff = ctypes.create_string_buffer(needed_buffer_size)
|
|
198
|
+
with IsolateManager(self.lib) as isolate_thread:
|
|
199
|
+
json_ptr = self.lib.vcellInfixToNumExprInfix(
|
|
200
|
+
isolate_thread, ctypes.c_char_p(vcell_infix.encode("utf-8")), buff, needed_buffer_size
|
|
201
|
+
)
|
|
202
|
+
value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
|
|
203
|
+
if value is None:
|
|
204
|
+
logging.error("Failed to regenerate vcml")
|
|
205
|
+
return ReturnValue(success=False, message="Failed to generate NumExpr infix")
|
|
206
|
+
json_str = value.decode("utf-8")
|
|
207
|
+
if "not enough room, need: `" in json_str:
|
|
208
|
+
if buffer_size is not None:
|
|
209
|
+
logging.error("Failed to identify correct buffer size reported by previous error")
|
|
210
|
+
return ReturnValue(
|
|
211
|
+
success=False, message="Failed to identify correct buffer size reported by previous error"
|
|
212
|
+
)
|
|
213
|
+
# get the size from the error
|
|
214
|
+
index = json_str.find("not enough room, need: `") + len("not enough room, need: `")
|
|
215
|
+
end_index = json_str.find("`", index)
|
|
216
|
+
size_as_string: str = json_str[index:end_index]
|
|
217
|
+
if not size_as_string.isnumeric():
|
|
218
|
+
logging.error("Buffer size reported by previous error is not an integer!")
|
|
219
|
+
return ReturnValue(
|
|
220
|
+
success=False, message="Buffer size reported by previous error is not an integer!"
|
|
221
|
+
)
|
|
222
|
+
return self.vcell_infix_to_num_expr_infix(vcell_infix, target_num_expr_infix, int(size_as_string))
|
|
223
|
+
# self.lib.freeString(json_ptr)
|
|
224
|
+
target_num_expr_infix.value = buff.value.decode("utf-8")
|
|
225
|
+
return ReturnValue.model_validate_json(json_data=json_str)
|
|
226
|
+
except Exception as e:
|
|
227
|
+
logging.exception("Error in vcell_infix_to_num_expr_infix()", exc_info=e)
|
|
228
|
+
raise
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import platform
|
|
3
|
+
from _ctypes import byref
|
|
4
|
+
from importlib.resources import files
|
|
5
|
+
|
|
6
|
+
import libvcell
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class VCellNativeLibraryLoader:
|
|
10
|
+
lib: ctypes.CDLL
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
self.lib = self._load_library()
|
|
14
|
+
self._define_entry_points()
|
|
15
|
+
|
|
16
|
+
def _load_library(self) -> ctypes.CDLL:
|
|
17
|
+
system = platform.system()
|
|
18
|
+
lib_ext = {"Linux": ".so", "Darwin": ".dylib", "Windows": ".dll"}.get(system)
|
|
19
|
+
|
|
20
|
+
if lib_ext is None:
|
|
21
|
+
raise OSError(f"Unsupported operating system: {system}")
|
|
22
|
+
|
|
23
|
+
libs_dir = files(libvcell).joinpath("lib")
|
|
24
|
+
if not libs_dir.is_dir():
|
|
25
|
+
raise OSError(f"Could not find the shared library directory {libs_dir}")
|
|
26
|
+
|
|
27
|
+
for file in libs_dir.iterdir():
|
|
28
|
+
if file.name.endswith(lib_ext):
|
|
29
|
+
print(f"Found shared library: {file}")
|
|
30
|
+
return ctypes.CDLL(name=str(file))
|
|
31
|
+
|
|
32
|
+
raise OSError("Could not find the shared library")
|
|
33
|
+
|
|
34
|
+
def _define_entry_points(self) -> None:
|
|
35
|
+
self.lib.vcmlToFiniteVolumeInput.restype = ctypes.c_char_p
|
|
36
|
+
self.lib.vcmlToFiniteVolumeInput.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
|
|
37
|
+
|
|
38
|
+
self.lib.sbmlToFiniteVolumeInput.restype = ctypes.c_char_p
|
|
39
|
+
self.lib.sbmlToFiniteVolumeInput.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
|
40
|
+
|
|
41
|
+
self.lib.sbmlToVcml.restype = ctypes.c_char_p
|
|
42
|
+
self.lib.sbmlToVcml.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
|
43
|
+
|
|
44
|
+
self.lib.vcmlToSbml.restype = ctypes.c_char_p
|
|
45
|
+
self.lib.vcmlToSbml.argtypes = [
|
|
46
|
+
ctypes.c_void_p,
|
|
47
|
+
ctypes.c_char_p,
|
|
48
|
+
ctypes.c_char_p,
|
|
49
|
+
ctypes.c_char_p,
|
|
50
|
+
ctypes.c_int,
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
self.lib.vcmlToVcml.restype = ctypes.c_char_p
|
|
54
|
+
self.lib.vcmlToVcml.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
|
55
|
+
|
|
56
|
+
# vcmlToMovingBoundaryInput is only present in native libraries built with moving-boundary
|
|
57
|
+
# support; guard so the package still loads against older shared libraries.
|
|
58
|
+
if hasattr(self.lib, "vcmlToMovingBoundaryInput"):
|
|
59
|
+
self.lib.vcmlToMovingBoundaryInput.restype = ctypes.c_char_p
|
|
60
|
+
self.lib.vcmlToMovingBoundaryInput.argtypes = [
|
|
61
|
+
ctypes.c_void_p,
|
|
62
|
+
ctypes.c_char_p,
|
|
63
|
+
ctypes.c_char_p,
|
|
64
|
+
ctypes.c_char_p,
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
self.lib.vcellInfixToPythonInfix.restype = ctypes.c_char_p
|
|
68
|
+
self.lib.vcellInfixToPythonInfix.argtypes = [
|
|
69
|
+
ctypes.c_void_p,
|
|
70
|
+
ctypes.c_char_p,
|
|
71
|
+
ctypes.c_char_p,
|
|
72
|
+
ctypes.c_longlong,
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
self.lib.vcellInfixToNumExprInfix.restype = ctypes.c_char_p
|
|
76
|
+
self.lib.vcellInfixToNumExprInfix.argtypes = [
|
|
77
|
+
ctypes.c_void_p,
|
|
78
|
+
ctypes.c_char_p,
|
|
79
|
+
ctypes.c_char_p,
|
|
80
|
+
ctypes.c_longlong,
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
self.lib.freeString.restype = None
|
|
84
|
+
self.lib.freeString.argtypes = [ctypes.c_char_p]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class IsolateManager:
|
|
88
|
+
lib: ctypes.CDLL
|
|
89
|
+
isolate: ctypes.c_void_p
|
|
90
|
+
isolate_thread: ctypes.c_void_p
|
|
91
|
+
|
|
92
|
+
def __init__(self, lib: ctypes.CDLL) -> None:
|
|
93
|
+
self.lib = lib
|
|
94
|
+
self.isolate = ctypes.c_void_p()
|
|
95
|
+
self.isolate_thread = ctypes.c_void_p()
|
|
96
|
+
|
|
97
|
+
def __enter__(self) -> ctypes.c_void_p:
|
|
98
|
+
self.lib.graal_create_isolate(None, byref(self.isolate), byref(self.isolate_thread))
|
|
99
|
+
return self.isolate_thread
|
|
100
|
+
|
|
101
|
+
def __exit__(self, exc_type: type | None, exc_val: Exception, exc_tb: object | None) -> None:
|
|
102
|
+
self.lib.graal_tear_down_isolate(self.isolate_thread)
|
libvcell/lib/.gitkeep
ADDED
|
File without changes
|
|
Binary file
|
libvcell/model_utils.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from libvcell._internal.native_calls import MutableString, ReturnValue, VCellNativeCalls
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def vcml_to_sbml(
|
|
7
|
+
vcml_content: str, application_name: str, sbml_file_path: Path, round_trip_validation: bool
|
|
8
|
+
) -> tuple[bool, str]:
|
|
9
|
+
"""
|
|
10
|
+
Convert VCML content to SBML file
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
vcml_content (str): VCML content
|
|
14
|
+
application_name (str): VCell Biomodel application name
|
|
15
|
+
sbml_file_path (Path): path to resulting SBML file
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
tuple[bool, str]: A tuple containing the success status and a message
|
|
19
|
+
"""
|
|
20
|
+
native = VCellNativeCalls()
|
|
21
|
+
return_value: ReturnValue = native.vcml_to_sbml(
|
|
22
|
+
vcml_content=vcml_content,
|
|
23
|
+
application_name=application_name,
|
|
24
|
+
sbml_file_path=sbml_file_path,
|
|
25
|
+
round_trip_validation=round_trip_validation,
|
|
26
|
+
)
|
|
27
|
+
return return_value.success, return_value.message
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def sbml_to_vcml(sbml_content: str, vcml_file_path: Path) -> tuple[bool, str]:
|
|
31
|
+
"""
|
|
32
|
+
Convert SBML content to finite volume input files
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
sbml_content (str): SBML content
|
|
36
|
+
vcml_file_path (Path): path to resulting VCML file
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
tuple[bool, str]: A tuple containing the success status and a message
|
|
40
|
+
"""
|
|
41
|
+
native = VCellNativeCalls()
|
|
42
|
+
return_value: ReturnValue = native.sbml_to_vcml(sbml_content=sbml_content, vcml_file_path=vcml_file_path)
|
|
43
|
+
return return_value.success, return_value.message
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def vcml_to_vcml(vcml_content: str, vcml_file_path: Path) -> tuple[bool, str]:
|
|
47
|
+
"""
|
|
48
|
+
Process VCML content to regenerated VCML file
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
vcml_content (str): VCML content
|
|
52
|
+
vcml_file_path (Path): path to resulting VCML file
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
tuple[bool, str]: A tuple containing the success status and a message
|
|
56
|
+
"""
|
|
57
|
+
native = VCellNativeCalls()
|
|
58
|
+
return_value: ReturnValue = native.vcml_to_vcml(vcml_content=vcml_content, vcml_file_path=vcml_file_path)
|
|
59
|
+
return return_value.success, return_value.message
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def vcell_infix_to_python_infix(vcell_infix: str) -> tuple[bool, str, str]:
|
|
63
|
+
"""
|
|
64
|
+
Converts an infix string version of a VCell Native Expression, and converts it to a Python compatible version
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
vcell_infix (str): the infix to convert
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
tuple[bool, str, str]: A tuple containing the success status, a message, and the converted infix
|
|
71
|
+
"""
|
|
72
|
+
native = VCellNativeCalls()
|
|
73
|
+
target_python_infix = MutableString("")
|
|
74
|
+
return_value: ReturnValue = native.vcell_infix_to_python_infix(vcell_infix, target_python_infix)
|
|
75
|
+
return return_value.success, return_value.message, target_python_infix.value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def vcell_infix_to_num_expr_infix(vcell_infix: str) -> tuple[bool, str, str]:
|
|
79
|
+
"""
|
|
80
|
+
Converts an infix string version of a VCell Native Expression, and converts it to a NumExpr compatible version
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
vcell_infix (str): the infix to convert
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
tuple[bool, str, str]: A tuple containing the success status, a message, and the converted infix
|
|
87
|
+
"""
|
|
88
|
+
native = VCellNativeCalls()
|
|
89
|
+
target_num_expr_infix = MutableString("")
|
|
90
|
+
return_value: ReturnValue = native.vcell_infix_to_num_expr_infix(vcell_infix, target_num_expr_infix)
|
|
91
|
+
return return_value.success, return_value.message, target_num_expr_infix.value
|
libvcell/py.typed
ADDED
|
File without changes
|
libvcell/solver_utils.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from libvcell._internal.native_calls import ReturnValue, VCellNativeCalls
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def vcml_to_finite_volume_input(vcml_content: str, simulation_name: str, output_dir_path: Path) -> tuple[bool, str]:
|
|
7
|
+
"""
|
|
8
|
+
Convert VCML content to finite volume input files
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
vcml_content (str): VCML content
|
|
12
|
+
simulation_name (str): simulation name
|
|
13
|
+
output_dir_path (Path): output directory path
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
tuple[bool, str]: A tuple containing the success status and a message
|
|
17
|
+
"""
|
|
18
|
+
native = VCellNativeCalls()
|
|
19
|
+
return_value: ReturnValue = native.vcml_to_finite_volume_input(vcml_content, simulation_name, output_dir_path)
|
|
20
|
+
return return_value.success, return_value.message
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def vcml_to_moving_boundary_input(vcml_content: str, simulation_name: str, output_dir_path: Path) -> tuple[bool, str]:
|
|
24
|
+
"""
|
|
25
|
+
Convert VCML content to Moving Boundary solver input (a MovingBoundarySetup XML file)
|
|
26
|
+
|
|
27
|
+
The named simulation must be configured for the Moving Boundary solver. The generated
|
|
28
|
+
MovingBoundarySetup XML can be consumed by the vcell-mbsolver package (``MovingBoundarySolver.from_xml``).
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
vcml_content (str): VCML content
|
|
32
|
+
simulation_name (str): simulation name (must use the Moving Boundary solver)
|
|
33
|
+
output_dir_path (Path): output directory path
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
tuple[bool, str]: A tuple containing the success status and a message
|
|
37
|
+
"""
|
|
38
|
+
native = VCellNativeCalls()
|
|
39
|
+
return_value: ReturnValue = native.vcml_to_moving_boundary_input(vcml_content, simulation_name, output_dir_path)
|
|
40
|
+
return return_value.success, return_value.message
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def sbml_to_finite_volume_input(sbml_content: str, output_dir_path: Path) -> tuple[bool, str]:
|
|
44
|
+
"""
|
|
45
|
+
Convert SBML content to finite volume input files
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
sbml_content (str): SBML content
|
|
49
|
+
output_dir_path (Path): output directory path
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
tuple[bool, str]: A tuple containing the success status and a message
|
|
53
|
+
"""
|
|
54
|
+
native = VCellNativeCalls()
|
|
55
|
+
return_value: ReturnValue = native.sbml_to_finite_volume_input(sbml_content, output_dir_path)
|
|
56
|
+
return return_value.success, return_value.message
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: libvcell
|
|
3
|
+
Version: 0.0.17
|
|
4
|
+
Summary: This is a python package which wraps a subset of VCell Java code as a native python package.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: Jim Schaff
|
|
7
|
+
Author-email: schaff@uchc.edu
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
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.10.6,<3.0.0)
|
|
16
|
+
Project-URL: Documentation, https://virtualcell.github.io/libvcell/
|
|
17
|
+
Project-URL: Repository, https://github.com/virtualcell/libvcell
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# libvcell
|
|
21
|
+
|
|
22
|
+
[](https://img.shields.io/github/v/release/virtualcell/libvcell)
|
|
23
|
+
[](https://github.com/virtualcell/libvcell/actions/workflows/main.yml?query=branch%3Amain)
|
|
24
|
+
[](https://codecov.io/gh/virtualcell/libvcell)
|
|
25
|
+
[](https://img.shields.io/github/commit-activity/m/virtualcell/libvcell)
|
|
26
|
+
[](https://img.shields.io/github/license/virtualcell/libvcell)
|
|
27
|
+
|
|
28
|
+
libvcell is a subset of VCell algorithms intended to support the [pyvcell](https://pypi.org/project/pyvcell/) python package.
|
|
29
|
+
|
|
30
|
+
- **Github repository**: <https://github.com/virtualcell/libvcell/>
|
|
31
|
+
- **Documentation** <https://virtualcell.github.io/libvcell/>
|
|
32
|
+
|
|
33
|
+
## Getting started with your project
|
|
34
|
+
|
|
35
|
+
First, create a repository on GitHub with the same name as this project, and then run the following commands:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git init -b main
|
|
39
|
+
git add .
|
|
40
|
+
git commit -m "init commit"
|
|
41
|
+
git remote add origin git@github.com:virtualcell/libvcell.git
|
|
42
|
+
git push -u origin main
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Finally, install the environment and the pre-commit hooks with
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
make install
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You are now ready to start development on your project!
|
|
52
|
+
The CI/CD pipeline will be triggered when you open a pull request, merge to main, or when you create a new release.
|
|
53
|
+
|
|
54
|
+
To finalize the set-up for publishing to PyPI or Artifactory, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/publishing/#set-up-for-pypi).
|
|
55
|
+
For activating the automatic documentation with MkDocs, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/mkdocs/#enabling-the-documentation-on-github).
|
|
56
|
+
To enable the code coverage reports, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/codecov/).
|
|
57
|
+
|
|
58
|
+
## Releasing a new version
|
|
59
|
+
|
|
60
|
+
- Create an API Token on [PyPI](https://pypi.org/).
|
|
61
|
+
- Add the API Token to your projects secrets with the name `PYPI_TOKEN` by visiting [this page](https://github.com/virtualcell/libvcell/settings/secrets/actions/new).
|
|
62
|
+
- Create a [new release](https://github.com/virtualcell/libvcell/releases/new) on Github.
|
|
63
|
+
- Create a new tag in the form `*.*.*`.
|
|
64
|
+
- For more details, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/cicd/#how-to-trigger-a-release).
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
Repository initiated with [fpgmaas/cookiecutter-poetry](https://github.com/fpgmaas/cookiecutter-poetry).
|
|
69
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
libvcell/__init__.py,sha256=QyvFcsVpgutciGNcV2fK4UoetJVwfIugmW-Nv4I2wKo,820
|
|
2
|
+
libvcell/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
libvcell/_internal/native_calls.py,sha256=xXRC7CcTCGU019WOZD-DF3r7abqHCwWrrbWyknrMiZw,11723
|
|
4
|
+
libvcell/_internal/native_utils.py,sha256=hmSEOI2pCxwtY9CH4oOogQgacMPxk0fSuvB0wibr1sQ,3740
|
|
5
|
+
libvcell/lib/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
libvcell/lib/libvcell.dll,sha256=jmUhgn0L8TMdM03uQ8tcAIKakeZdl5SFA0PhKBQtxMQ,126296064
|
|
7
|
+
libvcell/model_utils.py,sha256=6ELwJHL3woshU78PxFpfHjK-GGG0ijRujEOsIi99a1c,3376
|
|
8
|
+
libvcell/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
libvcell/solver_utils.py,sha256=UX6N05i_dMbi2o3ZqD7bocRZQZ8jZf3Pwt040rwwBVY,2255
|
|
10
|
+
libvcell-0.0.17.dist-info/licenses/LICENSE,sha256=uuKYfnhjRd2IOKltacW_z7JSZLVs9Mqj50o9fVnRFyI,1089
|
|
11
|
+
libvcell-0.0.17.dist-info/METADATA,sha256=2EeiBlGt6p5fSuTwU1jeePTpkeSvE94sdG3tLtbkT-U,3424
|
|
12
|
+
libvcell-0.0.17.dist-info/WHEEL,sha256=XOk4v-vrrJbRYNcumfISWdtugVtPNeJCWuvSVkp4yOk,96
|
|
13
|
+
libvcell-0.0.17.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Jim Schaff
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|