Skip to content
Merged
1 change: 1 addition & 0 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def main() -> None:
"src/test/resources/TinySpatialProject_Application0.xml "
"src/test/resources/TinySpatialProject_Application0.vcml "
"Simulation0 "
"unnamed_spatialGeom "
"target/sbml-input",
cwd=vcell_native_dir,
)
Expand Down
3 changes: 2 additions & 1 deletion libvcell/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from libvcell.model_utils import sbml_to_vcml, vcml_to_sbml, vcml_to_vcml
from libvcell.solver_utils import sbml_to_finite_volume_input, vcml_to_finite_volume_input

__all__ = ["vcml_to_finite_volume_input", "sbml_to_finite_volume_input"]
__all__ = ["vcml_to_finite_volume_input", "sbml_to_finite_volume_input", "sbml_to_vcml", "vcml_to_sbml", "vcml_to_vcml"]
61 changes: 61 additions & 0 deletions libvcell/_internal/native_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,64 @@ def sbml_to_finite_volume_input(self, sbml_content: str, output_dir_path: Path)
except Exception as e:
logging.exception("Error in sbml_to_finite_volume_input()", exc_info=e)
raise

def vcml_to_sbml(self, vcml_content: str, application_name: str, sbml_file_path: Path) -> ReturnValue:
try:
with IsolateManager(self.lib) as isolate_thread:
json_ptr: ctypes.c_char_p = self.lib.vcmlToSbml(
isolate_thread,
ctypes.c_char_p(vcml_content.encode("utf-8")),
ctypes.c_char_p(application_name.encode("utf-8")),
ctypes.c_char_p(str(sbml_file_path).encode("utf-8")),
)

value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
if value is None:
logging.error("Failed to convert vcml application to sbml")
return ReturnValue(success=False, message="Failed to convert vcml to sbml")
json_str: str = value.decode("utf-8")
# self.lib.freeString(json_ptr)
return ReturnValue.model_validate_json(json_data=json_str)
except Exception as e:
logging.exception("Error in vcml_to_sbml()", exc_info=e)
raise

def sbml_to_vcml(self, sbml_content: str, vcml_file_path: Path) -> ReturnValue:
try:
with IsolateManager(self.lib) as isolate_thread:
json_ptr: ctypes.c_char_p = self.lib.sbmlToVcml(
isolate_thread,
ctypes.c_char_p(sbml_content.encode("utf-8")),
ctypes.c_char_p(str(vcml_file_path).encode("utf-8")),
)

value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
if value is None:
logging.error("Failed to convert sbml to vcml")
return ReturnValue(success=False, message="Failed to convert sbml to vcml")
json_str: str = value.decode("utf-8")
# self.lib.freeString(json_ptr)
return ReturnValue.model_validate_json(json_data=json_str)
except Exception as e:
logging.exception("Error in sbml_to_vcml()", exc_info=e)
raise

def vcml_to_vcml(self, vcml_content: str, vcml_file_path: Path) -> ReturnValue:
try:
with IsolateManager(self.lib) as isolate_thread:
json_ptr: ctypes.c_char_p = self.lib.vcmlToVcml(
isolate_thread,
ctypes.c_char_p(vcml_content.encode("utf-8")),
ctypes.c_char_p(str(vcml_file_path).encode("utf-8")),
)

value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
if value is None:
logging.error("Failed to regenerate vcml")
return ReturnValue(success=False, message="Failed to regenerate vcml")
json_str: str = value.decode("utf-8")
# self.lib.freeString(json_ptr)
return ReturnValue.model_validate_json(json_data=json_str)
except Exception as e:
logging.exception("Error in vcml_to_vcml()", exc_info=e)
raise
9 changes: 9 additions & 0 deletions libvcell/_internal/native_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ def _define_entry_points(self) -> None:
self.lib.sbmlToFiniteVolumeInput.restype = ctypes.c_char_p
self.lib.sbmlToFiniteVolumeInput.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]

self.lib.sbmlToVcml.restype = ctypes.c_char_p
self.lib.sbmlToVcml.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]

self.lib.vcmlToSbml.restype = ctypes.c_char_p
self.lib.vcmlToSbml.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]

self.lib.vcmlToVcml.restype = ctypes.c_char_p
self.lib.vcmlToVcml.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]

self.lib.freeString.restype = None
self.lib.freeString.argtypes = [ctypes.c_char_p]

Expand Down
52 changes: 52 additions & 0 deletions libvcell/model_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from pathlib import Path

from libvcell._internal.native_calls import ReturnValue, VCellNativeCalls


