Skip to content

Commit

Permalink
SBML To Finite Volume Endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
AvocadoMoon committed Jan 29, 2025
1 parent d0462fc commit cafee0a
Show file tree
Hide file tree
Showing 4 changed files with 461 additions and 0 deletions.
5 changes: 5 additions & 0 deletions vcell-rest/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.11.5</version>
</dependency>
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>log4j2-jboss-logmanager</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.vcell.restq.handlers;

import cbit.util.xml.VCLoggerException;
import cbit.vcell.biomodel.BioModel;
import cbit.vcell.mapping.MappingException;
import cbit.vcell.mapping.SimulationContext;
import cbit.vcell.parser.ExpressionException;
import cbit.vcell.solver.Simulation;
import cbit.vcell.solver.SolverException;
import cbit.vcell.solver.TimeBounds;
import cbit.vcell.solver.UniformOutputTimeSpec;
import jakarta.enterprise.context.RequestScoped;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import net.lingala.zip4j.ZipFile;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.RestForm;
import org.vcell.sbml.SBMLFakeSpatialBioModel;
import org.vcell.sbml.vcell.SBMLExporter;
import org.vcell.sbml.vcell.SBMLImporter;
import org.w3c.www.http.HTTP;

import java.beans.PropertyVetoException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;

@Path("/api/v1/spatial")
@RequestScoped
public class SpatialResource {
public SpatialResource() { }

@GET
@Path("/retrieveFiniteVolumeInputFromSpatialModel")
@Operation(operationId = "retrieveFiniteVolumeInputFromSpatialModel", summary = "Retrieve finite volume input from spatial model")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public File retrieveFiniteVolumeInputFromSpatialModel(@RestForm File sbmlFile) {
try {
File workingDir = Files.createTempDirectory("vcell-").toFile();
File zipFile = Files.createTempFile("finite-volume", ".zip").toFile();
sbmlToFiniteVolumeInput(sbmlFile, workingDir);
ZipFile zip = new ZipFile(zipFile);

zip.addFolder(workingDir);
zip.close();

return zipFile;
}catch (Exception e){
throw new WebApplicationException("Error processing spatial model", HTTP.INTERNAL_SERVER_ERROR);
}
}

public record FiniteVolumeInput(
@RestForm @PartType(MediaType.APPLICATION_OCTET_STREAM)
File zipFile
) { }



public static void sbmlToFiniteVolumeInput(File sbmlFile, File outputDir) throws IOException, MappingException, VCLoggerException, PropertyVetoException, SolverException, ExpressionException {
SBMLExporter.MemoryVCLogger vcl = new SBMLExporter.MemoryVCLogger();
boolean bValidateSBML = true;
InputStream is = new FileInputStream(sbmlFile);
SBMLImporter importer = new SBMLImporter(is, vcl, bValidateSBML);
BioModel bioModel = importer.getBioModel();
bioModel.updateAll(false);

final double duration = 5.0; // endpoint arg
final double time_step = 0.1; // endpoint arg
//final ISize meshSize = new ISize(10, 10, 10); // future endpoint arg
SimulationContext simContext = bioModel.getSimulationContext(0);
Simulation sim = new Simulation(simContext.getMathDescription(), simContext);
sim.getSolverTaskDescription().setTimeBounds(new TimeBounds(0.0, duration));
sim.getSolverTaskDescription().setOutputTimeSpec(new UniformOutputTimeSpec(time_step));

SBMLFakeSpatialBioModel.writeInputFilesOnly(outputDir, sim);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package org.vcell.restq.apiclient;

import cbit.util.xml.VCLoggerException;
import cbit.vcell.mapping.MappingException;
import cbit.vcell.parser.ExpressionException;
import cbit.vcell.resource.PropertyLoader;
import cbit.vcell.solver.AnnotatedFunction;
import cbit.vcell.solver.SolverException;
import cbit.vcell.solvers.FunctionFileGenerator;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.keycloak.client.KeycloakTestClient;
import jakarta.inject.Inject;
import net.lingala.zip4j.ZipFile;
import org.apache.commons.io.FileUtils;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jose4j.lang.JoseException;
import org.junit.jupiter.api.*;
import org.testcontainers.shaded.com.google.common.io.Files;
import org.vcell.restclient.ApiClient;
import org.vcell.restclient.ApiException;
import org.vcell.restclient.api.SpatialResourceApi;
import org.vcell.restq.TestEndpointUtils;
import org.vcell.restq.config.CDIVCellConfigProvider;
import org.vcell.restq.db.AgroalConnectionFactory;
import org.vcell.restq.handlers.SpatialResource;
import org.vcell.util.DataAccessException;

import java.beans.PropertyVetoException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Scanner;
import java.util.Vector;

@QuarkusTest
public class SpacialResourceTest {
@ConfigProperty(name = "quarkus.http.test-port")
Integer testPort;

@Inject
AgroalConnectionFactory agroalConnectionFactory;

KeycloakTestClient keycloakClient = new KeycloakTestClient();

private ApiClient aliceAPIClient;
private static String previousServerID;
private static String previousMongoHost;
private static String previousMongoPort;
private static String previousMongoDB;

@BeforeAll
public static void setupConfig(){
PropertyLoader.setConfigProvider(new CDIVCellConfigProvider());
previousServerID = PropertyLoader.getProperty(PropertyLoader.vcellServerIDProperty, null);
PropertyLoader.setProperty(PropertyLoader.vcellServerIDProperty, "TEST2");

previousMongoDB = PropertyLoader.getProperty(PropertyLoader.mongodbDatabase, null);
PropertyLoader.setProperty(PropertyLoader.mongodbDatabase, "test");

previousMongoHost = PropertyLoader.getProperty(PropertyLoader.mongodbHostInternal, null);
PropertyLoader.setProperty(PropertyLoader.mongodbHostInternal, "localhost");

previousMongoPort = PropertyLoader.getProperty(PropertyLoader.mongodbPortInternal, null);
PropertyLoader.setProperty(PropertyLoader.mongodbPortInternal, "9080");
}

@AfterAll
public static void resetConfig(){
if (previousServerID != null) {PropertyLoader.setProperty(PropertyLoader.vcellServerIDProperty, previousServerID);};
if (previousMongoHost != null) {PropertyLoader.setProperty(PropertyLoader.mongodbHostInternal, previousMongoHost);}
if (previousMongoDB != null) {PropertyLoader.setProperty(PropertyLoader.mongodbDatabase, previousMongoDB);}
if (previousMongoPort != null) {PropertyLoader.setProperty(PropertyLoader.mongodbPortInternal, previousMongoPort);}
}

@BeforeEach
public void createClients() throws JoseException {
aliceAPIClient = TestEndpointUtils.createAuthenticatedAPIClient(keycloakClient, testPort, TestEndpointUtils.TestOIDCUsers.alice);
}

@AfterEach
public void removeOIDCMappings() throws SQLException, DataAccessException {
TestEndpointUtils.removeAllMappings(agroalConnectionFactory);
}

// Ensure the round trip from request to resulting Zip is equivalent to a local run of input file generation
@Test
public void testSpacialSBMLResults() throws ApiException, IOException, VCLoggerException, SolverException, ExpressionException, PropertyVetoException, MappingException {
SpatialResourceApi spatialResourceApi = new SpatialResourceApi(aliceAPIClient);
File sbmlFile = TestEndpointUtils.getResourceFile("/TinySpacialProject_Application0.xml");

File zip = spatialResourceApi.retrieveFiniteVolumeInputFromSpatialModel(sbmlFile);
File unzipDir = Files.createTempDir();
File outputDir = Files.createTempDir();

SpatialResource.sbmlToFiniteVolumeInput(sbmlFile, outputDir);

ZipFile zipFile = new ZipFile(zip);
zipFile.extractAll(unzipDir.getAbsolutePath());
zipFile.close();

unzipDir = unzipDir.listFiles()[0];

for (int i = 0; i < outputDir.listFiles().length; i++) {
File output = outputDir.listFiles()[i];
File unzip = Arrays.stream(unzipDir.listFiles()).filter(
f -> f.getName().split("\\.")[1].equals(output.getName().split("\\.")[1])
).findFirst().orElse(null);
if (output.getName().split("\\.")[1].equals("functions")){
Vector<AnnotatedFunction> outPutAnnotated = FunctionFileGenerator.readFunctionsFile(output, "0");
Vector<AnnotatedFunction> unzipAnnotated = FunctionFileGenerator.readFunctionsFile(unzip, "0");
for (int j = 0; j < outPutAnnotated.size(); j++) {
Assertions.assertTrue(outPutAnnotated.get(j).compareEqual(unzipAnnotated.get(j)));
}
} else if (output.getName().split("\\.")[1].equals("fvinput")) {
Scanner outputReader = new Scanner(output);
Scanner unzipReader = new Scanner(unzip);

while (outputReader.hasNextLine()){
String outputLine = outputReader.nextLine();
String unzipLine = unzipReader.nextLine();
// Don't compare the file path
if (!outputLine.contains("FILE")){
Assertions.assertEquals(outputLine, unzipLine);
}
}
} else{
Assertions.assertTrue(FileUtils.contentEquals(output, unzip));
}
}
}
}
Loading

0 comments on commit cafee0a

Please sign in to comment.