Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion libvcell/_internal/native_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,17 @@ def sbml_to_finite_volume_input(self, sbml_content: str, output_dir_path: Path)
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:
def vcml_to_sbml(
self, vcml_content: str, application_name: str, sbml_file_path: Path, round_trip_validation: bool
) -> 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")),
ctypes.c_int(int(round_trip_validation)),
)

value: bytes | None = ctypes.cast(json_ptr, ctypes.c_char_p).value
Expand Down
8 changes: 7 additions & 1 deletion libvcell/_internal/native_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ def _define_entry_points(self) -> None:
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.vcmlToSbml.argtypes = [
ctypes.c_void_p,
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.c_char_p,
ctypes.c_int,
]

self.lib.vcmlToVcml.restype = ctypes.c_char_p
self.lib.vcmlToVcml.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
Expand Down
15 changes: 11 additions & 4 deletions libvcell/model_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
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]:
def vcml_to_sbml(
vcml_content: str, application_name: str, sbml_file_path: Path, round_trip_validation: bool
) -> tuple[bool, str]:
"""
Convert VCML content to SBML file

Expand All @@ -16,7 +18,12 @@ def vcml_to_sbml(vcml_content: str, application_name: str, sbml_file_path: Path)
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_value: ReturnValue = native.vcml_to_sbml(
vcml_content=vcml_content,
application_name=application_name,
sbml_file_path=sbml_file_path,
round_trip_validation=round_trip_validation,
)
return return_value.success, return_value.message


Expand All @@ -32,7 +39,7 @@ def sbml_to_vcml(sbml_content: str, vcml_file_path: Path) -> tuple[bool, str]:
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_value: ReturnValue = native.sbml_to_vcml(sbml_content=sbml_content, vcml_file_path=vcml_file_path)
return return_value.success, return_value.message


Expand All @@ -48,5 +55,5 @@ def vcml_to_vcml(vcml_content: str, vcml_file_path: Path) -> tuple[bool, str]:
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_value: ReturnValue = native.vcml_to_vcml(vcml_content=vcml_content, vcml_file_path=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.10"
version = "0.0.11"
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
23 changes: 21 additions & 2 deletions tests/test_libvcell.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,32 @@ def test_sbml_to_vcml(sbml_file_path: Path) -> None:
assert msg == "Success"


def test_vcml_to_sbml(vcml_file_path: Path, vcml_app_name: str) -> None:
def test_vcml_to_sbml_with_validation(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
vcml_content=vcml_content,
application_name=vcml_app_name,
sbml_file_path=sbml_file_path,
round_trip_validation=True,
)
assert sbml_file_path.exists()
assert success is True
assert msg == "Success"


def test_vcml_to_sbml_without_validation(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,
round_trip_validation=False,
)
assert sbml_file_path.exists()
assert success is True
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,15 @@ public static CCharPointer entrypoint_vcmlToSbml(
IsolateThread ignoredThread,
CCharPointer vcml_content,
CCharPointer application_name,
CCharPointer sbml_file_path) {
CCharPointer sbml_file_path,
int roundTripValidation) {
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);
boolean bRoundTripValidation = CTypeConversion.toBoolean(roundTripValidation);
vcml_to_sbml(vcmlContentStr, applicationName, sbmlFilePath, bRoundTripValidation);
returnValue = new ReturnValue(true, "Success");
}catch (Throwable t) {
logger.error("Error translating vcml application to sbml", t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public static void main(String[] args) {

// create a temporary file for the SBML output
File temp_sbml_file = new File(output_dir, "temp.vcml");
vcml_to_sbml(vcml_str, vcml_app_name, temp_sbml_file.toPath());
vcml_to_sbml(vcml_str, vcml_app_name, temp_sbml_file.toPath(), true);
// remove temporary file
if (temp_sbml_file.exists()) {
boolean deleted = temp_sbml_file.delete();
Expand Down
42 changes: 37 additions & 5 deletions vcell-native/src/main/java/org/vcell/libvcell/ModelUtils.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package org.vcell.libvcell;

import cbit.image.ImageException;
import cbit.util.xml.VCLogger;
import cbit.util.xml.VCLoggerException;
import cbit.util.xml.XmlUtil;
import cbit.vcell.biomodel.BioModel;
import cbit.vcell.biomodel.ModelUnitConverter;
import cbit.vcell.geometry.GeometryException;
import cbit.vcell.geometry.GeometrySpec;
import cbit.vcell.mapping.MappingException;
import cbit.vcell.mapping.SimulationContext;
import cbit.vcell.mongodb.VCMongoMessage;
import cbit.vcell.parser.ExpressionException;
import cbit.vcell.xml.XMLSource;
import cbit.vcell.xml.XmlHelper;
import cbit.vcell.xml.XmlParseException;
import org.vcell.sbml.SbmlException;
import org.vcell.sbml.vcell.SBMLAnnotationUtil;
import org.vcell.sbml.vcell.SBMLExporter;
import org.vcell.sbml.vcell.SBMLImporter;
import org.vcell.util.Pair;

import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayInputStream;
Expand Down Expand Up @@ -66,17 +72,43 @@ record LoggerMessage(VCLogger.Priority priority, VCLogger.ErrorType errorType, S
}


public static void vcml_to_sbml(String vcml_content, String applicationName, Path sbmlPath)
throws XmlParseException, IOException, XMLStreamException, SbmlException, MappingException {
public static void vcml_to_sbml(String vcml_content, String applicationName, Path sbmlPath, boolean roundTripValidation)
throws XmlParseException, IOException, XMLStreamException, SbmlException, MappingException, ImageException, GeometryException, ExpressionException {
GeometrySpec.avoidAWTImageCreation = true;
VCMongoMessage.enabled = false;

BioModel bioModel = XmlHelper.XMLToBioModel(new XMLSource(vcml_content));
bioModel.updateAll(false);
SimulationContext simContext = bioModel.getSimulationContext(applicationName);
boolean validateSBML = true;
SBMLExporter sbmlExporter = new SBMLExporter(simContext, 3, 1, validateSBML);

if (applicationName == null || applicationName.isEmpty()) {
throw new RuntimeException("Error: Application name is null or empty");
}

if (bioModel.getSimulationContext(applicationName) == null) {
throw new RuntimeException("Error: Simulation context not found for application name: " + applicationName);
}

// change the unit system to SBML preferred units if not already.
final BioModel sbmlPreferredUnitsBM;
if (!bioModel.getModel().getUnitSystem().compareEqual(ModelUnitConverter.createSbmlModelUnitSystem())) {
sbmlPreferredUnitsBM = ModelUnitConverter.createBioModelWithSBMLUnitSystem(bioModel);
if(sbmlPreferredUnitsBM == null) {
throw new RuntimeException("Unable to clone BioModel with SBML unit system");
}
} else {
sbmlPreferredUnitsBM = bioModel;
}

SimulationContext simContext = sbmlPreferredUnitsBM.getSimulationContext(applicationName);

int sbml_level = 3;
int sbml_version = 1;
SBMLExporter sbmlExporter = new SBMLExporter(simContext, sbml_level, sbml_version, roundTripValidation);
String sbml_string = sbmlExporter.getSBMLString();

// cleanup the string of all the "sameAs" statements
sbml_string = SBMLAnnotationUtil.postProcessCleanup(sbml_string);

XmlUtil.writeXMLStringToFile(sbml_string, sbmlPath.toFile().getAbsolutePath(), true);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.vcell.libvcell;

import cbit.image.ImageException;
import cbit.util.xml.VCLoggerException;
import cbit.vcell.geometry.GeometryException;
import cbit.vcell.mapping.MappingException;
import cbit.vcell.parser.ExpressionException;
import cbit.vcell.xml.XmlParseException;
import org.junit.jupiter.api.Test;
import org.vcell.sbml.SbmlException;
Expand All @@ -26,13 +29,25 @@ public void test_sbml_to_vcml() throws MappingException, IOException, XmlParseEx
}

@Test
public void test_vcml_to_sbml() throws MappingException, IOException, XmlParseException, XMLStreamException, SbmlException {
public void test_vcml_to_sbml_with_round_trip() throws MappingException, IOException, XmlParseException, XMLStreamException, SbmlException, ImageException, GeometryException, ExpressionException {
String vcmlContent = getFileContentsAsString("/TinySpatialProject_Application0.vcml");
File parent_dir = Files.createTempDirectory("vcmlToSbml").toFile();
File sbml_temp_file = new File(parent_dir, "temp.sbml");
String applicationName = "unnamed_spatialGeom";
vcml_to_sbml(vcmlContent, applicationName, sbml_temp_file.toPath());
assert(sbml_temp_file.exists());

File sbml_temp_file_true = new File(parent_dir, "temp_true.sbml");
vcml_to_sbml(vcmlContent, applicationName, sbml_temp_file_true.toPath(), true);
assert(sbml_temp_file_true.exists());
}

@Test
public void test_vcml_to_sbml_without_round_trip() throws MappingException, IOException, XmlParseException, XMLStreamException, SbmlException, ImageException, GeometryException, ExpressionException {
String vcmlContent = getFileContentsAsString("/TinySpatialProject_Application0.vcml");
File parent_dir = Files.createTempDirectory("vcmlToSbml").toFile();
String applicationName = "unnamed_spatialGeom";

File sbml_temp_file_false = new File(parent_dir, "temp_false.sbml");
vcml_to_sbml(vcmlContent, applicationName, sbml_temp_file_false.toPath(), false);
assert(sbml_temp_file_false.exists());
}

@Test
Expand Down