def vcml_to_sbml(vcml_content: str, application_name: str, sbml_file_path: Path) -> tuple[bool, str]:
"""
Convert VCML content to SBML file

Args:
vcml_content (str): VCML content
application_name (str): VCell Biomodel application name
sbml_file_path (Path): path to resulting SBML file

Returns:
tuple[bool, str]: A tuple containing the success status and a message
"""
native = VCellNativeCalls()
return_value: ReturnValue = native.vcml_to_sbml(vcml_content, application_name, sbml_file_path)
return return_value.success, return_value.message


def sbml_to_vcml(sbml_content: str, vcml_file_path: Path) -> tuple[bool, str]:
"""
Convert SBML content to finite volume input files

Args:
sbml_content (str): SBML content
vcml_file_path (Path): path to resulting VCML file

Returns:
tuple[bool, str]: A tuple containing the success status and a message
"""
native = VCellNativeCalls()
return_value: ReturnValue = native.sbml_to_vcml(sbml_content, vcml_file_path)
return return_value.success, return_value.message


def vcml_to_vcml(vcml_content: str, vcml_file_path: Path) -> tuple[bool, str]:
"""
Process VCML content to regenerated VCML file

Args:
vcml_content (str): VCML content
vcml_file_path (Path): path to resulting VCML file

Returns:
tuple[bool, str]: A tuple containing the success status and a message
"""
native = VCellNativeCalls()
return_value: ReturnValue = native.vcml_to_vcml(vcml_content, vcml_file_path)
return return_value.success, return_value.message
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "libvcell"
version = "0.0.8"
version = "0.0.9"
description = "This is a python package which wraps a subset of VCell Java code as a native python package."
authors = ["Jim Schaff <[email protected]>", "Ezequiel Valencia <[email protected]>"]
repository = "https://github.com/virtualcell/libvcell"
Expand Down
8 changes: 7 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
from tests.fixtures.data_fixtures import sbml_file_path, temp_output_dir, vcml_file_path, vcml_sim_name # noqa: F401
from tests.fixtures.data_fixtures import ( # noqa: F401
sbml_file_path,
temp_output_dir,
vcml_app_name,
vcml_file_path,
vcml_sim_name,
)
5 changes: 5 additions & 0 deletions tests/fixtures/data_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ def vcml_sim_name() -> str:
return "Simulation0"


@pytest.fixture
def vcml_app_name() -> str:
return "unnamed_spatialGeom"


@pytest.fixture
def sbml_file_path() -> Path:
return fixtures_dir / "TinySpatialProject_Application0.xml"
Expand Down
38 changes: 37 additions & 1 deletion tests/test_libvcell.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import tempfile
from pathlib import Path

from libvcell import sbml_to_finite_volume_input, vcml_to_finite_volume_input
from libvcell import sbml_to_finite_volume_input, sbml_to_vcml, vcml_to_finite_volume_input, vcml_to_sbml, vcml_to_vcml


def test_vcml_to_finite_volume_input(temp_output_dir: Path, vcml_file_path: Path, vcml_sim_name: str) -> None:
Expand All @@ -19,3 +20,38 @@ def test_sbml_to_finite_volume_input(temp_output_dir: Path, sbml_file_path: Path
assert len(list(temp_output_dir.iterdir())) > 0
assert success is True
assert msg == "Success"


def test_sbml_to_vcml(sbml_file_path: Path) -> None:
sbml_content = sbml_file_path.read_text()
with tempfile.TemporaryDirectory() as temp_dir:
temp_output_dir = Path(temp_dir)
vcml_file_path = temp_output_dir / "test.vcml"
success, msg = sbml_to_vcml(sbml_content=sbml_content, vcml_file_path=vcml_file_path)
assert vcml_file_path.exists()
assert success is True
assert msg == "Success"


def test_vcml_to_sbml(vcml_file_path: Path, vcml_app_name: str) -> None:
vcml_content = vcml_file_path.read_text()
with tempfile.TemporaryDirectory() as temp_dir:
temp_output_dir = Path(temp_dir)
sbml_file_path = temp_output_dir / "test.sbml"
success, msg = vcml_to_sbml(
vcml_content=vcml_content, application_name=vcml_app_name, sbml_file_path=sbml_file_path
)
assert sbml_file_path.exists()
assert success is True
assert msg == "Success"


def test_vcml_to_vcml(vcml_file_path: Path) -> None:
vcml_content = vcml_file_path.read_text()
with tempfile.TemporaryDirectory() as temp_dir:
temp_output_dir = Path(temp_dir)
vcml_file_path = temp_output_dir / "test.vcml"
success, msg = vcml_to_vcml(vcml_content=vcml_content, vcml_file_path=vcml_file_path)
assert vcml_file_path.exists()
assert success is True
assert msg == "Success"
95 changes: 94 additions & 1 deletion vcell-native/src/main/java/org/vcell/libvcell/Entrypoints.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@
import org.json.simple.JSONValue;

import java.io.File;
import java.nio.file.Path;
import java.util.concurrent.ConcurrentHashMap;

import static org.vcell.libvcell.SolverUtils.sbmlToFiniteVolumeInput;
import static org.vcell.libvcell.SolverUtils.vcmlToFiniteVolumeInput;
import static org.vcell.libvcell.ModelUtils.vcml_to_sbml;
import static org.vcell.libvcell.ModelUtils.sbml_to_vcml;
import static org.vcell.libvcell.ModelUtils.vcml_to_vcml;


public class Entrypoints {
Expand Down Expand Up @@ -52,6 +56,7 @@ public String toJson() {
documentation = """
Converts VCML file into Finite Volume Input files.
vcml_content: text of VCML XML document
simulation_name: name of the simulation to convert
output_dir_path: path to the output directory (expected to be subdirectory of the workspace)
Returns a JSON string with success status and message"""
)
Expand Down Expand Up @@ -106,4 +111,92 @@ public static CCharPointer entrypoint_sbmlToFiniteVolumeInput(
logger.info("Returning from sbmlToFiniteVolumeInput: " + json);
return createString(json);
}
}

@CEntryPoint(
name = "vcmlToSbml",
documentation = """
Converts VCML file into an SBML file.
vcml_content: text of VCML XML document
application_name: name of the application to export
sbml_file_path: path to the SBML file to write
Returns a JSON string with success status and message"""
)
public static CCharPointer entrypoint_vcmlToSbml(
IsolateThread ignoredThread,
CCharPointer vcml_content,
CCharPointer application_name,
CCharPointer sbml_file_path) {
ReturnValue returnValue;
try {
String vcmlContentStr = CTypeConversion.toJavaString(vcml_content);
String applicationName = CTypeConversion.toJavaString(application_name);
Path sbmlFilePath = new File(CTypeConversion.toJavaString(sbml_file_path)).toPath();
vcml_to_sbml(vcmlContentStr, applicationName, sbmlFilePath);
returnValue = new ReturnValue(true, "Success");
}catch (Throwable t) {
logger.error("Error translating vcml application to sbml", t);
returnValue = new ReturnValue(false, t.getMessage());
}
// return result as a json string
String json = returnValue.toJson();
logger.info("Returning from vcellToSbml: " + json);
return createString(json);
}

@CEntryPoint(
name = "sbmlToVcml",
documentation = """
Converts SBML file into a VCML file.
sbml_content: text of SBML XML document
vcml_file_path: path to the VCML file to write
Returns a JSON string with success status and message"""
)
public static CCharPointer entrypoint_sbmlToVcml(
IsolateThread ignoredThread,
CCharPointer sbml_content,
CCharPointer vcml_file_path) {
ReturnValue returnValue;
try {
String sbmlContentStr = CTypeConversion.toJavaString(sbml_content);
Path vcmlFilePath = new File(CTypeConversion.toJavaString(vcml_file_path)).toPath();
sbml_to_vcml(sbmlContentStr, vcmlFilePath);
returnValue = new ReturnValue(true, "Success");
}catch (Throwable t) {
logger.error("Error translating sbml to vcml", t);
returnValue = new ReturnValue(false, t.getMessage());
}
// return result as a json string
String json = returnValue.toJson();
logger.info("Returning from sbmlToVcell: " + json);
return createString(json);
}

@CEntryPoint(
name = "vcmlToVcml",
documentation = """
Updates a VCML file into a fully populated VCML file.
vcml_content: text of VCML XML document
vcml_file_path: path to the VCML file to write
Returns a JSON string with success status and message"""
)
public static CCharPointer entrypoint_vcmlToVcml(
IsolateThread ignoredThread,
CCharPointer vcml_content,
CCharPointer vcml_file_path) {
ReturnValue returnValue;
try {
String vcmlContentStr = CTypeConversion.toJavaString(vcml_content);
Path vcmlFilePath = new File(CTypeConversion.toJavaString(vcml_file_path)).toPath();
vcml_to_vcml(vcmlContentStr, vcmlFilePath);
returnValue = new ReturnValue(true, "Success");
}catch (Throwable t) {
logger.error("Error refreshing vcml", t);
returnValue = new ReturnValue(false, t.getMessage());
}
// return result as a json string
String json = returnValue.toJson();
logger.info("Returning from vcellToVcml: " + json);
return createString(json);
}

}
Loading