diff --git a/vcell-cli/pom.xml b/vcell-cli/pom.xml index 1d2605e44b..40e292a049 100644 --- a/vcell-cli/pom.xml +++ b/vcell-cli/pom.xml @@ -98,6 +98,11 @@ picocli ${picocli.version} + + org.jfree + jfreechart + 1.5.5 + org.junit.jupiter junit-jupiter diff --git a/vcell-cli/src/main/java/org/vcell/cli/CLIPythonManager.java b/vcell-cli/src/main/java/org/vcell/cli/CLIPythonManager.java index a363ecc063..6c0d9f32f7 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/CLIPythonManager.java +++ b/vcell-cli/src/main/java/org/vcell/cli/CLIPythonManager.java @@ -46,7 +46,10 @@ public class CLIPythonManager { /** * Retrieve the Python Manager, or create and return if it doesn't exist. * @return the manager + * @deprecated CLIPythonManager is no longer used in CLI, and has no current use; should this be used again + * you need to check whether there are still bugs with Arm Macs, and whether single-instance python shells are not a viable alternative */ + @Deprecated public static CLIPythonManager getInstance(){ lg.trace("Getting Python instance"); if (instance == null){ @@ -62,7 +65,9 @@ public static CLIPythonManager getInstance(){ * @param arguments the arguments to provide to the function call, each as their own string, in the correct order * @return the return response from Python * @throws PythonStreamException if there is any exception encountered in this exchange. + * @deprecated See getInstance() */ + @Deprecated public String callPython(String functionName, String... arguments) throws PythonStreamException { String command = this.formatPythonFunctionCall(functionName, arguments); try { @@ -82,7 +87,9 @@ public String callPython(String functionName, String... arguments) throws Python * @param cliCommand the command to run * @throws InterruptedException if the python process was interrupted * @throws IOException if there was a system IO failure + * @deprecated See getInstance() */ + @Deprecated private static String callNonSharedPython(String cliCommand) throws InterruptedException, IOException, PythonStreamException { Path cliWorkingDir = Paths.get(PropertyLoader.getRequiredProperty(PropertyLoader.cliWorkingDir)); @@ -96,7 +103,9 @@ private static String callNonSharedPython(String cliCommand) * Shuts down the python session and cleans up. * * @throws IOException if there is a system IO issue. + * @deprecated See getInstance() */ + @Deprecated public void closePythonProcess() throws IOException { // Exit the living Python Process lg.debug("Closing Python Instance"); @@ -122,7 +131,9 @@ public void closePythonProcess() throws IOException { * as means of installation verification. * * @throws IOException if there is a problem with System I/O + * @deprecated this entire system is unstable on ARM Macs, and still a bit slow. */ + @Deprecated public void instantiatePythonProcess() throws IOException, PythonStreamException { if (this.pythonProcess != null) return; // prevent override lg.info("Initializing Python..."); @@ -338,10 +349,26 @@ private static String runAndPrintProcessStreams(ProcessBuilder pb, String outStr return os; } + /** + * Checks whether python returned successfully or not + * @param returnedString + * @throws PythonStreamException + * @deprecated See getInstance() + */ + @Deprecated public void parsePythonReturn(String returnedString) throws PythonStreamException { this.parsePythonReturn(returnedString, null, null); } + /** + * Checks whether python returned successfully or not + * @param returnedString + * @param outString + * @param errString + * @throws PythonStreamException + * @deprecated See getInstance() + */ + @Deprecated public void parsePythonReturn(String returnedString, String outString, String errString) throws PythonStreamException { boolean DEBUG_NORMAL_OUTPUT = lg.isTraceEnabled(); // Consider getting rid of this, currently redundant String ERROR_PHRASE1 = "Traceback", ERROR_PHRASE2 = "File \"\""; diff --git a/vcell-cli/src/main/java/org/vcell/cli/biosimulation/BiosimulationsCommand.java b/vcell-cli/src/main/java/org/vcell/cli/biosimulation/BiosimulationsCommand.java index 97933e8441..0af384a69b 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/biosimulation/BiosimulationsCommand.java +++ b/vcell-cli/src/main/java/org/vcell/cli/biosimulation/BiosimulationsCommand.java @@ -42,14 +42,38 @@ public class BiosimulationsCommand implements Callable { private boolean help; public Integer call() { - CLIRecorder cliRecorder = null; int returnCode; - - if ((returnCode = this.noFurtherActionNeeded(bQuiet, bDebug, bVersion)) != -1) + + if ((returnCode = BiosimulationsCommand.noFurtherActionNeeded(bQuiet, bDebug, bVersion)) != -1) return returnCode; - + + if (this.ARCHIVE == null) { + logger.error("ARCHIVE file not specified, try --help for usage"); + return 1; + } + if (this.OUT_DIR == null) { + logger.error("OUT_DIR not specified, try --help for usage"); + return 1; + } + String trace_args = String.format( + "Arguments:\nArchive\t: \"%s\"\nOut Dir\t: \"%s\"\nDebug\t: %b\n" + + "Quiet\t: %b\nVersion\t: %b\nHelp\t: %b", + ARCHIVE.getAbsolutePath(), OUT_DIR.getAbsolutePath(), bDebug, bQuiet, bVersion, help + ); + + logger.trace(trace_args); + return BiosimulationsCommand.executeVCellBiosimulationsMode(ARCHIVE, OUT_DIR, bQuiet, bDebug); + } + + public static int executeVCellBiosimulationsMode(File inFile, File outDir){ + return BiosimulationsCommand.executeVCellBiosimulationsMode(inFile, outDir, false, false); + } + + public static int executeVCellBiosimulationsMode(File inFile, File outDir, boolean bQuiet, boolean bDebug){ + CLIRecorder cliRecorder; + try { - cliRecorder = new CLIRecorder(OUT_DIR); // CLILogger will throw an execption if our output dir isn't valid. + cliRecorder = new CLIRecorder(outDir); // CLILogger will throw an execption if our output dir isn't valid. Level logLevel = logger.getLevel(); if (!bQuiet && bDebug) { logLevel = Level.DEBUG; @@ -58,45 +82,29 @@ public Integer call() { } LoggerContext config = (LoggerContext)(LogManager.getContext(false)); config.getConfiguration().getLoggerConfig(LogManager.getLogger("org.vcell").getName()).setLevel(logLevel); - config.getConfiguration().getLoggerConfig(LogManager.getLogger("cbit").getName()).setLevel(logLevel); + + config.getConfiguration().getLoggerConfig(LogManager.getLogger("cbit").getName()).setLevel(bDebug ? logLevel : Level.WARN ); + config.getConfiguration().getLoggerConfig(LogManager.getLogger("org.jlibsedml").getName()).setLevel(bDebug ? logLevel : Level.WARN); config.updateLoggers(); logger.debug("Biosimulations mode requested"); - String trace_args = String.format( - "Arguments:\nArchive\t: \"%s\"\nOut Dir\t: \"%s\"\nDebug\t: %b\n" + - "Quiet\t: %b\nVersion\t: %b\nHelp\t: %b", - ARCHIVE.getAbsolutePath(), OUT_DIR.getAbsolutePath(), bDebug, bQuiet, bVersion, help - ); - - logger.trace(trace_args); - logger.trace("Validating input"); - if (ARCHIVE == null) { - logger.error("ARCHIVE file not specified, try --help for usage"); - return 1; - } - if (!ARCHIVE.isFile()) { - logger.error("ARCHIVE file " + ARCHIVE.getAbsolutePath() + " not found, try --help for usage"); + if (!inFile.isFile()) { + logger.error("ARCHIVE file " + inFile.getAbsolutePath() + " not found, try --help for usage"); return 1; } logger.trace("Validating output"); - if (OUT_DIR == null) { - logger.error("OUT_DIR not specified, try --help for usage"); - return 1; - } - if (!OUT_DIR.isDirectory()) { - logger.error("OUT_DIR " + OUT_DIR.getAbsolutePath() + " not found or is not a directory, try --help for usage"); + if (!outDir.isDirectory()) { + logger.error("OUT_DIR " + outDir.getAbsolutePath() + " not found or is not a directory, try --help for usage"); return 1; } logger.info("Beginning execution"); File tmpDir = Files.createTempDirectory("VCell_CLI_" + Long.toHexString(new Date().getTime())).toFile(); try { - CLIPythonManager.getInstance().instantiatePythonProcess(); - ExecuteImpl.singleMode(ARCHIVE, tmpDir, cliRecorder, true); - CLIPythonManager.getInstance().closePythonProcess(); // Give the process time to finish + ExecuteImpl.singleMode(inFile, tmpDir, cliRecorder, true); if (!Tracer.hasErrors()) return 0; if (!bQuiet) { logger.error("Errors occurred during execution"); @@ -104,13 +112,8 @@ public Integer call() { } return 1; } finally { - try { - CLIPythonManager.getInstance().closePythonProcess(); // WARNING: Python will need reinstantiation after this is called - } catch (Exception e) { - logger.error(e.getMessage(), e); - } logger.debug("Finished all execution."); - FileUtils.copyDirectoryContents(tmpDir, OUT_DIR, true, null); + FileUtils.copyDirectoryContents(tmpDir, outDir, true, null); } } catch (Exception e) { if (!bQuiet) { @@ -121,7 +124,7 @@ public Integer call() { } } - private int noFurtherActionNeeded(boolean bQuiet, boolean bDebug, boolean bVersion){ + private static int noFurtherActionNeeded(boolean bQuiet, boolean bDebug, boolean bVersion){ logger.debug("Validating CLI arguments"); if (bVersion) { String version = PropertyLoader.getRequiredProperty(PropertyLoader.vcellSoftwareVersion); diff --git a/vcell-cli/src/main/java/org/vcell/cli/exceptions/PreProcessingException.java b/vcell-cli/src/main/java/org/vcell/cli/exceptions/PreProcessingException.java new file mode 100644 index 0000000000..41b3a8b904 --- /dev/null +++ b/vcell-cli/src/main/java/org/vcell/cli/exceptions/PreProcessingException.java @@ -0,0 +1,20 @@ +package org.vcell.cli.exceptions; + +public class PreProcessingException extends RuntimeException { + public PreProcessingException(String message) { + super(message); + } + + public PreProcessingException(Throwable cause) { + super(cause); + } + + public PreProcessingException(String message, Throwable cause){ + super(message, cause); + } + + @Override + public int hashCode() { + return super.hashCode(); + } +} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteCommand.java b/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteCommand.java index 1026ac4cdb..4dedf942a2 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteCommand.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteCommand.java @@ -110,8 +110,6 @@ public Integer call() { System.err.println("cannot specify both debug and quiet, try --help for usage"); return 1; } - - CLIPythonManager.getInstance().instantiatePythonProcess(); Executable.setGlobalTimeoutMS(EXECUTABLE_MAX_WALLCLOCK_MILLIS); @@ -132,7 +130,7 @@ public Integer call() { bEncapsulateOutput, bSmallMeshOverride); } } - CLIPythonManager.getInstance().closePythonProcess(); + // WARNING: Python needs re-instantiation once the above line is called! FileUtils.copyDirectoryContents(tmpDir, outputFilePath, true, null); return 0; diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteImpl.java b/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteImpl.java index ce4df84908..d6bb8ff4e0 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteImpl.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteImpl.java @@ -5,7 +5,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.vcell.cli.CLIRecordable; -import org.vcell.cli.PythonStreamException; import org.vcell.cli.exceptions.ExecutionException; import org.vcell.cli.run.hdf5.BiosimulationsHdfWriterException; import org.vcell.sedml.log.BiosimulationLog; @@ -55,9 +54,9 @@ public static void batchMode(File dirOfArchivesToProcess, File outputDir, CLIRec String targetOutputDir = Paths.get(outputBaseDir, bioModelBaseName).toString(); Span span = null; - try { + // Initialization call generates Status YAML + try (BiosimulationLog bioSimLog = BiosimulationLog.initialize(inputFile.getAbsolutePath(), targetOutputDir)) { span = Tracer.startSpan(Span.ContextType.OMEX_EXECUTE, inputFileName, Map.of("filename", inputFileName)); - BiosimulationLog.initialize(inputFile.getAbsolutePath(), targetOutputDir); // generate Status YAML System.out.println("\n\n"); logger.info("Processing " + inputFileName + "(" + inputFile + ")"); @@ -79,7 +78,6 @@ public static void batchMode(File dirOfArchivesToProcess, File outputDir, CLIRec if (span != null) { span.close(); } - BiosimulationLog.instance().close(); } } if (failedFiles.isEmpty()){ @@ -108,7 +106,8 @@ public static void batchMode(File dirOfArchivesToProcess, File outputDir, CLIRec private static void runSingleExecOmex(File inputFile, File outputDir, CLIRecordable cliLogger, boolean bKeepTempFiles, boolean bExactMatchOnly, boolean bSmallMeshOverride) - throws IOException, ExecutionException, PythonStreamException, InterruptedException, BiosimulationsHdfWriterException { + throws IOException, ExecutionException, BiosimulationsHdfWriterException { + String bioModelBaseName = inputFile.getName().substring(0, inputFile.getName().indexOf(".")); // ".omex"?? Files.createDirectories(Paths.get(outputDir.getAbsolutePath() + File.separator + bioModelBaseName)); // make output subdir final boolean bEncapsulateOutput = true; @@ -133,19 +132,16 @@ public static void singleMode(File inputFile, File rootOutputDir, CLIRecordable String targetOutputDir = bEncapsulateOutput ? Paths.get(outputBaseDir, bioModelBaseName).toString() : outputBaseDir; File adjustedOutputDir = new File(targetOutputDir); - logger.info("Preparing output directory..."); + if (logger.isDebugEnabled()) logger.info("Preparing output directory..."); // we don't want to accidentally delete the input... // if the output directory is a subset of the input file's housing directory, we shouldn't delete!! if (!inputFile.getParentFile().getCanonicalPath().contains(adjustedOutputDir.getCanonicalPath())) RunUtils.removeAndMakeDirs(adjustedOutputDir); - try { - BiosimulationLog.initialize(inputFile.getAbsolutePath(), targetOutputDir); // generate Status YAML - + // Initialization line generates Status YAML + try (BiosimulationLog bioSimLog = BiosimulationLog.initialize(inputFile.getAbsolutePath(), targetOutputDir)) { ExecuteImpl.singleExecOmex(inputFile, rootOutputDir, cliLogger, bKeepTempFiles, bExactMatchOnly, bEncapsulateOutput, bSmallMeshOverride, bBioSimMode); - } finally { - BiosimulationLog.instance().close(); } } @@ -205,8 +201,7 @@ public static void singleExecVcml(File vcmlFile, File outputDir, CLIRecordable c for (String simName : resultsHash.keySet()) { String CSVFilePath = Paths.get(outDirForCurrentVcml.toString(), simName + ".csv").toString(); - RunUtils.createCSVFromODEResultSet(resultsHash.get(simName), new File(CSVFilePath)); - PythonCalls.transposeVcmlCsv(CSVFilePath); + RunUtils.createCSVFromODEResultSet(resultsHash.get(simName), new File(CSVFilePath), true); } } catch (IOException e) { Tracer.failure(e, "IOException while processing VCML " + vcmlFile.getName()); @@ -237,7 +232,7 @@ public static void singleExecVcml(File vcmlFile, File outputDir, CLIRecordable c private static void singleExecOmex(File inputFile, File rootOutputDir, CLIRecordable cliRecorder, boolean bKeepTempFiles, boolean bExactMatchOnly, boolean bEncapsulateOutput, boolean bSmallMeshOverride, boolean bBioSimMode) - throws ExecutionException, PythonStreamException, IOException, BiosimulationsHdfWriterException { + throws ExecutionException, IOException, BiosimulationsHdfWriterException { ExecutionJob requestedExecution = new ExecutionJob(inputFile, rootOutputDir, cliRecorder, bKeepTempFiles, bExactMatchOnly, bEncapsulateOutput, bSmallMeshOverride); diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteOmexCommand.java b/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteOmexCommand.java index 3b9a17716c..ba28a756ab 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteOmexCommand.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/ExecuteOmexCommand.java @@ -93,8 +93,6 @@ public Integer call() { config.updateLoggers(); - CLIPythonManager.getInstance().instantiatePythonProcess(); - Executable.setGlobalTimeoutMS(EXECUTABLE_MAX_WALLCLOCK_MILLIS); logger.info("Beginning execution"); File tmpDir = Files.createTempDirectory("VCell_CLI_" + Long.toHexString(new Date().getTime())).toFile(); @@ -102,7 +100,7 @@ public Integer call() { Tracer.clearTraceEvents(); ExecuteImpl.singleMode(inputFilePath, tmpDir, cliTracer, bKeepTempFiles, bExactMatchOnly, bEncapsulateOutput, bSmallMeshOverride); - CLIPythonManager.getInstance().closePythonProcess(); + // WARNING: Python needs re-instantiation once the above line is called! FileUtils.copyDirectoryContents(tmpDir, outputFilePath, true, null); final OmexExecSummary omexExecSummary; diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/ExecutionJob.java b/vcell-cli/src/main/java/org/vcell/cli/run/ExecutionJob.java index 459b9d7114..d62f3d1765 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/ExecutionJob.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/ExecutionJob.java @@ -1,10 +1,11 @@ package org.vcell.cli.run; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.vcell.cli.CLIRecordable; -import org.vcell.cli.PythonStreamException; import org.vcell.cli.exceptions.ExecutionException; +import org.vcell.cli.exceptions.PreProcessingException; import org.vcell.cli.run.hdf5.BiosimulationsHdf5Writer; import org.vcell.cli.run.hdf5.BiosimulationsHdfWriterException; import org.vcell.cli.run.hdf5.HDF5ExecutionResults; @@ -26,8 +27,7 @@ public class ExecutionJob { private long startTime_ms, endTime_ms; private boolean bExactMatchOnly, bSmallMeshOverride, bKeepTempFiles; - private StringBuilder logOmexMessage; - private String inputFilePath; + private final StringBuilder logOmexMessage; private String bioModelBaseName; private String outputDir; private boolean anySedmlDocumentHasSucceeded = false; // set to true if at least one sedml document run is successful @@ -56,8 +56,7 @@ public ExecutionJob(File inputFile, File rootOutputDir, CLIRecordable cliRecorde this(); this.inputFile = inputFile; this.cliRecorder = cliRecorder; - - this.inputFilePath = inputFile.getAbsolutePath(); + this.bioModelBaseName = FileUtils.getBaseName(inputFile.getName()); // input file without the path String outputBaseDir = rootOutputDir.getAbsolutePath(); this.outputDir = bEncapsulateOutput ? Paths.get(outputBaseDir, bioModelBaseName).toString() : outputBaseDir; @@ -67,39 +66,38 @@ public ExecutionJob(File inputFile, File rootOutputDir, CLIRecordable cliRecorde } private ExecutionJob(){ - this.logOmexMessage = new StringBuilder(""); + this.logOmexMessage = new StringBuilder(); } /** * Run the neexed steps to prepare an archive for execution. * Follow-up call: `executeArchive()` - * - * @throws PythonStreamException if calls to the python-shell instance are not working correctly + * * @throws IOException if there are system I/O issues. */ - public void preprocessArchive() throws PythonStreamException, IOException { + public void preprocessArchive() throws IOException { // Start the clock this.startTime_ms = System.currentTimeMillis(); // Beginning of Execution logger.info("Executing OMEX archive `{}`", this.inputFile.getName()); - logger.info("Archive location: {}", this.inputFilePath); + if (logger.isDebugEnabled()) logger.info("Archive location: {}", this.inputFile.getAbsolutePath()); RunUtils.drawBreakLine("-", 100); // Unpack the Omex Archive try { // It's unlikely, but if we get errors here they're fatal. this.sedmlPath2d3d = Paths.get(this.outputDir, "temp"); - this.omexHandler = new OmexHandler(this.inputFilePath, this.outputDir); + this.omexHandler = new OmexHandler(this.inputFile.getAbsolutePath(), this.outputDir); this.omexHandler.extractOmex(); this.sedmlLocations = this.omexHandler.getSedmlLocationsAbsolute(); } catch (IOException e){ - String error = e.getMessage() + ", error for OmexHandler with " + this.inputFilePath; + String error = e.getMessage() + ", error for OmexHandler with " + this.inputFile.getAbsolutePath(); this.cliRecorder.writeErrorList(e, this.bioModelBaseName); this.cliRecorder.writeDetailedResultList(this.bioModelBaseName + ", " + "IO error with OmexHandler"); logger.error(error); throw new RuntimeException(error, e); } catch (Exception e) { - String error = e.getMessage() + ", error for archive " + this.inputFilePath; + String error = e.getMessage() + ", error for archive " + this.inputFile.getAbsolutePath(); logger.error(error); if (this.omexHandler != null) this.omexHandler.deleteExtractedOmex(); this.cliRecorder.writeErrorList(e, this.bioModelBaseName); @@ -114,57 +112,48 @@ public void preprocessArchive() throws PythonStreamException, IOException { /** * Run solvers on all the models in the archive. - * + *
* Called after: `preprocessArchive()` * Called before: `postProcessArchive()` - * - * @throws InterruptedException if there is an issue with accessing data - * @throws PythonStreamException if calls to the python-shell instance are not working correctly - * @throws IOException if there are system I/O issues - * @throws ExecutionException if an execution specfic error occurs + * + * @throws ExecutionException if an execution specific error occurs */ - public void executeArchive(boolean isBioSimSedml) throws BiosimulationsHdfWriterException, PythonStreamException, ExecutionException { + public void executeArchive(boolean isBioSimSedml) throws BiosimulationsHdfWriterException, ExecutionException { + HDF5ExecutionResults cumulativeHdf5Results = new HDF5ExecutionResults(isBioSimSedml); + try { - HDF5ExecutionResults masterHdf5File = new HDF5ExecutionResults(isBioSimSedml); - this.queueAllSedml(); - - for (String sedmlLocation : this.sedmlLocations){ - SedmlJob job = new SedmlJob(sedmlLocation, this.omexHandler, this.inputFile, - this.outputDir, this.sedmlPath2d3d.toString(), this.cliRecorder, - this.bKeepTempFiles, this.bExactMatchOnly, this.bSmallMeshOverride, this.logOmexMessage); - if (!job.preProcessDoc()){ - SedmlStatistics stats = job.getDocStatistics(); // Must process document first - logger.error("Statistics of failed SedML:\n" + stats.toString()); + for (String sedmlLocation : this.sedmlLocations) { + try { + this.executeSedmlDocument(sedmlLocation, cumulativeHdf5Results); + } catch (PreProcessingException e) { this.anySedmlDocumentHasFailed = true; } - SedmlStatistics stats = job.getDocStatistics(); - boolean hasSucceeded = job.simulateSedml(masterHdf5File); - this.anySedmlDocumentHasSucceeded |= hasSucceeded; - this.anySedmlDocumentHasFailed &= hasSucceeded; - if (hasSucceeded){ - String formattedStats = stats.toFormattedString(); - logger.info("Processing of SedML succeeded.\n" + formattedStats); - } - else logger.error("Processing of SedML has failed.\n" + stats.toString()); } - BiosimulationsHdf5Writer.writeHdf5(masterHdf5File, new File(this.outputDir)); - - } catch(PythonStreamException e){ - logger.error("Python-processing encountered fatal error. Execution is unable to properly continue.", e); - throw e; - } catch(InterruptedException | IOException e){ + BiosimulationsHdf5Writer.writeHdf5(cumulativeHdf5Results, new File(this.outputDir)); + + } catch (IOException e){ logger.error("System IO encountered a fatal error"); throw new ExecutionException(e); } } + private void executeSedmlDocument(String sedmlLocation, HDF5ExecutionResults cumulativeHdf5Results) throws IOException, PreProcessingException { + BiosimulationLog.instance().updateSedmlDocStatusYml(sedmlLocation, BiosimulationLog.Status.QUEUED); + SedmlJob job = new SedmlJob(sedmlLocation, this.omexHandler, this.inputFile, + this.outputDir, this.sedmlPath2d3d.toString(), this.cliRecorder, + this.bKeepTempFiles, this.bExactMatchOnly, this.bSmallMeshOverride, this.logOmexMessage); + SedmlStatistics stats = job.preProcessDoc(); + boolean hasSucceeded = job.simulateSedml(cumulativeHdf5Results); + this.anySedmlDocumentHasSucceeded |= hasSucceeded; + this.anySedmlDocumentHasFailed &= hasSucceeded; + logger.log(hasSucceeded ? Level.INFO : Level.ERROR, "Processing of SedML ({}) {}", stats.getSedmlName(), hasSucceeded ? "succeeded." : "failed!"); + } + /** * Clean up and analyze the results of the archive's execution - * + *
* Called after: `executeArchive()` - * - * @throws InterruptedException if there is an issue with accessing data - * @throws PythonStreamException if calls to the python-shell instance are not working correctly + * * @throws IOException if there are system I/O issues */ public void postProcessessArchive() throws IOException { @@ -173,7 +162,7 @@ public void postProcessessArchive() throws IOException { this.endTime_ms = System.currentTimeMillis(); long elapsedTime_ms = this.endTime_ms - this.startTime_ms; double duration_s = elapsedTime_ms / 1000.0; - logger.info("Omex " + inputFile.getName() + " processing completed (" + duration_s + "s)"); + logger.info("Omex `" + inputFile.getName() + "` processing completed (" + duration_s + "s)"); // // failure if at least one of the documents in the omex archive fails // @@ -194,13 +183,7 @@ public void postProcessessArchive() throws IOException { } BiosimulationLog.instance().setOutputMessage("null", "null", "omex", logOmexMessage.toString()); - logger.debug("Finished Execution of Archive: " + bioModelBaseName); - } - - private void queueAllSedml() throws PythonStreamException, InterruptedException, IOException { - for (String sedmlLocation: sedmlLocations){ - BiosimulationLog.instance().updateSedmlDocStatusYml(sedmlLocation, BiosimulationLog.Status.QUEUED); - } + if (logger.isDebugEnabled()) logger.info("Finished Execution of Archive: {}", this.bioModelBaseName); } } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/PythonCalls.java b/vcell-cli/src/main/java/org/vcell/cli/run/PythonCalls.java deleted file mode 100644 index 5dfc0afec0..0000000000 --- a/vcell-cli/src/main/java/org/vcell/cli/run/PythonCalls.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.vcell.cli.run; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.vcell.cli.CLIPythonManager; -import org.vcell.cli.PythonStreamException; - -import java.io.IOException; - - -public class PythonCalls { - - private final static Logger logger = LogManager.getLogger(PythonCalls.class); - - public static void genSedmlForSed2DAnd3D(String omexFilePath, String outputDir) throws PythonStreamException { - logger.trace("Dialing Python function genSedml2d3d"); - CLIPythonManager cliPythonManager = CLIPythonManager.getInstance(); - String results = cliPythonManager.callPython("genSedml2d3d", omexFilePath, outputDir); - cliPythonManager.parsePythonReturn(results, "", "Failed generating SED-ML for plot2d and 3D "); - } - - public static void genPlots(String sedmlPath, String resultOutDir) throws PythonStreamException { - logger.trace("Dialing Python function genPlotPdfs"); - CLIPythonManager cliPythonManager = CLIPythonManager.getInstance(); - String results = cliPythonManager.callPython("genPlotPdfs", sedmlPath, resultOutDir); - cliPythonManager.parsePythonReturn(results); - } - - public static void transposeVcmlCsv(String csvFilePath) throws PythonStreamException { - logger.trace("Dialing Python function transposeVcmlCsv"); - CLIPythonManager cliPythonManager = CLIPythonManager.getInstance(); - String results = cliPythonManager.callPython("transposeVcmlCsv", csvFilePath); - cliPythonManager.parsePythonReturn(results); - } - - // Due to what appears to be a leaky python function call, this method will continue using execShellCommand until the underlying python is fixed - public static void genPlotsPseudoSedml(String sedmlPath, String resultOutDir) throws PythonStreamException, InterruptedException, IOException { - logger.trace("Dialing Python function genPlotsPseudoSedml"); - //CLIPythonManager.callNonSharedPython("genPlotsPseudoSedml", sedmlPath, resultOutDir); - CLIPythonManager cliPythonManager = CLIPythonManager.getInstance(); - String results = cliPythonManager.callPython("genPlotsPseudoSedml", sedmlPath, resultOutDir); - cliPythonManager.parsePythonReturn(results); - } - - private static String stripIllegalChars(String s){ - StringBuilder fStr = new StringBuilder(); - for (char c : s.toCharArray()){ - char cAppend = ((int)c) < 16 ? ' ' : c; - if (cAppend == '"') - cAppend = '\''; - fStr.append(cAppend); - } - return fStr.toString(); - } -} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/RunUtils.java b/vcell-cli/src/main/java/org/vcell/cli/run/RunUtils.java index 1e3d73000b..1fa6e16ed2 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/RunUtils.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/RunUtils.java @@ -23,7 +23,6 @@ import org.jlibsedml.*; import org.jlibsedml.execution.IXPathToVariableIDResolver; import org.jlibsedml.modelsupport.SBMLSupport; -import org.vcell.cli.CLIUtils; import org.vcell.sbml.vcell.SBMLNonspatialSimResults; import org.vcell.util.DataAccessException; import org.vcell.util.GenericExtensionFilter; @@ -193,13 +192,13 @@ public static HashMap generateReportsAsCSV(SedML sedml, SolverHand for (Output sedmlOutput : allSedmlOutputs) { // We only want Reports if (!(sedmlOutput instanceof Report sedmlReport)) { - logger.info("Ignoring unsupported output `" + sedmlOutput.getId() + "` while CSV generation."); + if (logger.isDebugEnabled()) logger.info("Ignoring unsupported output `" + sedmlOutput.getId() + "` while CSV generation."); continue; } StringBuilder sb = new StringBuilder(); - logger.info("Generating report `" + sedmlReport.getId() +"`."); + if (logger.isDebugEnabled()) logger.info("Generating report `" + sedmlReport.getId() +"`."); /* * we go through each entry (dataset) in the list of datasets * for each dataset, we use the data reference to obtain the data generator @@ -372,16 +371,16 @@ public static HashMap generateReportsAsCSV(SedML sedml, SolverHand } // end of dataset - if (sb.length() > 0) { + if (!sb.isEmpty()) { File f = new File(outDirForCurrentSedml, sedmlReport.getId() + ".csv"); PrintWriter out = new PrintWriter(f); - out.print(sb.toString()); + out.print(sb); out.flush(); out.close(); - logger.info("created csv file for report " + sedmlReport.getId() + ": " + f.getAbsolutePath()); + if (logger.isDebugEnabled()) logger.info("created csv file for report " + sedmlReport.getId() + ": " + f.getAbsolutePath()); reportsHash.put(sedmlReport.getId(), f); } else { - logger.info("no csv file for report " + sedmlReport.getId()); + if (logger.isDebugEnabled()) logger.info("no csv file for report " + sedmlReport.getId()); } } catch (Exception e) { throw new RuntimeException("CSV generation failed: " + e.getMessage(), e); @@ -443,15 +442,13 @@ public static void zipResFiles(File dirPath) throws IOException { srcFiles = listFilesForFolder(dirPath, ext); if (srcFiles.isEmpty()) { - logger.warn("No " + ext.toUpperCase() + " files found, skipping archiving `" + extensionListMap.get(ext) + "` files"); + if (logger.isDebugEnabled()) logger.warn("No {} files found, skipping archiving `{}` files", ext.toUpperCase(), extensionListMap.get(ext)); } else { fileOutputStream = new FileOutputStream(Paths.get(dirPath.toString(), extensionListMap.get(ext)).toFile()); zipOutputStream = new ZipOutputStream(fileOutputStream); - if (!srcFiles.isEmpty()) logger.info("Archiving resultant " + ext.toUpperCase() + " files to `" + extensionListMap.get(ext) + "`."); + if (!srcFiles.isEmpty() && logger.isDebugEnabled()) logger.info("Archiving resultant {} files to `{}`.", ext.toUpperCase(), extensionListMap.get(ext)); for (File srcFile : srcFiles) { - fileInputstream = new FileInputStream(srcFile); - // get relative path relativePath = dirPath.toURI().relativize(srcFile.toURI()).toString(); zipEntry = new ZipEntry(relativePath); @@ -473,7 +470,7 @@ public static void zipResFiles(File dirPath) throws IOException { public static String getTempDir() throws IOException { String tempPath = String.valueOf(java.nio.file.Files.createTempDirectory( RunUtils.VCELL_TEMP_DIR_PREFIX + UUID.randomUUID()).toAbsolutePath()); - logger.info("TempPath Created: " + tempPath); + logger.trace("TempPath Created: " + tempPath); return tempPath; } @@ -511,75 +508,83 @@ public static boolean removeAndMakeDirs(File f) { return true; } - public static void createCSVFromODEResultSet(ODESolverResultSet resultSet, File f) throws ExpressionException { + public static void createCSVFromODEResultSet(ODESolverResultSet resultSet, File f, boolean shouldTranspose) throws ExpressionException { ColumnDescription[] descriptions = resultSet.getColumnDescriptions(); - StringBuilder sb = new StringBuilder(); - + Map> resultsMapping = new LinkedHashMap<>(); - int numberOfColumns = descriptions.length; - int numberOfRows = resultSet.getRowCount(); - - double[][] dataPoints = new double[numberOfColumns][]; - // Write headers - for (ColumnDescription description : descriptions) { - sb.append(description.getDisplayName()); - sb.append(","); + for (int i = 0; i < descriptions.length; i++){ + resultsMapping.put(descriptions[i].getDisplayName(), Arrays.stream(resultSet.extractColumn(i)).boxed().toList()); } - sb.deleteCharAt(sb.lastIndexOf(",")); - sb.append("\n"); + try (PrintWriter out = new PrintWriter(f)) { + out.print(RunUtils.formatCSVContents(resultsMapping, !shouldTranspose)); + out.flush(); + } catch (FileNotFoundException e) { + logger.error("Unable to find path, failed with err: " + e.getMessage(), e); + } + } - // Write rows - for (int i = 0; i < numberOfColumns; i++) { - dataPoints[i] = resultSet.extractColumn(i); + public static String formatCSVContents(Map> csvContents, boolean organizeDataVertically){ + if (!(csvContents instanceof LinkedHashMap>)) + logger.warn("Warning; using a non-linked hash map will result in random ordering of lines!"); + List> csvLines = new ArrayList<>(); + int numTopics = csvContents.size(); + int maxNumValuesPerTopic = 0; + for (List values : csvContents.values()) + if (values.size() > maxNumValuesPerTopic) + maxNumValuesPerTopic = values.size(); + + // Initialize with empties + for (int i = 0; i < (organizeDataVertically ? maxNumValuesPerTopic + 1 : numTopics); i++){ + csvLines.add(new ArrayList<>()); } - for (int rowNum = 0; rowNum < numberOfRows; rowNum++) { - for (int colNum = 0; colNum < numberOfColumns; colNum++) { - sb.append(dataPoints[colNum][rowNum]); - sb.append(","); + // Fill out lines + List topics = new ArrayList<>(csvContents.keySet()); + if (organizeDataVertically){ + for (String topic : topics) csvLines.get(0).add(topic); + } else { + for (int topicNum = 0; topicNum < topics.size(); topicNum++){ + csvLines.get(topicNum).add(topics.get(topicNum)); } + } + for (int topicNum = 0; topicNum < topics.size(); topicNum++){ + String topic = topics.get(topicNum); + List data = csvContents.get(topic); - sb.deleteCharAt(sb.lastIndexOf(",")); - sb.append("\n"); + for (int i = 0; i < maxNumValuesPerTopic; i++){ + String value = i < data.size() ? data.get(i).toString() : ""; + csvLines.get(organizeDataVertically ? i + 1 : topicNum).add(value); + } } - PrintWriter out = null; - try { - out = new PrintWriter(f); - out.print(sb.toString()); - out.flush(); - } catch (FileNotFoundException e) { - logger.error("Unable to find path, failed with err: " + e.getMessage(), e); - } finally { - if (out != null) out.close(); + // Build CSV + StringBuilder sb = new StringBuilder(); + for (List row : csvLines){ + for (String value : row){ + sb.append(value).append(","); + } + sb.deleteCharAt(sb.lastIndexOf(",")).append("\n"); } + return sb.deleteCharAt(sb.lastIndexOf("\n")).toString(); } + @SuppressWarnings({"ConstantConditions", "ResultOfMethodCallIgnored"}) public static void removeIntermediarySimFiles(File path) { + if (!path.isDirectory()) throw new IllegalArgumentException("Provided path does not lead to a directory!"); File[] files = path.listFiles(); for (File f : files) { - if (f.getName().endsWith(".csv")) { - // Do nothing - continue; - } else { - f.delete(); - } + if (f.getName().endsWith(".csv")) continue; + f.delete(); } } + @SuppressWarnings("ConstantConditions") public static boolean containsExtension(String folder, String ext) { GenericExtensionFilter filter = new GenericExtensionFilter(ext); File dir = new File(folder); - if (dir.isDirectory() == false) { - return false; - } - String[] list = dir.list(filter); - if (list.length > 0) { - return true; - } - return false; + return dir.isDirectory() && dir.list(filter).length > 0; } private static List getListOfVariableNames(DataIdentifier... dataIDArr){ diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/SedmlJob.java b/vcell-cli/src/main/java/org/vcell/cli/run/SedmlJob.java index e76c935a69..9e5032b121 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/SedmlJob.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/SedmlJob.java @@ -8,11 +8,16 @@ import org.apache.logging.log4j.Logger; import org.jlibsedml.*; import org.vcell.cli.CLIRecordable; -import org.vcell.cli.PythonStreamException; import org.vcell.cli.exceptions.ExecutionException; +import org.vcell.cli.exceptions.PreProcessingException; import org.vcell.cli.run.hdf5.HDF5ExecutionResults; import org.vcell.cli.run.hdf5.Hdf5DataContainer; import org.vcell.cli.run.hdf5.Hdf5DataExtractor; +import org.vcell.cli.run.results.NonSpatialValueHolder; +import org.vcell.cli.run.results.NonSpatialResultsConverter; +import org.vcell.cli.run.plotting.PlottingDataExtractor; +import org.vcell.cli.run.plotting.ChartCouldNotBeProducedException; +import org.vcell.cli.run.plotting.Results2DLinePlot; import org.vcell.sedml.log.BiosimulationLog; import org.vcell.trace.Span; import org.vcell.trace.Tracer; @@ -22,12 +27,12 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Class that deals with the processing quest of a sedml file. @@ -36,15 +41,15 @@ public class SedmlJob { private final boolean SHOULD_KEEP_TEMP_FILES, ACCEPT_EXACT_MATCH_ONLY, SHOULD_OVERRIDE_FOR_SMALL_MESH; - private final String SEDML_LOCATION, BIOMODEL_BASE_NAME, RESULTS_DIRECTORY_PATH; + private final String SEDML_NAME, SEDML_LOCATION, BIOMODEL_BASE_NAME, RESULTS_DIRECTORY_PATH; + private final String[] SEDML_NAME_SPLIT; private final StringBuilder LOG_OMEX_MESSAGE; - private final SedmlStatistics DOC_STATISTICS; + private final SedmlStatistics DOC_STATISTICS; // We keep this in object memory for debugging private final File MASTER_OMEX_ARCHIVE, PLOTS_DIRECTORY, OUTPUT_DIRECTORY_FOR_CURRENT_SEDML; private final CLIRecordable CLI_RECORDER; private boolean somethingFailed, hasScans, hasOverrides; - private String logDocumentMessage, logDocumentError, sedmlName; + private String logDocumentMessage, logDocumentError; private SedML sedml; - private File plotFile; private final static Logger logger = LogManager.getLogger(SedmlJob.class); @@ -67,10 +72,15 @@ public SedmlJob(String sedmlLocation, OmexHandler omexHandler, File masterOmexAr String resultsDirPath, String sedmlPath2d3dString, CLIRecordable cliRecorder, boolean bKeepTempFiles, boolean bExactMatchOnly, boolean bSmallMeshOverride, StringBuilder logOmexMessage){ + final String SAFE_WINDOWS_FILE_SEPARATOR = "\\\\"; + final String SAFE_UNIX_FILE_SEPARATOR = "/"; this.MASTER_OMEX_ARCHIVE = masterOmexArchive; this.SEDML_LOCATION = sedmlLocation; this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML = new File(omexHandler.getOutputPathFromSedml(sedmlLocation)); - this.DOC_STATISTICS = new SedmlStatistics(); + this.SEDML_NAME_SPLIT = this.SEDML_LOCATION.split(OperatingSystemInfo.getInstance().isWindows() ? + SAFE_WINDOWS_FILE_SEPARATOR : SAFE_UNIX_FILE_SEPARATOR, -2); + this.SEDML_NAME = this.SEDML_NAME_SPLIT[this.SEDML_NAME_SPLIT.length - 1]; + this.DOC_STATISTICS = new SedmlStatistics(this.SEDML_NAME); this.BIOMODEL_BASE_NAME = FileUtils.getBaseName(masterOmexArchive.getName()); this.RESULTS_DIRECTORY_PATH = resultsDirPath; this.LOG_OMEX_MESSAGE = logOmexMessage; @@ -81,143 +91,92 @@ public SedmlJob(String sedmlLocation, OmexHandler omexHandler, File masterOmexAr this.SHOULD_OVERRIDE_FOR_SMALL_MESH = bSmallMeshOverride; this.somethingFailed = false; - this.plotFile = null; this.logDocumentMessage = "Initializing SED-ML document... "; this.logDocumentError = ""; - } - /** - * Returns an object with variables containing useful information about the sedml document - * @return the statistics of the document - */ - public SedmlStatistics getDocStatistics(){ - return this.DOC_STATISTICS; - } /** * Prepare the SedML model for execution * Called before: `simulateSedml()` - * - * @throws InterruptedException if there is an issue with accessing data - * @throws PythonStreamException if calls to the python-shell instance are not working correctly + * * @throws IOException if there are system I/O issues */ - public boolean preProcessDoc() throws PythonStreamException, InterruptedException, IOException { - final String SAFE_WINDOWS_FILE_SEPARATOR = "\\\\"; - final String SAFE_UNIX_FILE_SEPARATOR = "/"; - logger.info("Initializing SED-ML document..."); + public SedmlStatistics preProcessDoc() throws IOException, PreProcessingException { BiosimulationLog biosimLog = BiosimulationLog.instance(); - Span span = null; - try { - span = Tracer.startSpan(Span.ContextType.PROCESSING_SEDML, "preProcessDoc", null); - SedML sedmlFromOmex, sedmlFromPython; - String[] sedmlNameSplit; - - RunUtils.removeAndMakeDirs(this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML); - - sedmlNameSplit = this.SEDML_LOCATION.split(OperatingSystemInfo.getInstance().isWindows() ? - SAFE_WINDOWS_FILE_SEPARATOR : SAFE_UNIX_FILE_SEPARATOR, -2); - sedmlFromOmex = SedmlJob.getSedMLFile(sedmlNameSplit, this.MASTER_OMEX_ARCHIVE); - this.sedmlName = sedmlNameSplit[sedmlNameSplit.length - 1]; - this.LOG_OMEX_MESSAGE.append("Processing ").append(this.sedmlName).append(". "); - logger.info("Processing SED-ML: " + this.sedmlName); - biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.RUNNING); - - this.DOC_STATISTICS.setNumModels(sedmlFromOmex.getModels().size()); - for(Model m : sedmlFromOmex.getModels()) { - List changes = m.getListOfChanges(); // change attribute caused by a math override - if(!changes.isEmpty()) { //m.getListOfChanges will never return null(?) - this.hasOverrides = true; - } - } - - for(AbstractTask at : sedmlFromOmex.getTasks()) { - if(at instanceof RepeatedTask) { - RepeatedTask rt = (RepeatedTask)at; - List changes = rt.getChanges(); - if(changes != null && !changes.isEmpty()) { - this.hasScans = true; - } - } - } - this.DOC_STATISTICS.setNumTasks(sedmlFromOmex.getTasks().size()); - List outputs = sedmlFromOmex.getOutputs(); - this.DOC_STATISTICS.setNumOutputs(outputs.size()); - for (Output output : outputs) { - if (output instanceof Report) this.DOC_STATISTICS.setReportsCount(this.DOC_STATISTICS.getReportsCount() + 1); - if (output instanceof Plot2D) this.DOC_STATISTICS.setPlots2DCount(this.DOC_STATISTICS.getPlots2DCount() + 1); - if (output instanceof Plot3D) this.DOC_STATISTICS.setPlots3DCount(this.DOC_STATISTICS.getPlots3DCount() + 1); - } - this.DOC_STATISTICS.setNumSimulations(sedmlFromOmex.getSimulations().size()); - String summarySedmlContentString = "Found one SED-ML document with " - + this.DOC_STATISTICS.getNumModels() + " model(s), " - + this.DOC_STATISTICS.getNumSimulations() + " simulation(s), " - + this.DOC_STATISTICS.getNumTasks() + " task(s), " - + this.DOC_STATISTICS.getReportsCount() + " report(s), " - + this.DOC_STATISTICS.getPlots2DCount() + " plot2D(s), and " - + this.DOC_STATISTICS.getPlots3DCount() + " plot3D(s)\n"; - logger.info(summarySedmlContentString); - - this.logDocumentMessage += "done. "; - String str = "Successful translation of SED-ML file"; - this.logDocumentMessage += str + ". "; - logger.info(str + " : " + this.sedmlName); - RunUtils.drawBreakLine("-", 100); + Span span = Tracer.startSpan(Span.ContextType.PROCESSING_SEDML, "preProcessDoc", null); + RunUtils.removeAndMakeDirs(this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML); - // For appending data for SED Plot2D and Plot3D to HDF5 files following a temp convention - logger.info("Creating pseudo SED-ML for HDF5 conversion..."); - this.plotFile = new File(this.PLOTS_DIRECTORY, "simulation_" + this.sedmlName); - Path path = Paths.get(this.plotFile.getAbsolutePath()); - if (!Files.exists(path)){ - // SED-ML file generated by python VCell_cli_util - PythonCalls.genSedmlForSed2DAnd3D(this.MASTER_OMEX_ARCHIVE.getAbsolutePath(), this.RESULTS_DIRECTORY_PATH); - } - if (!Files.exists(path)) { - String message = "Failed to create plot file " + this.plotFile.getAbsolutePath(); - this.CLI_RECORDER.writeDetailedResultList(this.BIOMODEL_BASE_NAME + "," + this.sedmlName + "," + message); - RuntimeException exception = new RuntimeException(message); - Tracer.failure(exception, this.BIOMODEL_BASE_NAME + "," + this.sedmlName + "," + message); - throw exception; - } + this.LOG_OMEX_MESSAGE.append("Processing ").append(this.SEDML_NAME).append(". "); - // Converting pseudo SED-ML to biomodel - logger.info("Creating Biomodel from pseudo SED-ML"); - sedmlFromPython = Libsedml.readDocument(this.plotFile).getSedMLModel(); - - /* If SED-ML has only plots as an output, we will use SED-ML that got generated from vcell_cli_util python code - * As of now, we are going to create a resultant dataSet for Plot output, using their respective data generators */ - - // We need the name and path of the sedml file, which sedmlFromPseudo doesn't have! - - this.sedml = SedmlJob.repairSedML(sedmlFromPython, sedmlNameSplit); - + // Load SedML + logger.info("Initializing and Pre-Processing SedML document: {}", this.SEDML_NAME); + biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.RUNNING); + try { + this.sedml = SedmlJob.getSedMLFile(this.SEDML_NAME_SPLIT, this.MASTER_OMEX_ARCHIVE); } catch (Exception e) { - String prefix = "SED-ML processing for " + this.SEDML_LOCATION + " failed with error: "; - this.logDocumentError = prefix + e.getMessage(); + String prefix = "SedML pre-processing for " + this.SEDML_LOCATION + " failed"; + this.logDocumentError = prefix + ": " + e.getMessage(); Tracer.failure(e, prefix); this.reportProblem(e); - this.somethingFailed = somethingDidFail(); + this.somethingFailed = SedmlJob.somethingDidFail(); biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.FAILED); - return false; - } finally { - if (span != null) { - span.close(); - } + span.close(); + throw new PreProcessingException(prefix, e); } - return true; + + // If we got here, we have a successful load!! + this.logDocumentMessage += "done. "; + String resultString = String.format("Successfully loaded and translated SED-ML file: %s.\n", this.SEDML_NAME); + this.logDocumentMessage += resultString; + + // Generate Doc Statistics + this.DOC_STATISTICS.setNumModels(this.sedml.getModels().size()); + this.DOC_STATISTICS.setNumSimulations(this.sedml.getSimulations().size()); + this.DOC_STATISTICS.setNumTasks(this.sedml.getTasks().size()); + this.DOC_STATISTICS.setNumOutputs(this.sedml.getOutputs().size()); + + for (Output output : this.sedml.getOutputs()) { + if (output instanceof Report) this.DOC_STATISTICS.setReportsCount(this.DOC_STATISTICS.getReportsCount() + 1); + if (output instanceof Plot2D) this.DOC_STATISTICS.setPlots2DCount(this.DOC_STATISTICS.getPlots2DCount() + 1); + if (output instanceof Plot3D) this.DOC_STATISTICS.setPlots3DCount(this.DOC_STATISTICS.getPlots3DCount() + 1); + } + + String summarySedmlContentString = "Found:\n" + + "\t" + this.DOC_STATISTICS.getNumModels() + " model(s)\n" + + "\t" + this.DOC_STATISTICS.getNumSimulations() + " simulation(s)\n" + + "\t" + this.DOC_STATISTICS.getNumTasks() + " task(s)\n" + + "\t" + this.DOC_STATISTICS.getReportsCount() + " report(s)\n" + + "\t" + this.DOC_STATISTICS.getPlots2DCount() + " plot2D(s)\n" + + "\t" + this.DOC_STATISTICS.getPlots3DCount() + " plot3D(s)\n"; + logger.info("{}{}", resultString, summarySedmlContentString); + + // Check for overrides + for(Model m : this.sedml.getModels()) { + if (m.getListOfChanges().isEmpty()) continue; + this.hasOverrides = true; + break; + } + + // Check for parameter scans + for(AbstractTask at : this.sedml.getTasks()) { + if (!(at instanceof RepeatedTask rt)) continue; + List changes = rt.getChanges(); + if(changes == null || changes.isEmpty()) continue; + this.hasScans = true; + } + span.close(); + return this.DOC_STATISTICS; } /** * Prepare the SedML model for execution * Called after: `preProcessDoc()` - * - * @throws InterruptedException if there is an issue with accessing data - * @throws PythonStreamException if calls to the python-shell instance are not working correctly + * * @throws IOException if there are system I/O issues */ - public boolean simulateSedml(HDF5ExecutionResults masterHdf5File) throws InterruptedException, PythonStreamException, IOException { + public boolean simulateSedml(HDF5ExecutionResults masterHdf5File) throws IOException { /* temp code to test plot name correctness String idNamePlotsMap = utils.generateIdNamePlotsMap(sedml, outDirForCurrentSedml); utils.execPlotOutputSedDoc(inputFile, idNamePlotsMap, this.resultsDirPath); @@ -260,6 +219,7 @@ private void runSimulations(SolverHandler solverHandler, ExternalDocInfo externa String str = "Building solvers and starting simulation of all tasks... "; logger.info(str); this.logDocumentMessage += str; + RunUtils.drawBreakLine("-", 100); solverHandler.simulateAllTasks(externalDocInfo, this.sedml, this.CLI_RECORDER, this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML, this.RESULTS_DIRECTORY_PATH, this.SEDML_LOCATION, this.SHOULD_KEEP_TEMP_FILES, @@ -285,7 +245,7 @@ private void runSimulations(SolverHandler solverHandler, ExternalDocInfo externa this.recordRunDetails(solverHandler); } - private void processOutputs(SolverHandler solverHandler, HDF5ExecutionResults masterHdf5File) throws InterruptedException, ExecutionException, PythonStreamException { + private void processOutputs(SolverHandler solverHandler, HDF5ExecutionResults masterHdf5File) throws ExecutionException { // WARNING!!! Current logic dictates that if any task fails we fail the sedml document // change implemented on Nov 11, 2021 // Previous logic was that if at least one task produces some results we declare the sedml document status as successful @@ -294,19 +254,26 @@ private void processOutputs(SolverHandler solverHandler, HDF5ExecutionResults ma if (solverHandler.nonSpatialResults.containsValue(null) || solverHandler.spatialResults.containsValue(null)) { // some tasks failed, but not all this.somethingFailed = somethingDidFail(); this.logDocumentMessage += "Failed to execute one or more tasks. "; - Tracer.failure(new Exception("Failed to execute one or more tasks in " + this.sedmlName), "Failed to execute one or more tasks in " + this.sedmlName); - logger.info("Failed to execute one or more tasks in " + this.sedmlName); + Tracer.failure(new Exception("Failed to execute one or more tasks in " + this.SEDML_NAME), "Failed to execute one or more tasks in " + this.SEDML_NAME); + logger.info("Failed to execute one or more tasks in " + this.SEDML_NAME); } + this.logDocumentMessage += "Generating outputs... "; logger.info("Generating outputs... "); + ///////////////////////////////////////////////////// + Map organizedNonSpatialResults = + NonSpatialResultsConverter.organizeNonSpatialResultsBySedmlDataGenerator( + this.sedml, solverHandler.nonSpatialResults, solverHandler.taskToTempSimulationMap); + boolean hasReports = !this.sedml.getOutputs().stream().filter(Report.class::isInstance).map(Report.class::cast).toList().isEmpty(); + boolean has2DPlots = !this.sedml.getOutputs().stream().filter(Plot2D.class::isInstance).map(Plot2D.class::cast).toList().isEmpty(); if (!solverHandler.nonSpatialResults.isEmpty()) { - this.generateCSV(solverHandler); - this.generatePlots(); + if (hasReports) this.generateCSV(solverHandler); + if (has2DPlots) this.generatePlots(organizedNonSpatialResults); } - this.generateHDF5(solverHandler, masterHdf5File); + this.indexHDF5Data(organizedNonSpatialResults, solverHandler, masterHdf5File); } catch (Exception e) { this.somethingFailed = somethingDidFail(); @@ -322,7 +289,7 @@ private void processOutputs(SolverHandler solverHandler, HDF5ExecutionResults ma } } - private boolean evaluateResults() throws PythonStreamException, InterruptedException, IOException { + private boolean evaluateResults() throws IOException { if (this.somethingFailed) { // something went wrong but no exception was fired Exception e = new RuntimeException("Failure executing the sed document. "); this.logDocumentError += e.getMessage(); @@ -340,15 +307,15 @@ private boolean evaluateResults() throws PythonStreamException, InterruptedExcep //Files.copy(new File(outDirForCurrentSedml,"reports.h5").toPath(),Paths.get(this.resultsDirPath,"reports.h5")); // archiving result files - logger.info("Archiving result files"); + if (logger.isDebugEnabled()) logger.info("Archiving result files"); RunUtils.zipResFiles(new File(this.RESULTS_DIRECTORY_PATH)); org.apache.commons.io.FileUtils.deleteDirectory(this.PLOTS_DIRECTORY); // removing sedml dir which stages results. // Declare success! BiosimulationLog biosimLog = BiosimulationLog.instance(); - biosimLog.setOutputMessage(this.SEDML_LOCATION, this.sedmlName, "sedml", this.logDocumentMessage); + biosimLog.setOutputMessage(this.SEDML_LOCATION, this.SEDML_NAME, "sedml", this.logDocumentMessage); biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.SUCCEEDED); - logger.info("SED-ML : " + this.sedmlName + " successfully completed"); + logger.info("SED-ML : " + this.SEDML_NAME + " successfully completed"); return true; } @@ -377,23 +344,29 @@ private void generateCSV(SolverHandler solverHandler) throws DataAccessException } } - private void generatePlots() throws PythonStreamException, InterruptedException, IOException { + private void generatePlots(Map organizedNonSpatialResults) throws ExecutionException { logger.info("Generating Plots... "); - PythonCalls.genPlotsPseudoSedml(this.SEDML_LOCATION, this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML.toString()); // generate the plots // We assume if no exception is returned that the plots pass - for (Output output : this.sedml.getOutputs()){ - if (!(output instanceof Plot2D plot)) continue; - BiosimulationLog.instance().updatePlotStatusYml(this.SEDML_LOCATION, plot.getId(), BiosimulationLog.Status.SUCCEEDED); + PlottingDataExtractor plotExtractor = new PlottingDataExtractor(this.sedml); + Map plot2Ds = plotExtractor.extractPlotRelevantData(organizedNonSpatialResults); + for (Results2DLinePlot plotToExport : plot2Ds.keySet()){ + try { + plotToExport.generatePng(plot2Ds.get(plotToExport) + ".png", this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML); + plotToExport.generatePdf(plot2Ds.get(plotToExport) + ".pdf", this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML); + } catch (ChartCouldNotBeProducedException e){ + logger.error("Failed creating plot:", e); + throw new ExecutionException("Failed to create plot: " + plotToExport.getTitle(), e); + } } } - private void generateHDF5(SolverHandler solverHandler, HDF5ExecutionResults masterHdf5File) { - this.logDocumentMessage += "Generating HDF5 file... "; - logger.info("Generating HDF5 file... "); + private void indexHDF5Data(Map organizedNonSpatialResults, SolverHandler solverHandler, HDF5ExecutionResults masterHdf5File) { + this.logDocumentMessage += "Indexing HDF5 data... "; + logger.info("Indexing HDF5 data... "); Hdf5DataExtractor hdf5Extractor = new Hdf5DataExtractor(this.sedml, solverHandler.taskToTempSimulationMap); - Hdf5DataContainer partialHdf5File = hdf5Extractor.extractHdf5RelevantData(solverHandler.nonSpatialResults, solverHandler.spatialResults, masterHdf5File.isBioSimHdf5); + Hdf5DataContainer partialHdf5File = hdf5Extractor.extractHdf5RelevantData(organizedNonSpatialResults, solverHandler.spatialResults, masterHdf5File.isBioSimHdf5); masterHdf5File.addResults(this.sedml, partialHdf5File); for (File tempH5File : solverHandler.spatialResults.values()) { @@ -407,27 +380,19 @@ private void generateHDF5(SolverHandler solverHandler, HDF5ExecutionResults mast } // This method is a bit weird; it uses a temp file as a reference to compare against while getting the file straight from the archive. - private static SedML getSedMLFile(String[] tokenizedPath, File inputFile) throws XMLException, IOException { - SedML file = null; - Path convertedPath = SedmlJob.getRelativePath(tokenizedPath); - if (convertedPath == null){ - RuntimeException exception = new RuntimeException("Was not able to get relative path to " + inputFile.getName()); - logger.error(exception); - throw exception; - } + private static SedML getSedMLFile(String[] tokenizedPathToSedml, File inputFile) throws XMLException, IOException { + Path convertedPath = SedmlJob.getRelativePath(tokenizedPathToSedml); + if (convertedPath == null) throw new RuntimeException("Was not able to get relative path to " + inputFile.getName()); String identifyingPath = FilenameUtils.separatorsToUnix(convertedPath.toString()); - FileInputStream omexStream = new FileInputStream(inputFile); - ArchiveComponents omexComponents = Libsedml.readSEDMLArchive(omexStream); - List sedmlDocuments = omexComponents.getSedmlDocuments(); - for (SEDMLDocument doc : sedmlDocuments){ - SedML potentiallyCorrectFile = doc.getSedMLModel(); - if (identifyingPath.equals(potentiallyCorrectFile.getPathForURI() + potentiallyCorrectFile.getFileName())){ - file = potentiallyCorrectFile; - break; + try (FileInputStream omexStream = new FileInputStream(inputFile)) { + for (SEDMLDocument doc : Libsedml.readSEDMLArchive(omexStream).getSedmlDocuments()){ + SedML potentiallyCorrectFile = doc.getSedMLModel(); + String potentiallyCorrectPath = potentiallyCorrectFile.getPathForURI() + potentiallyCorrectFile.getFileName(); + if (!identifyingPath.equals(potentiallyCorrectPath)) continue; + return potentiallyCorrectFile; } } - omexStream.close(); - return file; + throw new PreProcessingException("Unable to find desired SedML within path"); } private static Path getRelativePath(String[] tokenizedPath){ @@ -446,28 +411,12 @@ private static boolean somethingDidFail(){ return true; } - /** - * In its current state, the sed-ml generated with python is missing two important fields; - * this function fixes that. - */ - private static SedML repairSedML(SedML brokenSedML, String[] tokenizedPath){ - Path relativePath = getRelativePath(tokenizedPath); - if (relativePath == null) return null; - String name = relativePath.getFileName().toString(); - brokenSedML.setFileName(name); - // Take the relative path, remove the file name, and... - String source = relativePath.toString().substring(0, relativePath.toString().length() - name.length()); - // Convert to unix file separators (java URI does not do windows style) - brokenSedML.setPathForURI(FilenameUtils.separatorsToUnix(source)); - return brokenSedML; // now fixed! - } - - private void reportProblem(Exception e) throws PythonStreamException, InterruptedException, IOException{ + private void reportProblem(Exception e) throws IOException{ logger.error(e.getMessage(), e); String type = e.getClass().getSimpleName(); BiosimulationLog biosimLog = BiosimulationLog.instance(); - biosimLog.setOutputMessage(this.SEDML_LOCATION, this.sedmlName, "sedml", this.logDocumentMessage); - biosimLog.setExceptionMessage(this.SEDML_LOCATION, this.sedmlName, "sedml", type, this.logDocumentError); + biosimLog.setOutputMessage(this.SEDML_LOCATION, this.SEDML_NAME, "sedml", this.logDocumentMessage); + biosimLog.setExceptionMessage(this.SEDML_LOCATION, this.SEDML_NAME, "sedml", type, this.logDocumentError); this.CLI_RECORDER.writeDetailedErrorList(e, this.BIOMODEL_BASE_NAME + ", doc: " + type + ": " + this.logDocumentError); biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.FAILED); } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/SedmlStatistics.java b/vcell-cli/src/main/java/org/vcell/cli/run/SedmlStatistics.java index cac38ab4ef..ef9d93e91f 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/SedmlStatistics.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/SedmlStatistics.java @@ -14,11 +14,13 @@ * - hasScans :: if the sedml has parameter scans */ public class SedmlStatistics { + private String sedmlName; private Integer nModels, nSimulations, nTasks, nOutputs, nReportsCount, nPlots2DCount, nPlots3DCount; public boolean hasOverrides, hasScans; - public SedmlStatistics(){ + public SedmlStatistics(String sedmlName){ // -1 indicates the value has not been initialized. + this.sedmlName = sedmlName; this.nModels = null; this.nSimulations = null; this.nTasks = null; @@ -30,6 +32,10 @@ public SedmlStatistics(){ this.hasScans = false; } + public String getSedmlName(){ + return this.sedmlName; + } + public int getNumModels(){ return this.nModels == null ? 0 : this.nModels; } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/SolverHandler.java b/vcell-cli/src/main/java/org/vcell/cli/run/SolverHandler.java index ba9fb83167..5cf4a02e89 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/SolverHandler.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/SolverHandler.java @@ -279,10 +279,10 @@ public void initialize(List bioModelList, SedML sedml) throws Expressi taskToChangeTargetMap.put(rt, targetIdSet); } } - System.out.println("taskToSimulationMap: " + this.taskToTempSimulationMap.size()); - System.out.println("taskToListOfSubTasksMap: " + taskToListOfSubTasksMap.size()); - System.out.println("taskToVariableMap: " + taskToVariableMap.size()); - System.out.println("topTaskToBaseTask: " + topTaskToBaseTask.size()); + if (logger.isDebugEnabled()){ + logger.info("Initialization Statistics:\n\t> taskToSimulationMap: {}\n\t> taskToListOfSubTasksMap: {}\n\t> taskToVariableMap: {}\n\t> topTaskToBaseTask: {}\n", + this.taskToTempSimulationMap.size(), this.taskToListOfSubTasksMap.size(), this.taskToVariableMap.size(), this.topTaskToBaseTask.size()); + } } private static class TempSimulationJob extends SimulationJob { @@ -326,7 +326,7 @@ public void simulateAllTasks(ExternalDocInfo externalDocInfo, SedML sedml, CLIRe String docName = null; List tempSims = null; //String outDirRoot = outputDirForSedml.toString().substring(0, outputDirForSedml.toString().lastIndexOf(System.getProperty("file.separator"))); - this.sedmlImporter = new SEDMLImporter(sedmlImportLogger, externalDocInfo, sedml, exactMatchOnly); + this.sedmlImporter = new SEDMLImporter(sedmlImportLogger, externalDocInfo.getFile(), sedml, exactMatchOnly); try { bioModelList = this.sedmlImporter.getBioModels(); } catch (Exception e) { @@ -431,22 +431,24 @@ public void simulateAllTasks(ExternalDocInfo externalDocInfo, SedML sedml, CLIRe } if (solver instanceof AbstractCompiledSolver) { ((AbstractCompiledSolver) solver).runSolver(); - logger.info("Solver: " + solver); - logger.info("Status: " + solver.getSolverStatus()); - if (solver instanceof ODESolver) { - odeSolverResultSet = ((ODESolver) solver).getODESolverResultSet(); - } else if (solver instanceof GibsonSolver) { - odeSolverResultSet = ((GibsonSolver) solver).getStochSolverResultSet(); - } else if (solver instanceof HybridSolver) { - odeSolverResultSet = ((HybridSolver) solver).getHybridSolverResultSet(); + if (logger.isDebugEnabled()){ + logger.info("Solver: " + solver); + logger.info("Status: " + solver.getSolverStatus()); + } + if (solver instanceof ODESolver odeSolver) { + odeSolverResultSet = odeSolver.getODESolverResultSet(); + } else if (solver instanceof GibsonSolver gibsonSolver) { + odeSolverResultSet = gibsonSolver.getStochSolverResultSet(); + } else if (solver instanceof HybridSolver hybridSolver) { + odeSolverResultSet = hybridSolver.getHybridSolverResultSet(); } else { - String str = "Solver results are not compatible with CSV format. "; + String str = "Solver results will not be compatible with CSV format. "; logger.warn(str); // keepTempFiles = true; // temp fix for Jasraj // throw new RuntimeException(str); } - } else if (solver instanceof AbstractJavaSolver) { - ((AbstractJavaSolver) solver).runSolver(); + } else if (solver instanceof AbstractJavaSolver abstractJavaSolver) { + abstractJavaSolver.runSolver(); odeSolverResultSet = ((ODESolver) solver).getODESolverResultSet(); // must interpolate data for uniform time course which is not supported natively by the Java solvers org.jlibsedml.Simulation sedmlSim = sedml.getSimulation(task.getSimulationReference()); @@ -478,8 +480,6 @@ public void simulateAllTasks(ExternalDocInfo externalDocInfo, SedML sedml, CLIRe if (solver.getSolverStatus().getStatus() == SolverStatus.SOLVER_FINISHED) { logTaskMessage += "done. "; - logger.info("Succesful execution: Model '" + docName + "' Task '" + sim.getDescription() + "'."); - long endTimeTask_ms = System.currentTimeMillis(); long elapsedTime_ms = endTimeTask_ms - startTimeTask_ms; int duration_ms = (int) elapsedTime_ms; @@ -489,14 +489,13 @@ public void simulateAllTasks(ExternalDocInfo externalDocInfo, SedML sedml, CLIRe simDuration_ms += duration_ms; simDurationMap_ms.put(originalSim, simDuration_ms); - String msg = "Running simulation " + simTask.getSimulation().getName() + ", " + elapsedTime_ms + " ms"; - logger.info(msg); + logger.info("Successful execution ({}s): Model '{}' Task '{}' ({}).", + ((double)elapsedTime_ms)/1000, docName, sim.getDescription(), simTask.getSimulation().getName()); countSuccessfulSimulationRuns++; // we only count the number of simulations (tasks) that succeeded if (simStatusMap.get(originalSim) != BiosimulationLog.Status.ABORTED && simStatusMap.get(originalSim) != BiosimulationLog.Status.FAILED) { simStatusMap.put(originalSim, BiosimulationLog.Status.SUCCEEDED); } BiosimulationLog.instance().setOutputMessage(sedmlLocation, sim.getImportedTaskID(), "task", logTaskMessage); - RunUtils.drawBreakLine("-", 100); } else { String error = solver.getSolverStatus().getSimulationMessage().getDisplayMessage(); solverStatus = solver.getSolverStatus().getStatus(); @@ -554,14 +553,15 @@ public void simulateAllTasks(ExternalDocInfo externalDocInfo, SedML sedml, CLIRe } else { cliLogger.writeDetailedErrorList(e,bioModelBaseName + ", solver: " + sdl + ": " + type + ": " + logTaskError); } - RunUtils.drawBreakLine("-", 100); } finally { if (sim_span != null) { sim_span.close(); } } + if (sd.isSpatial()) { + logger.info("Processing spatial results of execution..."); File hdf5Results = new File(outDir + System.getProperty("file.separator") + task.getId() + "_job_" + tempSimulationJob.getJobIndex() + "_results.h5"); try { RunUtils.exportPDE2HDF5(tempSimulationJob, outputDirForSedml, hdf5Results); @@ -573,6 +573,7 @@ public void simulateAllTasks(ExternalDocInfo externalDocInfo, SedML sedml, CLIRe spatialResults.put(new TaskJob(task.getId(), tempSimulationJob.getJobIndex()), null); } } else { + logger.info("Processing non-spatial results of execution..."); MathSymbolMapping mathMapping = (MathSymbolMapping) simTask.getSimulation().getMathDescription().getSourceSymbolMapping(); SBMLSymbolMapping sbmlMapping = this.sedmlImporter.getSBMLSymbolMapping(bioModel); @@ -581,9 +582,10 @@ public void simulateAllTasks(ExternalDocInfo externalDocInfo, SedML sedml, CLIRe this.nonSpatialResults.put(taskJob, nonspatialSimResults); } - if (keepTempFiles == false) { + if (!keepTempFiles) { RunUtils.removeIntermediarySimFiles(outputDirForSedml); } + RunUtils.drawBreakLine("-", 100); simulationJobCount++; } for (Map.Entry entry : simStatusMap.entrySet()) { diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/BiosimulationsHdf5Writer.java b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/BiosimulationsHdf5Writer.java index 9bf5c0135f..5febd21a5e 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/BiosimulationsHdf5Writer.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/BiosimulationsHdf5Writer.java @@ -36,7 +36,7 @@ public static void writeHdf5(HDF5ExecutionResults hdf5ExecutionResults, File out boolean didFail = false; // Create and open the Hdf5 file - logger.info("Creating hdf5 file `reports.h5` in {}", outDirForCurrentSedml.getAbsolutePath()); + logger.info("Creating HDF5 file `reports.h5` in {}", outDirForCurrentSedml.getAbsolutePath()); File tempFile = new File(outDirForCurrentSedml, "reports.h5"); try { try (WritableHdfFile hdf5File = HdfFile.write(tempFile.toPath())){ diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataExtractor.java b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataExtractor.java index 1e4e3cebf2..4bfcf7d1e2 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataExtractor.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataExtractor.java @@ -2,13 +2,16 @@ import cbit.vcell.solver.TempSimulation; +import org.jlibsedml.DataGenerator; import org.jlibsedml.Report; import org.jlibsedml.SedML; import org.jlibsedml.AbstractTask; -import org.vcell.sbml.vcell.SBMLNonspatialSimResults; +import org.vcell.cli.run.results.NonSpatialValueHolder; +import org.vcell.cli.run.results.NonSpatialResultsConverter; import org.vcell.cli.run.TaskJob; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.vcell.cli.run.results.SpatialResultsConverter; import java.io.File; import java.nio.file.Paths; @@ -21,9 +24,9 @@ * Factory class to create Hdf5DataWrappers from a sedml object and simulation data. */ public class Hdf5DataExtractor { - private SedML sedml; - private Map taskToSimulationMap; - private String sedmlLocation, sedmlRoot; + private final SedML sedml; + private final Map taskToSimulationMap; + private final String sedmlLocation; private final static Logger logger = LogManager.getLogger(Hdf5DataExtractor.class); @@ -36,30 +39,31 @@ public class Hdf5DataExtractor { public Hdf5DataExtractor(SedML sedml, Map taskToSimulationMap){ this.sedml = sedml; this.taskToSimulationMap = taskToSimulationMap; - this.sedmlRoot = Paths.get(sedml.getPathForURI()).toString(); - this.sedmlLocation = Paths.get(this.sedmlRoot, this.sedml.getFileName()).toString(); + this.sedmlLocation = Paths.get(sedml.getPathForURI(), sedml.getFileName()).toString(); } /** * - * @param nonSpatialResults the nonspatial results set of a sedml execution + * @param organizedNonSpatialResults the nonspatial results set of a sedml execution * @param spatialResults the spatial results set of a sedml execution * @return a wrapper for hdf5 relevant data - * @see NonspatialResultsConverter::convertNonspatialResultsToSedmlFormat - * @see SpatialResultsConverter::collectSpatialDatasets + * @see NonSpatialResultsConverter ::convertNonspatialResultsToSedmlFormat + * @see SpatialResultsConverter ::collectSpatialDatasets */ - public Hdf5DataContainer extractHdf5RelevantData(Map nonSpatialResults, Map spatialResults, boolean isBioSimMode) { + public Hdf5DataContainer extractHdf5RelevantData(Map organizedNonSpatialResults, Map spatialResults, boolean isBioSimMode) { Map> wrappers = new LinkedHashMap<>(); Hdf5DataContainer hdf5FileWrapper = new Hdf5DataContainer(isBioSimMode); Exception nonSpatialException = null, spatialException = null; - try { - Map> nonspatialWrappers = NonspatialResultsConverter.convertNonspatialResultsToSedmlFormat( - this.sedml, nonSpatialResults, this.taskToSimulationMap, this.sedmlLocation); - Hdf5DataExtractor.addWrappers(wrappers, nonspatialWrappers); - } catch (Exception e){ - logger.warn("Collection of nonspatial datasets failed for " + this.sedml.getFileName(), e); - nonSpatialException = e; + if (!organizedNonSpatialResults.isEmpty()){ + try { + Map> nonSpatialWrappers = NonSpatialResultsConverter.prepareNonSpatialDataForHdf5( + this.sedml, organizedNonSpatialResults, this.sedmlLocation); + Hdf5DataExtractor.addWrappers(wrappers, nonSpatialWrappers); + } catch (Exception e){ + logger.warn("Collection of non-spatial datasets failed for " + this.sedml.getFileName(), e); + nonSpatialException = e; + } } try { @@ -77,8 +81,8 @@ public Hdf5DataContainer extractHdf5RelevantData(Map dataMapping = new HashMap<>(); + public Map dataMapping = new HashMap<>(); } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/NonspatialResultsConverter.java b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/NonspatialResultsConverter.java deleted file mode 100644 index a9a0854b18..0000000000 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/NonspatialResultsConverter.java +++ /dev/null @@ -1,311 +0,0 @@ -package org.vcell.cli.run.hdf5; - -import cbit.vcell.parser.Expression; -import cbit.vcell.parser.ExpressionException; -import cbit.vcell.solver.MathOverrides; -import cbit.vcell.solver.Simulation; -import cbit.vcell.solver.TempSimulation; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jlibsedml.*; -import org.jlibsedml.execution.IXPathToVariableIDResolver; -import org.jlibsedml.modelsupport.SBMLSupport; -import org.vcell.cli.run.TaskJob; -import org.vcell.sbml.vcell.SBMLNonspatialSimResults; -import org.vcell.sedml.log.BiosimulationLog; - -import java.io.IOException; -import java.nio.file.Paths; -import java.util.*; -public class NonspatialResultsConverter { - private final static Logger logger = LogManager.getLogger(NonspatialResultsConverter.class); - - - public static Map> convertNonspatialResultsToSedmlFormat(SedML sedml, Map nonspatialResultsHash, Map taskToSimulationMap, String sedmlLocation) throws ExpressionException { - Map> results = new LinkedHashMap<>(); - - for (Report report : NonspatialResultsConverter.getReports(sedml.getOutputs())){ - Map dataSetValues = new LinkedHashMap<>(); - - // go through each entry (dataset) - for (DataSet dataset : report.getListOfDataSets()) { - List varIDs = new ArrayList<>(); - Map resultsByVariable = new HashMap<>(); - int maxLengthOfAllData = 0; // We have to pad up to this value - - // use the data reference to obtain the data generator - DataGenerator datagen = sedml.getDataGeneratorWithId(dataset.getDataReference()); assert datagen != null; - - // get the list of variables associated with the data reference - for (Variable var : datagen.getListOfVariables()) { - // for each variable we recover the task - AbstractTask topLevelTask = sedml.getTaskWithId(var.getReference()); - AbstractTask baseTask = NonspatialResultsConverter.getBaseTask(topLevelTask, sedml); // if !RepeatedTask, baseTask == topLevelTask - - // from the task we get the sbml model - org.jlibsedml.Simulation sedmlSim = sedml.getSimulation(baseTask.getSimulationReference()); - - if (!(sedmlSim instanceof UniformTimeCourse)){ - logger.error("only uniform time course simulations are supported"); - continue; - } - - // must get variable ID from SBML model - String vcellVarId = convertToVCellSymbol(var); - - // If the task isn't in our results hash, it's unwanted and skippable. - boolean bFoundTaskInNonspatial = nonspatialResultsHash.keySet().stream().anyMatch(taskJob -> taskJob.getTaskId().equals(topLevelTask.getId())); - if (!bFoundTaskInNonspatial){ - logger.warn("Was not able to find simulation data for task with ID: " + topLevelTask.getId()); - break; - } - - // ================================================================================== - - ArrayList taskJobs = new ArrayList<>(); - - for (Map.Entry entry : nonspatialResultsHash.entrySet()) { - TaskJob taskJob = entry.getKey(); - if (entry.getValue() != null && taskJob.getTaskId().equals(topLevelTask.getId())) { - taskJobs.add(taskJob); - if (!(topLevelTask instanceof RepeatedTask)) - break; // No need to keep looking if it's not a repeated task - } - } - - if (taskJobs.isEmpty()) continue; - - varIDs.add(var.getId()); - - // we want to keep the last outputNumberOfPoints only - int outputNumberOfPoints = ((UniformTimeCourse) sedmlSim).getNumberOfPoints(); - double outputStartTime = ((UniformTimeCourse) sedmlSim).getOutputStartTime(); - NonspatialValueHolder resultsHolder; - - for (TaskJob taskJob : taskJobs) { - SBMLNonspatialSimResults nonspatialResults = nonspatialResultsHash.get(taskJob); - double[] data = nonspatialResults.getDataForSBMLVar(vcellVarId, outputStartTime, outputNumberOfPoints); - - maxLengthOfAllData = Integer.max(maxLengthOfAllData, data.length); - if (topLevelTask instanceof RepeatedTask && resultsByVariable.containsKey(var)) { // double[] exists - resultsHolder = resultsByVariable.get(var); - } else { // this is the first double[] - resultsHolder = new NonspatialValueHolder(taskToSimulationMap.get(topLevelTask)); - } - resultsHolder.listOfResultSets.add(data); - resultsByVariable.put(var, resultsHolder); - } - } - - - if (resultsByVariable.isEmpty()) continue; - int numJobs, maxLengthOfData = 0; - String exampleReference = resultsByVariable.keySet().iterator().next().getReference(); - NonspatialValueHolder synthesizedResults = new NonspatialValueHolder(taskToSimulationMap.get(sedml.getTaskWithId(exampleReference))); - SimpleDataGenCalculator calc = new SimpleDataGenCalculator(datagen); - - // Get padding value - for (NonspatialValueHolder nvh : resultsByVariable.values()){ - for (double[] dataSet : nvh.listOfResultSets){ - if (dataSet.length > maxLengthOfData){ - maxLengthOfData = dataSet.length; - } - } - } - - // Determine the num of jobs - numJobs = resultsByVariable.values().iterator().next().getNumSets(); - - // Perform the math! - for (int jobNum = 0; jobNum < numJobs; jobNum++){ - double[] synthesizedDataset = new double[maxLengthOfData]; - for (int datumIndex = 0; datumIndex < synthesizedDataset.length; datumIndex++){ - - for (Variable var : resultsByVariable.keySet()){ - //if (processedDataSet == null) processedDataSet = new NonspatialValueHolder(sedml.getTaskWithId(var.getReference())); - if (jobNum >= resultsByVariable.get(var).getNumSets()) continue; - NonspatialValueHolder nonspatialValue = resultsByVariable.get(var); - double[] specficJobDataSet = nonspatialValue.listOfResultSets.get(jobNum); - double datum = datumIndex >= specficJobDataSet.length ? Double.NaN : specficJobDataSet[datumIndex]; - calc.setArgument(var.getId(), datum); - if (!synthesizedResults.vcSimulation.equals(nonspatialValue.vcSimulation)){ - logger.warn("Simulations differ across variables; need to fix data structures to accomodate?"); - } - } - synthesizedDataset[datumIndex] = calc.evaluateWithCurrentArguments(true); - } - synthesizedResults.listOfResultSets.add(synthesizedDataset); - //synthesizedResults.vcSimulation; - } - - dataSetValues.put(dataset, synthesizedResults); - BiosimulationLog.instance().updateDatasetStatusYml(sedmlLocation, report.getId(), dataset.getId(), BiosimulationLog.Status.SUCCEEDED); - } // end of current dataset processing - - if (dataSetValues.isEmpty()) { - logger.warn("We did not get any entries in the final data set. " + - "This may mean a problem has been encountered."); - continue; - } - List shapes = new LinkedList<>(); - Hdf5SedmlResultsNonspatial dataSourceNonspatial = new Hdf5SedmlResultsNonspatial(); - Hdf5SedmlResults hdf5DatasetWrapper = new Hdf5SedmlResults(); - - if (dataSetValues.entrySet().iterator().next().getValue().isEmpty()) continue; // Check if we have data to work with. - - hdf5DatasetWrapper.datasetMetadata._type = NonspatialResultsConverter.getKind(report.getId()); - hdf5DatasetWrapper.datasetMetadata.sedmlId = NonspatialResultsConverter.removeVCellPrefixes(report.getId(), report.getId()); - hdf5DatasetWrapper.datasetMetadata.sedmlName = report.getName(); - hdf5DatasetWrapper.datasetMetadata.uri = Paths.get(sedmlLocation, report.getId()).toString(); - - for (DataSet dataSet : dataSetValues.keySet()){ - NonspatialValueHolder dataSetValuesSource = dataSetValues.get(dataSet); - - dataSourceNonspatial.dataItems.put(report, dataSet, new LinkedList<>()); - dataSourceNonspatial.scanBounds = dataSetValuesSource.vcSimulation.getMathOverrides().getScanBounds(); - dataSourceNonspatial.scanParameterNames = dataSetValuesSource.vcSimulation.getMathOverrides().getScannedConstantNames(); - double[][] scanValues = new double[dataSourceNonspatial.scanBounds.length][]; - for (int nameIndex = 0; nameIndex < dataSourceNonspatial.scanBounds.length; nameIndex++){ - String nameKey = dataSourceNonspatial.scanParameterNames[nameIndex]; - scanValues[nameIndex] = new double[dataSourceNonspatial.scanBounds[nameIndex] + 1]; - for (int scanIndex = 0; scanIndex < dataSourceNonspatial.scanBounds[nameIndex] + 1; scanIndex++){ - Expression overrideExp = dataSetValuesSource.vcSimulation.getMathOverrides().getActualExpression(nameKey, new MathOverrides.ScanIndex(scanIndex)); - try { scanValues[nameIndex][scanIndex] = overrideExp.evaluateConstant(); } - catch (ExpressionException e){ throw new RuntimeException(e); } - } - } - dataSourceNonspatial.scanParameterValues = scanValues; - - for (double[] data : dataSetValuesSource.listOfResultSets) { - dataSourceNonspatial.dataItems.get(report, dataSet).add(data); - shapes.add(Integer.toString(data.length)); - } - - hdf5DatasetWrapper.dataSource = dataSourceNonspatial; // Using upcasting - hdf5DatasetWrapper.datasetMetadata.sedmlDataSetDataTypes.add("float64"); - hdf5DatasetWrapper.datasetMetadata.sedmlDataSetIds.add( - NonspatialResultsConverter.removeVCellPrefixes(dataSet.getId(), hdf5DatasetWrapper.datasetMetadata.sedmlId)); - hdf5DatasetWrapper.datasetMetadata.sedmlDataSetLabels.add(dataSet.getLabel()); - hdf5DatasetWrapper.datasetMetadata.sedmlDataSetNames.add(dataSet.getName()); - hdf5DatasetWrapper.datasetMetadata.sedmlDataSetShapes = shapes; - } - if (!results.containsKey(report)) results.put(report, new LinkedList<>()); - results.get(report).add(hdf5DatasetWrapper); - } // outputs/reports - return results; - } - - private static List getReports(List outputs){ - List reports = new LinkedList<>(); - for (Output out : outputs) { - if (out instanceof Report){ - reports.add((Report)out); - } else { - logger.info("Ignoring unsupported output `" + out.getId() + "` while CSV generation."); - } - } - return reports; - } - - private static AbstractTask getBaseTask(AbstractTask task, SedML sedml){ - while (task instanceof RepeatedTask) { // We need to find the original task burried beneath. - // We assume that we can never have a sequential repeated task at this point, we check for that in SEDMLImporter - SubTask st = ((RepeatedTask)task).getSubTasks().entrySet().iterator().next().getValue(); // single subtask - task = sedml.getTaskWithId(st.getTaskId()); - } - return task; - } - - private static String convertToVCellSymbol(Variable var){ - // must get variable ID from SBML model - String sbmlVarId = ""; - if (var.getSymbol() != null) { // it is a predefined symbol - // search the sbml model to find the vcell variable name associated with the run - switch(var.getSymbol().name()){ - case "TIME": { // TIME is t, etc - sbmlVarId = "t"; // this is VCell reserved symbold for time - break; - } - default:{ - sbmlVarId = var.getSymbol().name(); - } - // etc, TODO: check spec for other symbols (CSymbols?) - // Delay? Avogadro? rateOf? - } - } else { // it is an XPATH target in model - String target = var.getTarget(); - IXPathToVariableIDResolver resolver = new SBMLSupport(); - sbmlVarId = resolver.getIdFromXPathIdentifer(target); - } - return sbmlVarId; - } - - private static class NonspatialValueHolder { - List listOfResultSets = new ArrayList<>(); - final Simulation vcSimulation; - - public NonspatialValueHolder(Simulation simulation) { - this.vcSimulation = simulation; - } - - public int getNumSets() { - return listOfResultSets.size(); - } - - public boolean isEmpty(){ - return listOfResultSets.size() == 0 ? true : false; - } - - /*public int[] getJobCoordinate(int index){ - String[] names = vcSimulation.getMathOverrides().getScannedConstantNames(); - java.util.Arrays.sort(names); // must do things in a consistent way - int[] bounds = new int[names.length]; // bounds of scanning matrix - for (int i = 0; i < names.length; i++){ - bounds[i] = vcSimulation.getMathOverrides().getConstantArraySpec(names[i]).getNumValues() - 1; - } - int[] coordinates = BeanUtils.indexToCoordinate(index, bounds); - return coordinates; - }*/ - } - - - private static String getKind(String prefixedSedmlId){ - String plotPrefix = "__plot__"; - if (prefixedSedmlId.startsWith(plotPrefix)) - return "SedPlot2D"; - return "SedReport"; - } - - /** - * We need the sedmlId to help remove prefixes, but the sedmlId itself may need to be fixed. - * - * If a sedmlId is being checked, just provide itself twice - * - * The reason for this, is having an overload with just "(String s)" as a requirment is misleading. - */ - private static String removeVCellPrefixes(String s, String sedmlId){ - String plotPrefix = "__plot__"; - String reservedPrefix = "__vcell_reserved_data_set_prefix__"; - - String checkedId = sedmlId.startsWith(plotPrefix) ? sedmlId.replace(plotPrefix, "") : sedmlId; - if (sedmlId.equals(s)) return checkedId; - - if (s.startsWith(plotPrefix)){ - s = s.replace(plotPrefix, ""); - } - - if (s.startsWith(reservedPrefix)){ - s = s.replace(reservedPrefix, ""); - } - - if (s.startsWith(checkedId + "_")){ - s = s.replace(checkedId + "_", ""); - } - - if (s.startsWith(checkedId)){ - s = s.replace(checkedId, ""); - } - - return s; - } -} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/plotting/ChartCouldNotBeProducedException.java b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/ChartCouldNotBeProducedException.java new file mode 100644 index 0000000000..64ecddf8dc --- /dev/null +++ b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/ChartCouldNotBeProducedException.java @@ -0,0 +1,19 @@ +package org.vcell.cli.run.plotting; + +public class ChartCouldNotBeProducedException extends RuntimeException { + public ChartCouldNotBeProducedException(){ + super(); + } + + public ChartCouldNotBeProducedException(String message){ + super(message); + } + + public ChartCouldNotBeProducedException(Throwable cause){ + super(cause); + } + + public ChartCouldNotBeProducedException(String message, Throwable cause){ + super(message, cause); + } +} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/plotting/PlottingDataExtractor.java b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/PlottingDataExtractor.java new file mode 100644 index 0000000000..a44a1dd10e --- /dev/null +++ b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/PlottingDataExtractor.java @@ -0,0 +1,86 @@ +package org.vcell.cli.run.plotting; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jlibsedml.*; +import org.vcell.cli.run.results.NonSpatialValueHolder; +import org.vcell.cli.run.results.NonSpatialResultsConverter; +import org.vcell.cli.run.results.SpatialResultsConverter; + +import java.util.*; + +public class PlottingDataExtractor { + private final SedML sedml; + + private final static Logger logger = LogManager.getLogger(PlottingDataExtractor.class); + + /** + * Constructor to initialize the factory for a given set of simulation and model data. + * + * @param sedml the sedml object to get outputs, datasets, and data generators from. + */ + public PlottingDataExtractor(SedML sedml){ + this.sedml = sedml; + } + + /** + * + * @param organizedNonSpatialResults the non-spatial results set of a sedml execution + * @return a wrapper for hdf5 relevant data + * @see NonSpatialResultsConverter ::convertNonspatialResultsToSedmlFormat + * @see SpatialResultsConverter ::collectSpatialDatasets + */ + public Map extractPlotRelevantData(Map organizedNonSpatialResults) { + Map plots = new LinkedHashMap<>(); + Set xAxisNames = new LinkedHashSet<>(); + if (organizedNonSpatialResults.isEmpty()) return plots; + + for (Plot2D requestedPlot : this.sedml.getOutputs().stream().filter(Plot2D.class::isInstance).map(Plot2D.class::cast).toList()){ + Results2DLinePlot plot = new Results2DLinePlot(); + plot.setTitle(requestedPlot.getName()); + + for (Curve curve : requestedPlot.getListOfCurves()){ + NonSpatialValueHolder xResults, yResults; + DataGenerator requestedXGenerator = this.sedml.getDataGeneratorWithId(curve.getXDataReference()); + DataGenerator requestedYGenerator = this.sedml.getDataGeneratorWithId(curve.getYDataReference()); + if (requestedXGenerator == null || requestedYGenerator == null) throw new RuntimeException("Unexpected null returns"); + if (null == (xResults = organizedNonSpatialResults.get(requestedXGenerator))) throw new RuntimeException("Unexpected lack of x-axis results!"); + if (null == (yResults = organizedNonSpatialResults.get(requestedYGenerator))) throw new RuntimeException("Unexpected lack of y-axis results!"); + + // There's two cases: 1 x-axis, n y-axes; or n x-axes, n y-axes. + final boolean hasSingleXSeries = xResults.listOfResultSets.size() == 1; + final boolean hasSingleYSeries = yResults.listOfResultSets.size() == 1; + boolean hasPairsOfSeries = xResults.listOfResultSets.size() == yResults.listOfResultSets.size(); + if (!hasSingleXSeries && !hasPairsOfSeries){ + throw new RuntimeException("Unexpected mismatch between number of x data sets, and y data sets!"); + } + + boolean hasBadXName = requestedXGenerator.getName() == null || "".equals(requestedXGenerator.getName()); + boolean hasBadYName = requestedYGenerator.getName() == null || "".equals(requestedYGenerator.getName()); + String xLabel = hasBadXName ? requestedXGenerator.getId() : requestedXGenerator.getName(); + String yLabel = hasBadYName ? requestedYGenerator.getId() : requestedYGenerator.getName(); + xAxisNames.add(xLabel); + + for (int i = 0; i < yResults.listOfResultSets.size(); i++){ + double[] xDataArray = xResults.listOfResultSets.get(hasSingleXSeries ? 0 : i); + double[] yDataArray = yResults.listOfResultSets.get(i); + List xData = Arrays.stream(xDataArray).boxed().toList(); + List yData = Arrays.stream(yDataArray).boxed().toList(); + + String xSeriesLabel = xLabel + (hasSingleXSeries ? "" : " (" + i + ")"); + String ySeriesLabel = yLabel + (hasSingleYSeries ? "" : " (" + i + ")"); + SingleAxisSeries xSeries = new SingleAxisSeries(xSeriesLabel, xData); + SingleAxisSeries ySeries = new SingleAxisSeries(ySeriesLabel, yData); + plot.addXYData(xSeries, ySeries); + } + } + + plot.setXAxisTitle(String.join("/", xAxisNames)); + boolean hasBadPlotName = requestedPlot.getName() == null || "".equals(requestedPlot.getName()); + String plotFileName = hasBadPlotName ? requestedPlot.getId() : requestedPlot.getName(); + plots.put(plot, plotFileName); + } + + return plots; + } +} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/plotting/Results2DLinePlot.java b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/Results2DLinePlot.java new file mode 100644 index 0000000000..04d413a8ac --- /dev/null +++ b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/Results2DLinePlot.java @@ -0,0 +1,325 @@ +package org.vcell.cli.run.plotting; + +import cbit.vcell.publish.PDFWriter; + +import com.lowagie.text.DocumentException; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.labels.StandardXYItemLabelGenerator; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.xy.XYDataset; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.*; +import java.util.List; + +/** + * Stores all relevant info to create a 2D plot, and lazily builds a PDF on request + */ +public class Results2DLinePlot implements ResultsLinePlot { + + public static final int AXIS_LABEL_FONT_SIZE = 15; + public static final int MAX_SERIES_DATA_POINT_LABELS = 10; + public static final int SERIES_DATA_POINT_LABEL_FONT_SIZE = 8; + + private static final Logger lg = LogManager.getLogger(Results2DLinePlot.class); + + private String plotTitle, xAxisTitle; + private final Map> dataSetMappings; + private final Set xLabels, yLabels; + private int largestSeriesSize; + + public Results2DLinePlot(){ + this("", ""); + } + + public Results2DLinePlot(String plotTitle, String xAxisTitle){ + this.plotTitle = plotTitle; + this.xAxisTitle = xAxisTitle; + this.dataSetMappings = new LinkedHashMap<>(); + this.xLabels = new HashSet<>(); + this.yLabels = new HashSet<>(); + this.largestSeriesSize = 0; + } + + @Override + public void setTitle(String newTitle) { + this.plotTitle = newTitle; + } + + @Override + public String getTitle() { + return this.plotTitle; + } + + public void setXAxisTitle(String newTitle) { + this.xAxisTitle = newTitle; + } + + public String getXAxisTitle() { + return this.xAxisTitle; + } + + public int getLargestSeriesSize(){ + return this.largestSeriesSize; + } + + /** + * Adds an XY pairing to this plot; will not duplicate pairings. + * @param xData the x-axis of data + * @param yData the y-axis of data + * @throws IllegalArgumentException if the length of data on each axis doesn't match + */ + public void addXYData(SingleAxisSeries xData, SingleAxisSeries yData){ + if (xData == null) throw new IllegalArgumentException("Parameter `xData` can not be null!"); + if (yData == null) throw new IllegalArgumentException("Parameter `yData` can not be null!"); + if (xData.data().size() != yData.data().size()) throw new IllegalArgumentException("Data lengths do not match!"); + if (this.xLabels.contains(xData.label()) && !this.dataSetMappings.containsKey(xData)) + throw new IllegalArgumentException("plot already has data for x-axis with the label`" + xData.label() + "` (but it has different values) "); + if (this.yLabels.contains(yData.label())) throw new IllegalArgumentException("plot already has data for y-axis `" + yData.label() + "`"); + if (!this.dataSetMappings.containsKey(xData)) this.dataSetMappings.put(xData, new LinkedHashSet<>()); + this.dataSetMappings.get(xData).add(yData); + this.xLabels.add(xData.label()); + this.yLabels.add(yData.label()); + if (this.largestSeriesSize < xData.data().size()) this.largestSeriesSize = xData.data().size(); + } + + /** + * Replaces the x-axis data associated with a certain label, with a newly provided set. + * @param axisLabel the label of the data you want to change + * @param axisData the data to replace with + * @throws IllegalArgumentException if changing the length of the data would create an illegal pairing. + */ + public void changeXAxisData(String axisLabel, double[] axisData){ + if (axisData == null) throw new IllegalArgumentException("Parameter `axisData` may not be null!"); + this.changeXAxisData(axisLabel, Arrays.stream(axisData).boxed().toList()); + } + + /** + * Replaces the x-axis data associated with a certain label, with a newly provided set. + * @param axisLabel the label of the data you want to change + * @param axisData the data to replace with + * @throws IllegalArgumentException if changing the length of the data would create an illegal pairing. + */ + public void changeXAxisData(String axisLabel, List axisData){ + this.changeXAxisData(new SingleAxisSeries(axisLabel, axisData)); + } + + /** + * Replaces the x-axis data associated with a certain label, with a newly provided set. + * @param axis the axis to replace with; matches by label. + * @throws IllegalArgumentException if changing the length of the data would create an illegal pairing. + */ + public void changeXAxisData(SingleAxisSeries axis){ + SingleAxisSeries axisToChange; + if (null == (axisToChange = this.getXAxisSeries(axis.label()))) throw new IllegalArgumentException("Axis with label `" + axis.label() + "` does not exist!"); + if (axis.data().size() != axisToChange.data().size()) throw new IllegalArgumentException("Data lengths do not match!"); + Set yAxes = this.dataSetMappings.remove(axisToChange); + this.dataSetMappings.put(axis, yAxes); + } + + /** + * Removes an XY paring to this plot, if it exists + *
+ * WARNING: Do not provide this method with freshly constructed series; `SingleAxisSeries` hashes by object_id for efficiency. + *
+ * See {@link SingleAxisSeries} for further details + * @return true if the mapping formerly existed, else false + */ + public boolean removeXYData(SingleAxisSeries xSeries, SingleAxisSeries ySeries){ + if (!this.dataSetMappings.containsKey(xSeries) || !this.dataSetMappings.get(xSeries).contains(ySeries)) return false; + this.dataSetMappings.get(xSeries).remove(ySeries); + if (this.dataSetMappings.get(xSeries).isEmpty()) this.dataSetMappings.remove(xSeries); + this.xLabels.remove(xSeries.label()); + this.xLabels.remove(ySeries.label()); + this.largestSeriesSize = this.findLargestSeriesSize(); + return true; + } + + /** + * Removes an XY paring to this plot, if it exists + *
+ * WARNING: this method must use iteration, and is the medium speed of it's counterparts. + * @param xSeries the x-series to find and remove + * @param yLabel the label of the y-series to find and remove + * @return true if the mapping formerly existed, else false + */ + public boolean removeXYData(SingleAxisSeries xSeries, String yLabel){ + if (yLabel == null) throw new IllegalArgumentException("Parameter `yLabel` can not be null!"); + if (!this.dataSetMappings.containsKey(xSeries)) return false; + for (SingleAxisSeries ySeries : this.dataSetMappings.get(xSeries)){ + if (!yLabel.equals(ySeries.label())) continue; + return this.removeXYData(xSeries, ySeries); + } + return false; + } + + /** + * Removes an XY paring to this plot, if it exists + *
+ * WARNING: this method can not utilize hashing, and is the slowest of its counterparts! + * @param xLabel the label of the x-series to find and remove + * @param yLabel the label of the y-series to find and remove + * @return true if the mapping formerly existed, else false + */ + public boolean removeXYData(String xLabel, String yLabel){ + SingleAxisSeries xSeries; + if (null == (xSeries = this.getXAxisSeries(xLabel))) return false; + return this.removeXYData(xSeries, yLabel); + } + + /** + * Gets the x-axis series with the provided label. + * @param label the x-axis series to find + * @return null if no axis series could be found, else the desired series + */ + public SingleAxisSeries getXAxisSeries(String label){ + for (SingleAxisSeries xSeries : this.dataSetMappings.keySet()){ + if (!xSeries.label().equals(label)) continue; + return xSeries; + } + return null; + } + + /** + * Gets the y-axis series with the provided label. + * @param label the y-axis series to find + * @return null if no axis series could be found, else the desired series + */ + public SingleAxisSeries getYAxisSeries(String label){ + for (SingleAxisSeries xSeries : this.dataSetMappings.keySet()){ + for (SingleAxisSeries ySeries : this.dataSetMappings.get(xSeries)){ + if (!ySeries.label().equals(label)) continue; + return ySeries; + } + } + return null; + } + + public int getNumXSeries(){ + return this.dataSetMappings.keySet().size(); + } + + public int getNumYSeries(){ + int sum = 0; + for (SingleAxisSeries xAxis : this.dataSetMappings.keySet()){ + sum += this.getNumYSeries(xAxis); + } + return sum; + } + + public int getNumYSeries(SingleAxisSeries xData){ + if (!this.dataSetMappings.containsKey(xData)) return 0; + return this.dataSetMappings.get(xData).size(); + } + + public void generatePng(String desiredFileName, File desiredParentDirectory) throws ChartCouldNotBeProducedException { + JFreeChart chart = this.createChart(); + PageFormat pageFormat = Results2DLinePlot.generateAlternatePageFormat(); + + File testfile = new File(desiredParentDirectory, desiredFileName); + try { + if (testfile.exists() && !testfile.isFile()) throw new IllegalArgumentException("desired PDF already exists, and is not a regular file"); + if (!testfile.exists() && !testfile.createNewFile()) throw new IllegalArgumentException("Unable to create desired PDF; creation itself failed."); + BufferedImage bfi = chart.createBufferedImage((int) pageFormat.getImageableWidth(), (int) pageFormat.getImageableHeight()); + ImageIO.write(bfi, "png", testfile); + } catch (IOException e){ + lg.error("Error while preparing PNG; see exception below"); + throw new ChartCouldNotBeProducedException(e); + } + } + + + @Override + public void generatePdf(String desiredFileName, File desiredParentDirectory) throws ChartCouldNotBeProducedException { + JFreeChart chart = this.createChart(); + + // Prepare for export + PDFWriter pdfWriter = new PDFWriter(); + PageFormat pageFormat = Results2DLinePlot.generateAlternatePageFormat(); + + File testfile = new File(desiredParentDirectory, desiredFileName); + try { + if (testfile.exists() && !testfile.isFile()) throw new IllegalArgumentException("desired PDF already exists, and is not a regular file"); + if (!testfile.exists() && !testfile.createNewFile()) throw new IllegalArgumentException("Unable to create desired PDF; creation itself failed."); + try (FileOutputStream fos = new FileOutputStream(testfile)) { + BufferedImage bfi = chart.createBufferedImage((int) pageFormat.getImageableWidth(), (int) pageFormat.getImageableHeight()); + pdfWriter.writePlotImageDocument("Test Document", fos, pageFormat, bfi); + } catch (DocumentException e) { + lg.error("Error while building PDF; see exception below"); + throw new ChartCouldNotBeProducedException(e); + } + } catch (IOException e){ + lg.error("Error while preparing PDF; see exception below"); + throw new ChartCouldNotBeProducedException(e); + } + } + + private XYDataset generateChartLibraryDataset(){ + XYSeriesCollection dataset2D = new XYSeriesCollection(); + for (SingleAxisSeries xAxis : this.dataSetMappings.keySet()){ + for (SingleAxisSeries yAxis : this.dataSetMappings.get(xAxis)){ + XYSeries xySeries = new XYSeries(yAxis.label(), true, false); + for (int i = 0; i < yAxis.data().size(); i++){ + xySeries.add(xAxis.data().get(i), yAxis.data().get(i)); + } + dataset2D.addSeries(xySeries); + } + } + return dataset2D; + } + + private JFreeChart createChart(){ + String yAxisLabel = ""; + XYDataset dataset2D = this.generateChartLibraryDataset(); + JFreeChart chart = ChartFactory.createXYLineChart(this.plotTitle, this.xAxisTitle, yAxisLabel, dataset2D); + + // Tweak Chart so it looks better + chart.setBorderVisible(true); + chart.getPlot().setBackgroundPaint(Color.white); + XYPlot chartPlot = chart.getXYPlot(); + chartPlot.getDomainAxis().setLabelFont(new Font(this.xAxisTitle, Font.PLAIN, Results2DLinePlot.AXIS_LABEL_FONT_SIZE)); + chartPlot.getRangeAxis().setLabelFont(new Font(yAxisLabel, Font.PLAIN, Results2DLinePlot.AXIS_LABEL_FONT_SIZE)); + if (this.largestSeriesSize <= Results2DLinePlot.MAX_SERIES_DATA_POINT_LABELS) { // if it's too crowded, having data point labels is bad + for (int i = 0; i < dataset2D.getSeriesCount(); i++) { + //DecimalFormat decimalformat1 = new DecimalFormat("##"); // Should we ever need it, this object is used in formating the labels. + chartPlot.getRenderer().setSeriesItemLabelGenerator(i, new StandardXYItemLabelGenerator("({1}, {2})")); + chartPlot.getRenderer().setSeriesItemLabelFont(i, new Font(null, Font.PLAIN, Results2DLinePlot.SERIES_DATA_POINT_LABEL_FONT_SIZE)); + chartPlot.getRenderer().setSeriesItemLabelsVisible(i, true); + } + } + return chart; + } + + private int findLargestSeriesSize(){ + int newMax = 0; + for (SingleAxisSeries xAxis : this.dataSetMappings.keySet()){ + if (newMax < xAxis.data().size()) newMax = xAxis.data().size(); + } + return newMax; + } + + private static PageFormat generateAlternatePageFormat(){ + java.awt.print.PageFormat pageFormat = java.awt.print.PrinterJob.getPrinterJob().defaultPage(); + Paper alternatePaper = new Paper(); // We want to try and increase the margins + double altOriginX = alternatePaper.getImageableX() / 2, altOriginY = alternatePaper.getImageableY() / 2; + double altWidth = alternatePaper.getWidth() - 2 * altOriginX, altHeight = alternatePaper.getHeight() - 2 * altOriginY; + alternatePaper.setImageableArea(altOriginX, altOriginY, altWidth, altHeight); + pageFormat.setPaper(alternatePaper); + pageFormat.setOrientation(PageFormat.LANDSCAPE); + return pageFormat; + } +} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/plotting/ResultsLinePlot.java b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/ResultsLinePlot.java new file mode 100644 index 0000000000..ccd562d38d --- /dev/null +++ b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/ResultsLinePlot.java @@ -0,0 +1,29 @@ +package org.vcell.cli.run.plotting; + +import java.io.File; + +/** + * Describes the basic functionality all LinePlots displaying simulation results should have. + */ +public interface ResultsLinePlot { + + /** + * Generates a PDF copy of the current plot state, and exports it as PDF at the desired location. + * @param desiredFileName name of the plot file; needs no file suffix: ".pdf" will be appended. + * @param desiredParentDirectory directory to place the new pdf into. + */ + void generatePdf(String desiredFileName, File desiredParentDirectory); + + /** + * Setter for the title of the plot + * @param newTitle the desired new title as a String + */ + void setTitle(String newTitle); + + /** + * Getter for the title of the plot + * @return the title as a String + */ + String getTitle(); + +} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/plotting/SingleAxisSeries.java b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/SingleAxisSeries.java new file mode 100644 index 0000000000..392ba0643a --- /dev/null +++ b/vcell-cli/src/main/java/org/vcell/cli/run/plotting/SingleAxisSeries.java @@ -0,0 +1,68 @@ +package org.vcell.cli.run.plotting; + +import java.util.List; + +/** + * "Record" class for storing label and data combos + *
+ * NOTE: This class can not realistically use `double[]`, because there is no direct immutable array type in java + * (except direct initializer; not useful at runtime). So we instead use UnmodifiableCollections. + * But that means extra-legwork making this happen. + */ +public class SingleAxisSeries { + final String immutableLabel; + final List immutableData; + final String STRING_REPRESENTATION; + + /** + * Record class for storing label and data combos + *
+ * NOTE: This class can not realistically use `double[]`, because there is no direct immutable array type in java + * (except direct initializer; not useful at runtime). So we instead use UnmodifiableCollections. + * But that means extra-legwork making this happen. + * @param label the label to assign to the data in a plot + * @param data the ***IMMUTABLE COLLECTION*** of data to be represented. + */ + public SingleAxisSeries(String label, List data) { + if (label == null) throw new IllegalArgumentException("Argument for `label` can not be null!"); + if (data == null) throw new IllegalArgumentException("Argument for `data` can not be null!"); + // We need to verify the data provided was provided as immutable! + try { + data.add(0.0); + throw new IllegalArgumentException("Provided data list can *not* be mutable. Use `Collections.unmodifiableList(List.copyOf())` on the data!"); + } catch (UnsupportedOperationException ignored) { + // Perfect! this is what we want! + } + this.immutableLabel = label; // Strings are one of the few objects classified as pass by value + this.immutableData = data; + StringBuilder stringRep = new StringBuilder(); + stringRep.append(label).append("(length:").append(data.size()).append(")::["); + for (Double d : data) stringRep.append(d).append(","); + this.STRING_REPRESENTATION = stringRep.deleteCharAt(stringRep.length() - 1).append(']').toString(); + } + + public String label(){ + return this.immutableLabel; + } + + public List data(){ + return this.immutableData; + } + + @Override + public String toString(){ + return this.STRING_REPRESENTATION; + } + + @Override + public int hashCode(){ + // By adding the length of the data in the StringRepresentation, no "label" could emulate the data, and thus this is safe. + return this.STRING_REPRESENTATION.hashCode(); + } + + @Override + public boolean equals(Object other){ + if (!(other instanceof SingleAxisSeries sas)) return false; + return this.STRING_REPRESENTATION.equals(sas.STRING_REPRESENTATION); + } +} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/NonspatialDataMapping.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialDataMapping.java similarity index 94% rename from vcell-cli/src/main/java/org/vcell/cli/run/hdf5/NonspatialDataMapping.java rename to vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialDataMapping.java index 4c774ae643..4ef34b01f9 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/NonspatialDataMapping.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialDataMapping.java @@ -1,4 +1,4 @@ -package org.vcell.cli.run.hdf5; +package org.vcell.cli.run.results; import org.jlibsedml.DataSet; import org.jlibsedml.Report; @@ -7,10 +7,10 @@ import java.util.List; import java.util.Map; -public class NonspatialDataMapping { +public class NonSpatialDataMapping { private final Map>> dataMapping; - public NonspatialDataMapping(){ + public NonSpatialDataMapping(){ this.dataMapping = new LinkedHashMap<>(); } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialResultsConverter.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialResultsConverter.java new file mode 100644 index 0000000000..bd8b5bb17c --- /dev/null +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialResultsConverter.java @@ -0,0 +1,312 @@ +package org.vcell.cli.run.results; + +import cbit.vcell.parser.Expression; +import cbit.vcell.parser.ExpressionException; +import cbit.vcell.solver.MathOverrides; +import cbit.vcell.solver.TempSimulation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jlibsedml.*; +import org.jlibsedml.execution.IXPathToVariableIDResolver; +import org.jlibsedml.modelsupport.SBMLSupport; +import org.vcell.cli.run.TaskJob; +import org.vcell.cli.run.hdf5.Hdf5SedmlResults; +import org.vcell.cli.run.hdf5.Hdf5SedmlResultsNonspatial; +import org.vcell.sbml.vcell.SBMLNonspatialSimResults; +import org.vcell.sedml.log.BiosimulationLog; + +import java.nio.file.Paths; +import java.util.*; +public class NonSpatialResultsConverter { + private final static Logger logger = LogManager.getLogger(NonSpatialResultsConverter.class); + + public static Map organizeNonSpatialResultsBySedmlDataGenerator(SedML sedml, Map nonSpatialResultsHash, Map taskToSimulationMap) throws ExpressionException { + Map nonSpatialOrganizedResultsMap = new HashMap<>(); + if (nonSpatialResultsHash.isEmpty()) return nonSpatialOrganizedResultsMap; + + for (Output output : NonSpatialResultsConverter.getValidOutputs(sedml)){ + Set dataGeneratorsToProcess; + if (output instanceof Report report){ + dataGeneratorsToProcess = new LinkedHashSet<>(); + for (DataSet dataSet : report.getListOfDataSets()){ + // use the data reference to obtain the data generator + dataGeneratorsToProcess.add(sedml.getDataGeneratorWithId(dataSet.getDataReference())); + BiosimulationLog.instance().updateDatasetStatusYml(Paths.get(sedml.getPathForURI(), sedml.getFileName()).toString(), output.getId(), dataSet.getId(), BiosimulationLog.Status.SUCCEEDED); + } + } + else if (output instanceof Plot2D plot2D){ + Set uniqueDataGens = new LinkedHashSet<>(); + for (Curve curve : plot2D.getListOfCurves()){ + uniqueDataGens.add(sedml.getDataGeneratorWithId(curve.getXDataReference())); + uniqueDataGens.add(sedml.getDataGeneratorWithId(curve.getYDataReference())); + } + dataGeneratorsToProcess = uniqueDataGens; + } else { + if (logger.isDebugEnabled()) logger.warn("Unrecognized output type: `{}` (id={})", output.getClass().getName(), output.getId()); + continue; + } + + for (DataGenerator dataGen : dataGeneratorsToProcess) { + NonSpatialValueHolder valueHolder = NonSpatialResultsConverter.getNonSpatialValueHolderForDataGenerator(sedml, dataGen, nonSpatialResultsHash, taskToSimulationMap); + if (valueHolder == null) continue; + nonSpatialOrganizedResultsMap.put(dataGen, valueHolder); + } + } + + return nonSpatialOrganizedResultsMap; + } + + + public static Map> prepareNonSpatialDataForHdf5(SedML sedml, Map organizedNonSpatialResults, String sedmlLocation) { + Map> results = new LinkedHashMap<>(); + + // Just get the Reports from the outputs, and nothing else + for (Report report : sedml.getOutputs().stream().filter(Report.class::isInstance).map(Report.class::cast).toList()){ + Map dataSetValues = new LinkedHashMap<>(); + + for (DataSet dataset : report.getListOfDataSets()) { + // use the data reference to obtain the data generator + DataGenerator dataGen = sedml.getDataGeneratorWithId(dataset.getDataReference()); + if (dataGen == null || !organizedNonSpatialResults.containsKey(dataGen)) + throw new RuntimeException("No data for Data Generator `" + dataset.getDataReference() + "` can be found!"); + NonSpatialValueHolder value = organizedNonSpatialResults.get(dataGen); + dataSetValues.put(dataset, value); + } // end of current dataset processing + + if (dataSetValues.isEmpty()) { + logger.warn("We did not get any entries in the final data set. This may mean a problem has been encountered."); + continue; + } + + List shapes = new LinkedList<>(); + Hdf5SedmlResultsNonspatial dataSourceNonSpatial = new Hdf5SedmlResultsNonspatial(); + Hdf5SedmlResults hdf5DatasetWrapper = new Hdf5SedmlResults(); + + if (dataSetValues.entrySet().iterator().next().getValue().isEmpty()) continue; // Check if we have data to work with. + + hdf5DatasetWrapper.datasetMetadata._type = NonSpatialResultsConverter.getKind(report.getId()); + hdf5DatasetWrapper.datasetMetadata.sedmlId = NonSpatialResultsConverter.removeVCellPrefixes(report.getId(), report.getId()); + hdf5DatasetWrapper.datasetMetadata.sedmlName = report.getName(); + hdf5DatasetWrapper.datasetMetadata.uri = Paths.get(sedmlLocation, report.getId()).toString(); + + for (DataSet dataSet : dataSetValues.keySet()){ + NonSpatialValueHolder dataSetValuesSource = dataSetValues.get(dataSet); + + dataSourceNonSpatial.dataItems.put(report, dataSet, new LinkedList<>()); + dataSourceNonSpatial.scanBounds = dataSetValuesSource.vcSimulation.getMathOverrides().getScanBounds(); + dataSourceNonSpatial.scanParameterNames = dataSetValuesSource.vcSimulation.getMathOverrides().getScannedConstantNames(); + double[][] scanValues = new double[dataSourceNonSpatial.scanBounds.length][]; + for (int nameIndex = 0; nameIndex < dataSourceNonSpatial.scanBounds.length; nameIndex++){ + String nameKey = dataSourceNonSpatial.scanParameterNames[nameIndex]; + scanValues[nameIndex] = new double[dataSourceNonSpatial.scanBounds[nameIndex] + 1]; + for (int scanIndex = 0; scanIndex < dataSourceNonSpatial.scanBounds[nameIndex] + 1; scanIndex++){ + Expression overrideExp = dataSetValuesSource.vcSimulation.getMathOverrides().getActualExpression(nameKey, new MathOverrides.ScanIndex(scanIndex)); + try { scanValues[nameIndex][scanIndex] = overrideExp.evaluateConstant(); } + catch (ExpressionException e){ throw new RuntimeException(e); } + } + } + dataSourceNonSpatial.scanParameterValues = scanValues; + + for (double[] data : dataSetValuesSource.listOfResultSets) { + dataSourceNonSpatial.dataItems.get(report, dataSet).add(data); + shapes.add(Integer.toString(data.length)); + } + + hdf5DatasetWrapper.dataSource = dataSourceNonSpatial; // Using upcasting + hdf5DatasetWrapper.datasetMetadata.sedmlDataSetDataTypes.add("float64"); + hdf5DatasetWrapper.datasetMetadata.sedmlDataSetIds.add( + NonSpatialResultsConverter.removeVCellPrefixes(dataSet.getId(), hdf5DatasetWrapper.datasetMetadata.sedmlId)); + hdf5DatasetWrapper.datasetMetadata.sedmlDataSetLabels.add(dataSet.getLabel()); + hdf5DatasetWrapper.datasetMetadata.sedmlDataSetNames.add(dataSet.getName()); + hdf5DatasetWrapper.datasetMetadata.sedmlDataSetShapes = shapes; + } + if (!results.containsKey(report)) results.put(report, new LinkedList<>()); + results.get(report).add(hdf5DatasetWrapper); + } // outputs/reports + return results; + } + + private static NonSpatialValueHolder getNonSpatialValueHolderForDataGenerator(SedML sedml, DataGenerator dataGen, Map nonSpatialResultsHash, Map taskToSimulationMap) throws ExpressionException { + if (dataGen == null) throw new IllegalArgumentException("Provided Data Generator can not be null!"); + Map resultsByVariable = new HashMap<>(); + + // get the list of variables associated with the data reference + for (Variable var : dataGen.getListOfVariables()) { + // for each variable we recover the task + AbstractTask topLevelTask = sedml.getTaskWithId(var.getReference()); + AbstractTask baseTask = NonSpatialResultsConverter.getBaseTask(topLevelTask, sedml); // if !RepeatedTask, baseTask == topLevelTask + + // from the task we get the sbml model + org.jlibsedml.Simulation sedmlSim = sedml.getSimulation(baseTask.getSimulationReference()); + + if (!(sedmlSim instanceof UniformTimeCourse utcSim)){ + logger.error("only uniform time course simulations are supported"); + continue; + } + + // must get variable ID from SBML model + String vcellVarId = convertToVCellSymbol(var); + + // If the task isn't in our results hash, it's unwanted and skippable. + boolean bFoundTaskInNonspatial = nonSpatialResultsHash.keySet().stream().anyMatch(taskJob -> taskJob.getTaskId().equals(topLevelTask.getId())); + if (!bFoundTaskInNonspatial){ + logger.warn("Was not able to find simulation data for task with ID: {}", topLevelTask.getId()); + break; + } + + // ================================================================================== + + ArrayList taskJobs = new ArrayList<>(); + + // We can have multiple TaskJobs sharing IDs (in the case of repeated tasks), so we need to get them all + for (TaskJob taskJob : nonSpatialResultsHash.keySet()) { + SBMLNonspatialSimResults simResults = nonSpatialResultsHash.get(taskJob); + if (simResults == null || !taskJob.getTaskId().equals(topLevelTask.getId())) continue; + taskJobs.add(taskJob); + if (!(topLevelTask instanceof RepeatedTask)) break; // No need to keep looking if it's not a repeated task, one "loop" is good + } + + if (taskJobs.isEmpty()) continue; + + boolean resultsAlreadyExist = topLevelTask instanceof RepeatedTask && resultsByVariable.containsKey(var); + NonSpatialValueHolder individualVarResultsHolder = resultsAlreadyExist ? resultsByVariable.get(var) : + new NonSpatialValueHolder(taskToSimulationMap.get(topLevelTask)); + for (TaskJob taskJob : taskJobs) { + // Leaving intermediate variables for debugging access + SBMLNonspatialSimResults nonSpatialResults = nonSpatialResultsHash.get(taskJob); + double[] data = nonSpatialResults.getDataForSBMLVar(vcellVarId, utcSim.getOutputStartTime(), utcSim.getNumberOfPoints()); + individualVarResultsHolder.listOfResultSets.add(data); + } + resultsByVariable.put(var, individualVarResultsHolder); + } + if (resultsByVariable.isEmpty()) return null; + + // We now need to condense the multiple variables into a single resolved value + + String exampleReference = resultsByVariable.keySet().iterator().next().getReference(); + int numJobs = resultsByVariable.values().iterator().next().listOfResultSets.size(); + NonSpatialValueHolder synthesizedResults = new NonSpatialValueHolder(taskToSimulationMap.get(sedml.getTaskWithId(exampleReference))); + SimpleDataGenCalculator calc = new SimpleDataGenCalculator(dataGen); + + // Get padding value + int maxLengthOfData = 0; + for (NonSpatialValueHolder nvh : resultsByVariable.values()){ + for (double[] dataSet : nvh.listOfResultSets){ + if (dataSet.length <= maxLengthOfData) continue; + maxLengthOfData = dataSet.length; + } + } + + // Perform the math! + for (int jobNum = 0; jobNum < numJobs; jobNum++){ + double[] synthesizedDataset = new double[maxLengthOfData]; + for (int datumIndex = 0; datumIndex < synthesizedDataset.length; datumIndex++){ + + for (Variable var : resultsByVariable.keySet()){ + //if (processedDataSet == null) processedDataSet = new NonspatialValueHolder(sedml.getTaskWithId(var.getReference())); + if (jobNum >= resultsByVariable.get(var).listOfResultSets.size()) continue; + NonSpatialValueHolder nonspatialValue = resultsByVariable.get(var); + double[] specficJobDataSet = nonspatialValue.listOfResultSets.get(jobNum); + double datum = datumIndex >= specficJobDataSet.length ? Double.NaN : specficJobDataSet[datumIndex]; + calc.setArgument(var.getId(), datum); + if (!synthesizedResults.vcSimulation.equals(nonspatialValue.vcSimulation)){ + logger.warn("Simulations differ across variables; need to fix data structures to accomodate?"); + } + } + synthesizedDataset[datumIndex] = calc.evaluateWithCurrentArguments(true); + } + synthesizedResults.listOfResultSets.add(synthesizedDataset); + } + return synthesizedResults; + } + + private static List getValidOutputs(SedML sedml){ + List nonPlot3DOutputs = new ArrayList<>(); + List plot3DOutputs = new ArrayList<>(); + for (Output output : sedml.getOutputs()){ + if (output instanceof Plot3D plot3D) plot3DOutputs.add(plot3D); + else nonPlot3DOutputs.add(output); + } + + if (!plot3DOutputs.isEmpty()) logger.warn("VCell currently does not support creation of 3D plots, {} plot{} will be skipped.", + plot3DOutputs.size(), plot3DOutputs.size() == 1 ? "" : "s"); + return nonPlot3DOutputs; + + } + + private static AbstractTask getBaseTask(AbstractTask task, SedML sedml){ + if (task == null) throw new IllegalArgumentException("task arguement is `null`!"); + while (task instanceof RepeatedTask repeatedTask) { // We need to find the original task burried beneath. + // We assume that we can never have a sequential repeated task at this point, we check for that in SEDMLImporter + SubTask st = repeatedTask.getSubTasks().entrySet().iterator().next().getValue(); // single subtask + task = sedml.getTaskWithId(st.getTaskId()); + if (task == null) throw new IllegalArgumentException("Bad SedML formatting; task with id " + st.getTaskId() +" not found"); + } + return task; + } + + private static String convertToVCellSymbol(Variable var){ + // must get variable ID from SBML model + String sbmlVarId = ""; + if (var.getSymbol() != null) { // it is a predefined symbol + // search the sbml model to find the vcell variable name associated with the run + switch(var.getSymbol().name()){ + case "TIME": { // TIME is t, etc + sbmlVarId = "t"; // this is VCell reserved symbold for time + break; + } + default:{ + sbmlVarId = var.getSymbol().name(); + } + // etc, TODO: check spec for other symbols (CSymbols?) + // Delay? Avogadro? rateOf? + } + } else { // it is an XPATH target in model + String target = var.getTarget(); + IXPathToVariableIDResolver resolver = new SBMLSupport(); + sbmlVarId = resolver.getIdFromXPathIdentifer(target); + } + return sbmlVarId; + } + + + private static String getKind(String prefixedSedmlId){ + String plotPrefix = "__plot__"; + if (prefixedSedmlId.startsWith(plotPrefix)) + return "SedPlot2D"; + return "SedReport"; + } + + /** + * We need the sedmlId to help remove prefixes, but the sedmlId itself may need to be fixed. + * + * If a sedmlId is being checked, just provide itself twice + * + * The reason for this, is having an overload with just "(String s)" as a requirment is misleading. + */ + private static String removeVCellPrefixes(String s, String sedmlId){ + String plotPrefix = "__plot__"; + String reservedPrefix = "__vcell_reserved_data_set_prefix__"; + + String checkedId = sedmlId.startsWith(plotPrefix) ? sedmlId.replace(plotPrefix, "") : sedmlId; + if (sedmlId.equals(s)) return checkedId; + + if (s.startsWith(plotPrefix)){ + s = s.replace(plotPrefix, ""); + } + + if (s.startsWith(reservedPrefix)){ + s = s.replace(reservedPrefix, ""); + } + + if (s.startsWith(checkedId + "_")){ + s = s.replace(checkedId + "_", ""); + } + + if (s.startsWith(checkedId)){ + s = s.replace(checkedId, ""); + } + + return s; + } +} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialValueHolder.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialValueHolder.java new file mode 100644 index 0000000000..acf869c639 --- /dev/null +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/NonSpatialValueHolder.java @@ -0,0 +1,30 @@ +package org.vcell.cli.run.results; + +import cbit.vcell.solver.Simulation; + +import java.util.ArrayList; +import java.util.List; + +public class NonSpatialValueHolder { + public final List listOfResultSets = new ArrayList<>(); + final Simulation vcSimulation; + + public NonSpatialValueHolder(Simulation simulation) { + this.vcSimulation = simulation; + } + + public boolean isEmpty() { + return listOfResultSets.isEmpty(); + } + + /*public int[] getJobCoordinate(int index){ + String[] names = vcSimulation.getMathOverrides().getScannedConstantNames(); + java.util.Arrays.sort(names); // must do things in a consistent way + int[] bounds = new int[names.length]; // bounds of scanning matrix + for (int i = 0; i < names.length; i++){ + bounds[i] = vcSimulation.getMathOverrides().getConstantArraySpec(names[i]).getNumValues() - 1; + } + int[] coordinates = BeanUtils.indexToCoordinate(index, bounds); + return coordinates; + }*/ +} diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/ReorganizedSpatialResults.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/ReorganizedSpatialResults.java similarity index 97% rename from vcell-cli/src/main/java/org/vcell/cli/run/hdf5/ReorganizedSpatialResults.java rename to vcell-cli/src/main/java/org/vcell/cli/run/results/ReorganizedSpatialResults.java index 715d30c2d1..a5a76412a0 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/ReorganizedSpatialResults.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/ReorganizedSpatialResults.java @@ -1,4 +1,4 @@ -package org.vcell.cli.run.hdf5; +package org.vcell.cli.run.results; import cbit.vcell.solver.TempSimulation; import io.jhdf.api.Dataset; @@ -8,6 +8,9 @@ import org.apache.logging.log4j.Logger; import org.jlibsedml.AbstractTask; import org.vcell.cli.run.TaskJob; +import org.vcell.cli.run.hdf5.Hdf5DataSourceSpatialSimMetadata; +import org.vcell.cli.run.hdf5.Hdf5DataSourceSpatialSimVars; +import org.vcell.cli.run.hdf5.Hdf5DataSourceSpatialVarDataLocation; import java.io.File; import java.nio.file.Paths; diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SimpleDataGenCalculator.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/SimpleDataGenCalculator.java similarity index 98% rename from vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SimpleDataGenCalculator.java rename to vcell-cli/src/main/java/org/vcell/cli/run/results/SimpleDataGenCalculator.java index 22b18c7c84..1b1379a8b4 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SimpleDataGenCalculator.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/SimpleDataGenCalculator.java @@ -1,4 +1,4 @@ -package org.vcell.cli.run.hdf5; +package org.vcell.cli.run.results; import cbit.vcell.parser.DivideByZeroException; import cbit.vcell.parser.Expression; diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SpatialDataMapping.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialDataMapping.java similarity index 95% rename from vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SpatialDataMapping.java rename to vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialDataMapping.java index 535b40316f..172fd61ee4 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SpatialDataMapping.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialDataMapping.java @@ -1,8 +1,8 @@ -package org.vcell.cli.run.hdf5; +package org.vcell.cli.run.results; import org.jlibsedml.DataSet; import org.jlibsedml.Report; -import org.jlibsedml.Variable; +import org.vcell.cli.run.hdf5.Hdf5DataSourceSpatialVarDataItem; import java.util.LinkedHashMap; import java.util.Map; diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SpatialResultsConverter.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialResultsConverter.java similarity index 99% rename from vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SpatialResultsConverter.java rename to vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialResultsConverter.java index aab2a102e8..bb826a3e79 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/SpatialResultsConverter.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialResultsConverter.java @@ -1,4 +1,4 @@ -package org.vcell.cli.run.hdf5; +package org.vcell.cli.run.results; import cbit.vcell.parser.Expression; @@ -11,10 +11,10 @@ import org.jlibsedml.execution.IXPathToVariableIDResolver; import org.jlibsedml.modelsupport.SBMLSupport; import org.vcell.cli.run.TaskJob; +import org.vcell.cli.run.hdf5.*; import org.vcell.sedml.log.BiosimulationLog; import java.io.File; -import java.io.IOException; import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; @@ -258,7 +258,7 @@ private static String removeVCellPrefixes(String s, String sedmlId){ return s; } -// public static Map> convertSpatialResultsToSedmlFormat(SedML sedml, Map spatialResultsHash, Map taskToSimulationMap, String sedmlLocation, String outDir) throws PythonStreamException { +// public static Map> convertSpatialResultsToSedmlFormat(SedML sedml, Map spatialResultsHash, Map taskToSimulationMap, String sedmlLocation, String outDir) { // Map> results = new LinkedHashMap<>(); // List allReports = SpatialResultsConverter.getReports(sedml.getOutputs()); // diff --git a/vcell-cli/src/main/resources/test_cases.ndjson b/vcell-cli/src/main/resources/test_cases.ndjson index 6cebbbdbc4..bb1a93b056 100644 --- a/vcell-cli/src/main/resources/test_cases.ndjson +++ b/vcell-cli/src/main/resources/test_cases.ndjson @@ -99,8 +99,8 @@ {"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorProducesLinear2DPlots/1.execution-should-succeed.omex","should_fail":false,"known_status":null,"known_failure_type":null,"known_failure_desc":null} {"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorProducesLinear2DPlots/2.execution-should-succeed.omex","should_fail":false,"known_status":null,"known_failure_type":null,"known_failure_desc":null} {"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorSupportsRepeatedTasksWithNestedFunctionalRanges/1.execution-should-succeed.omex","should_fail":false,"known_status":null,"known_failure_type":null,"known_failure_desc":null} -{"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorSupportsRepeatedTasksWithSubTasksOfMixedTypes/1.execution-should-succeed.omex","should_fail":false,"known_status":null,"known_failure_type":null,"known_failure_desc":null} -{"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorSupportsRepeatedTasksWithSubTasksOfMixedTypes/2.execution-should-succeed.omex","should_fail":false,"known_status":null,"known_failure_type":null,"known_failure_desc":null} +{"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorSupportsRepeatedTasksWithSubTasksOfMixedTypes/1.execution-should-succeed.omex","should_fail":false,"known_status":"SKIP","known_failure_type":"NESTED_SEDML_REPEATED_TASK","known_failure_desc":null} +{"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorSupportsRepeatedTasksWithSubTasksOfMixedTypes/2.execution-should-succeed.omex","should_fail":false,"known_status":"SKIP","known_failure_type":"NESTED_SEDML_REPEATED_TASK","known_failure_desc":null} {"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorCanResolveModelSourcesDefinedByUriFragments/1.execution-should-succeed.omex","should_fail":false,"known_status":null,"known_failure_type":null,"known_failure_desc":null} {"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorSupportsSubstitutingAlgorithms/3.execute-should-fail.omex","should_fail":true,"known_status":null,"known_failure_type":null,"known_failure_desc":null} {"test_collection":"VCELL_BSTS_SYNTHS","file_path":"sedml/SimulatorSupportsSubstitutingAlgorithms/4.execution-should-succeed.omex","should_fail":false,"known_status":null,"known_failure_type":null,"known_failure_desc":null} diff --git a/vcell-cli/src/test/java/org/vcell/cli/run/BSTSBasedOmexExecTest.java b/vcell-cli/src/test/java/org/vcell/cli/run/BSTSBasedOmexExecTest.java index 69cb4f6212..8fea1f979f 100644 --- a/vcell-cli/src/test/java/org/vcell/cli/run/BSTSBasedOmexExecTest.java +++ b/vcell-cli/src/test/java/org/vcell/cli/run/BSTSBasedOmexExecTest.java @@ -8,9 +8,7 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.vcell.cli.CLIPythonManager; import org.vcell.cli.CLIRecordable; -import org.vcell.cli.PythonStreamException; import org.vcell.sedml.testsupport.FailureType; import org.vcell.sedml.testsupport.OmexTestCase; import org.vcell.sedml.testsupport.OmexTestingDatabase; @@ -36,20 +34,18 @@ public class BSTSBasedOmexExecTest { static List omexTestCases; @BeforeAll - public static void setup() throws PythonStreamException, IOException { + public static void setup() throws IOException { PropertyLoader.setProperty(PropertyLoader.installationRoot, new File("..").getAbsolutePath()); VCellUtilityHub.startup(VCellUtilityHub.MODE.CLI); PropertyLoader.setProperty(PropertyLoader.cliWorkingDir, new File("../vcell-cli-utils").getAbsolutePath()); VCMongoMessage.enabled = false; - CLIPythonManager.getInstance().instantiatePythonProcess(); omexTestCases = OmexTestingDatabase.loadOmexTestCases(); } @AfterAll public static void teardown() throws Exception { - CLIPythonManager.getInstance().closePythonProcess(); VCellUtilityHub.shutdown(); } diff --git a/vcell-cli/src/test/java/org/vcell/cli/run/ExecuteImplTest.java b/vcell-cli/src/test/java/org/vcell/cli/run/ExecuteImplTest.java index c7bb4b4a65..70f0d8b194 100644 --- a/vcell-cli/src/test/java/org/vcell/cli/run/ExecuteImplTest.java +++ b/vcell-cli/src/test/java/org/vcell/cli/run/ExecuteImplTest.java @@ -26,7 +26,6 @@ public void test_singleExecOmex() throws Exception { PropertyLoader.setProperty(PropertyLoader.cliWorkingDir, new File("../vcell-cli-utils").getAbsolutePath()); VCMongoMessage.enabled = false; try { - CLIPythonManager.getInstance().instantiatePythonProcess(); InputStream omexInputStream = ExecuteImplTest.class.getResourceAsStream("/BioModel1.omex"); File tempOutputDir = Files.createTempDirectory("ExecuteImplTest_temp").toFile(); @@ -46,7 +45,6 @@ public void test_singleExecOmex() throws Exception { tempOmexFile.delete(); } finally { - CLIPythonManager.getInstance().closePythonProcess(); } } } diff --git a/vcell-cli/src/test/java/org/vcell/cli/run/QuantOmexExecTest.java b/vcell-cli/src/test/java/org/vcell/cli/run/QuantOmexExecTest.java index 470e921dad..5b9846dab3 100644 --- a/vcell-cli/src/test/java/org/vcell/cli/run/QuantOmexExecTest.java +++ b/vcell-cli/src/test/java/org/vcell/cli/run/QuantOmexExecTest.java @@ -8,9 +8,7 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.vcell.cli.CLIPythonManager; import org.vcell.cli.CLIRecordable; -import org.vcell.cli.PythonStreamException; import org.vcell.sedml.testsupport.FailureType; import org.vcell.sedml.testsupport.OmexTestCase; import org.vcell.sedml.testsupport.OmexTestingDatabase; @@ -33,19 +31,16 @@ @Tag("BSTS_IT") public class QuantOmexExecTest { @BeforeAll - public static void setup() throws PythonStreamException, IOException { + public static void setup() { PropertyLoader.setProperty(PropertyLoader.installationRoot, new File("..").getAbsolutePath()); VCellUtilityHub.startup(VCellUtilityHub.MODE.CLI); PropertyLoader.setProperty(PropertyLoader.cliWorkingDir, new File("../vcell-cli-utils").getAbsolutePath()); VCMongoMessage.enabled = false; - - CLIPythonManager.getInstance().instantiatePythonProcess(); } @AfterAll public static void teardown() throws Exception { - CLIPythonManager.getInstance().closePythonProcess(); VCellUtilityHub.shutdown(); } diff --git a/vcell-cli/src/test/java/org/vcell/cli/run/RunUtilsTest.java b/vcell-cli/src/test/java/org/vcell/cli/run/RunUtilsTest.java new file mode 100644 index 0000000000..e001efe76e --- /dev/null +++ b/vcell-cli/src/test/java/org/vcell/cli/run/RunUtilsTest.java @@ -0,0 +1,34 @@ +package org.vcell.cli.run; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Tag("Fast") +public class RunUtilsTest { + private final String VERTICAL_CSV = """ + Street-based,Sea-based,Snow-based + Skateboard,Wake Board,Snowboard + Rollerblades,Water Skis,Alpine / Cross-country Skis + Bike,Water Bike, + Motorcycle,Boatercycle (Jet-Ski),Snow Bike / Snowmobile"""; + private final String HORIZONTAL_CSV = """ + Street-based,Skateboard,Rollerblades,Bike,Motorcycle + Sea-based,Wake Board,Water Skis,Water Bike,Boatercycle (Jet-Ski) + Snow-based,Snowboard,Alpine / Cross-country Skis,,Snow Bike / Snowmobile"""; + + @Test + public void testCSVFormatting(){ + Map> csvContents = new LinkedHashMap<>(); + csvContents.put("Street-based", List.of("Skateboard", "Rollerblades", "Bike", "Motorcycle")); + csvContents.put("Sea-based", List.of("Wake Board", "Water Skis", "Water Bike", "Boatercycle (Jet-Ski)")); + csvContents.put("Snow-based", List.of("Snowboard", "Alpine / Cross-country Skis", "", "Snow Bike / Snowmobile")); + Assertions.assertEquals(VERTICAL_CSV, RunUtils.formatCSVContents(csvContents, true)); + Assertions.assertEquals(HORIZONTAL_CSV, RunUtils.formatCSVContents(csvContents, false)); + } +} diff --git a/vcell-cli/src/test/java/org/vcell/cli/run/SpatialExecTest.java b/vcell-cli/src/test/java/org/vcell/cli/run/SpatialExecTest.java index 303e7c166b..0477032744 100644 --- a/vcell-cli/src/test/java/org/vcell/cli/run/SpatialExecTest.java +++ b/vcell-cli/src/test/java/org/vcell/cli/run/SpatialExecTest.java @@ -11,9 +11,7 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import org.vcell.cli.CLIPythonManager; import org.vcell.cli.CLIRecordable; -import org.vcell.cli.PythonStreamException; import org.vcell.sedml.testsupport.FailureType; import org.vcell.sedml.testsupport.OmexTestCase; import org.vcell.sedml.testsupport.OmexTestingDatabase; @@ -24,19 +22,16 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; -import static org.junit.jupiter.api.Assertions.assertFalse; - @Tag("Spatial_IT") public class SpatialExecTest { @BeforeAll - public static void setup() throws PythonStreamException, IOException { + public static void setup() { PropertyLoader.setProperty(PropertyLoader.installationRoot, new File("..").getAbsolutePath()); VCellUtilityHub.startup(VCellUtilityHub.MODE.CLI); @@ -49,13 +44,10 @@ public static void setup() throws PythonStreamException, IOException { config.updateLoggers(); config.getConfiguration().getLoggerConfig(LogManager.getLogger("io.jhdf").getName()).setLevel(Level.WARN); config.updateLoggers(); - - CLIPythonManager.getInstance().instantiatePythonProcess(); } @AfterAll public static void teardown() throws Exception { - CLIPythonManager.getInstance().closePythonProcess(); VCellUtilityHub.shutdown(); } diff --git a/vcell-cli/src/test/java/org/vcell/cli/run/plotting/TestJFreeChartLibrary.java b/vcell-cli/src/test/java/org/vcell/cli/run/plotting/TestJFreeChartLibrary.java new file mode 100644 index 0000000000..1e2a94211e --- /dev/null +++ b/vcell-cli/src/test/java/org/vcell/cli/run/plotting/TestJFreeChartLibrary.java @@ -0,0 +1,141 @@ +package org.vcell.cli.run.plotting; + +import cbit.vcell.publish.PDFWriter; + +import com.lowagie.text.DocumentException; + +import org.jfree.chart.*; +import org.jfree.chart.labels.StandardXYItemLabelGenerator; +import org.jfree.chart.plot.XYPlot; +import org.jfree.data.xy.*; + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import org.vcell.util.Pair; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Paper; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + + +@Tag("Fast") +public class TestJFreeChartLibrary { + + private static List testData = List.of( + new XYDataItem(0.0, 0.0), + new XYDataItem(0.5, 0.25), + new XYDataItem(1.0, 1.0), + new XYDataItem(1.5, 2.25), + new XYDataItem(2.0, 4.0), + new XYDataItem(2.5, 6.25), + new XYDataItem(3.0, 9.0), + new XYDataItem(3.5, 12.25), + new XYDataItem(4.0, 16.0), + new XYDataItem(4.5, 20.25), + new XYDataItem(5.0, 25.0), + new XYDataItem(5.5, 30.25) + ); + + @Test + public void testDirectChartCreation() throws IOException, DocumentException { + PageFormat pageFormat = TestJFreeChartLibrary.generateAlternatePageFormat(); + + // Build Chart + XYSeries xySeries = new XYSeries("Ka+", true, false); // The "key" field is what to name the curve itself, when shown in a Legend + for (XYDataItem valuePair: TestJFreeChartLibrary.testData){ + xySeries.add(valuePair); + } + XYDataset dataset2D = new XYSeriesCollection(xySeries); + this.generateChart(dataset2D, pageFormat, "Time"); + } + + @Test + public void testDirectChartCreationFromCSV() throws IOException, DocumentException { + PageFormat pageFormat = TestJFreeChartLibrary.generateAlternatePageFormat(); + Pair datasetPair = TestJFreeChartLibrary.csvToXYDataset(); + this.generateChart(datasetPair.two, pageFormat, datasetPair.one); + } + + private void generateChart(XYDataset dataset2D, PageFormat pageFormat, String xAxisLabel) throws IOException, DocumentException { + String yAxisLabel = ""; + JFreeChart chart = ChartFactory.createXYLineChart("Test Sim Results", xAxisLabel, yAxisLabel, dataset2D); + + // Tweak Chart so it looks better + chart.setBorderVisible(true); + chart.getPlot().setBackgroundPaint(Color.white); + XYPlot chartPlot = chart.getXYPlot(); + chartPlot.getDomainAxis().setLabelFont(new Font(xAxisLabel, Font.PLAIN, Results2DLinePlot.AXIS_LABEL_FONT_SIZE)); + chartPlot.getRangeAxis().setLabelFont(new Font(yAxisLabel, Font.PLAIN, Results2DLinePlot.AXIS_LABEL_FONT_SIZE)); + if (dataset2D.getItemCount(0) <= Results2DLinePlot.MAX_SERIES_DATA_POINT_LABELS) { // if it's too crowded, having data point labels is bad + for (int i = 0; i < dataset2D.getSeriesCount(); i++){ + //DecimalFormat decimalformat1 = new DecimalFormat("##"); + chartPlot.getRenderer().setSeriesItemLabelGenerator(i, new StandardXYItemLabelGenerator("({1}, {2})")); + chartPlot.getRenderer().setSeriesItemLabelFont(i, new Font(null, Font.PLAIN, Results2DLinePlot.SERIES_DATA_POINT_LABEL_FONT_SIZE)); + chartPlot.getRenderer().setSeriesItemLabelsVisible(i, true); + } + } + + // Prepare for export + PDFWriter pdfWriter = new PDFWriter(); + + File testfile = File.createTempFile("VCell::TestJFreeChartLibrary::testDirectChartCreation::", ".pdf"); + if (!testfile.exists()) throw new IllegalArgumentException("Unable to create the testfile, somehow."); + try (FileOutputStream fos = new FileOutputStream(testfile)){ + BufferedImage bfi = chart.createBufferedImage((int)pageFormat.getImageableWidth(), (int)pageFormat.getImageableHeight()); + pdfWriter.writePlotImageDocument("Test Document", fos, pageFormat, bfi); + } + } + + private static PageFormat generateAlternatePageFormat(){ + java.awt.print.PageFormat pageFormat = java.awt.print.PrinterJob.getPrinterJob().defaultPage(); + Paper alternatePaper = new Paper(); // We want to try and increase the margins + double altOriginX = alternatePaper.getImageableX() / 2, altOriginY = alternatePaper.getImageableY() / 2; + double altWidth = alternatePaper.getWidth() - 2 * altOriginX, altHeight = alternatePaper.getHeight() - 2 * altOriginY; + alternatePaper.setImageableArea(altOriginX, altOriginY, altWidth, altHeight); + pageFormat.setPaper(alternatePaper); + pageFormat.setOrientation(PageFormat.LANDSCAPE); + return pageFormat; + } + + private static Pair csvToXYDataset() throws IOException { + String CSV_DATA_FILE_LOCAL_PATH = "plot2d_SimpleSimulation.csv"; + InputStream inputStream = TestJFreeChartLibrary.class.getResourceAsStream(CSV_DATA_FILE_LOCAL_PATH); + if (inputStream == null) + throw new FileNotFoundException(String.format("can not find `%s`; maybe it moved?", CSV_DATA_FILE_LOCAL_PATH)); + XYSeriesCollection dataset2D = new XYSeriesCollection(); + List linesOfData; + try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))){ + linesOfData = br.lines().toList(); + } + String[] indepDataParts = linesOfData.get(linesOfData.size() - 1).split(","); + List xSeriesData = new ArrayList<>(); + for (int partNum = 3; partNum < indepDataParts.length; partNum++){ // Skip first three; no need for them + xSeriesData.add(Double.parseDouble(indepDataParts[partNum])); + } + + for (int lineNum = 0; lineNum < linesOfData.size() - 1; lineNum++){ // Skip last line; that's our independent-var data + XYSeries series = getXySeries(linesOfData, lineNum, xSeriesData); + dataset2D.addSeries(series); + } + + return new Pair<>(indepDataParts[2], dataset2D); + } + + private static XYSeries getXySeries(List linesOfData, int lineNum, List xSeriesData) { + String[] parts = linesOfData.get(lineNum).split(","); + String ySeriesName = parts[2]; + XYSeries series = new XYSeries(ySeriesName, true); + if (parts.length - 3 != xSeriesData.size()) throw new RuntimeException("Mismatched data length!"); + for (int partNum = 3; partNum < parts.length; partNum++){ // Skip first two; third we've already grabbed + double xData = xSeriesData.get(partNum - 3), yData = Double.parseDouble(parts[partNum]); + series.add(xData, yData); + } + return series; + } +} diff --git a/vcell-cli/src/test/java/org/vcell/cli/run/plotting/TestResults2DLinePlot.java b/vcell-cli/src/test/java/org/vcell/cli/run/plotting/TestResults2DLinePlot.java new file mode 100644 index 0000000000..60a51c997a --- /dev/null +++ b/vcell-cli/src/test/java/org/vcell/cli/run/plotting/TestResults2DLinePlot.java @@ -0,0 +1,244 @@ +package org.vcell.cli.run.plotting; + +import cbit.vcell.mongodb.VCMongoMessage; +import cbit.vcell.resource.PropertyLoader; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.data.xy.XYDataItem; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; +import org.vcell.cli.biosimulation.BiosimulationsCommand; +import org.vcell.cli.run.ExecuteImpl; +import org.vcell.util.Pair; +import org.vcell.util.VCellUtilityHub; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +@Tag("Fast") +public class TestResults2DLinePlot { + private static List paraData = List.of( + new XYDataItem(0.0, 0.0), + new XYDataItem(0.5, 0.25), + new XYDataItem(1.0, 1.0), + new XYDataItem(1.5, 2.25), + new XYDataItem(2.0, 4.0), + new XYDataItem(2.5, 6.25), + new XYDataItem(3.0, 9.0), + new XYDataItem(3.5, 12.25), + new XYDataItem(4.0, 16.0), + new XYDataItem(4.5, 20.25), + new XYDataItem(5.0, 25.0), + new XYDataItem(5.5, 30.25) + ); + + private static Pair parabolicData = new Pair<>( + Stream.of(0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5).mapToDouble(Double::valueOf).toArray(), + Stream.of(0.0, 0.25, 1.0, 2.25, 4.0, 6.25, 9.0, 12.25, 16.0, 20.25, 25.0, 30.25).mapToDouble(Double::valueOf).toArray() + ); + + private static Pair, List> parabolicListData = new Pair<>( + List.of(0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0), + List.of(0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0) + ); + + private static Pair, List> linearListData = new Pair<>( + List.of(0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0), + List.of(0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0) + ); + + @Test + public void testConstructors(){ + Results2DLinePlot[] testInstances = { + new Results2DLinePlot(), + new Results2DLinePlot("Title", "Label") + }; + + for (Results2DLinePlot instance : testInstances){ + Assertions.assertTrue("".equals(instance.getTitle()) || "Title".equals(instance.getTitle())); + Assertions.assertTrue("".equals(instance.getXAxisTitle()) || "Label".equals(instance.getXAxisTitle())); + } + } + + @Test + public void testSettingAndGetting(){ + Results2DLinePlot[] testInstances = { + new Results2DLinePlot(), + new Results2DLinePlot("Title", "Label") + }; + for (Results2DLinePlot plot : testInstances){ + String alternateTitle = "AltTitle", alternateAxisTitle = "AltAxisTitle"; + plot.setTitle(alternateTitle); + Assertions.assertEquals(alternateTitle, plot.getTitle()); + plot.setXAxisTitle(alternateAxisTitle); + Assertions.assertEquals(alternateAxisTitle, plot.getXAxisTitle()); + + Assertions.assertThrows(IllegalArgumentException.class, () -> plot.addXYData(null, new SingleAxisSeries("Linear", linearListData.two))); + Assertions.assertThrows(IllegalArgumentException.class, () -> plot.addXYData(new SingleAxisSeries("Numbers", linearListData.one), null)); + Assertions.assertThrows(IllegalArgumentException.class, + () -> plot.addXYData(new SingleAxisSeries(null, linearListData.one), new SingleAxisSeries("Linear", linearListData.two))); + Assertions.assertThrows(IllegalArgumentException.class, + () -> plot.addXYData(new SingleAxisSeries("Numbers", null), new SingleAxisSeries("Linear", linearListData.two))); + Assertions.assertThrows(IllegalArgumentException.class, + () -> plot.addXYData(new SingleAxisSeries("Numbers", linearListData.one), new SingleAxisSeries(null, linearListData.two))); + Assertions.assertThrows(IllegalArgumentException.class, + () -> plot.addXYData(new SingleAxisSeries("Numbers", linearListData.one), new SingleAxisSeries("Linear", null))); + Assertions.assertThrows(IllegalArgumentException.class, + () -> plot.addXYData(new SingleAxisSeries("Numbers", linearListData.one), new SingleAxisSeries("Linear", new ArrayList<>(List.of(0.2, 1.2))))); + + + SingleAxisSeries xNumbers = new SingleAxisSeries("Numbers", linearListData.one); + plot.addXYData(xNumbers, new SingleAxisSeries("Linear", linearListData.two)); + plot.addXYData(xNumbers, new SingleAxisSeries("Quadratic", parabolicListData.two)); + plot.addXYData(new SingleAxisSeries("ToRemoveX", List.of(0.0, 1.0)), new SingleAxisSeries("ToRemoveY1", List.of(0.1, 1.1))); + plot.addXYData(new SingleAxisSeries("ToRemoveX", List.of(0.0, 1.0)), new SingleAxisSeries("ToRemoveY2", List.of(0.2, 1.2))); + plot.addXYData(new SingleAxisSeries("ToRemoveX", List.of(0.0, 1.0)), new SingleAxisSeries("ToRemoveY3", List.of(0.3, 1.3))); + Assertions.assertEquals(plot.getXAxisSeries("ToRemoveX"), new SingleAxisSeries("ToRemoveX", List.of(0.0, 1.0))); + Assertions.assertEquals(plot.getYAxisSeries("ToRemoveY2"), new SingleAxisSeries("ToRemoveY2", List.of(0.2, 1.2))); + + Assertions.assertEquals(3, plot.getNumYSeries(new SingleAxisSeries("ToRemoveX", List.of(0.0, 1.0)))); + Assertions.assertTrue(plot.removeXYData("ToRemoveX", "ToRemoveY3")); + Assertions.assertFalse(plot.removeXYData("ToRemoveX", "ToRemoveY3")); + Assertions.assertEquals(2, plot.getNumYSeries(new SingleAxisSeries("ToRemoveX", List.of(0.0, 1.0)))); + Assertions.assertTrue(plot.removeXYData("ToRemoveX", "ToRemoveY2")); + Assertions.assertEquals(2, plot.getNumXSeries()); + Assertions.assertTrue(plot.removeXYData("ToRemoveX", "ToRemoveY1")); + Assertions.assertEquals(1, plot.getNumXSeries()); + Assertions.assertEquals(0, plot.getNumYSeries(new SingleAxisSeries("ToRemoveX", List.of(0.0, 1.0)))); + Assertions.assertNull(plot.getXAxisSeries("ToRemoveX")); + Assertions.assertEquals(2, plot.getNumYSeries(xNumbers)); + Assertions.assertEquals(2, plot.getNumYSeries()); + + + Assertions.assertEquals(parabolicListData.one.size(), plot.getLargestSeriesSize()); + Assertions.assertThrows(IllegalArgumentException.class, ()->plot.changeXAxisData(null, linearListData.one)); + Assertions.assertThrows(IllegalArgumentException.class, ()->plot.changeXAxisData("Numbers", (double[]) null)); + Assertions.assertThrows(IllegalArgumentException.class, ()->plot.changeXAxisData("Nummmmbers", linearListData.one)); + Assertions.assertThrows(IllegalArgumentException.class, ()->plot.changeXAxisData("Numbers", new double[0])); + Assertions.assertThrows(IllegalArgumentException.class, ()->plot.changeXAxisData(new SingleAxisSeries("Nummmmbers", linearListData.one))); + Assertions.assertThrows(IllegalArgumentException.class, ()->plot.changeXAxisData(new SingleAxisSeries("Numbers", List.of()))); + plot.changeXAxisData(new SingleAxisSeries("Numbers", parabolicListData.one)); + Assertions.assertThrows(IllegalArgumentException.class, ()-> plot.changeXAxisData("Numbers", parabolicData.one)); + plot.changeXAxisData("Numbers", parabolicListData.one.stream().mapToDouble(Double::doubleValue).toArray()); + plot.changeXAxisData("Numbers", parabolicListData.one); + } + + } + + @Test + public void pngRoundTripTest() throws IOException { + File dupe = File.createTempFile("VCellPNG::", ".png"); + XYSeries series = new XYSeries("key"); + for (int i = 0; i < TestResults2DLinePlot.parabolicData.one.length; i++){ + series.add(TestResults2DLinePlot.parabolicData.one[i], TestResults2DLinePlot.parabolicData.two[i]); + } + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + JFreeChart chart = ChartFactory.createXYLineChart("Test", "X-Axis","Y-Axis", dataset); + BufferedImage originalImage = chart.createBufferedImage(1000,1000); + ImageIO.write(originalImage, "png", dupe); + BufferedImage roundTrippedImage = ImageIO.read(dupe); + Assertions.assertEquals(originalImage.getWidth(), roundTrippedImage.getWidth()); + Assertions.assertEquals(originalImage.getHeight(), roundTrippedImage.getHeight()); + for (int wPix = 0; wPix < originalImage.getWidth(); wPix++){ + for (int hPix = 0; hPix < originalImage.getHeight(); hPix++){ + Assertions.assertEquals(originalImage.getRGB(wPix, hPix), roundTrippedImage.getRGB(wPix, hPix)); + } + } + } + + @Test + public void pngLibraryLevelTest() throws IOException { + String STANDARD_IMAGE_LOCAL_PATH = "Parabolic.png"; + InputStream standardImageStream = TestResults2DLinePlot.class.getResourceAsStream(STANDARD_IMAGE_LOCAL_PATH); + if (standardImageStream == null) + throw new FileNotFoundException(String.format("can not find `%s`; maybe it moved?", STANDARD_IMAGE_LOCAL_PATH)); + BufferedImage standardImage = ImageIO.read(standardImageStream); + XYSeries series = new XYSeries("key"); + for (int i = 0; i < TestResults2DLinePlot.parabolicData.one.length; i++){ + series.add(TestResults2DLinePlot.parabolicData.one[i], TestResults2DLinePlot.parabolicData.two[i]); + } + XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(series); + JFreeChart chart = ChartFactory.createXYLineChart("Test", "X-Axis","Y-Axis", dataset); + BufferedImage currentImage = chart.createBufferedImage(1000,1000); + Assertions.assertEquals(currentImage.getWidth(), standardImage.getWidth()); + Assertions.assertEquals(currentImage.getHeight(), standardImage.getHeight()); + for (int wPix = 0; wPix < currentImage.getWidth(); wPix++){ + for (int hPix = 0; hPix < currentImage.getHeight(); hPix++){ + Assertions.assertEquals(currentImage.getRGB(wPix, hPix), standardImage.getRGB(wPix, hPix)); + } + } + } + + @Test + public void pngExecutionLevelTest() throws IOException { + String INPUT_FILE_LOCAL_PATH = "MultiplePlotsTest.omex"; + Path inputFilePath = Files.createTempFile("VCellInputOmex", ".omex"); + Path outputFile = Files.createTempDirectory("VCellCliOut"); + try (InputStream inputFileStream = TestResults2DLinePlot.class.getResourceAsStream(INPUT_FILE_LOCAL_PATH)){ + if (inputFileStream == null) + throw new FileNotFoundException(String.format("can not find `%s`; maybe it moved?", INPUT_FILE_LOCAL_PATH)); + Files.copy(inputFileStream, inputFilePath, StandardCopyOption.REPLACE_EXISTING); + } + + ///////////////////////////////////////// + File installRoot = new File(".."); + PropertyLoader.setProperty(PropertyLoader.installationRoot, installRoot.getAbsolutePath()); + VCMongoMessage.enabled = false; + VCellUtilityHub.startup(VCellUtilityHub.MODE.CLI); + int result = BiosimulationsCommand.executeVCellBiosimulationsMode(inputFilePath.toFile(), outputFile.toFile()); + if (result != 0) throw new RuntimeException("VCell Execution failed!"); + ///////////////////////////////////////// + + File generatedPlot0 = new File(outputFile.toFile(), "BIOMD0000000912_sim.sedml/plot_0.png"); + File generatedPlot1 = new File(outputFile.toFile(), "BIOMD0000000912_sim.sedml/plot_1.png"); + BufferedImage generatedImage0 = ImageIO.read(generatedPlot0); + if (generatedImage0 == null) throw new RuntimeException("Plot_0 PNG was not found; check paths?"); + BufferedImage generatedImage1 = ImageIO.read(generatedPlot1); + if (generatedImage1 == null) throw new RuntimeException("Plot_1 PNG was not found; check paths?"); + + + String PLOT_0_PATH = "plot_0.png"; + String PLOT_1_PATH = "plot_1.png"; + InputStream standardImageStream0 = TestResults2DLinePlot.class.getResourceAsStream(PLOT_0_PATH); + if (standardImageStream0 == null) + throw new FileNotFoundException(String.format("can not find `%s`; maybe it moved?", PLOT_0_PATH)); + BufferedImage standardImage0 = ImageIO.read(standardImageStream0); + InputStream standardImageStream1 = TestResults2DLinePlot.class.getResourceAsStream(PLOT_1_PATH); + if (standardImageStream1 == null) + throw new FileNotFoundException(String.format("can not find `%s`; maybe it moved?", PLOT_1_PATH)); + BufferedImage standardImage1 = ImageIO.read(standardImageStream1); + + Assertions.assertEquals(standardImage0.getWidth(), generatedImage0.getWidth()); + Assertions.assertEquals(standardImage0.getHeight(), generatedImage0.getHeight()); + Assertions.assertEquals(standardImage1.getWidth(), generatedImage1.getWidth()); + Assertions.assertEquals(standardImage1.getHeight(), generatedImage1.getHeight()); + + + for (int wPix = 0; wPix < generatedImage0.getWidth(); wPix++){ + for (int hPix = 0; hPix < generatedImage0.getHeight(); hPix++){ + Assertions.assertEquals(generatedImage0.getRGB(wPix, hPix), standardImage0.getRGB(wPix, hPix)); + } + } + + for (int wPix = 0; wPix < generatedImage1.getWidth(); wPix++){ + for (int hPix = 0; hPix < generatedImage1.getHeight(); hPix++){ + Assertions.assertEquals(generatedImage1.getRGB(wPix, hPix), standardImage1.getRGB(wPix, hPix)); + } + } + } +} diff --git a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/MultiplePlotsTest.omex b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/MultiplePlotsTest.omex new file mode 100644 index 0000000000..2262596e0b Binary files /dev/null and b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/MultiplePlotsTest.omex differ diff --git a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/Parabolic.png b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/Parabolic.png new file mode 100644 index 0000000000..9b72945444 Binary files /dev/null and b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/Parabolic.png differ diff --git a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot2d_SimpleSimulation.csv b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot2d_SimpleSimulation.csv new file mode 100644 index 0000000000..3b22f9b7b9 --- /dev/null +++ b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot2d_SimpleSimulation.csv @@ -0,0 +1,3 @@ +dataGen_tsk_0_0_SpeciesAlpha,dataGen_tsk_0_0_SpeciesAlpha,"SpeciesAlpha",10.0,9.987016241437045,9.974064893924155,9.961145887611409,9.948259137986712,9.935404564900482,9.922582088174698,9.909791627690753,9.89703310346457,9.884306435723548,9.871611544913684,9.858948351676503,9.84631677685886,9.833716741523588,9.821148166930389,9.808610974531152,9.796105085968858,9.78363042308044,9.771186907898187,9.758774462649843,9.746393009757767,9.734042471838068,9.721722771699977,9.709433832345374,9.697175576968265,9.684947928954436,9.67275081188104,9.660584149516083,9.648447865817857,9.636341884934554,9.624266131203779,9.612220529152037,9.60020500349427,9.5882194791334,9.57626388115986,9.564338134851125,9.552442165671241,9.540575899270385,9.528739261484354,9.51693217833411,9.50515457602533,9.493406380947961,9.481687519675752,9.469997918965785,9.458337505758013,9.446706207174815,9.435103950520537,9.423530663281038,9.411986273123237,9.400470707894655,9.388983895622973,9.377525764515575,9.366096242959106,9.354695259519021,9.343322742939135,9.33197862214118,9.320662826224368,9.309375284464934,9.298115926315708,9.286884681405663,9.275681479539482,9.264506250697119,9.253358925033355,9.24223943287737,9.2311477047323,9.220083671274809,9.209047263354652,9.198038411994244,9.187057048388226,9.176103103903042,9.165176510076503,9.15427719861736,9.143405101404882,9.132560150488429,9.121742278087018,9.110951416588913,9.100187498551195,9.08945045669934,9.078740223926804,9.068056733294595,9.057399918030864,9.046769711530478,9.036166047354614,9.025588859230336,9.015038081050182,9.004513646871755,8.99401549091731,8.983543547573333,8.97309775139015,8.962678037081494,8.952284339524121,8.941916593757387,8.931574734982847,8.92125869856385,8.910968420025135,8.900703835052427,8.890464879492036,8.880251489350458,8.87006360079397,8.859901150148236,8.849764073897905,8.839652308686219,8.829565791314613,8.819504458742319,8.809468248085977,8.799457096619237,8.78947094177237,8.779509721131875,8.769573372440092,8.759661833594809,8.749775042648874,8.739912937809814,8.73007545743944,8.720262540053469,8.710474124321134,8.700710149064806,8.690970553259607,8.681255276033031,8.671564256664565,8.661897434585306,8.652254749377585,8.642636140774588,8.633041548659978,8.623470913067525,8.613924174180726,8.60440127233243,8.59490214800447,8.58542674182729,8.575974994579566,8.566546847187851,8.557142240726192,8.547761116415767,8.53840341562452,8.52906907986679,8.519758050802947,8.510470270239031,8.501205680126382,8.491964222561283,8.482745839784592,8.473550474181387,8.464378068280604,8.455228564754677,8.446101906419178,8.436998036232463,8.427916897295313,8.41885843285058,8.409822586282834,8.400809301118,8.391818521023021,8.38285018980549,8.373904251413308,8.364980649934331,8.356079329596021,8.347200234765097,8.338343309947188,8.329508499786483,8.320695749065392,8.311905002704192,8.30313620576069,8.29438930342988,8.28566424104359,8.276960964070152,8.26827941811406,8.259619548915621,8.250981302350624,8.24236462443,8.233769461299485,8.22519575923928,8.21664346466372,8.208112524120937,8.199602884292522,8.191114491993202,8.182647294170495,8.174201237904388,8.165776270407001,8.157372339022261,8.14898939122557,8.140627374623474,8.132286236953345,8.123965926083043,8.1156663900106,8.10738757686389,8.099129434900298,8.090891912506414,8.082674958197696,8.074478520618154,8.066302548540024,8.058146990863456,8.050011796616186,8.041896914953224,8.033802295156534,8.025727886634714,8.017673638922687,8.009639501681377,8.001625424697401,7.9936313578827525,7.985657251274486,7.977703055034411,7.969768719448773,7.96185419492795,7.953959432006138,7.946084381341042,7.9382289937135715,7.930393220027527,7.922577011309298,7.914780318707554,7.907003093492942,7.899245287057777,7.8915068509157456,7.883787736701596,7.876087896170839,7.868407281199446,7.860745843783549,7.853103536039139,7.8454803102017685,7.83787611862625,7.830290913786361,7.822724648274548,7.815177274801626,7.807648746196487,7.800139015405803,7.79264803549373,7.785175759641621,7.777722141147727,7.770287133426908,7.762870690010343,7.755472764545234,7.748093310794525,7.740732282636605,7.733389634065024,7.726065319188205,7.718759292229156,7.711471507525184,7.704201919527612,7.6969504828014905,7.689717152025317,7.68250188199075,7.675304627602329,7.66812534387719,7.660963985944786,7.653820509046608,7.646694868535901,7.639587019877389,7.632496918646995,7.6254245205315625,7.618369781328581,7.6113326569459065,7.6043131034014895,7.597311076823098,7.590326533448041,7.583359429622902,7.576409721803259,7.569477366553414,7.562562320546126,7.555664540562335,7.548783983490892,7.5419206063282935,7.53507436617841,7.528245220252218,7.521433125867534,7.514638040448746,7.507859921526547,7.5010987267376725,7.494354413824633,7.48762694063545,7.480916265123395,7.474222345346724,7.467545139468417,7.460884605755914,7.454240702580859,7.447613388418835,7.441002621849108,7.434408361554365,7.4278305663204565,7.421269195036142,7.414724206692829,7.408195560384319,7.4016832153065515,7.395187130757349,7.388707266136161,7.382243580943814,7.375796034782253,7.369364587354294,7.362949198463371,7.35654982801328,7.350166436007937,7.34379898255112,7.337447427846225,7.331111732196014,7.324791856002369,7.318487759766041,7.312199404086411,7.305926749661234,7.2996697572863996,7.293428387855685,7.287202602360511,7.280992361889699,7.2747976276292246,7.2686183608619785,7.262454522967524,7.256306075421854,7.250172979797151,7.244055197761548,7.2379526910788865,7.231865421608481,7.225793351304875,7.219736442217612,7.21369465649099,7.207667956363828,7.201656304169231,7.195659662334353,7.189677993380163,7.18371125992121,7.177759424665391,7.171822450413716,7.165900300060077,7.159992936591015,7.15410032308549,7.148222422714646,7.142359198741588,7.136510614521148,7.130676633499654,7.124857219214706,7.119052335294946,7.113261945459831,7.107486013519405,7.101724503374075,7.095977379014383,7.090244604520784,7.084526144063418,7.07882196190189,7.073132022385042,7.067456289950733,7.061794729125617,7.056147304524921,7.050513980852222,7.04489472289923,7.039289495545563,7.033698263758533,7.028120992592922,7.0225576471907685,7.0170081927811445,7.011472594679942,7.005950818289655,7.000442829099163,6.994948592683516,6.989468074703719,6.984001240906516,6.978548057124178,6.9731084892742885,6.967682503359531,6.962270065467475,6.9568711417703675,6.951485698524917,6.946113702072085,6.940755118836878,6.935409915328132,6.930078058138308,6.924759513943282,6.919454249502134,6.914162231656944,6.908883427332585,6.903617803536511,6.898365327358557,6.89312596597073,6.887899686627003,6.882686456663115,6.87748624349636,6.87229901462539,6.867124737630006,6.8619633801709625,6.856814909989756,6.8516792949084335,6.846556502829382,6.841446501735136,6.83634925968817,6.8312647448307064,6.82619292538451,6.821133769650693,6.816087246009515,6.811053322920189,6.806031968920676,6.8010231526275,6.796026842735541,6.791043008017847,6.786071617325434,6.781112639587095,6.7761660438092015,6.771231799075515,6.76630987454699,6.761400239461581,6.756502863134054,6.75161771495579,6.746744764394597,6.741883980994519,6.737035334375643,6.732198794233913,6.727374330340936,6.722561912543798,6.717761510764872,6.71297309500163,6.70819663532646,6.703432101886472,6.698679464903317,6.693938694672998,6.689209761565686,6.684492636025533,6.67978728857049,6.67509368979212,6.670411810355414,6.665741620998612,6.661083092533015,6.6564361958428035,6.65180090188486,6.647177181688581,6.6425650063557,6.637964347060107,6.633375175047667,6.628797461636038,6.624231178214498,6.61967629624376,6.6151327872557975,6.610600622853664,6.606079774711316,6.601570214573439,6.597071914255267,6.592584845642409,6.588108980690672,6.583644291425885,6.579190749943725,6.5747483284095445,6.5703169990581936,6.565896734193849,6.561487506189842,6.557089287488482,6.552702050600889,6.548325768106817,6.543960412654488,6.539605956960414,6.535262373809235,6.53092963605354,6.5266077166137055,6.52229658847772,6.517996224701019,6.513706598406315,6.5094276827834285,6.505159451089125,6.5009018766469415,6.496654932847023,6.492418593145958,6.488192831066609,6.48397762019795,6.479772934194899,6.4755787467781545,6.471395031734032,6.467221762914298,6.463058914236011,6.458906459681351,6.454764373297463,6.450632629196296,6.446511201554434,6.442400064612941,6.438299192677198,6.434208560116743,6.430128141365109,6.426057910919664,6.4219978433414555,6.417947913255048,6.413908095348365,6.409878364372531,6.405858695141716,6.401849062532972,6.397849441486084,6.393859807003406,6.389880134149711,6.385910398052029,6.3819505738994975,6.378000636943202,6.374060562496025,6.370130325932487,6.366209902688597,6.3622992682616974,6.35839839821031,6.354507268153984,6.350625853773145,6.346754130808939,6.3428920750630855,6.339039662397724,6.335196868735262,6.331363670058227,6.327540042409115,6.323725961890241,6.319921404663588,6.3161263469506626,6.31234076503234,6.308564635248723,6.304797933998986,6.301040637741235,6.297292722992357,6.293554166327872,6.289824944381787,6.286105033846455,6.2823944114724215,6.2786930540682855,6.2750009385005505,6.271318041693483,6.267644340628966,6.263979812346355,6.260324433942336,6.256678182570783,6.253041035442614,6.249412969825645,6.2457939630444566,6.242183992480244,6.238583035570678,6.234991069809768,6.231408072747715,6.227834021990776,6.2242688952011225,6.220712670096701,6.217165324451091,6.213626836093373,6.210097182907981,6.206576342834573,6.203064293867887,6.199561014057604,6.196066481508216,6.192580674378881,6.189103570883295,6.185635149289547,6.182175387919994,6.178724265151114,6.17528175941338,6.171847849191119,6.168422513022381,6.165005729498803,6.161597477265479,6.15819773502082,6.154806481516426,6.151423695556951,6.148049355999973,6.144683441755858,6.141325931787631,6.137976805110845,6.1346360407934455,6.131303617955646,6.127979515769792,6.124663713460235,6.1213561903031986,6.118056925626653,6.114765898810183,6.11148308928486,6.108208476533115,6.104942040088606,6.101683759536096,6.09843361451132,6.095191584700862,6.091957649842024,6.0887317897227025,6.085513984181262,6.082304213106408,6.0791024564370595,6.075908694162228,6.072722906320888,6.069545073001856,6.066375174343662,6.06321319053443,6.060059101811752,6.056912888462561,6.053774530823015,6.0506440092783675,6.047521304262849,6.044406396259544,6.041299265800266,6.03819989346544,6.0351082598839785,6.032024345733162,6.028948131738517,6.025879598673695,6.022818727360356,6.019765498668043,6.016719893514068,6.013681892863388,6.010651477728489,6.007628629169266,6.0046133282929075,6.001605556253772,5.998605294253274,5.995612523539767,5.992627225408424,5.989649381201122,5.986678972306326,5.983715980158968,5.98076038624034,5.97781217207797,5.974871319245509,5.971937809362618,5.9690116240948505,5.9660927451535395,5.963181154295681,5.960276833323823,5.9573797640859505,5.954489928475371,5.951607308430603,5.948731885935261,5.945863643017946,5.943002561752129,5.940148624256044,5.9373018126925725,5.934462109269132,5.9316294962375675,5.928803955894038,5.925985470578907,5.923174022676633,5.920369594615657,5.917572168868294,5.914781727950625,5.911998254422384,5.909221730886853,5.90645213999075,5.903689464424125,5.9009336869202444,5.898184790255492,5.895442757249253,5.892707570763812,5.889979213704246,5.887257669018314,5.884542919696352,5.881834948771169,5.879133739317937,5.876439274454089,5.87375153733921,5.871070511174935,5.868396179204841,5.865728524714345,5.863067531030597,5.86041318152238,5.857765459599999,5.855124348715183,5.852489832360981,5.849861894071658,5.84724051742259,5.844625686030166,5.842017383551679,5.839415593685231,5.836820300169628,5.834231486784276,5.831649137349083,5.829073235724356,5.826503765810703,5.823940711548927,5.821384056919931,5.818833785944617,5.8162898826837806,5.813752331238019,5.811221115747628,5.808696220392502,5.806177629392037,5.803665327005032,5.801159297529587,5.798659525303011,5.796165994701719,5.793678690141136,5.791197596075602,5.788722696998269,5.786253977441011,5.783791421974323,5.781335015207224,5.778884741787165,5.776440586399928,5.774002533769535,5.771570568658149,5.769144675865979,5.766724840231189,5.764311046629796,5.761903279975584,5.759501525220004,5.757105767352079,5.7547159913983155,5.752332182422606,5.7499543255261365,5.747582405847294,5.745216408561573,5.742856318881483,5.740502122056454,5.73815380337275,5.735811348153371,5.733474741757964,5.731143969582732,5.72881901706034,5.726499869659829,5.7241865128865195,5.721878932281924,5.7195771134236555,5.7172810419253395,5.714990703436522,5.712706083642579,5.71042716826463,5.708153943059446,5.7058863938193625,5.703624506372189,5.701368266581122,5.699117660344656,5.696872673596493,5.69463329230546,5.692399502475416,5.690171290145167,5.687948641388379,5.685731542313491,5.683519979063625,5.681313937816505,5.679113404784364,5.676918366213865,5.67472880838601,5.672544717616054,5.670366080253424,5.668192882681629,5.666025111318176,5.663862752614489,5.661705793055817,5.659554219161157,5.6574080174831645,5.655267174608072,5.6531316771556055,5.651001511778898,5.64887666516441,5.646757124031843,5.644642875134056,5.642533905256989,5.640430201219572,5.6383317498736485,5.636238538103891,5.634150552827719,5.632067780995217,5.629990209589055,5.6279178256244045,5.625850616148859,5.623788568242353,5.62173166901708,5.619679905617414,5.617633265219825,5.615591735032806,5.613555302296787,5.611523954284054,5.609497678298677,5.607476461676425,5.605460291784687,5.6034491560223945,5.601443041819943,5.599441936639111,5.597445827972986,5.595454703345881,5.593468550313262,5.591487356461665,5.5895111094086225,5.5875397968025835,5.585573406322839,5.58361192567944,5.581655342613128,5.579703644895251,5.577756820327694,5.575814856742794,5.573877742003275,5.571945464002163,5.5700180106627135,5.568095369938337,5.566177529812523,5.564264478298765,5.562356203440483,5.560452693310955,5.558553936013236,5.556659919680086,5.554770632473897,5.552886062586618,5.551006198239681,5.549131027683927,5.547260539199535,5.5453947210959464,5.54353356171179,5.541677049414815,5.539825172601813,5.537977919698548,5.536135279159684,5.53429723946871,5.532463789137874,5.530634916708104,5.528810610748942,5.526990859858469,5.525175652663237,5.523364977818195,5.521558824006619,5.519757179940042,5.5179600343581825,5.516167376028875,5.514379193747999,5.512595476339409,5.510816212654865,5.509041391573965,5.50727100200407,5.50550503288024,5.503743473165162,5.501986311849082,5.500233537949737,5.498485140512283,5.496741108609231,5.495001431340376,5.493266097832729,5.49153509724045,5.48980841874478,5.488086051553973,5.486367984903227,5.4846542080546214,5.482944710297045,5.481239480946131,5.479538509344191,5.4778417848601455,5.476149296889463,5.4744610348540865,5.472776988202374,5.471097146409027,5.469421498975029,5.46775003542758,5.466082745320025,5.464419618231798,5.4627606437683465,5.461105811561076,5.45945511126728,5.457808532570076,5.456166065178342,5.454527698826651,5.452893423275209,5.451263228309788,5.449637103741663,5.448015039407552,5.446397025169546,5.444783050915051,5.443173106556724,5.441567182032407,5.439965267305065,5.438367352362728,5.43677342721842,5.435183481910106,5.433597506500621,5.432015491077614,5.4304374257534835,5.428863300665316,5.427293105974826,5.42572683186829,5.42416446855649,5.42260600627465,5.421051435282376,5.419500745863594,5.41795392832649,5.41641097300345,5.414871870250998,5.413336610449736,5.411805184004287,5.4102775813432284,5.408753792919041,5.40723380920804,5.405717620710323,5.404205217949708,5.4026965914736715,5.401191731853293,5.399690629683197,5.398193275581487,5.396699660189698,5.395209774172727,5.3937236082187825,5.392241153039322,5.390762399368995,5.389287337965588,5.387815959609962,5.386348255105997,5.384884215280534,5.383423830983321,5.381967093086951,5.380513992486807,5.379064520101005,5.377618666870339,5.3761764237582215,5.374737781750628,5.373302731856041,5.371871265105395,5.3704433725520175,5.369019045271575,5.367598274362018,5.366181050943522,5.364767366158439,5.363357211171233,5.361950577168431,5.360547455358565,5.359147836972119,5.357751713261475,5.356359075500854,5.354969914986266,5.353584223035453,5.352201990987836,5.35082321020446,5.349447872067941,5.348075967982411,5.346707489373466,5.3453424276881085,5.3439807743947,5.342622520982901,5.341267658963625,5.33991617986898,5.3385680752522156,5.337223336687675,5.335881955770738,5.334543924117768,5.333209233366064,5.331877875173805,5.330549841219997,5.329225123204424,5.3279037128475935,5.326585601890686,5.325270782095505,5.323959245244422,5.322650983140328,5.321345987606579,5.32004425048695,5.318745763645579,5.317450518966919,5.316158508355688,5.314869723736814,5.31358415705539,5.312301800276619,5.311022645385767,5.309746684388113,5.308473909308895,5.3072043121932655,5.305937885106238,5.30467462013264,5.303414509377061,5.302157544963803,5.300903719036836,5.299653023759745,5.298405451315677,5.297160993907303,5.2959196437567595,5.294681393105604,5.293446234214765,5.292214159364496,5.290985160854326,5.28975923100301,5.288536362148481,5.287316546647806,5.286099776877133,5.284886045231649,5.283675344125526,5.282467665991878,5.281263003282713,5.280061348468885,5.278862694040048,5.277667032504607,5.2764743563896745,5.275284658241021,5.274097930623028,5.272914166118644,5.271733357329339,5.270555496875052,5.269380577394153,5.26820859154339,5.267039531997848,5.265873391450902,5.264710162614169,5.2635498382174655,5.26239241100876,5.261237873754127,5.260086219237707,5.258937440261655,5.257791529646099,5.256648480229093,5.2555082848665755,5.254370936432321,5.253236427817898,5.252104751932624,5.250975901703521,5.2498498700752725,5.248726650010176,5.247606234488103,5.246488616506452,5.245373789080109,5.244261745241396,5.243152478040038,5.24204598054311,5.240942245834999,5.2398412670173595,5.238743037209071,5.237647549546193,5.236554797181925,5.23546477328656,5.234377471047445,5.2332928836689385,5.232211004372366,5.231131826395977,5.230055342994906,5.22898154744113,5.227910433023421,5.226841993047312 +dataGen_tsk_0_0_SpeciesGamma,dataGen_tsk_0_0_SpeciesGamma,"SpeciesGamma",2.0,2.012983758562952,2.0259351060758437,2.0388541123885893,2.0517408620132853,2.0645954350995153,2.0774179118252993,2.090208372309244,2.1029668965354267,2.115693564276449,2.128388455086312,2.1410516483234927,2.1536832231411362,2.166283258476408,2.1788518330696065,2.191389025468843,2.2038949140311384,2.2163695769195555,2.2288130921018086,2.2412255373501533,2.2536069902422287,2.265957528161927,2.2782772283000186,2.290566167654622,2.3028244230317303,2.31505207104556,2.3272491881189574,2.339415850483914,2.3515521341821413,2.3636581150654434,2.3757338687962193,2.3877794708479616,2.3997949965057277,2.4117805208665977,2.4237361188401385,2.435661865148874,2.447557834328757,2.459424100729613,2.4712607385156433,2.483067821665888,2.494845423974668,2.506593619052037,2.5183124803242456,2.5300020810342136,2.5416624942419856,2.5532937928251838,2.564896049479462,2.576469336718961,2.5880137268767633,2.5995292921053457,2.6110161043770286,2.6224742354844257,2.6339037570408936,2.645304740480979,2.6566772570608665,2.668021377858821,2.679337173775635,2.690624715535069,2.7018840736842966,2.7131153185943417,2.7243185204605225,2.7354937493028872,2.7466410749666523,2.7577605671226393,2.76885229526771,2.7799163287252022,2.7909527366453606,2.8019615880057707,2.812942951611789,2.8238968960969744,2.834823489923515,2.8457228013826583,2.856594898595136,2.867439849511591,2.8782577219130023,2.8890485834111073,2.899812501448826,2.9105495433006805,2.9212597760732173,2.9319432667054266,2.942600081969159,2.9532302884695456,2.9638339526454107,2.9744111407696905,2.984961918949845,2.9954863531282716,3.005984509082719,3.0164564524266955,3.0269022486098813,3.037321962918537,3.04771566047591,3.0580834062426447,3.068425265017186,3.078741301436184,3.0890315799749004,3.0992961649476096,3.1095351205080015,3.1197485106495813,3.1299363992060703,3.1400988498518054,3.150235926102136,3.1603476913138224,3.1704342086854296,3.1804955412577236,3.1905317519140666,3.200542903380807,3.210529058227675,3.2204902788681697,3.2304266275599534,3.2403381664052375,3.2502249573511723,3.2600870621902334,3.2699245425606076,3.27973745994658,3.2895258756789154,3.2992898509352444,3.309029446740444,3.31874472396702,3.3284357433354863,3.338102565414746,3.347745250622468,3.3573638592254667,3.3669584513400768,3.3765290869325306,3.3860758258193315,3.3955987276676285,3.4050978519955892,3.414573258172772,3.424025005420496,3.4334531528122123,3.4428577592738727,3.452238883584298,3.4615965843755463,3.4709309201332776,3.480241949197121,3.489529729761038,3.4987943198736877,3.5080357774387885,3.51725416021548,3.5264495258186845,3.5356219317194677,3.5447714352453956,3.5538980935808953,3.563001963767611,3.5720831027047613,3.5811415671494933,3.59017741371724,3.5991906988820723,3.608181478977051,3.617149810194581,3.626095748586762,3.635019350065738,3.643920670404047,3.65279976523497,3.661656690052879,3.6704915002135827,3.6793042509346736,3.688094997295872,3.6968637942393716,3.7056106965701816,3.7143357589564703,3.723039035929906,3.731720581885998,3.740380451084437,3.7490186976494333,3.7576353755700564,3.766230538700571,3.774804240760775,3.783356535336334,3.7918874758791166,3.8003971157075296,3.808885508006849,3.817352705829555,3.825798762095661,3.8342237295930466,3.8426276609777856,3.8510106087744767,3.859372625376571,3.8677137630467,3.8760340739170003,3.8843336099894423,3.892612423136154,3.9008705650997437,3.909108087493626,3.9173250418023424,3.9255214793818847,3.933697451460014,3.941853009136582,3.949988203383852,3.958103085046813,3.9661977048435024,3.9742721133653207,3.982326361077347,3.990360498318656,3.9983745753026314,4.00636864211728,4.0143427487255465,4.022296944965622,4.03023128055126,4.038145805072083,4.046040567993895,4.053915618658991,4.061771006286461,4.0696067799725055,4.077422988690735,4.085219681292479,4.092996906507091,4.100754712942256,4.108493149084287,4.116212263298437,4.123912103829194,4.1315927188005865,4.1392541562164835,4.146896463960894,4.154519689798264,4.162123881373783,4.169709086213672,4.177275351725485,4.184822725198407,4.192351253803546,4.19986098459423,4.2073519645063024,4.2148242403584115,4.222277858852306,4.2297128665731245,4.23712930998969,4.2445272354547985,4.251906689205508,4.259267717363428,4.2666103659350085,4.273934680811828,4.281240707770877,4.288528492474849,4.295798080472421,4.303049517198542,4.310282847974716,4.317498118009283,4.324695372397704,4.331874656122843,4.339036014055247,4.346179490953425,4.353305131464132,4.360412980122644,4.367503081353038,4.37457547946847,4.381630218671452,4.388667343054126,4.395686896598543,4.402688923176935,4.409673466551991,4.4166405703771305,4.423590278196774,4.430522633446619,4.4374376794539065,4.444335459437698,4.451216016509141,4.458079393671739,4.464925633821623,4.4717547797478145,4.478566874132499,4.485361959551287,4.4921400784734855,4.49890127326236,4.5056455861754,4.512373059364583,4.519083734876638,4.525777654653309,4.532454860531616,4.539115394244119,4.545759297419174,4.5523866115811975,4.5589973781509245,4.565591638445668,4.572169433679576,4.578730804963891,4.585275793307204,4.591804439615714,4.598316784693481,4.604812869242684,4.611292733863872,4.617756419056219,4.62420396521778,4.630635412645739,4.637050801536662,4.6434501719867525,4.649833563992096,4.656201017448913,4.662552572153808,4.668888267804019,4.675208143997664,4.681512240233991,4.687800595913621,4.694073250338798,4.700330242713632,4.706571612144347,4.7127973976395205,4.719007638110333,4.725202372370807,4.731381639138053,4.737545477032508,4.743693924578178,4.749827020202881,4.755944802238484,4.7620473089211455,4.768134578391551,4.774206648695157,4.78026355778242,4.786305343509042,4.792332043636204,4.798343695830801,4.804340337665679,4.810322006619869,4.816288740078822,4.822240575334641,4.828177549586316,4.834099699939955,4.840007063409017,4.845899676914542,4.851777577285386,4.8576408012584436,4.863489385478884,4.869323366500378,4.875142780785326,4.880947664705086,4.886738054540201,4.892513986480627,4.898275496625957,4.904022620985649,4.909755395479248,4.915473855936614,4.921178038098142,4.92686797761499,4.932543710049299,4.938205270874415,4.943852695475111,4.94948601914781,4.955105277100802,4.960710504454469,4.966301736241499,4.97187900740711,4.9774423528092635,4.9829918072188875,4.98852740532009,4.994049181710377,4.999557170900869,5.0050514073165155,5.010531925296313,5.015998759093516,5.021451942875854,5.0268915107257435,5.032317496640501,5.037729934532557,5.0431288582296645,5.048514301475115,5.053886297927947,5.059244881163154,5.0645900846719,5.069921941861724,5.07524048605675,5.080545750497898,5.0858377683430875,5.091116572667447,5.096382196463521,5.101634672641475,5.106874034029302,5.112100313373029,5.117313543336917,5.122513756503672,5.127700985374642,5.132875262370026,5.1380366198290695,5.143185090010276,5.1483207050915984,5.15344349717065,5.158553498264896,5.163650740311862,5.1687352551693255,5.173807074615522,5.178866230349339,5.1839127539905165,5.188946677079843,5.193968031079356,5.198976847372532,5.203973157264491,5.208956991982185,5.213928382674598,5.218887360412937,5.2238339561908305,5.228768200924517,5.233690125453042,5.238599760538451,5.243497136865978,5.248382285044242,5.253255235605435,5.258116019005513,5.262964665624389,5.267801205766119,5.272625669659096,5.277438087456234,5.28223848923516,5.2870269049984016,5.291803364673572,5.29656789811356,5.301320535096715,5.306061305327034,5.310790238434346,5.315507363974499,5.320212711429542,5.324906310207912,5.329588189644618,5.33425837900142,5.338916907467017,5.343563804157228,5.348199098115172,5.352822818311451,5.357434993644332,5.362035652939925,5.366624824952365,5.371202538363994,5.375768821785534,5.380323703756272,5.3848672127442345,5.389399377146368,5.393920225288717,5.398429785426594,5.402928085744766,5.407415154357624,5.411891019309361,5.416355708574148,5.420809250056307,5.425251671590488,5.429683000941839,5.434103265806184,5.438512493810191,5.442910712511551,5.447297949399144,5.451674231893215,5.456039587345545,5.460394043039619,5.464737626190798,5.469070363946493,5.473392283386327,5.477703411522313,5.482003775299014,5.486293401593718,5.490572317216604,5.494840548910908,5.499098123353091,5.50334506715301,5.507581406854075,5.511807168933424,5.516022379802083,5.520227065805134,5.524421253221878,5.528604968266001,5.532778237085735,5.536941085764022,5.541093540318682,5.54523562670257,5.549367370803737,5.553488798445599,5.557599935387092,5.5617008073228345,5.56579143988329,5.569871858634924,5.573942089080369,5.578002156658577,5.5820520867449845,5.586091904651668,5.590121635627502,5.594141304858317,5.598150937467061,5.602150558513949,5.606140192996627,5.610119865850322,5.614089601948004,5.618049426100535,5.621999363056831,5.625939437504008,5.629869674067546,5.633790097311436,5.637700731738335,5.641601601789723,5.645492731846049,5.649374146226888,5.653245869191094,5.657107924936947,5.660960337602309,5.664803131264771,5.668636329941806,5.672459957590918,5.676274038109792,5.680078595336445,5.68387365304937,5.687659234967692,5.69143536475131,5.6952020660010465,5.6989593622587975,5.702707277007676,5.706445833672161,5.710175055618246,5.713894966153578,5.717605588527611,5.721306945931747,5.724999061499482,5.72868195830655,5.732355659371067,5.736020187653678,5.739675566057697,5.7433218174292495,5.746958964557419,5.750587030174388,5.754206036955576,5.757816007519789,5.761416964429355,5.765008930190265,5.768591927252318,5.772165978009257,5.77573110479891,5.779287329903332,5.7828346755489415,5.78637316390666,5.7899028170920515,5.79342365716546,5.796935706132146,5.800438985942429,5.803933518491817,5.807419325621152,5.810896429116738,5.8143648507104855,5.817824612080039,5.821275734848919,5.824718240586653,5.828152150808914,5.831577486977652,5.834994270501229,5.838402522734554,5.841802264979213,5.845193518483607,5.848576304443082,5.85195064400006,5.855316558244175,5.858674068212402,5.862023194889188,5.865363959206587,5.868696382044387,5.872020484230241,5.875336286539798,5.878643809696834,5.88194307437338,5.88523410118985,5.888516910715173,5.891791523466918,5.895057959911427,5.898316240463937,5.901566385488713,5.904808415299171,5.908042350158009,5.91126821027733,5.91448601581877,5.917695786893625,5.920897543562973,5.924091305837805,5.927277093679145,5.930454926998177,5.933624825656371,5.936786809465603,5.939940898188281,5.943087111537472,5.946225469177018,5.949355990721665,5.952478695737184,5.955593603740489,5.958700734199767,5.961800106534593,5.964891740116054,5.967975654266871,5.971051868261516,5.974120401326338,5.977181272639677,5.9802345013319895,5.983280106485965,5.986318107136645,5.989348522271544,5.992371370830766,5.995386671707125,5.998394443746261,6.001394705746759,6.004387476460266,6.007372774591609,6.010350618798911,6.013321027693707,6.0162840198410645,6.019239613759693,6.022187827922063,6.0251286807545235,6.0280621906374146,6.030988375905182,6.033907254846493,6.036818845704352,6.03972316667621,6.042620235914082,6.045510071524662,6.04839269156943,6.051268114064772,6.054136356982087,6.056997438247904,6.059851375743989,6.06269818730746,6.0655378907309005,6.068370503762465,6.071196044105995,6.074014529421126,6.0768259773233995,6.079630405384376,6.082427831131739,6.085218272049408,6.088001745577649,6.09077826911318,6.0935478600092825,6.096310535575908,6.099066313079788,6.101815209744541,6.10455724275078,6.107292429236221,6.110020786295787,6.112742330981719,6.115457080303681,6.118165051228864,6.120866260682096,6.123560725545944,6.126248462660823,6.128929488825098,6.131603820795192,6.134271475285688,6.136932468969436,6.139586818477653,6.142234540400034,6.14487565128485,6.147510167639052,6.150138105928375,6.1527594825774425,6.155374313969867,6.157982616448354,6.160584406314801,6.163179699830405,6.165768513215757,6.16835086265095,6.170926764275677,6.17349623418933,6.176059288451106,6.178615943080102,6.181166214055416,6.183710117316252,6.186247668762014,6.188778884252405,6.191303779607531,6.193822370607996,6.196334672995001,6.198840702470446,6.201340474697022,6.203834005298314,6.206321309858897,6.208802403924431,6.211277303001764,6.213746022559022,6.21620857802571,6.218664984792809,6.221115258212868,6.223559413600105,6.225997466230498,6.228429431341884,6.230855324134054,6.233275159768844,6.235688953370237,6.238096720024449,6.240498474780029,6.242894232647954,6.245284008601717,6.247667817577427,6.250045674473896,6.252417594152739,6.25478359143846,6.25714368111855,6.259497877943579,6.261846196627283,6.264188651846662,6.266525258242069,6.268856030417301,6.2711809829396925,6.273500130340204,6.275813487113513,6.278121067718109,6.280422886576377,6.282718958074693,6.285009296563511,6.287293916357454,6.289572831735403,6.291846056940587,6.29411360618067,6.296375493627844,6.298631733418911,6.300882339655377,6.30312732640354,6.3053667076945725,6.307600497524617,6.309828709854866,6.312051358611654,6.314268457686542,6.316480020936408,6.318686062183528,6.3208865952156685,6.323081633786168,6.325271191614023,6.327455282383979,6.329633919746609,6.331807117318404,6.3339748886818565,6.336137247385544,6.338294206944216,6.340445780838876,6.342591982516868,6.344732825391961,6.346868322844427,6.3489984882211346,6.351123334835623,6.35324287596819,6.355357124865977,6.357466094743044,6.359569798780461,6.361668250126384,6.363761461896142,6.365849447172314,6.367932219004816,6.370009790410978,6.372082174375628,6.374149383851174,6.37621143175768,6.378268330982952,6.380320094382619,6.3823667347802076,6.3844082649672265,6.386444697703246,6.388476045715979,6.390502321701356,6.392523538323608,6.394539708215346,6.396550843977638,6.39855695818009,6.400558063360922,6.402554172027047,6.404545296654152,6.406531449686771,6.4085126435383675,6.41048889059141,6.412460203197449,6.414426593677194,6.416388074320593,6.418344657386905,6.420296355104782,6.422243179672339,6.4241851432572386,6.426122257996758,6.42805453599787,6.429981989337319,6.4319046300616955,6.4338224701875095,6.435735521701268,6.437643796559549,6.439547306689078,6.441446063986797,6.443340080319947,6.445229367526136,6.447113937413415,6.448993801760352,6.450868972316106,6.4527394608004975,6.454605278904086,6.456466438288243,6.4583229505852175,6.46017482739822,6.462022080301485,6.463864720840349,6.465702760531323,6.467536210862159,6.469365083291929,6.471189389251091,6.4730091401415635,6.474824347336796,6.476635022181838,6.478441175993414,6.480242820059991,6.48203996564185,6.483832623971158,6.485620806252034,6.487404523660624,6.489183787345167,6.490958608426068,6.492728997995963,6.494494967119793,6.496256526834871,6.498013688150951,6.499766462050296,6.50151485948775,6.503258891390802,6.504998568659657,6.506733902167304,6.508464902759583,6.510191581255253,6.51191394844606,6.513632015096806,6.515345791945411,6.517055289702988,6.5187605190539015,6.520461490655842,6.522158215139887,6.52385070311057,6.525538965145946,6.527223011797659,6.528902853591006,6.530578501025004,6.532249964572453,6.5339172546800075,6.535580381768235,6.537239356231686,6.538894188438957,6.540544888732753,6.542191467429957,6.5438339348216905,6.5454723011733815,6.547106576724824,6.548736771690245,6.5503628962583695,6.551984960592481,6.553602974830487,6.555216949084982,6.556826893443309,6.558432817967626,6.560034732694968,6.561632647637305,6.563226572781613,6.564816518089927,6.566402493499412,6.567984508922419,6.569562574246549,6.5711366993347164,6.572706894025207,6.574273168131743,6.575835531443543,6.577393993725383,6.578948564717657,6.580499254136439,6.582046071673543,6.583589026996583,6.585128129749035,6.586663389550297,6.588194815995746,6.589722418656804,6.591246207080992,6.592766190791993,6.59428237928971,6.595794782050325,6.597303408526361,6.5988082681467395,6.600309370316836,6.601806724418545,6.6033003398103345,6.604790225827306,6.60627639178125,6.607758846960711,6.609237600631038,6.6107126620344445,6.612184040390071,6.613651744894036,6.615115784719499,6.616576169016712,6.618032906913082,6.619486007513226,6.620935479899027,6.6223813331296935,6.623823576241811,6.6252622182494045,6.6266972681439915,6.628128734894638,6.629556627448015,6.630980954728458,6.632401725638015,6.6338189490565105,6.635232633841594,6.6366427888288,6.638049422831602,6.639452544641468,6.640852163027914,6.642248286738558,6.643640924499179,6.645030085013767,6.64641577696458,6.647798009012197,6.649176789795573,6.650552127932092,6.651924032017622,6.653292510626567,6.654657572311924,6.656019225605333,6.657377479017132,6.658732341036408,6.660083820131053,6.661431924747817,6.662776663312358,6.664118044229295,6.665456075882265,6.6667907666339685,6.668122124826228,6.669450158780036,6.670774876795609,6.672096287152439,6.673414398109347,6.6747292179045274,6.67604075475561,6.677349016859705,6.678654012393454,6.679955749513083,6.681254236354454,6.682549481033114,6.683841491644345,6.6851302762632185,6.686415842944643,6.687698199723414,6.6889773546142655,6.69025331561192,6.691526090691138,6.692795687806767,6.694062114893795,6.695325379867393,6.696585490622972,6.69784245503623,6.699096280963197,6.700346976240288,6.701594548684356,6.70283900609273,6.704080356243273,6.705318606894429,6.706553765785268,6.707785840635537,6.7090148391457065,6.710240768997023,6.711463637851552,6.712683453352227,6.7139002231228995,6.715113954768384,6.716324655874507,6.717532334008155,6.71873699671732,6.719938651531148,6.721137305959985,6.722332967495426,6.723525643610358,6.724715341759012,6.725902069377005,6.727085833881389,6.728266642670694,6.729444503124981,6.73061942260588,6.731791408456643,6.732960468002185,6.734126608549131,6.735289837385864,6.736450161782567,6.737607588991273,6.738762126245906,6.7399137807623255,6.741062559738378,6.742208470353934,6.74335151977094,6.744491715133457,6.745629063567712,6.746763572182135,6.747895248067409,6.749024098296512,6.75015012992476,6.751273349989857,6.75239376551193,6.7535113834935805,6.754626210919924,6.755738254758636,6.756847521959995,6.757954019456923,6.759057754165034,6.760158732982673,6.761256962790962,6.7623524504538395,6.763445202818108,6.764535226713473,6.765622528952588,6.766707116331094,6.767788995627667,6.768868173604056,6.7699446570051265,6.771018452558903,6.772089566976612,6.773158006952721 +time_tsk_0_0,time_tsk_0_0,"time_tsk_0_0",0.0,0.001,0.002,0.003,0.004,0.005,0.006,0.007,0.008,0.009000000000000001,0.01,0.011,0.012,0.013000000000000001,0.014,0.015,0.016,0.017,0.018000000000000002,0.019,0.02,0.021,0.022,0.023,0.024,0.025,0.026000000000000002,0.027,0.028,0.029,0.03,0.031,0.032,0.033,0.034,0.035,0.036000000000000004,0.037,0.038,0.039,0.04,0.041,0.042,0.043000000000000003,0.044,0.045,0.046,0.047,0.048,0.049,0.05,0.051000000000000004,0.052000000000000005,0.053,0.054,0.055,0.056,0.057,0.058,0.059000000000000004,0.06,0.061,0.062,0.063,0.064,0.065,0.066,0.067,0.068,0.069,0.07,0.07100000000000001,0.07200000000000001,0.073,0.074,0.075,0.076,0.077,0.078,0.079,0.08,0.081,0.082,0.083,0.084,0.085,0.08600000000000001,0.08700000000000001,0.088,0.089,0.09,0.091,0.092,0.093,0.094,0.095,0.096,0.097,0.098,0.099,0.1,0.101,0.10200000000000001,0.10300000000000001,0.10400000000000001,0.105,0.106,0.107,0.108,0.109,0.11,0.111,0.112,0.113,0.114,0.115,0.116,0.117,0.11800000000000001,0.11900000000000001,0.12,0.121,0.122,0.123,0.124,0.125,0.126,0.127,0.128,0.129,0.13,0.131,0.132,0.133,0.134,0.135,0.136,0.137,0.138,0.139,0.14,0.14100000000000001,0.14200000000000002,0.14300000000000002,0.14400000000000002,0.145,0.146,0.147,0.148,0.149,0.15,0.151,0.152,0.153,0.154,0.155,0.156,0.157,0.158,0.159,0.16,0.161,0.162,0.163,0.164,0.165,0.166,0.167,0.168,0.169,0.17,0.171,0.17200000000000001,0.17300000000000001,0.17400000000000002,0.17500000000000002,0.176,0.177,0.178,0.179,0.18,0.181,0.182,0.183,0.184,0.185,0.186,0.187,0.188,0.189,0.19,0.191,0.192,0.193,0.194,0.195,0.196,0.197,0.198,0.199,0.2,0.201,0.202,0.203,0.20400000000000001,0.20500000000000002,0.20600000000000002,0.20700000000000002,0.20800000000000002,0.209,0.21,0.211,0.212,0.213,0.214,0.215,0.216,0.217,0.218,0.219,0.22,0.221,0.222,0.223,0.224,0.225,0.226,0.227,0.228,0.229,0.23,0.231,0.232,0.233,0.234,0.23500000000000001,0.23600000000000002,0.23700000000000002,0.23800000000000002,0.23900000000000002,0.24,0.241,0.242,0.243,0.244,0.245,0.246,0.247,0.248,0.249,0.25,0.251,0.252,0.253,0.254,0.255,0.256,0.257,0.258,0.259,0.26,0.261,0.262,0.263,0.264,0.265,0.266,0.267,0.268,0.269,0.27,0.271,0.272,0.273,0.274,0.275,0.276,0.277,0.278,0.279,0.28,0.281,0.28200000000000003,0.28300000000000003,0.28400000000000003,0.28500000000000003,0.28600000000000003,0.28700000000000003,0.28800000000000003,0.289,0.29,0.291,0.292,0.293,0.294,0.295,0.296,0.297,0.298,0.299,0.3,0.301,0.302,0.303,0.304,0.305,0.306,0.307,0.308,0.309,0.31,0.311,0.312,0.313,0.314,0.315,0.316,0.317,0.318,0.319,0.32,0.321,0.322,0.323,0.324,0.325,0.326,0.327,0.328,0.329,0.33,0.331,0.332,0.333,0.334,0.335,0.336,0.337,0.338,0.339,0.34,0.341,0.342,0.343,0.34400000000000003,0.34500000000000003,0.34600000000000003,0.34700000000000003,0.34800000000000003,0.34900000000000003,0.35000000000000003,0.35100000000000003,0.352,0.353,0.354,0.355,0.356,0.357,0.358,0.359,0.36,0.361,0.362,0.363,0.364,0.365,0.366,0.367,0.368,0.369,0.37,0.371,0.372,0.373,0.374,0.375,0.376,0.377,0.378,0.379,0.38,0.381,0.382,0.383,0.384,0.385,0.386,0.387,0.388,0.389,0.39,0.391,0.392,0.393,0.394,0.395,0.396,0.397,0.398,0.399,0.4,0.401,0.402,0.403,0.404,0.405,0.406,0.40700000000000003,0.40800000000000003,0.40900000000000003,0.41000000000000003,0.41100000000000003,0.41200000000000003,0.41300000000000003,0.41400000000000003,0.41500000000000004,0.41600000000000004,0.417,0.418,0.419,0.42,0.421,0.422,0.423,0.424,0.425,0.426,0.427,0.428,0.429,0.43,0.431,0.432,0.433,0.434,0.435,0.436,0.437,0.438,0.439,0.44,0.441,0.442,0.443,0.444,0.445,0.446,0.447,0.448,0.449,0.45,0.451,0.452,0.453,0.454,0.455,0.456,0.457,0.458,0.459,0.46,0.461,0.462,0.463,0.464,0.465,0.466,0.467,0.468,0.46900000000000003,0.47000000000000003,0.47100000000000003,0.47200000000000003,0.47300000000000003,0.47400000000000003,0.47500000000000003,0.47600000000000003,0.47700000000000004,0.47800000000000004,0.47900000000000004,0.48,0.481,0.482,0.483,0.484,0.485,0.486,0.487,0.488,0.489,0.49,0.491,0.492,0.493,0.494,0.495,0.496,0.497,0.498,0.499,0.5,0.501,0.502,0.503,0.504,0.505,0.506,0.507,0.508,0.509,0.51,0.511,0.512,0.513,0.514,0.515,0.516,0.517,0.518,0.519,0.52,0.521,0.522,0.523,0.524,0.525,0.526,0.527,0.528,0.529,0.53,0.531,0.532,0.533,0.534,0.535,0.536,0.537,0.538,0.539,0.54,0.541,0.542,0.543,0.544,0.545,0.546,0.547,0.548,0.549,0.55,0.551,0.552,0.553,0.554,0.555,0.556,0.557,0.558,0.559,0.56,0.561,0.562,0.5630000000000001,0.5640000000000001,0.5650000000000001,0.5660000000000001,0.5670000000000001,0.5680000000000001,0.5690000000000001,0.5700000000000001,0.5710000000000001,0.5720000000000001,0.5730000000000001,0.5740000000000001,0.5750000000000001,0.5760000000000001,0.577,0.578,0.579,0.58,0.581,0.582,0.583,0.584,0.585,0.586,0.587,0.588,0.589,0.59,0.591,0.592,0.593,0.594,0.595,0.596,0.597,0.598,0.599,0.6,0.601,0.602,0.603,0.604,0.605,0.606,0.607,0.608,0.609,0.61,0.611,0.612,0.613,0.614,0.615,0.616,0.617,0.618,0.619,0.62,0.621,0.622,0.623,0.624,0.625,0.626,0.627,0.628,0.629,0.63,0.631,0.632,0.633,0.634,0.635,0.636,0.637,0.638,0.639,0.64,0.641,0.642,0.643,0.644,0.645,0.646,0.647,0.648,0.649,0.65,0.651,0.652,0.653,0.654,0.655,0.656,0.657,0.658,0.659,0.66,0.661,0.662,0.663,0.664,0.665,0.666,0.667,0.668,0.669,0.67,0.671,0.672,0.673,0.674,0.675,0.676,0.677,0.678,0.679,0.68,0.681,0.682,0.683,0.684,0.685,0.686,0.687,0.6880000000000001,0.6890000000000001,0.6900000000000001,0.6910000000000001,0.6920000000000001,0.6930000000000001,0.6940000000000001,0.6950000000000001,0.6960000000000001,0.6970000000000001,0.6980000000000001,0.6990000000000001,0.7000000000000001,0.7010000000000001,0.7020000000000001,0.7030000000000001,0.704,0.705,0.706,0.707,0.708,0.709,0.71,0.711,0.712,0.713,0.714,0.715,0.716,0.717,0.718,0.719,0.72,0.721,0.722,0.723,0.724,0.725,0.726,0.727,0.728,0.729,0.73,0.731,0.732,0.733,0.734,0.735,0.736,0.737,0.738,0.739,0.74,0.741,0.742,0.743,0.744,0.745,0.746,0.747,0.748,0.749,0.75,0.751,0.752,0.753,0.754,0.755,0.756,0.757,0.758,0.759,0.76,0.761,0.762,0.763,0.764,0.765,0.766,0.767,0.768,0.769,0.77,0.771,0.772,0.773,0.774,0.775,0.776,0.777,0.778,0.779,0.78,0.781,0.782,0.783,0.784,0.785,0.786,0.787,0.788,0.789,0.79,0.791,0.792,0.793,0.794,0.795,0.796,0.797,0.798,0.799,0.8,0.801,0.802,0.803,0.804,0.805,0.806,0.807,0.808,0.809,0.81,0.811,0.812,0.8130000000000001,0.8140000000000001,0.8150000000000001,0.8160000000000001,0.8170000000000001,0.8180000000000001,0.8190000000000001,0.8200000000000001,0.8210000000000001,0.8220000000000001,0.8230000000000001,0.8240000000000001,0.8250000000000001,0.8260000000000001,0.8270000000000001,0.8280000000000001,0.8290000000000001,0.8300000000000001,0.8310000000000001,0.8320000000000001,0.833,0.834,0.835,0.836,0.837,0.838,0.839,0.84,0.841,0.842,0.843,0.844,0.845,0.846,0.847,0.848,0.849,0.85,0.851,0.852,0.853,0.854,0.855,0.856,0.857,0.858,0.859,0.86,0.861,0.862,0.863,0.864,0.865,0.866,0.867,0.868,0.869,0.87,0.871,0.872,0.873,0.874,0.875,0.876,0.877,0.878,0.879,0.88,0.881,0.882,0.883,0.884,0.885,0.886,0.887,0.888,0.889,0.89,0.891,0.892,0.893,0.894,0.895,0.896,0.897,0.898,0.899,0.9,0.901,0.902,0.903,0.904,0.905,0.906,0.907,0.908,0.909,0.91,0.911,0.912,0.913,0.914,0.915,0.916,0.917,0.918,0.919,0.92,0.921,0.922,0.923,0.924,0.925,0.926,0.927,0.928,0.929,0.93,0.931,0.932,0.933,0.934,0.935,0.936,0.937,0.9380000000000001,0.9390000000000001,0.9400000000000001,0.9410000000000001,0.9420000000000001,0.9430000000000001,0.9440000000000001,0.9450000000000001,0.9460000000000001,0.9470000000000001,0.9480000000000001,0.9490000000000001,0.9500000000000001,0.9510000000000001,0.9520000000000001,0.9530000000000001,0.9540000000000001,0.9550000000000001,0.9560000000000001,0.9570000000000001,0.9580000000000001,0.9590000000000001,0.96,0.961,0.962,0.963,0.964,0.965,0.966,0.967,0.968,0.969,0.97,0.971,0.972,0.973,0.974,0.975,0.976,0.977,0.978,0.979,0.98,0.981,0.982,0.983,0.984,0.985,0.986,0.987,0.988,0.989,0.99,0.991,0.992,0.993,0.994,0.995,0.996,0.997,0.998,0.999,1.0 diff --git a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_0.png b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_0.png new file mode 100644 index 0000000000..a709083200 Binary files /dev/null and b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_0.png differ diff --git a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_1.png b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_1.png new file mode 100644 index 0000000000..d1a380f496 Binary files /dev/null and b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_1.png differ diff --git a/vcell-core/src/main/java/cbit/vcell/geometry/surface/GeometrySurfaceUtils.java b/vcell-core/src/main/java/cbit/vcell/geometry/surface/GeometrySurfaceUtils.java index c2df955759..3f67ac33b3 100644 --- a/vcell-core/src/main/java/cbit/vcell/geometry/surface/GeometrySurfaceUtils.java +++ b/vcell-core/src/main/java/cbit/vcell/geometry/surface/GeometrySurfaceUtils.java @@ -85,7 +85,7 @@ public static GeometricRegion[] getUpdatedGeometricRegions(GeometrySurfaceDescri cbit.vcell.geometry.RegionImage.RegionInfo regionInfos[] = regionImage.getRegionInfos(); for(int i = 0; i < regionInfos.length; i++){ cbit.vcell.geometry.RegionImage.RegionInfo regionInfo = regionInfos[i]; - lg.info(regionInfo); + lg.debug(regionInfo); cbit.vcell.geometry.SubVolume subVolume = geometrySpec.getSubVolume(regionInfo.getPixelValue()); String name = subVolume.getName() + regionInfo.getRegionIndex(); int numPixels = regionInfo.getNumPixels(); @@ -214,18 +214,16 @@ public static GeometricRegion[] getUpdatedGeometricRegions(GeometrySurfaceDescri } size -= sizeOfPixel * 0.125 * numOctantsToRemove; - if(lg.isInfoEnabled()){ - lg.info("size=" + size); - } + lg.debug("size={}", size); + break; } } VolumeGeometricRegion volumeRegion = new VolumeGeometricRegion(name, size, volumeUnit, subVolume, regionInfo.getRegionIndex()); regionList.add(volumeRegion); - if(lg.isInfoEnabled()){ - lg.info("added volumeRegion(" + volumeRegion.getName() + ")"); - } + lg.debug("added volumeRegion({})", volumeRegion.getName()); + } // // parse surfaceCollection into ResolvedMembraneLocations @@ -268,9 +266,8 @@ public static GeometricRegion[] getUpdatedGeometricRegions(GeometrySurfaceDescri } surfaceRegion.addAdjacentGeometricRegion(interiorVolumeRegion); interiorVolumeRegion.addAdjacentGeometricRegion(surfaceRegion); - if(lg.isInfoEnabled()){ - lg.info("added surfaceRegion(" + surfaceRegion.getName() + ")"); - } + lg.debug("added surfaceRegion({})", surfaceRegion.getName()); + } return regionList.toArray(GeometricRegion[]::new); diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/LangevinMathMapping.java b/vcell-core/src/main/java/cbit/vcell/mapping/LangevinMathMapping.java index 55b000aa46..5b51b62947 100644 --- a/vcell-core/src/main/java/cbit/vcell/mapping/LangevinMathMapping.java +++ b/vcell-core/src/main/java/cbit/vcell/mapping/LangevinMathMapping.java @@ -415,7 +415,7 @@ protected void refreshMathDescription() throws MappingException, MatrixException for (int i = 0; i < mappedSMs.length; i++) { if (mappedSMs[i] instanceof FeatureMapping){ if (mappedFM!=null){ - lg.warn("WARNING:::: MathMapping.refreshMathDescription() ... assigning boundary condition types not unique"); + lg.info("WARNING:::: MathMapping.refreshMathDescription() ... assigning boundary condition types not unique"); } mappedFM = (FeatureMapping)mappedSMs[i]; } diff --git a/vcell-core/src/main/java/cbit/vcell/mapping/ParticleMathMapping.java b/vcell-core/src/main/java/cbit/vcell/mapping/ParticleMathMapping.java index 537216c22b..024d4ee7d9 100644 --- a/vcell-core/src/main/java/cbit/vcell/mapping/ParticleMathMapping.java +++ b/vcell-core/src/main/java/cbit/vcell/mapping/ParticleMathMapping.java @@ -487,7 +487,7 @@ private void refreshMathDescription() throws MappingException, MatrixException, for (int i = 0; i < mappedSMs.length; i++) { if (mappedSMs[i] instanceof FeatureMapping){ if (mappedFM!=null){ - lg.warn("WARNING:::: MathMapping.refreshMathDescription() ... assigning boundary condition types not unique"); + lg.info("WARNING:::: MathMapping.refreshMathDescription() ... assigning boundary condition types not unique"); } mappedFM = (FeatureMapping)mappedSMs[i]; } diff --git a/vcell-core/src/main/java/cbit/vcell/publish/ITextWriter.java b/vcell-core/src/main/java/cbit/vcell/publish/ITextWriter.java index 46cacb1855..555312bcc1 100644 --- a/vcell-core/src/main/java/cbit/vcell/publish/ITextWriter.java +++ b/vcell-core/src/main/java/cbit/vcell/publish/ITextWriter.java @@ -9,6 +9,7 @@ */ package cbit.vcell.publish; + import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; @@ -19,10 +20,10 @@ import java.awt.image.IndexColorModel; import java.awt.image.WritableRaster; import java.awt.print.PageFormat; -import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; +import java.io.*; import java.util.ArrayList; import java.util.Enumeration; +import java.util.List; import java.util.Vector; import javax.imageio.IIOImage; @@ -134,41 +135,42 @@ import cbit.vcell.units.VCUnitDefinition; /** -This is the root class that handles publishing of models in the Virtual Cell. It supports the publishing of BioModels, MathModels, -and Geometries. This class should receive the object to be published, an output stream, a page format, as well as the -publishing preferences object. The same ITextWriter instance can be reused to publish different models with different preferences. - + * This is the root class that handles publishing of models in the Virtual Cell. It supports the publishing of BioModels, MathModels, + * and Geometries. This class should receive the object to be published, an output stream, a page format, as well as the + * publishing preferences object. The same ITextWriter instance can be reused to publish different models with different preferences. + *

* Creation date: (4/18/2003 2:09:05 PM) + * * @author: John Wagner & Rashad Badrawi -*/ - + */ + public abstract class ITextWriter { - private final static Logger lg = LogManager.getLogger(ITextWriter.class); - - public static final String PDF_WRITER = "PDF_WRITER"; - public static final String HTM_WRITER = "HTML_WRITER"; - protected static int DEF_IMAGE_WIDTH = 400; - protected static int DEF_IMAGE_HEIGHT = 400; - protected static final int DEF_GEOM_WIDTH = 150; - protected static final int DEF_GEOM_HEIGHT = 150; - + private final static Logger lg = LogManager.getLogger(ITextWriter.class); - //image resolution settings, for saving individual reaction and structure images. - public static final String HIGH_RESOLUTION = "high resolution"; //default_scale*2.5 - public static final String MEDIUM_RESOLUTION = "medium resolution"; //default_scale*1.5 - public static final String LOW_RESOLUTION = "low resolution"; //default_scale - - private static int DEF_FONT_SIZE = 9; - private static int DEF_HEADER_FONT_SIZE = 11; - private Font fieldFont = null; - private Font fieldBold = null; - - protected Document document; - + public static final String PDF_WRITER = "PDF_WRITER"; + public static final String HTM_WRITER = "HTML_WRITER"; + protected static int DEF_IMAGE_WIDTH = 400; + protected static int DEF_IMAGE_HEIGHT = 400; + protected static final int DEF_GEOM_WIDTH = 150; + protected static final int DEF_GEOM_HEIGHT = 150; -/** - * Comment - */ + + //image resolution settings, for saving individual reaction and structure images. + public static final String HIGH_RESOLUTION = "high resolution"; //default_scale*2.5 + public static final String MEDIUM_RESOLUTION = "medium resolution"; //default_scale*1.5 + public static final String LOW_RESOLUTION = "low resolution"; //default_scale + + private static final int DEF_FONT_SIZE = 9; + private static final int DEF_HEADER_FONT_SIZE = 11; + private Font fieldFont = null; + private Font fieldBold = null; + + protected Document document; + + + /** + * Comment + */ /* private void createRegionImageIcon() throws Exception{ final int DISPLAY_DIM_MAX = 256; @@ -337,100 +339,112 @@ public abstract class ITextWriter { } } */ -protected ITextWriter() { - super(); -} - -//helper method. - private void addImage(Section container, ByteArrayOutputStream bos) throws Exception { - com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(Toolkit.getDefaultToolkit().createImage(bos.toByteArray()), null); - //com.lowagie.text.Image structImage = com.lowagie.text.Image.getInstance(bos.toByteArray()); - //Gif structImage = new Gif(bos.toByteArray()); - //setNewPage(container, image); - image.setBackgroundColor(java.awt.Color.white); //? - Table imageTable = getTable(1,100, 2, 0, 0); - Cell imageCell = new Cell(); - imageCell.setLeading(0); - imageCell.add(image); - imageTable.addCell(imageCell); - imageTable.setTableFitsPage(true); - imageTable.setCellsFitPage(true); - container.add(imageTable); + protected ITextWriter() { + super(); + } + + //helper method. + private void addImage(Section container, ByteArrayOutputStream bos) throws Exception { + com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(Toolkit.getDefaultToolkit().createImage(bos.toByteArray()), null); + //com.lowagie.text.Image structImage = com.lowagie.text.Image.getInstance(bos.toByteArray()); + //Gif structImage = new Gif(bos.toByteArray()); + //setNewPage(container, image); + image.setBackgroundColor(java.awt.Color.white); //? + Table imageTable = getTable(1, 100, 2, 0, 0); + Cell imageCell = new Cell(); + imageCell.setLeading(0); + imageCell.add(image); + imageTable.addCell(imageCell); + imageTable.setTableFitsPage(true); + imageTable.setCellsFitPage(true); + container.add(imageTable); /* com.lowagie.text.pdf.PdfPTable imageTable = new com.lowagie.text.pdf.PdfPTable(1); imageTable.setTotalWidth(image.width()); com.lowagie.text.pdf.PdfPCell imageCell = new com.lowagie.text.pdf.PdfPCell(image); imageCell.setBorderWidth(1); */ - } + } -protected Cell createCell(String text, Font font) throws DocumentException { - - return createCell(text, font, 1, 1, Element.ALIGN_LEFT, false); -} + protected Cell createCell(String text, Font font) throws DocumentException { + return createCell(text, font, 1, 1, Element.ALIGN_LEFT, false); + } -protected Cell createCell(String text, Font font, int colspan) throws DocumentException { - return createCell(text, font, colspan, 1, Element.ALIGN_LEFT, false); -} + protected Cell createCell(String text, Font font, int colspan) throws DocumentException { + return createCell(text, font, colspan, 1, Element.ALIGN_LEFT, false); + } - protected Cell createCell(String text, Font font, int colspan, int borderWidth, int alignment, boolean isHeader) throws DocumentException { - Cell cell = new Cell(new Paragraph(text, font)); - cell.setBorderWidth(borderWidth); - cell.setHorizontalAlignment(alignment); - if (colspan > 1) { - cell.setColspan(colspan); - } - if (isHeader) { - cell.setHeader(true); - } - return(cell); - } + protected Cell createCell(String text, Font font, int colspan, int borderWidth, int alignment, boolean isHeader) throws DocumentException { + Cell cell = new Cell(new Paragraph(text, font)); + cell.setBorderWidth(borderWidth); + cell.setHorizontalAlignment(alignment); + if (colspan > 1) { + cell.setColspan(colspan); + } + if (isHeader) { + cell.setHeader(true); + } + return (cell); + } -public abstract DocWriter createDocWriter(FileOutputStream fileOutputStream) throws DocumentException; -protected Cell createHeaderCell(String text, Font font, int colspan) throws DocumentException { - - return createCell(text, font, colspan, 1, Element.ALIGN_LEFT, true); -} + public abstract DocWriter createDocWriter(FileOutputStream fileOutputStream) throws DocumentException; + + protected Cell createHeaderCell(String text, Font font, int colspan) throws DocumentException { + + return createCell(text, font, colspan, 1, Element.ALIGN_LEFT, true); + } + + public static BufferedImage generateDocReactionsImage(Model model, Integer width) throws Exception { - public static BufferedImage generateDocReactionsImage(Model model,Integer width) throws Exception { - // if (model == null || !isValidResolutionSetting(resolution)) { // throw new IllegalArgumentException("Invalid parameters for generating reactions image for model: " + model.getName()); // } - ReactionCartoon rcartoon = new ReactionCartoonFull(); - rcartoon.setModel(model); - StructureSuite structureSuite = new AllStructureSuite(new Model.Owner() { - @Override - public Model getModel() { - return model; - } - }); - rcartoon.setStructureSuite(structureSuite); - rcartoon.refreshAll(); - //dummy settings to get the real dimensions. - BufferedImage dummyBufferedImage = new BufferedImage(DEF_IMAGE_WIDTH, DEF_IMAGE_HEIGHT, BufferedImage.TYPE_3BYTE_BGR); - Graphics2D dummyGraphics = (Graphics2D)dummyBufferedImage.getGraphics(); - Dimension prefDim = rcartoon.getPreferedCanvasSize(dummyGraphics); - dummyGraphics.dispose(); + ReactionCartoon rcartoon = new ReactionCartoonFull(); + rcartoon.setModel(model); + StructureSuite structureSuite = new AllStructureSuite(() -> model); + rcartoon.setStructureSuite(structureSuite); + rcartoon.refreshAll(); + //dummy settings to get the real dimensions. + BufferedImage dummyBufferedImage = new BufferedImage(DEF_IMAGE_WIDTH, DEF_IMAGE_HEIGHT, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D dummyGraphics = (Graphics2D) dummyBufferedImage.getGraphics(); + Dimension prefDim = rcartoon.getPreferedCanvasSize(dummyGraphics); + dummyGraphics.dispose(); // double width = prefDim.getWidth(); // double height = prefDim.getHeight(); - double widthHeightRatio = (double)prefDim.getWidth()/(double)prefDim.getHeight(); - if(width == null){ - width = ITextWriter.DEF_IMAGE_WIDTH; - } - int height = (int)((double)width/widthHeightRatio); - + double widthHeightRatio = prefDim.getWidth() / prefDim.getHeight(); + if (width == null) { + width = ITextWriter.DEF_IMAGE_WIDTH; + } + Dimension newDim = ITextWriter.getNewDimensions(width, widthHeightRatio); + + rcartoon.getResizeManager().setZoomPercent((int) (100 * width / prefDim.getWidth())); + + BufferedImage bufferedImage = new BufferedImage(newDim.width, newDim.height, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = (Graphics2D) bufferedImage.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + GraphContainerLayoutReactions containerLayout = new GraphContainerLayoutReactions(); + containerLayout.layout(rcartoon, g, prefDim); + + rcartoon.paint(g); + g.dispose(); + return bufferedImage; + } + + private static Dimension getNewDimensions(Integer width, double widthHeightRatio) { + int height = (int) ((double) width / widthHeightRatio); + // int MAX_IMAGE_HEIGHT = 532; // if (width < ITextWriter.DEF_IMAGE_WIDTH) { // width = ITextWriter.DEF_IMAGE_WIDTH; -// } +// } // height= height * width/prefDim.getWidth(); // if (height < ITextWriter.DEF_IMAGE_HEIGHT) { // height = ITextWriter.DEF_IMAGE_HEIGHT; @@ -438,38 +452,24 @@ public Model getModel() { // height = MAX_IMAGE_HEIGHT; // } // width= width * height/prefDim.getHeight(); - Dimension newDim = new Dimension((int)width,(int)height); - - rcartoon.getResizeManager().setZoomPercent((int)(100*width/prefDim.getWidth())); - - BufferedImage bufferedImage = new BufferedImage(newDim.width,newDim.height, BufferedImage.TYPE_3BYTE_BGR); - Graphics2D g = (Graphics2D)bufferedImage.getGraphics(); - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - GraphContainerLayoutReactions containerLayout = new GraphContainerLayoutReactions(); - containerLayout.layout(rcartoon, g, prefDim); - - rcartoon.paint(g); - g.dispose(); - return bufferedImage; + return new Dimension(width, height); } - - public static ByteArrayOutputStream encodeJPEG(BufferedImage bufferedImage) throws Exception{ - ImageWriter imageWriter = ImageIO.getImageWritersBySuffix("jpeg").next(); - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(byteArrayOutputStream); - imageWriter.setOutput(imageOutputStream); - ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam(); - imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - imageWriteParam.setCompressionQuality(1.0f); // quality 0(very compressed, lossy) -> 1.0(less compressed,loss-less) - IIOImage iioImage = new IIOImage(bufferedImage, null, null); - imageWriter.write(null, iioImage, imageWriteParam); - imageOutputStream.close(); - imageWriter.dispose(); - return byteArrayOutputStream; - } + public static ByteArrayOutputStream encodeJPEG(BufferedImage bufferedImage) throws Exception { + ImageWriter imageWriter = ImageIO.getImageWritersBySuffix("jpeg").next(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(byteArrayOutputStream); + imageWriter.setOutput(imageOutputStream); + ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam(); + imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + imageWriteParam.setCompressionQuality(1.0f); // quality 0(very compressed, lossy) -> 1.0(less compressed,loss-less) + IIOImage iioImage = new IIOImage(bufferedImage, null, null); + imageWriter.write(null, iioImage, imageWriteParam); + imageOutputStream.close(); + imageWriter.dispose(); + return byteArrayOutputStream; + } //pretty similar to its static counterpart /* @@ -518,187 +518,192 @@ protected ByteArrayOutputStream generateDocStructureImage(Model model, String re } */ - protected ByteArrayOutputStream generateGeometryImage(Geometry geom) throws Exception{ + protected ByteArrayOutputStream generateGeometryImage(Geometry geom) throws Exception { + + GeometrySpec geomSpec = geom.getGeometrySpec(); + IndexColorModel icm = DisplayAdapterService.getHandleColorMap(); + geom.precomputeAll(new GeometryThumbnailImageFactoryAWT()); + VCImage geomImage = geomSpec.getSampledImage().getCurrentValue(); + if (geomImage == null) { + throw new Exception("generateGeometryImage error : No Image"); + } + int x = geomImage.getNumX(); + int y = geomImage.getNumY(); + int z = geomImage.getNumZ(); + + BufferedImage bufferedImage = null; + WritableRaster pixelWR = null; + Image adjImage; + BufferedImage newBufferedImage = null; + + if (geom.getDimension() > 0 && geom.getDimension() < 3) { + bufferedImage = new BufferedImage(x, y, BufferedImage.TYPE_BYTE_INDEXED, icm); + pixelWR = bufferedImage.getRaster(); + for (int i = 0; i < x; i++) { + for (int j = 0; j < y; j++) { + pixelWR.setSample(i, j, 0, geomImage.getPixel(i, j, 0)); + } + + } + // Adjust the image width and height + // retaining the aspect ratio. Start by adjusting the height, then adjust width to maintain aspect ratio. + + double scaleFactor = 1.0; + if (x * scaleFactor > DEF_GEOM_WIDTH) { + scaleFactor = ((double) DEF_GEOM_WIDTH) / x; + } + if (y * scaleFactor > DEF_GEOM_HEIGHT) { + scaleFactor = ((double) DEF_GEOM_HEIGHT) / y; + } + int adjX = (int) Math.ceil(x * scaleFactor); + int adjY = (int) Math.ceil(y * scaleFactor); + + adjImage = bufferedImage.getScaledInstance(adjX, adjY, BufferedImage.SCALE_REPLICATE); + newBufferedImage = new BufferedImage(adjX, adjY, BufferedImage.TYPE_BYTE_INDEXED, icm); + newBufferedImage.getGraphics().drawImage(adjImage, 0, 0, null); + } else if (geom.getDimension() == 3) { + WritableRaster smallPixelWR = null; + int[] cmap = new int[256]; + final int DISPLAY_DIM_MAX = 256; + + try { + // int RGB interpretation as follows: + // int bits(32): (alpha)31-24,(red)23-16,(green)15-8,(blue)7-0 + // for alpha: 0-most transparent(see-through), 255-most opaque(solid) + + //Reset colormap (grayscale) + for (int i = 0; i < cmap.length; i += 1) { + int iv = 0x000000FF & i; + cmap[i] = 0xFF << 24 | iv << 16 | iv << 8 | i; + } + //stretch cmap grays + if (geomImage != null && geomImage.getPixelClasses().length < 32) { + for (int i = 0; i < geomImage.getPixelClasses().length; i += 1) { + int stretchIndex = 0xFF & geomImage.getPixelClasses()[i].getPixel(); + int newI = 32 + (i * ((256 - 32) / geomImage.getPixelClasses().length)); + cmap[stretchIndex] = 0xFF << 24 | newI << 16 | newI << 8 | newI; + } + } + //Set grid color + cmap[cmap.length - 1] = 0xFFFFFFFF; //white + + //Initialize image data + int xSide = 0; + int ySide = 0; + if (pixelWR == null) { + double side = Math.sqrt(x * y * z); + xSide = (int) Math.round(side / (double) x); + if (xSide == 0) { + xSide = 1; + } + if (xSide > z) { + xSide = z; + } + ySide = (int) Math.ceil((double) z / (double) xSide); + if (ySide == 0) { + ySide = 1; + } + if (ySide > z) { + ySide = z; + } + pixelWR = icm.createCompatibleWritableRaster(xSide * x, ySide * y); + byte[] sib = geomImage.getPixels(); + + //write the image to buffer + int ystride = x; + int zstride = x * y; + for (int row = 0; row < ySide; row += 1) { + for (int col = 0; col < xSide; col += 1) { + int xoffset = col * x; + int yoffset = (row * y); + int zoffset = (col + (row * xSide)) * zstride; + if (zoffset >= sib.length) { + for (int xi = 0; xi < x; xi += 1) { + for (int yi = 0; yi < y; yi += 1) { + pixelWR.setSample(xi + xoffset, yi + yoffset, 0, cmap.length - 1); + } + } + } else { + for (int xi = 0; xi < x; xi += 1) { + for (int yi = 0; yi < y; yi += 1) { + pixelWR.setSample(xi + xoffset, yi + yoffset, 0, 0xFF & sib[xi + (ystride * yi) + zoffset]); + } + } + } + } + } + + // scale if necessary + double displayScale = 1.0; + if (pixelWR.getWidth() < DISPLAY_DIM_MAX || pixelWR.getHeight() < DISPLAY_DIM_MAX) { + displayScale = Math.min((DISPLAY_DIM_MAX / pixelWR.getWidth()), (DISPLAY_DIM_MAX / pixelWR.getHeight())); + if (displayScale == 0) { + displayScale = 1; + } + } + if ((displayScale == 1) && (pixelWR.getWidth() > DISPLAY_DIM_MAX || pixelWR.getHeight() > DISPLAY_DIM_MAX)) { + displayScale = Math.max((pixelWR.getWidth() / DISPLAY_DIM_MAX), (pixelWR.getHeight() / DISPLAY_DIM_MAX)); + //displayScale = Math.min(((double)DISPLAY_DIM_MAX/(double)pixelWR.getWidth()),((double)DISPLAY_DIM_MAX/(double)pixelWR.getHeight())); + if (displayScale == 0) { + displayScale = 1; + } + displayScale = 1.0 / displayScale; + } + if (displayScale != 1) { + java.awt.geom.AffineTransform at = new java.awt.geom.AffineTransform(); + at.setToScale(displayScale, displayScale); + java.awt.image.AffineTransformOp ato = new java.awt.image.AffineTransformOp(at, java.awt.image.AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + smallPixelWR = ato.createCompatibleDestRaster(pixelWR); + ato.filter(pixelWR, smallPixelWR); + } + } + + //Create display image, re-use image data and colormap + // draw labels and grid + if (pixelWR != null) { + bufferedImage = new java.awt.image.BufferedImage(icm, smallPixelWR, false, null); + + if (xSide > 0 || ySide > 0) { + float gridXBlockLen = ((float) (bufferedImage.getWidth()) / xSide); + float gridYBlockLen = ((float) (bufferedImage.getHeight()) / ySide); + + java.awt.Graphics g = bufferedImage.getGraphics(); + g.setColor(java.awt.Color.white); + // horiz lines + for (int row = 0; row < ySide; row += 1) { + if (row > 0) { + g.drawLine(0, (int) (row * gridYBlockLen), bufferedImage.getWidth(), (int) (row * gridYBlockLen)); + } + } + // vert lines + for (int col = 0; col < xSide; col += 1) { + if (col > 0) { + g.drawLine((int) (col * gridXBlockLen), 0, (int) (col * gridXBlockLen), bufferedImage.getHeight()); + } + } + // z markers + if (xSide > 1 || ySide > 1) { + for (int row = 0; row < xSide; row += 1) { + for (int col = 0; col < ySide; col += 1) { + g.drawString("" + (1 + row + (col * xSide)), (int) (row * gridXBlockLen) + 3, (int) (col * gridYBlockLen) + 12); + } + } + } + } + } + } catch (Throwable e) { + throw new Exception("CreateGeometryImageIcon error\n" + (e.getMessage() != null ? e.getMessage() : e.getClass().getName())); + } + + // Adjust the image width and height + adjImage = bufferedImage.getScaledInstance(smallPixelWR.getWidth(), smallPixelWR.getHeight(), BufferedImage.SCALE_REPLICATE); + newBufferedImage = new BufferedImage(smallPixelWR.getWidth(), smallPixelWR.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, icm); + newBufferedImage.getGraphics().drawImage(adjImage, 0, 0, null); + } - GeometrySpec geomSpec = geom.getGeometrySpec(); - IndexColorModel icm = DisplayAdapterService.getHandleColorMap(); - geom.precomputeAll(new GeometryThumbnailImageFactoryAWT()); - VCImage geomImage = geomSpec.getSampledImage().getCurrentValue(); - if(geomImage == null){ - throw new Exception("generateGeometryImage error : No Image"); - } - int x = geomImage.getNumX(); - int y = geomImage.getNumY(); - int z = geomImage.getNumZ(); - - BufferedImage bufferedImage = null; - WritableRaster pixelWR = null; - Image adjImage = null; - BufferedImage newBufferedImage = null; - - if (geom.getDimension() > 0 && geom.getDimension() < 3) { - bufferedImage = new BufferedImage(x, y, BufferedImage.TYPE_BYTE_INDEXED, icm); - pixelWR = bufferedImage.getRaster(); - for (int i = 0; i < x; i++){ - for (int j = 0; j < y; j++){ - pixelWR.setSample(i , j, 0, geomImage.getPixel(i, j, 0)); - } - - } - // Adjust the image width and height - // retaining the aspect ratio. Start by adjusting the height, then adjust width to maintain aspect ratio. - - double scaleFactor = 1.0; - if (x * scaleFactor > DEF_GEOM_WIDTH) { - scaleFactor = ((double) DEF_GEOM_WIDTH) / x; - } - if (y * scaleFactor > DEF_GEOM_HEIGHT) { - scaleFactor = ((double) DEF_GEOM_HEIGHT) / y; - } - int adjX = (int)Math.ceil(x*scaleFactor); - int adjY = (int)Math.ceil(y*scaleFactor); - - adjImage = bufferedImage.getScaledInstance(adjX, adjY, BufferedImage.SCALE_REPLICATE); - newBufferedImage = new BufferedImage(adjX, adjY, BufferedImage.TYPE_BYTE_INDEXED, icm); - newBufferedImage.getGraphics().drawImage(adjImage, 0, 0, null); - } else if (geom.getDimension() == 3) { - WritableRaster smallPixelWR = null; - int[] cmap = new int[256]; - final int DISPLAY_DIM_MAX = 256; - - try{ - // int RGB interpretation as follows: - // int bits(32): (alpha)31-24,(red)23-16,(green)15-8,(blue)7-0 - // for alpha: 0-most transparent(see-through), 255-most opaque(solid) - - //Reset colormap (grayscale) - for(int i = 0; i < cmap.length; i += 1){ - int iv = (int)(0x000000FF&i); - cmap[i] = 0xFF<<24 | iv<<16 | iv<<8 | i; - } - //stretch cmap grays - if(geomImage != null && geomImage.getPixelClasses().length < 32){ - for(int i=0;i< geomImage.getPixelClasses().length;i+= 1){ - int stretchIndex = (int)(0xFF&geomImage.getPixelClasses()[i].getPixel()); - int newI = 32+(i*((256-32)/geomImage.getPixelClasses().length)); - cmap[stretchIndex] = 0xFF<<24 | newI<<16 | newI<<8 | newI; - } - } - //Set grid color - cmap[cmap.length-1] = 0xFFFFFFFF; //white - - //Initialize image data - int xSide = 0; - int ySide = 0; - if(pixelWR == null){ - VCImage sampledImage = geomImage; - double side = Math.sqrt(x*y*z); - xSide = (int)Math.round(side/(double)x); - if(xSide == 0){xSide = 1;} - if(xSide > z){ - xSide = z; - } - ySide = (int)Math.ceil((double)z/(double)xSide); - if(ySide == 0){ySide = 1;} - if(ySide > z){ - ySide = z; - } - pixelWR = icm.createCompatibleWritableRaster(xSide*x,ySide*y); - byte[] sib = sampledImage.getPixels(); - - //write the image to buffer - int ystride = x; - int zstride = x*y; - for(int row = 0; row < ySide; row += 1){ - for(int col = 0; col < xSide; col += 1){ - int xoffset = col*x; - int yoffset = (row*y); - int zoffset = (col+(row*xSide))*zstride; - if(zoffset >= sib.length){ - for(int xi = 0; xi < x; xi += 1){ - for(int yi = 0; yi < y; yi += 1){ - pixelWR.setSample(xi + xoffset, yi + yoffset, 0, cmap.length-1); - } - } - }else{ - for(int xi = 0; xi < x; xi += 1){ - for(int yi = 0; yi < y; yi += 1){ - pixelWR.setSample(xi + xoffset, yi + yoffset,0,(int)(0xFF&sib[xi + (ystride*yi) + zoffset])); - } - } - } - } - } - - // scale if necessary - double displayScale = 1.0; - if(pixelWR.getWidth() < DISPLAY_DIM_MAX || pixelWR.getHeight() < DISPLAY_DIM_MAX){ - displayScale = (int)Math.min((DISPLAY_DIM_MAX/pixelWR.getWidth()),(DISPLAY_DIM_MAX/pixelWR.getHeight())); - if(displayScale == 0){displayScale = 1;} - } - if((displayScale == 1) && (pixelWR.getWidth() > DISPLAY_DIM_MAX || pixelWR.getHeight() > DISPLAY_DIM_MAX)){ - displayScale = Math.max((pixelWR.getWidth()/DISPLAY_DIM_MAX),(pixelWR.getHeight()/DISPLAY_DIM_MAX)); - //displayScale = Math.min(((double)DISPLAY_DIM_MAX/(double)pixelWR.getWidth()),((double)DISPLAY_DIM_MAX/(double)pixelWR.getHeight())); - if(displayScale == 0) {displayScale = 1;} - displayScale = 1.0/displayScale; - } - if(displayScale != 1){ - java.awt.geom.AffineTransform at = new java.awt.geom.AffineTransform(); - at.setToScale(displayScale, displayScale); - java.awt.image.AffineTransformOp ato = new java.awt.image.AffineTransformOp(at,java.awt.image.AffineTransformOp.TYPE_NEAREST_NEIGHBOR); - smallPixelWR = ato.createCompatibleDestRaster(pixelWR); - ato.filter(pixelWR, smallPixelWR); - } - } - - //Create display image, re-use image data and colormap - // draw labels and grid - if(pixelWR != null){ - bufferedImage = new java.awt.image.BufferedImage(icm,smallPixelWR,false,null); - - if(xSide > 0 || ySide > 0){ - float gridXBlockLen = ((float)(bufferedImage.getWidth())/xSide); - float gridYBlockLen = ((float)(bufferedImage.getHeight())/ySide); - - java.awt.Graphics g = bufferedImage.getGraphics(); - g.setColor(java.awt.Color.white); - // horiz lines - for(int row=0;row < ySide;row+= 1){ - if(row > 0){ - g.drawLine(0,(int)(row*gridYBlockLen),bufferedImage.getWidth(),(int)(row*gridYBlockLen)); - } - } - // vert lines - for(int col=0;col 0){ - g.drawLine((int)(col*gridXBlockLen),0,(int)(col*gridXBlockLen),bufferedImage.getHeight()); - } - } - // z markers - if(xSide > 1 || ySide > 1){ - for(int row=0;row < xSide;row+= 1){ - for(int col=0;col 0) { - Chapter simContextsChapter = new Chapter("Applications For " + name, chapterNum++); - if (introSection == null) { - introSection = simContextsChapter.addSection("General Info", simContextsChapter.getNumberDepth() + 1); - String freeTextAnnotation = bioModel.getVCMetaData().getFreeTextAnnotation(bioModel); - writeMetadata(introSection, name, freeTextAnnotation, userName, "BioModel"); - } - for (int i = 0; i < simContexts.length; i++) { - writeSimulationContext(simContextsChapter, simContexts[i], preferences); - } - document.add(simContextsChapter); - } else { - System.err.println("Bad Request: No applications to publish for Biomodel: " + bioModel.getName()); - } - } - document.close(); -} + public void writeBioModel(BioModel bioModel, FileOutputStream fos, PageFormat pageFormat, PublishPreferences preferences) throws DocumentException { - protected void writeEquation(Section container, Equation eq) throws DocumentException { - - if (eq instanceof FilamentRegionEquation) { - writeFilamentRegionEquation(container, (FilamentRegionEquation)eq); - } else if (eq instanceof MembraneRegionEquation) { - writeMemRegionEquation(container, (MembraneRegionEquation)eq); - } else if (eq instanceof OdeEquation) { - writeOdeEquation(container, (OdeEquation)eq); - } else if (eq instanceof PdeEquation) { - writePdeEquation(container, (PdeEquation)eq); - } else if (eq instanceof VolumeRegionEquation) { - writeVolumeRegionEquation(container, (VolumeRegionEquation)eq); - } - } + if (bioModel == null || fos == null || pageFormat == null || preferences == null) { + throw new IllegalArgumentException("One or more null params while publishing BioModel."); + } + this.createDocument(pageFormat); + this.createDocWriter(fos); + // Add metadata before you open the document... + String name = bioModel.getName().trim(); + String userName = "Unknown"; + if (bioModel.getVersion() != null) { + userName = bioModel.getVersion().getOwner().getName(); + } + document.addTitle(name + "[owned by " + userName + "]"); + document.addCreator("Virtual Cell"); + document.addCreationDate(); + //writeWatermark(document, pageFormat); + writeHeaderFooter("BioModel: " + name); + document.open(); + // + Section introSection = null; + int chapterNum = 1; + if (preferences.includePhysio()) { + Chapter physioChapter = new Chapter("Physiology For " + name, chapterNum++); + introSection = physioChapter.addSection("General Info", physioChapter.getNumberDepth() + 1); + String freeTextAnnotation = bioModel.getVCMetaData().getFreeTextAnnotation(bioModel); + writeMetadata(introSection, name, freeTextAnnotation, userName, "BioModel"); + writeModel(physioChapter, bioModel.getModel()); + document.add(physioChapter); + } + if (preferences.includeApp()) { + SimulationContext[] simContexts = bioModel.getSimulationContexts(); + if (simContexts.length > 0) { + Chapter simContextsChapter = new Chapter("Applications For " + name, chapterNum++); + if (introSection == null) { + introSection = simContextsChapter.addSection("General Info", simContextsChapter.getNumberDepth() + 1); + String freeTextAnnotation = bioModel.getVCMetaData().getFreeTextAnnotation(bioModel); + writeMetadata(introSection, name, freeTextAnnotation, userName, "BioModel"); + } + for (SimulationContext simContext : simContexts) { + writeSimulationContext(simContextsChapter, simContext, preferences); + } + document.add(simContextsChapter); + } else { + lg.error("Bad Request: No applications to publish for Biomodel: {}", bioModel.getName()); + } + } + document.close(); + } + + protected void writeEquation(Section container, Equation eq) throws DocumentException { + + if (eq instanceof FilamentRegionEquation) { + writeFilamentRegionEquation(container, (FilamentRegionEquation) eq); + } else if (eq instanceof MembraneRegionEquation) { + writeMemRegionEquation(container, (MembraneRegionEquation) eq); + } else if (eq instanceof OdeEquation) { + writeOdeEquation(container, (OdeEquation) eq); + } else if (eq instanceof PdeEquation) { + writePdeEquation(container, (PdeEquation) eq); + } else if (eq instanceof VolumeRegionEquation) { + writeVolumeRegionEquation(container, (VolumeRegionEquation) eq); + } + } - protected void writeFastSystem(Section container, FastSystem fs) throws DocumentException { - - Table eqTable = getTable(2, 100, 1, 2, 2); - eqTable.addCell(createCell(VCML.FastSystem, - getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - eqTable.endHeaders(); - Enumeration enum_fi = fs.getFastInvariants(); - while (enum_fi.hasMoreElements()){ - FastInvariant fi = enum_fi.nextElement(); - eqTable.addCell(createCell(VCML.FastInvariant, getFont())); - eqTable.addCell(createCell(fi.getFunction().infix(), getFont())); - } - Enumeration enum_fr = fs.getFastRates(); - while (enum_fr.hasMoreElements()){ - FastRate fr = enum_fr.nextElement(); - eqTable.addCell(createCell(VCML.FastRate, getFont())); - eqTable.addCell(createCell(fr.getFunction().infix(), getFont())); - } - - container.add(eqTable); - } + protected void writeFastSystem(Section container, FastSystem fs) throws DocumentException { + Table eqTable = getTable(2, 100, 1, 2, 2); + eqTable.addCell(createCell(VCML.FastSystem, + getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + eqTable.endHeaders(); + Enumeration enum_fi = fs.getFastInvariants(); + while (enum_fi.hasMoreElements()) { + FastInvariant fi = enum_fi.nextElement(); + eqTable.addCell(createCell(VCML.FastInvariant, getFont())); + eqTable.addCell(createCell(fi.getFunction().infix(), getFont())); + } + Enumeration enum_fr = fs.getFastRates(); + while (enum_fr.hasMoreElements()) { + FastRate fr = enum_fr.nextElement(); + eqTable.addCell(createCell(VCML.FastRate, getFont())); + eqTable.addCell(createCell(fr.getFunction().infix(), getFont())); + } - protected void writeFilamentRegionEquation(Section container, FilamentRegionEquation eq) throws DocumentException { + container.add(eqTable); + } - Table eqTable = getTable(2, 100, 1, 2, 2); - eqTable.addCell(createCell(VCML.FilamentRegionEquation + " " + eq.getVariable().getName(), - getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - eqTable.endHeaders(); - String exp = "0.0"; - eqTable.addCell(createCell(VCML.FilamentRate, getFont())); - if (eq.getFilamentRateExpression() != null) { - exp = eq.getFilamentRateExpression().infix(); - } - eqTable.addCell(createCell(exp, getFont())); - if (eq.getInitialExpression() != null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); - } - int solutionType = eq.getSolutionType(); - switch (solutionType) { - case Equation.UNKNOWN_SOLUTION:{ - if (eq.getInitialExpression() == null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell("0.0", getFont())); - } - break; - } - case Equation.EXACT_SOLUTION:{ - eqTable.addCell(createCell(VCML.Exact, getFont())); - eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); - break; - } - } - - container.add(eqTable); - } + protected void writeFilamentRegionEquation(Section container, FilamentRegionEquation eq) throws DocumentException { -//Section used can be a chapter or a section of one, based on the document type. - protected void writeGeom(Section container, Geometry geom, GeometryContext geomCont) throws Exception { - - try { - Section geomSection = container.addSection("Geometry: " + geom.getName(), container.getNumberDepth() + 1); - if (geom.getDimension() == 0) { - Paragraph p = new Paragraph(new Phrase("Non spatial geometry.")); - p.setAlignment(Paragraph.ALIGN_CENTER); - geomSection.add(p); - return; - } - ByteArrayOutputStream bos = generateGeometryImage(geom); - com.lowagie.text.Image geomImage = com.lowagie.text.Image.getInstance(java.awt.Toolkit.getDefaultToolkit().createImage(bos.toByteArray()), null); - geomImage.setAlignment(Table.ALIGN_LEFT); - geomSection.add(geomImage); - //addImage(geomSection, bos); - Table geomTable = getTable(2, 50, 1, 2, 2); - geomTable.setAlignment(Table.ALIGN_LEFT); - Extent extent = geom.getExtent(); - String extentStr = "(" + extent.getX() + ", " + extent.getY() + ", " + extent.getZ() + ")"; - Origin origin = geom.getOrigin(); - String originStr = "(" + origin.getX() + ", " + origin.getY() + ", " + origin.getZ() + ")"; - geomTable.addCell(createCell("Size", getFont())); - geomTable.addCell(createCell(extentStr, getFont())); - geomTable.addCell(createCell("Origin", getFont())); - geomTable.addCell(createCell(originStr, getFont())); - geomSection.add(geomTable); - } catch (Exception e) { - lg.error("Unable to add geometry image to report.", e); - } - } + Table eqTable = getTable(2, 100, 1, 2, 2); + eqTable.addCell(createCell(VCML.FilamentRegionEquation + " " + eq.getVariable().getName(), + getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + eqTable.endHeaders(); + String exp = "0.0"; + eqTable.addCell(createCell(VCML.FilamentRate, getFont())); + if (eq.getFilamentRateExpression() != null) { + exp = eq.getFilamentRateExpression().infix(); + } + eqTable.addCell(createCell(exp, getFont())); + if (eq.getInitialExpression() != null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); + } + int solutionType = eq.getSolutionType(); + switch (solutionType) { + case Equation.UNKNOWN_SOLUTION: { + if (eq.getInitialExpression() == null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell("0.0", getFont())); + } + break; + } + case Equation.EXACT_SOLUTION: { + eqTable.addCell(createCell(VCML.Exact, getFont())); + eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); + break; + } + } + container.add(eqTable); + } + + + //Section used can be a chapter or a section of one, based on the document type. + protected void writeGeom(Section container, Geometry geom, GeometryContext geomCont) { + + try { + Section geomSection = container.addSection("Geometry: " + geom.getName(), container.getNumberDepth() + 1); + if (geom.getDimension() == 0) { + Paragraph p = new Paragraph(new Phrase("Non spatial geometry.")); + p.setAlignment(Paragraph.ALIGN_CENTER); + geomSection.add(p); + return; + } + ByteArrayOutputStream bos = generateGeometryImage(geom); + com.lowagie.text.Image geomImage = com.lowagie.text.Image.getInstance(java.awt.Toolkit.getDefaultToolkit().createImage(bos.toByteArray()), null); + geomImage.setAlignment(Table.ALIGN_LEFT); + geomSection.add(geomImage); + //addImage(geomSection, bos); + Table geomTable = getTable(2, 50, 1, 2, 2); + geomTable.setAlignment(Table.ALIGN_LEFT); + Extent extent = geom.getExtent(); + String extentStr = "(" + extent.getX() + ", " + extent.getY() + ", " + extent.getZ() + ")"; + Origin origin = geom.getOrigin(); + String originStr = "(" + origin.getX() + ", " + origin.getY() + ", " + origin.getZ() + ")"; + geomTable.addCell(createCell("Size", getFont())); + geomTable.addCell(createCell(extentStr, getFont())); + geomTable.addCell(createCell("Origin", getFont())); + geomTable.addCell(createCell(originStr, getFont())); + geomSection.add(geomTable); + } catch (Exception e) { + lg.error("Unable to add geometry image to report.", e); + } + } - public void writeGeometry(Geometry geom, FileOutputStream fos, PageFormat format) throws Exception { - writeGeometry(geom, fos, format, PublishPreferences.DEFAULT_GEOM_PREF); - } + public void writeGeometry(Geometry geom, FileOutputStream fos, PageFormat format) throws Exception { + writeGeometry(geom, fos, format, PublishPreferences.DEFAULT_GEOM_PREF); + } - //for now, the preferences for a geometry is a dummy. - public void writeGeometry(Geometry geom, FileOutputStream fos, PageFormat pageFormat, PublishPreferences preferences) throws Exception { - - if (geom == null || fos == null || pageFormat == null || preferences == null) { - throw new IllegalArgumentException("One or more null params while publishing Geometry."); - } - try { - createDocument(pageFormat); - createDocWriter(fos); - //Add metadata before you open the document... - String name = geom.getName().trim(); - String userName = "Unknown"; - if (geom.getVersion() != null) { - userName = geom.getVersion().getOwner().getName(); - } - document.addTitle(name + "[owned by " + userName + "]"); - document.addCreator("Virtual Cell"); - document.addCreationDate(); - //writeWatermark(document, pageFormat); - writeHeaderFooter("Geometry: " + name); - document.open(); - // - Section introSection = null; - int chapterNum = 1; - Chapter geomChapter = new Chapter("Geometry", chapterNum++); - introSection = geomChapter.addSection("General Info", geomChapter.getNumberDepth() + 1); - writeMetadata(introSection, name, geom.getDescription(), userName, "Geometry"); - Section geomSection = geomChapter.addSection("Geometry", geomChapter.getNumberDepth() + 1); //title? - writeGeom(geomSection, geom, null); - document.add(geomChapter); - document.close(); - } catch (DocumentException e) { - lg.error("Unable to publish BioModel.", e); - throw e; - } - } + //for now, the preferences for a geometry is a dummy. + public void writeGeometry(Geometry geom, FileOutputStream fos, PageFormat pageFormat, PublishPreferences preferences) throws Exception { -/** - * Default is no header or footer... - */ -protected void writeHeaderFooter(String headerStr) throws DocumentException { + if (geom == null || fos == null || pageFormat == null || preferences == null) { + throw new IllegalArgumentException("One or more null params while publishing Geometry."); + } + try { + createDocument(pageFormat); + createDocWriter(fos); + //Add metadata before you open the document... + String name = geom.getName().trim(); + String userName = "Unknown"; + if (geom.getVersion() != null) { + userName = geom.getVersion().getOwner().getName(); + } + document.addTitle(name + "[owned by " + userName + "]"); + document.addCreator("Virtual Cell"); + document.addCreationDate(); + //writeWatermark(document, pageFormat); + writeHeaderFooter("Geometry: " + name); + document.open(); + // + Section introSection; + int chapterNum = 1; + Chapter geomChapter = new Chapter("Geometry", chapterNum++); + introSection = geomChapter.addSection("General Info", geomChapter.getNumberDepth() + 1); + writeMetadata(introSection, name, geom.getDescription(), userName, "Geometry"); + Section geomSection = geomChapter.addSection("Geometry", geomChapter.getNumberDepth() + 1); //title? + writeGeom(geomSection, geom, null); + document.add(geomChapter); + document.close(); + } catch (DocumentException e) { + lg.error("Unable to publish BioModel.", e); + throw e; + } + } -} + /** + * Default is no header or footer... + */ + protected void writeHeaderFooter(String headerStr) throws DocumentException { - protected void writeJumpCondition(Section container, JumpCondition eq) throws DocumentException { + } - Table eqTable = getTable(2, 100, 1, 2, 2); - eqTable.addCell(createCell(VCML.JumpCondition + " " + eq.getVariable().getName(), - getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - eqTable.endHeaders(); - String exp = "0.0"; - eqTable.addCell(createCell(VCML.InFlux, getFont())); - if (eq.getInFluxExpression() != null) { - exp = eq.getInFluxExpression().infix(); - } - eqTable.addCell(createCell(exp, getFont())); - exp = "0.0"; - eqTable.addCell(createCell(VCML.OutFlux, getFont())); - if (eq.getOutFluxExpression() != null) { - exp = eq.getOutFluxExpression().infix(); - } - eqTable.addCell(createCell(exp, getFont())); - - container.add(eqTable); - } + protected void writeJumpCondition(Section container, JumpCondition eq) throws DocumentException { - protected void writeKineticsParams(Section reactionSection, ReactionStep rs) throws DocumentException { - - Kinetics.KineticsParameter kineticsParameters[] = rs.getKinetics().getKineticsParameters(); - Table paramTable = null; - int widths [] = {1, 4, 3, 2}; - if (kineticsParameters.length > 0) { - paramTable = getTable(4, 100, 1, 3, 3); - paramTable.addCell(createCell("Kinetics Parameters", getBold(DEF_HEADER_FONT_SIZE), 4, 1, Element.ALIGN_CENTER, true)); - paramTable.addCell(createHeaderCell("Name", getBold(), 1)); - paramTable.addCell(createHeaderCell("Expression", getBold(), 1)); - paramTable.addCell(createHeaderCell("Role", getBold(), 1)); - paramTable.addCell(createHeaderCell("Unit", getBold(), 1)); - paramTable.endHeaders(); - for (int k = 0; k < kineticsParameters.length; k++) { - String name = kineticsParameters[k].getName(); - Expression expression = kineticsParameters[k].getExpression(); - String role = rs.getKinetics().getDefaultParameterDesc(kineticsParameters[k].getRole()); - VCUnitDefinition unit = kineticsParameters[k].getUnitDefinition(); - paramTable.addCell(createCell(name, getFont())); - paramTable.addCell(createCell((expression == null ? "": expression.infix()), getFont())); - paramTable.addCell(createCell(role, getFont())); - paramTable.addCell(createCell((unit == null ? "": unit.getSymbolUnicode()), getFont())); //dimensionless will show as '1'. - } - } - if (paramTable != null) { - paramTable.setWidths(widths); - reactionSection.add(paramTable); - } - } + Table eqTable = getTable(2, 100, 1, 2, 2); + eqTable.addCell(createCell(VCML.JumpCondition + " " + eq.getVariable().getName(), + getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + eqTable.endHeaders(); + String exp = "0.0"; + eqTable.addCell(createCell(VCML.InFlux, getFont())); + if (eq.getInFluxExpression() != null) { + exp = eq.getInFluxExpression().infix(); + } + eqTable.addCell(createCell(exp, getFont())); + exp = "0.0"; + eqTable.addCell(createCell(VCML.OutFlux, getFont())); + if (eq.getOutFluxExpression() != null) { + exp = eq.getOutFluxExpression().infix(); + } + eqTable.addCell(createCell(exp, getFont())); + + container.add(eqTable); + } + + + protected void writeKineticsParams(Section reactionSection, ReactionStep rs) throws DocumentException { + + Kinetics.KineticsParameter[] kineticsParameters = rs.getKinetics().getKineticsParameters(); + Table paramTable = null; + int[] widths = {1, 4, 3, 2}; + if (kineticsParameters.length > 0) { + paramTable = getTable(4, 100, 1, 3, 3); + paramTable.addCell(createCell("Kinetics Parameters", getBold(DEF_HEADER_FONT_SIZE), 4, 1, Element.ALIGN_CENTER, true)); + paramTable.addCell(createHeaderCell("Name", getBold(), 1)); + paramTable.addCell(createHeaderCell("Expression", getBold(), 1)); + paramTable.addCell(createHeaderCell("Role", getBold(), 1)); + paramTable.addCell(createHeaderCell("Unit", getBold(), 1)); + paramTable.endHeaders(); + for (Kinetics.KineticsParameter kineticsParameter : kineticsParameters) { + String name = kineticsParameter.getName(); + Expression expression = kineticsParameter.getExpression(); + String role = rs.getKinetics().getDefaultParameterDesc(kineticsParameter.getRole()); + VCUnitDefinition unit = kineticsParameter.getUnitDefinition(); + paramTable.addCell(createCell(name, getFont())); + paramTable.addCell(createCell((expression == null ? "" : expression.infix()), getFont())); + paramTable.addCell(createCell(role, getFont())); + paramTable.addCell(createCell((unit == null ? "" : unit.getSymbolUnicode()), getFont())); //dimensionless will show as '1'. + } + } + if (paramTable != null) { + paramTable.setWidths(widths); + reactionSection.add(paramTable); + } + } -//container can be a chapter or a section of a chapter. + //container can be a chapter or a section of a chapter. //MathDescription.description ignored. //currently not used. - protected void writeMathDescAsImages(Section container, MathDescription mathDesc) throws DocumentException { + protected void writeMathDescAsImages(Section container, MathDescription mathDesc) throws DocumentException { - if (mathDesc == null) { - return; - } - Section mathDescSection = container.addSection("Math Description: " + mathDesc.getName(), container.getDepth() + 1); - Section mathDescSubSection = null; - Expression expArray [] = null; - BufferedImage dummy = new BufferedImage(500, 50, BufferedImage.TYPE_3BYTE_BGR); - int scale = 1; - int viewableWidth = (int)(document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin()); - //add Constants - Enumeration constantsList = mathDesc.getConstants(); - while (constantsList.hasMoreElements()) { - Constant constant = constantsList.nextElement(); - Expression exp = constant.getExpression(); - try { - expArray = new Expression[] { Expression.assign(new Expression(constant.getName()), exp.flatten()) }; - } catch(ExpressionException ee) { - lg.error("Unable to process constant " + constant.getName() + " for publishing", ee); - continue; - } - try { - Dimension dim = ExpressionCanvas.getExpressionImageSize(expArray, (Graphics2D)dummy.getGraphics()); - BufferedImage bufferedImage = new BufferedImage((int)dim.getWidth()*scale, (int)dim.getHeight()*scale, BufferedImage.TYPE_3BYTE_BGR); - ExpressionCanvas.getExpressionAsImage(expArray, bufferedImage, scale); - com.lowagie.text.Image expImage = com.lowagie.text.Image.getInstance(bufferedImage, null); - expImage.setAlignment(com.lowagie.text.Image.ALIGN_LEFT); - if (mathDescSubSection == null) { - mathDescSubSection = mathDescSection.addSection("Constants", mathDescSection.getDepth() + 1); - } - if (viewableWidth < Math.floor(expImage.getScaledWidth())) { - expImage.scaleToFit(viewableWidth, expImage.getPlainHeight()); - System.out.println("Constant After scaling: " + expImage.getScaledWidth()); - } - mathDescSubSection.add(expImage); - } catch (Exception e) { - lg.error("Unable to add structure mapping image to report.", e); - } - } - mathDescSubSection = null; - //add functions - Enumeration functionsList = mathDesc.getFunctions(); - while (functionsList.hasMoreElements()) { - Function function = functionsList.nextElement(); - Expression exp = function.getExpression(); - try { - expArray = new Expression[] { Expression.assign(new Expression(function.getName()), exp.flatten()) }; - } catch(ExpressionException ee) { - lg.error("Unable to process function " + function.getName() + " for publishing", ee); - continue; - } - try { - Dimension dim = ExpressionCanvas.getExpressionImageSize(expArray, (Graphics2D)dummy.getGraphics()); - BufferedImage bufferedImage = new BufferedImage((int)dim.getWidth()*scale, (int)dim.getHeight()*scale, BufferedImage.TYPE_3BYTE_BGR); - ExpressionCanvas.getExpressionAsImage(expArray, bufferedImage, scale); - com.lowagie.text.Image expImage = com.lowagie.text.Image.getInstance(bufferedImage, null); - expImage.setAlignment(com.lowagie.text.Image.ALIGN_LEFT); - if (mathDescSubSection == null) { - mathDescSubSection = mathDescSection.addSection("Functions", mathDescSection.getDepth() + 1); - } - if (viewableWidth < Math.floor(expImage.getScaledWidth())) { - expImage.scaleToFit(viewableWidth, expImage.getHeight()); - System.out.println("Function After scaling: " + expImage.getScaledWidth()); - } - mathDescSubSection.add(expImage); - } catch (Exception e) { - lg.error("Unable to add structure mapping image to report.", e); - } - } - writeSubDomainsEquationsAsImages(mathDescSection, mathDesc); - } + if (mathDesc == null) { + return; + } + Section mathDescSection = container.addSection("Math Description: " + mathDesc.getName(), container.getDepth() + 1); + Section mathDescSubSection = null; + Expression[] expArray; + BufferedImage dummy = new BufferedImage(500, 50, BufferedImage.TYPE_3BYTE_BGR); + int scale = 1; + int viewableWidth = (int) (document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin()); + //add Constants + Enumeration constantsList = mathDesc.getConstants(); + while (constantsList.hasMoreElements()) { + Constant constant = constantsList.nextElement(); + Expression exp = constant.getExpression(); + try { + expArray = new Expression[]{Expression.assign(new Expression(constant.getName()), exp.flatten())}; + } catch (ExpressionException ee) { + lg.error("Unable to process constant " + constant.getName() + " for publishing", ee); + continue; + } + try { + Dimension dim = ExpressionCanvas.getExpressionImageSize(expArray, (Graphics2D) dummy.getGraphics()); + BufferedImage bufferedImage = new BufferedImage((int) dim.getWidth() * scale, (int) dim.getHeight() * scale, BufferedImage.TYPE_3BYTE_BGR); + ExpressionCanvas.getExpressionAsImage(expArray, bufferedImage, scale); + com.lowagie.text.Image expImage = com.lowagie.text.Image.getInstance(bufferedImage, null); + expImage.setAlignment(com.lowagie.text.Image.ALIGN_LEFT); + if (mathDescSubSection == null) { + mathDescSubSection = mathDescSection.addSection("Constants", mathDescSection.getDepth() + 1); + } + if (viewableWidth < Math.floor(expImage.getScaledWidth())) { + expImage.scaleToFit(viewableWidth, expImage.getPlainHeight()); + lg.debug("Constant After scaling: {}", expImage.getScaledWidth()); + } + mathDescSubSection.add(expImage); + } catch (Exception e) { + lg.error("Unable to add structure mapping image to report.", e); + } + } + mathDescSubSection = null; + //add functions + Enumeration functionsList = mathDesc.getFunctions(); + while (functionsList.hasMoreElements()) { + Function function = functionsList.nextElement(); + Expression exp = function.getExpression(); + try { + expArray = new Expression[]{Expression.assign(new Expression(function.getName()), exp.flatten())}; + } catch (ExpressionException ee) { + lg.error("Unable to process function " + function.getName() + " for publishing", ee); + continue; + } + try { + Dimension dim = ExpressionCanvas.getExpressionImageSize(expArray, (Graphics2D) dummy.getGraphics()); + BufferedImage bufferedImage = new BufferedImage((int) dim.getWidth() * scale, (int) dim.getHeight() * scale, BufferedImage.TYPE_3BYTE_BGR); + ExpressionCanvas.getExpressionAsImage(expArray, bufferedImage, scale); + com.lowagie.text.Image expImage = com.lowagie.text.Image.getInstance(bufferedImage, null); + expImage.setAlignment(com.lowagie.text.Image.ALIGN_LEFT); + if (mathDescSubSection == null) { + mathDescSubSection = mathDescSection.addSection("Functions", mathDescSection.getDepth() + 1); + } + if (viewableWidth < Math.floor(expImage.getScaledWidth())) { + expImage.scaleToFit(viewableWidth, expImage.getHeight()); + lg.debug("Function After scaling: {}", expImage.getScaledWidth()); + } + mathDescSubSection.add(expImage); + } catch (Exception e) { + lg.error("Unable to add structure mapping image to report.", e); + } + } + writeSubDomainsEquationsAsImages(mathDescSection, mathDesc); + } - //container can be a chapter or a section of a chapter. - //MathDescription.description ignored. - protected void writeMathDescAsText(Section container, MathDescription mathDesc) throws DocumentException { + //container can be a chapter or a section of a chapter. + //MathDescription.description ignored. + protected void writeMathDescAsText(Section container, MathDescription mathDesc) throws DocumentException { - if (mathDesc == null) { - return; - } - Section mathDescSection = container.addSection("Math Description: " + mathDesc.getName(), container.getDepth() + 1); - Section mathDescSubSection = null; - Table expTable = null; - int widths [] = {2, 8}; - //add Constants - Enumeration constantsList = mathDesc.getConstants(); - while (constantsList.hasMoreElements()) { - Constant constant = constantsList.nextElement(); - Expression exp = constant.getExpression(); - if (expTable == null) { - expTable = getTable(2, 100, 1, 2, 2); - expTable.addCell(createHeaderCell("Constant Name", getBold(), 1)); - expTable.addCell(createHeaderCell("Expression", getBold(), 1)); - expTable.setWidths(widths); - expTable.endHeaders(); - } - //widths[0] = Math.max(constant.getName().length(), widths[0]); - //widths[1] = Math.max(exp.infix().length(), widths[1]); - expTable.addCell(createCell(constant.getName(), getFont())); - expTable.addCell(createCell(exp.infix(), getFont())); - } - //expTable.setWidths(widths); breaks the contents of the cell, also, widths[1] = widths[1]/widths[0], widths[0] = 1 - if (expTable != null) { - mathDescSubSection = mathDescSection.addSection("Constants", mathDescSection.getDepth() + 1); - mathDescSubSection.add(expTable); - expTable = null; - } - mathDescSubSection = null; - //add functions - Enumeration functionsList = mathDesc.getFunctions(); - while (functionsList.hasMoreElements()) { - Function function = functionsList.nextElement(); - Expression exp = function.getExpression(); - if (expTable == null) { - expTable = getTable(2, 100, 1, 2, 2); - expTable.addCell(createHeaderCell("Function Name", getBold(), 1)); - expTable.addCell(createHeaderCell("Expression", getBold(), 1)); - expTable.endHeaders(); - expTable.setWidths(widths); - } - expTable.addCell(createCell(function.getName(), getFont())); - expTable.addCell(createCell(exp.infix(), getFont())); - } - if (expTable != null) { - mathDescSubSection = mathDescSection.addSection("Functions", mathDescSection.getDepth() + 1); - mathDescSubSection.add(expTable); - } - writeSubDomainsEquationsAsText(mathDescSection, mathDesc); - } + if (mathDesc == null) { + return; + } + Section mathDescSection = container.addSection("Math Description: " + mathDesc.getName(), container.getDepth() + 1); + Section mathDescSubSection; + Table expTable = null; + int[] widths = {2, 8}; + //add Constants + Enumeration constantsList = mathDesc.getConstants(); + while (constantsList.hasMoreElements()) { + Constant constant = constantsList.nextElement(); + Expression exp = constant.getExpression(); + if (expTable == null) { + expTable = getTable(2, 100, 1, 2, 2); + expTable.addCell(createHeaderCell("Constant Name", getBold(), 1)); + expTable.addCell(createHeaderCell("Expression", getBold(), 1)); + expTable.setWidths(widths); + expTable.endHeaders(); + } + //widths[0] = Math.max(constant.getName().length(), widths[0]); + //widths[1] = Math.max(exp.infix().length(), widths[1]); + expTable.addCell(createCell(constant.getName(), getFont())); + expTable.addCell(createCell(exp.infix(), getFont())); + } + //expTable.setWidths(widths); breaks the contents of the cell, also, widths[1] = widths[1]/widths[0], widths[0] = 1 + if (expTable != null) { + mathDescSubSection = mathDescSection.addSection("Constants", mathDescSection.getDepth() + 1); + mathDescSubSection.add(expTable); + expTable = null; + } + mathDescSubSection = null; + //add functions + Enumeration functionsList = mathDesc.getFunctions(); + while (functionsList.hasMoreElements()) { + Function function = functionsList.nextElement(); + Expression exp = function.getExpression(); + if (expTable == null) { + expTable = getTable(2, 100, 1, 2, 2); + expTable.addCell(createHeaderCell("Function Name", getBold(), 1)); + expTable.addCell(createHeaderCell("Expression", getBold(), 1)); + expTable.endHeaders(); + expTable.setWidths(widths); + } + expTable.addCell(createCell(function.getName(), getFont())); + expTable.addCell(createCell(exp.infix(), getFont())); + } + if (expTable != null) { + mathDescSubSection = mathDescSection.addSection("Functions", mathDescSection.getDepth() + 1); + mathDescSubSection.add(expTable); + } + writeSubDomainsEquationsAsText(mathDescSection, mathDesc); + } - public void writeMathModel(MathModel mathModel, FileOutputStream fos, PageFormat pageFormat) throws Exception { + public void writeMathModel(MathModel mathModel, FileOutputStream fos, PageFormat pageFormat) throws Exception { - writeMathModel(mathModel, fos, pageFormat, PublishPreferences.DEFAULT_MATH_PREF); - } + writeMathModel(mathModel, fos, pageFormat, PublishPreferences.DEFAULT_MATH_PREF); + } - public void writeMathModel(MathModel mathModel, FileOutputStream fos, PageFormat pageFormat, PublishPreferences preferences) throws Exception { + public void writeMathModel(MathModel mathModel, FileOutputStream fos, PageFormat pageFormat, PublishPreferences preferences) throws Exception { - if (mathModel == null || fos == null || pageFormat == null || preferences == null) { - throw new IllegalArgumentException("One or more null params while publishing MathModel."); - } - try { - createDocument(pageFormat); - createDocWriter(fos); - // Add metadata before you open the document... - String name = mathModel.getName().trim(); - String userName = "Unknown"; - if (mathModel.getVersion() != null) { - userName = mathModel.getVersion().getOwner().getName(); - } - document.addTitle(name + "[owned by " + userName + "]"); - document.addCreator("Virtual Cell"); - document.addCreationDate(); - //writeWatermark(document, pageFormat); - writeHeaderFooter("MathModel: " + name); - document.open(); - // - int chapterNum = 1; - Section introSection = null; - if (preferences.includeMathDesc()) { - MathDescription mathDesc = mathModel.getMathDescription(); - Chapter mathDescChapter = new Chapter("Math Description", chapterNum++); - introSection = mathDescChapter.addSection("General Info", mathDescChapter.getNumberDepth() + 1); - writeMetadata(introSection, name, mathModel.getDescription(), userName, "MathModel"); - writeMathDescAsText(mathDescChapter, mathDesc); - document.add(mathDescChapter); - } - if (preferences.includeSim()) { //unlike biomodels, simulations are chapters, not chapter sections. - Simulation [] sims = mathModel.getSimulations(); - if (sims != null) { - Chapter simChapter = new Chapter("Simulations", chapterNum++); - if (introSection == null) { - introSection = simChapter.addSection("General Info", simChapter.getNumberDepth() + 1); - writeMetadata(introSection, name, mathModel.getDescription(), userName, "MathModel"); - } - for (int i = 0; i < sims.length; i++) { - writeSimulation(simChapter, sims[i]); - } - document.add(simChapter); - } else { - System.err.println("Bad Request: No simulations to publish for Mathmodel: " + name); - } - } - if (preferences.includeGeom()) { //unlike biomodels, geometry is a chapter, not a chapter section. - Geometry geom = mathModel.getMathDescription().getGeometry(); - if (geom != null) { - Chapter geomChapter = new Chapter("Geometry", chapterNum++); - if (introSection == null) { - introSection = geomChapter.addSection("General Info", geomChapter.getNumberDepth() + 1); - writeMetadata(introSection, name, mathModel.getDescription(), userName, "MathModel"); - } - writeGeom(geomChapter, geom, null); - document.add(geomChapter); - } else { - System.err.println("Bad Request: No geometry to publish for Mathmodel: " + name); - } - } - document.close(); - } catch (DocumentException e) { - lg.error("Unable to publish MathModel.", e); - throw e; - } - } + if (mathModel == null || fos == null || pageFormat == null || preferences == null) { + throw new IllegalArgumentException("One or more null params while publishing MathModel."); + } + try { + createDocument(pageFormat); + createDocWriter(fos); + // Add metadata before you open the document... + String name = mathModel.getName().trim(); + String userName = "Unknown"; + if (mathModel.getVersion() != null) { + userName = mathModel.getVersion().getOwner().getName(); + } + document.addTitle(name + "[owned by " + userName + "]"); + document.addCreator("Virtual Cell"); + document.addCreationDate(); + //writeWatermark(document, pageFormat); + writeHeaderFooter("MathModel: " + name); + document.open(); + // + int chapterNum = 1; + Section introSection = null; + if (preferences.includeMathDesc()) { + MathDescription mathDesc = mathModel.getMathDescription(); + Chapter mathDescChapter = new Chapter("Math Description", chapterNum++); + introSection = mathDescChapter.addSection("General Info", mathDescChapter.getNumberDepth() + 1); + writeMetadata(introSection, name, mathModel.getDescription(), userName, "MathModel"); + writeMathDescAsText(mathDescChapter, mathDesc); + document.add(mathDescChapter); + } + if (preferences.includeSim()) { //unlike biomodels, simulations are chapters, not chapter sections. + Simulation[] sims = mathModel.getSimulations(); + if (sims != null) { + Chapter simChapter = new Chapter("Simulations", chapterNum++); + if (introSection == null) { + introSection = simChapter.addSection("General Info", simChapter.getNumberDepth() + 1); + writeMetadata(introSection, name, mathModel.getDescription(), userName, "MathModel"); + } + for (Simulation sim : sims) { + writeSimulation(simChapter, sim); + } + document.add(simChapter); + } else { + lg.error("Bad Request: No simulations to publish for Mathmodel: {}", name); + } + } + if (preferences.includeGeom()) { //unlike biomodels, geometry is a chapter, not a chapter section. + Geometry geom = mathModel.getMathDescription().getGeometry(); + if (geom != null) { + Chapter geomChapter = new Chapter("Geometry", chapterNum++); + if (introSection == null) { + introSection = geomChapter.addSection("General Info", geomChapter.getNumberDepth() + 1); + writeMetadata(introSection, name, mathModel.getDescription(), userName, "MathModel"); + } + writeGeom(geomChapter, geom, null); + document.add(geomChapter); + } else { + lg.error("Bad Request: No geometry to publish for Mathmodel: {}", name); + } + } + document.close(); + } catch (DocumentException e) { + lg.error("Unable to publish MathModel.", e); + throw e; + } + } - protected void writeMembraneMapping(Section simContextSection, SimulationContext simContext) throws DocumentException { + protected void writeMembraneMapping(Section simContextSection, SimulationContext simContext) throws DocumentException { - GeometryContext geoContext = simContext.getGeometryContext(); - if (geoContext == null) { - return; - } - Section memMapSection = null; - Table memMapTable = null; - StructureMapping structMappings [] = geoContext.getStructureMappings(); - for (int i = 0; i < structMappings.length; i++) { - MembraneMapping memMapping = null; - if (structMappings[i] instanceof FeatureMapping) { - continue; - } else { - memMapping = (MembraneMapping)structMappings[i]; - } - String structName = memMapping.getStructure().getName(); - String initVoltage = ""; - Expression tempExp = memMapping.getInitialVoltageParameter().getExpression(); - VCUnitDefinition tempUnit = memMapping.getInitialVoltageParameter().getUnitDefinition(); - if (tempExp != null) { - initVoltage = tempExp.infix(); - if (tempUnit != null) { - initVoltage += " " + tempUnit.getSymbolUnicode(); - } - } - String spCap = ""; - tempExp = memMapping.getSpecificCapacitanceParameter().getExpression(); - tempUnit = memMapping.getSpecificCapacitanceParameter().getUnitDefinition(); - if (tempExp != null) { - spCap = tempExp.infix(); - if (tempUnit != null) { - spCap += " " + tempUnit.getSymbolUnicode(); - } - } - if (memMapTable == null) { - memMapTable = getTable(4, 100, 1, 3, 3); - memMapTable.addCell(createCell("Electrical Mapping - Membrane Potential", getBold(DEF_HEADER_FONT_SIZE), 4, 1, Element.ALIGN_CENTER, true)); - memMapTable.addCell(createHeaderCell("Membrane", getBold(), 1)); - memMapTable.addCell(createHeaderCell("Calculate V (T/F)", getBold(), 1)); - memMapTable.addCell(createHeaderCell("V initial", getBold(), 1)); - memMapTable.addCell(createHeaderCell("Specific Capacitance", getBold(), 1)); - memMapTable.endHeaders(); - } - memMapTable.addCell(createCell(structName, getFont())); - memMapTable.addCell(createCell((memMapping.getCalculateVoltage() ? " T ": " F "), getFont())); - memMapTable.addCell(createCell(initVoltage, getFont())); - memMapTable.addCell(createCell(spCap, getFont())); - } - if (memMapTable != null) { - memMapSection = simContextSection.addSection("Membrane Mapping For " + simContext.getName(), simContextSection.getNumberDepth() + 1); - memMapSection.add(memMapTable); - } - int[] widths = {1, 1, 1, 5, 8}; - Table electTable = null; - ElectricalStimulus[] electricalStimuli = simContext.getElectricalStimuli(); - for (int j = 0; j < electricalStimuli.length; j++) { - if (j == 0) { - electTable = getTable(5, 100, 1, 3, 3); - electTable.addCell(createCell("Electrical Mapping - Electrical Stimulus", getBold(DEF_HEADER_FONT_SIZE), 5, 1, Element.ALIGN_CENTER, true)); - electTable.addCell(createHeaderCell("Stimulus Name", getBold(), 1)); - electTable.addCell(createHeaderCell("Current Name", getBold(), 1)); - electTable.addCell(createHeaderCell("Clamp Type", getBold(), 1)); - electTable.addCell(createHeaderCell("Voltage/Current Density", getBold(), 1)); - electTable.addCell(createHeaderCell("Clamp Device", getBold(), 1)); - electTable.endHeaders(); - } - String stimName = electricalStimuli[j].getName(); - String currName = ""; - String clampType = "", expStr = ""; - Expression tempExp = null; - VCUnitDefinition tempUnit = null; - if (electricalStimuli[j] instanceof CurrentDensityClampStimulus) { - CurrentDensityClampStimulus stimulus = (CurrentDensityClampStimulus) electricalStimuli[j]; - LocalParameter currentDensityParameter = stimulus.getCurrentDensityParameter(); - tempExp = currentDensityParameter.getExpression(); - tempUnit = currentDensityParameter.getUnitDefinition(); - clampType = "Current Density (deprecated)"; - } else if (electricalStimuli[j] instanceof TotalCurrentClampStimulus) { - TotalCurrentClampStimulus stimulus = (TotalCurrentClampStimulus) electricalStimuli[j]; - LocalParameter totalCurrentParameter = stimulus.getCurrentParameter(); - tempExp = totalCurrentParameter.getExpression(); - tempUnit = totalCurrentParameter.getUnitDefinition(); - clampType = "Current"; - } else if (electricalStimuli[j] instanceof VoltageClampStimulus) { - VoltageClampStimulus stimulus = (VoltageClampStimulus) electricalStimuli[j]; - Parameter voltageParameter = stimulus.getVoltageParameter(); - tempExp = voltageParameter.getExpression(); - tempUnit = voltageParameter.getUnitDefinition(); - clampType = "Voltage"; - } - if (tempExp != null) { - expStr = tempExp.infix(); - if (tempUnit != null) { - expStr += " " + tempUnit.getSymbolUnicode(); - } - } - electTable.addCell(createCell(stimName, getFont())); - electTable.addCell(createCell(currName, getFont())); - electTable.addCell(createCell(clampType, getFont())); - electTable.addCell(createCell(expStr, getFont())); - //add electrode info - Electrode electrode = electricalStimuli[j].getElectrode(); - if (electrode == null) { - electTable.addCell(createCell("N/A", getFont())); - } else { - Coordinate c = electrode.getPosition(); - String location = c.getX() + ", " + c.getY() + ", " + c.getZ(); - String featureName = electrode.getFeature().getName(); - electTable.addCell(createCell("(" + location + ") in " + featureName, getFont())); - } - } - if (electTable != null) { - if (memMapSection == null) { - memMapSection = simContextSection.addSection("Membrane Mapping For " + simContext.getName(), 1); - } - electTable.setWidths(widths); - memMapSection.add(electTable); - } - //add temperature - Table tempTable = getTable(1, 75, 1, 3, 3); - tempTable.setAlignment(Table.ALIGN_LEFT); - tempTable.addCell(createCell("Temperature: " + simContext.getTemperatureKelvin() + " K", getFont())); - if (memMapSection != null) { - memMapSection.add(tempTable); - } - } + GeometryContext geoContext = simContext.getGeometryContext(); + if (geoContext == null) { + return; + } + Section memMapSection = null; + Table memMapTable = null; + StructureMapping[] structMappings = geoContext.getStructureMappings(); + for (StructureMapping structMapping : structMappings) { + MembraneMapping memMapping; + if (structMapping instanceof FeatureMapping) { + continue; + } else { + memMapping = (MembraneMapping) structMapping; + } + String structName = memMapping.getStructure().getName(); + String initVoltage = ""; + Expression tempExp = memMapping.getInitialVoltageParameter().getExpression(); + VCUnitDefinition tempUnit = memMapping.getInitialVoltageParameter().getUnitDefinition(); + if (tempExp != null) { + initVoltage = tempExp.infix(); + if (tempUnit != null) { + initVoltage += " " + tempUnit.getSymbolUnicode(); + } + } + String spCap = ""; + tempExp = memMapping.getSpecificCapacitanceParameter().getExpression(); + tempUnit = memMapping.getSpecificCapacitanceParameter().getUnitDefinition(); + if (tempExp != null) { + spCap = tempExp.infix(); + if (tempUnit != null) { + spCap += " " + tempUnit.getSymbolUnicode(); + } + } + if (memMapTable == null) { + memMapTable = getTable(4, 100, 1, 3, 3); + memMapTable.addCell(createCell("Electrical Mapping - Membrane Potential", getBold(DEF_HEADER_FONT_SIZE), 4, 1, Element.ALIGN_CENTER, true)); + memMapTable.addCell(createHeaderCell("Membrane", getBold(), 1)); + memMapTable.addCell(createHeaderCell("Calculate V (T/F)", getBold(), 1)); + memMapTable.addCell(createHeaderCell("V initial", getBold(), 1)); + memMapTable.addCell(createHeaderCell("Specific Capacitance", getBold(), 1)); + memMapTable.endHeaders(); + } + memMapTable.addCell(createCell(structName, getFont())); + memMapTable.addCell(createCell((memMapping.getCalculateVoltage() ? " T " : " F "), getFont())); + memMapTable.addCell(createCell(initVoltage, getFont())); + memMapTable.addCell(createCell(spCap, getFont())); + } + if (memMapTable != null) { + memMapSection = simContextSection.addSection("Membrane Mapping For " + simContext.getName(), simContextSection.getNumberDepth() + 1); + memMapSection.add(memMapTable); + } + int[] widths = {1, 1, 1, 5, 8}; + Table electTable = null; + ElectricalStimulus[] electricalStimuli = simContext.getElectricalStimuli(); + for (int j = 0; j < electricalStimuli.length; j++) { + if (j == 0) { + electTable = getTable(5, 100, 1, 3, 3); + electTable.addCell(createCell("Electrical Mapping - Electrical Stimulus", getBold(DEF_HEADER_FONT_SIZE), 5, 1, Element.ALIGN_CENTER, true)); + electTable.addCell(createHeaderCell("Stimulus Name", getBold(), 1)); + electTable.addCell(createHeaderCell("Current Name", getBold(), 1)); + electTable.addCell(createHeaderCell("Clamp Type", getBold(), 1)); + electTable.addCell(createHeaderCell("Voltage/Current Density", getBold(), 1)); + electTable.addCell(createHeaderCell("Clamp Device", getBold(), 1)); + electTable.endHeaders(); + } + String stimName = electricalStimuli[j].getName(); + String currName = ""; + String clampType = "", expStr = ""; + Expression tempExp = null; + VCUnitDefinition tempUnit = null; + if (electricalStimuli[j] instanceof CurrentDensityClampStimulus stimulus) { + LocalParameter currentDensityParameter = stimulus.getCurrentDensityParameter(); + tempExp = currentDensityParameter.getExpression(); + tempUnit = currentDensityParameter.getUnitDefinition(); + clampType = "Current Density (deprecated)"; + } else if (electricalStimuli[j] instanceof TotalCurrentClampStimulus stimulus) { + LocalParameter totalCurrentParameter = stimulus.getCurrentParameter(); + tempExp = totalCurrentParameter.getExpression(); + tempUnit = totalCurrentParameter.getUnitDefinition(); + clampType = "Current"; + } else if (electricalStimuli[j] instanceof VoltageClampStimulus stimulus) { + Parameter voltageParameter = stimulus.getVoltageParameter(); + tempExp = voltageParameter.getExpression(); + tempUnit = voltageParameter.getUnitDefinition(); + clampType = "Voltage"; + } + if (tempExp != null) { + expStr = tempExp.infix(); + if (tempUnit != null) { + expStr += " " + tempUnit.getSymbolUnicode(); + } + } + electTable.addCell(createCell(stimName, getFont())); + electTable.addCell(createCell(currName, getFont())); + electTable.addCell(createCell(clampType, getFont())); + electTable.addCell(createCell(expStr, getFont())); + //add electrode info + Electrode electrode = electricalStimuli[j].getElectrode(); + if (electrode == null) { + electTable.addCell(createCell("N/A", getFont())); + } else { + Coordinate c = electrode.getPosition(); + String location = c.getX() + ", " + c.getY() + ", " + c.getZ(); + String featureName = electrode.getFeature().getName(); + electTable.addCell(createCell("(" + location + ") in " + featureName, getFont())); + } + } + if (electTable != null) { + if (memMapSection == null) { + memMapSection = simContextSection.addSection("Membrane Mapping For " + simContext.getName(), 1); + } + electTable.setWidths(widths); + memMapSection.add(electTable); + } + //add temperature + Table tempTable = getTable(1, 75, 1, 3, 3); + tempTable.setAlignment(Table.ALIGN_LEFT); + tempTable.addCell(createCell("Temperature: " + simContext.getTemperatureKelvin() + " K", getFont())); + if (memMapSection != null) { + memMapSection.add(tempTable); + } + } - protected void writeMemRegionEquation(Section container, MembraneRegionEquation eq) throws DocumentException { - - Table eqTable = getTable(2, 100, 1, 2, 2); - eqTable.addCell(createCell(VCML.MembraneRegionEquation + " " + eq.getVariable().getName(), - getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - eqTable.endHeaders(); - String exp = "0.0"; - eqTable.addCell(createCell(VCML.UniformRate, getFont())); - if (eq.getUniformRateExpression() != null) { - exp = eq.getUniformRateExpression().infix(); - } - exp = "0.0"; - eqTable.addCell(createCell(VCML.MembraneRate, getFont())); - if (eq.getMembraneRateExpression() != null) { - exp = eq.getMembraneRateExpression().infix(); - } - eqTable.addCell(createCell(exp, getFont())); - if (eq.getInitialExpression() != null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); - } - int solutionType = eq.getSolutionType(); - switch (solutionType) { - case Equation.UNKNOWN_SOLUTION:{ - if (eq.getInitialExpression() == null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell("0.0", getFont())); - } - break; - } - case Equation.EXACT_SOLUTION:{ - eqTable.addCell(createCell(VCML.Exact, getFont())); - eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); - break; - } - } - - container.add(eqTable); - } + protected void writeMemRegionEquation(Section container, MembraneRegionEquation eq) throws DocumentException { + Table eqTable = getTable(2, 100, 1, 2, 2); + eqTable.addCell(createCell(VCML.MembraneRegionEquation + " " + eq.getVariable().getName(), + getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + eqTable.endHeaders(); + String exp; + eqTable.addCell(createCell(VCML.UniformRate, getFont())); + if (eq.getUniformRateExpression() != null) { + exp = eq.getUniformRateExpression().infix(); + } + exp = "0.0"; + eqTable.addCell(createCell(VCML.MembraneRate, getFont())); + if (eq.getMembraneRateExpression() != null) { + exp = eq.getMembraneRateExpression().infix(); + } + eqTable.addCell(createCell(exp, getFont())); + if (eq.getInitialExpression() != null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); + } + int solutionType = eq.getSolutionType(); + switch (solutionType) { + case Equation.UNKNOWN_SOLUTION: { + if (eq.getInitialExpression() == null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell("0.0", getFont())); + } + break; + } + case Equation.EXACT_SOLUTION: { + eqTable.addCell(createCell(VCML.Exact, getFont())); + eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); + break; + } + } -protected void writeMetadata(Section metaSection, String name, String description, String userName, String type) throws DocumentException { + container.add(eqTable); + } - Table metaTable = getTable(1, 100, 0, 3, 3); - // - if (name != null && name.trim().length() > 0) { - metaTable.addCell(createCell(type + " Name: " + name.trim(), getBold(DEF_HEADER_FONT_SIZE))); - } - if (description != null && description.trim().length() > 0) { - metaTable.addCell(createCell(type + " Description: " + description.trim(), getBold(DEF_HEADER_FONT_SIZE))); - } - if (userName != null) { - metaTable.addCell(createCell("Owner: " + userName, getBold(DEF_HEADER_FONT_SIZE))); - } - // - metaSection.add(metaTable); -} + + protected void writeMetadata(Section metaSection, String name, String description, String userName, String type) throws DocumentException { + + Table metaTable = getTable(1, 100, 0, 3, 3); + // + if (name != null && !name.trim().isEmpty()) { + metaTable.addCell(createCell(type + " Name: " + name.trim(), getBold(DEF_HEADER_FONT_SIZE))); + } + if (description != null && !description.trim().isEmpty()) { + metaTable.addCell(createCell(type + " Description: " + description.trim(), getBold(DEF_HEADER_FONT_SIZE))); + } + if (userName != null) { + metaTable.addCell(createCell("Owner: " + userName, getBold(DEF_HEADER_FONT_SIZE))); + } + // + metaSection.add(metaTable); + } -//model description ignored. -protected void writeModel(Chapter physioChapter, Model model) throws DocumentException { + //model description ignored. + protected void writeModel(Chapter physioChapter, Model model) throws DocumentException { - Section structSection = null; - //add structures image + Section structSection = null; + //add structures image // if (model.getNumStructures() > 0) { // try { // ByteArrayOutputStream bos = generateDocStructureImage(model, ITextWriter.LOW_RESOLUTION); @@ -1592,608 +1606,605 @@ protected void writeModel(Chapter physioChapter, Model model) throws DocumentExc // lg.error(e); // } // } - //write structures - Table structTable = null; - for (int i = 0; i < model.getNumStructures(); i++) { - if (structTable == null) { - structTable = getTable(4, 100, 1, 3, 3); - structTable.addCell(createCell("Structures", getBold(DEF_HEADER_FONT_SIZE), 4, 1, Element.ALIGN_CENTER, true)); - structTable.addCell(createHeaderCell("Name", getFont(), 1)); - structTable.addCell(createHeaderCell("Type", getFont(), 1)); - structTable.addCell(createHeaderCell("Inside", getFont(), 1)); - structTable.addCell(createHeaderCell("Outside", getFont(), 1)); - structTable.endHeaders(); - } - writeStructure(model, model.getStructure(i), structTable); - } - + //write structures + Table structTable = null; + for (int i = 0; i < model.getNumStructures(); i++) { + if (structTable == null) { + structTable = getTable(4, 100, 1, 3, 3); + structTable.addCell(createCell("Structures", getBold(DEF_HEADER_FONT_SIZE), 4, 1, Element.ALIGN_CENTER, true)); + structTable.addCell(createHeaderCell("Name", getFont(), 1)); + structTable.addCell(createHeaderCell("Type", getFont(), 1)); + structTable.addCell(createHeaderCell("Inside", getFont(), 1)); + structTable.addCell(createHeaderCell("Outside", getFont(), 1)); + structTable.endHeaders(); + } + writeStructure(model, model.getStructure(i), structTable); + } + // if (structTable != null) { // structSection.add(structTable); // } - //write reactions - writeReactions(physioChapter, model); -} + //write reactions + writeReactions(physioChapter, model); + } + + protected void writeOdeEquation(Section container, OdeEquation eq) throws DocumentException { + + Table eqTable = getTable(2, 100, 1, 2, 2); + eqTable.addCell(createCell(VCML.OdeEquation + " " + eq.getVariable().getName(), + getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + eqTable.endHeaders(); + String exp = "0.0"; + eqTable.addCell(createCell(VCML.Rate, getFont())); + if (eq.getRateExpression() != null) { + exp = eq.getRateExpression().infix(); + } + eqTable.addCell(createCell(exp, getFont())); + if (eq.getInitialExpression() != null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); + } + int solutionType = eq.getSolutionType(); + switch (solutionType) { + case Equation.UNKNOWN_SOLUTION: { + if (eq.getInitialExpression() == null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell("0.0", getFont())); + } + break; + } + case Equation.EXACT_SOLUTION: { + eqTable.addCell(createCell(VCML.Exact, getFont())); + eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); + break; + } + } - protected void writeOdeEquation(Section container, OdeEquation eq) throws DocumentException { + container.add(eqTable); + } - Table eqTable = getTable(2, 100, 1, 2, 2); - eqTable.addCell(createCell(VCML.OdeEquation + " " + eq.getVariable().getName(), - getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - eqTable.endHeaders(); - String exp = "0.0"; - eqTable.addCell(createCell(VCML.Rate, getFont())); - if (eq.getRateExpression() != null) { - exp = eq.getRateExpression().infix(); - } - eqTable.addCell(createCell(exp, getFont())); - if (eq.getInitialExpression() != null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); - } - int solutionType = eq.getSolutionType(); - switch (solutionType) { - case Equation.UNKNOWN_SOLUTION:{ - if (eq.getInitialExpression() == null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell("0.0", getFont())); - } - break; - } - case Equation.EXACT_SOLUTION:{ - eqTable.addCell(createCell(VCML.Exact, getFont())); - eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); - break; - } - } - - container.add(eqTable); - } + protected void writePdeEquation(Section container, PdeEquation eq) throws DocumentException { - protected void writePdeEquation(Section container, PdeEquation eq) throws DocumentException { + Table eqTable = getTable(2, 100, 1, 2, 2); + eqTable.addCell(createCell(VCML.PdeEquation + " " + eq.getVariable().getName(), + getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + eqTable.endHeaders(); + if (eq.getBoundaryXm() != null) { + eqTable.addCell(createCell(VCML.BoundaryXm, getFont())); + eqTable.addCell(createCell(eq.getBoundaryXm().infix(), getFont())); + } + if (eq.getBoundaryXp() != null) { + eqTable.addCell(createCell(VCML.BoundaryXp, getFont())); + eqTable.addCell(createCell(eq.getBoundaryXp().infix(), getFont())); + } + if (eq.getBoundaryYm() != null) { + eqTable.addCell(createCell(VCML.BoundaryYm, getFont())); + eqTable.addCell(createCell(eq.getBoundaryYm().infix(), getFont())); + } + if (eq.getBoundaryYp() != null) { + eqTable.addCell(createCell(VCML.BoundaryYp, getFont())); + eqTable.addCell(createCell(eq.getBoundaryYp().infix(), getFont())); + } + if (eq.getBoundaryZm() != null) { + eqTable.addCell(createCell(VCML.BoundaryZm, getFont())); + eqTable.addCell(createCell(eq.getBoundaryZm().infix(), getFont())); + } + if (eq.getBoundaryZp() != null) { + eqTable.addCell(createCell(VCML.BoundaryZp, getFont())); + eqTable.addCell(createCell(eq.getBoundaryZp().infix(), getFont())); + } + if (eq.getVelocityX() != null) { + eqTable.addCell(createCell(VCML.VelocityX, getFont())); + eqTable.addCell(createCell(eq.getVelocityX().infix(), getFont())); + } + if (eq.getVelocityY() != null) { + eqTable.addCell(createCell(VCML.VelocityY, getFont())); + eqTable.addCell(createCell(eq.getVelocityY().infix(), getFont())); + } + if (eq.getVelocityZ() != null) { + eqTable.addCell(createCell(VCML.VelocityZ, getFont())); + eqTable.addCell(createCell(eq.getVelocityZ().infix(), getFont())); + } + String exp = "0.0"; + if (eq.getRateExpression() != null) { + exp = eq.getRateExpression().infix(); + } + eqTable.addCell(createCell(VCML.Rate, getFont())); + eqTable.addCell(createCell(exp, getFont())); + exp = "0.0"; + if (eq.getDiffusionExpression() != null) { + exp = eq.getDiffusionExpression().infix(); + } + eqTable.addCell(createCell(VCML.Diffusion, getFont())); + eqTable.addCell(createCell(exp, getFont())); + if (eq.getInitialExpression() != null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); + } + int solutionType = eq.getSolutionType(); + switch (solutionType) { + case Equation.UNKNOWN_SOLUTION: { + if (eq.getInitialExpression() == null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell("0.0", getFont())); + } + break; + } + case Equation.EXACT_SOLUTION: { + eqTable.addCell(createCell(VCML.Exact, getFont())); + eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); + break; + } + } - Table eqTable = getTable(2, 100, 1, 2, 2); - eqTable.addCell(createCell(VCML.PdeEquation + " " + eq.getVariable().getName(), - getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - eqTable.endHeaders(); - if (eq.getBoundaryXm() != null) { - eqTable.addCell(createCell(VCML.BoundaryXm, getFont())); - eqTable.addCell(createCell(eq.getBoundaryXm().infix(), getFont())); - } - if (eq.getBoundaryXp() != null) { - eqTable.addCell(createCell(VCML.BoundaryXp, getFont())); - eqTable.addCell(createCell(eq.getBoundaryXp().infix(), getFont())); - } - if (eq.getBoundaryYm() != null) { - eqTable.addCell(createCell(VCML.BoundaryYm, getFont())); - eqTable.addCell(createCell(eq.getBoundaryYm().infix(), getFont())); - } - if (eq.getBoundaryYp() != null) { - eqTable.addCell(createCell(VCML.BoundaryYp, getFont())); - eqTable.addCell(createCell(eq.getBoundaryYp().infix(), getFont())); - } - if (eq.getBoundaryZm() != null) { - eqTable.addCell(createCell(VCML.BoundaryZm, getFont())); - eqTable.addCell(createCell(eq.getBoundaryZm().infix(), getFont())); - } - if (eq.getBoundaryZp() != null) { - eqTable.addCell(createCell(VCML.BoundaryZp, getFont())); - eqTable.addCell(createCell(eq.getBoundaryZp().infix(), getFont())); - } - if (eq.getVelocityX() != null) { - eqTable.addCell(createCell(VCML.VelocityX, getFont())); - eqTable.addCell(createCell(eq.getVelocityX().infix(), getFont())); - } - if (eq.getVelocityY() != null) { - eqTable.addCell(createCell(VCML.VelocityY, getFont())); - eqTable.addCell(createCell(eq.getVelocityY().infix(), getFont())); - } - if (eq.getVelocityZ() != null) { - eqTable.addCell(createCell(VCML.VelocityZ, getFont())); - eqTable.addCell(createCell(eq.getVelocityZ().infix(), getFont())); - } - String exp = "0.0"; - if (eq.getRateExpression() != null) { - exp = eq.getRateExpression().infix(); - } - eqTable.addCell(createCell(VCML.Rate, getFont())); - eqTable.addCell(createCell(exp, getFont())); - exp = "0.0"; - if (eq.getDiffusionExpression() != null) { - exp = eq.getDiffusionExpression().infix(); - } - eqTable.addCell(createCell(VCML.Diffusion, getFont())); - eqTable.addCell(createCell(exp, getFont())); - if (eq.getInitialExpression() != null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); - } - int solutionType = eq.getSolutionType(); - switch (solutionType) { - case Equation.UNKNOWN_SOLUTION:{ - if (eq.getInitialExpression() == null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell("0.0", getFont())); - } - break; - } - case Equation.EXACT_SOLUTION:{ - eqTable.addCell(createCell(VCML.Exact, getFont())); - eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); - break; - } - } - - container.add(eqTable); - } + container.add(eqTable); + } -//ReactionContext - SpeciesContextSpec: ignored boundary conditions. - protected void writeReactionContext(Section simContextSection, SimulationContext simContext) throws DocumentException { + //ReactionContext - SpeciesContextSpec: ignored boundary conditions. + protected void writeReactionContext(Section simContextSection, SimulationContext simContext) throws DocumentException { - ReactionContext rc = simContext.getReactionContext(); - if (rc == null) { - return; - } - Section rcSection = null; - //add reaction specs - ReactionSpec reactionSpecs [] = rc.getReactionSpecs(); - Table reactionSpecTable = null; - for (int i = 0; i < reactionSpecs.length; i++) { - if (i == 0) { - reactionSpecTable = getTable(4, 100, 1, 3, 3); - //reactionSpecTable.setTableFitsPage(true); - reactionSpecTable.addCell(createCell("Reaction Mapping", getBold(DEF_HEADER_FONT_SIZE), 4, 1, Element.ALIGN_CENTER, true)); - reactionSpecTable.addCell(createHeaderCell("Name", getBold(), 1)); - reactionSpecTable.addCell(createHeaderCell("Type", getBold(), 1)); - reactionSpecTable.addCell(createHeaderCell("Enabled (T/F)", getBold(), 1)); - reactionSpecTable.addCell(createHeaderCell("Fast (T/F)", getBold(), 1)); - reactionSpecTable.endHeaders(); - } - String reactionName = reactionSpecs[i].getReactionStep().getName(); - String reactionType = reactionSpecs[i].getReactionStep().getDisplayType(); - reactionSpecTable.addCell(createCell(reactionName, getFont())); - reactionSpecTable.addCell(createCell(reactionType, getFont())); - reactionSpecTable.addCell(createCell((reactionSpecs[i].isExcluded() ? " F ": " T "), getFont())); - reactionSpecTable.addCell(createCell((reactionSpecs[i].isFast() ? " T ": " F "), getFont())); - } - if (reactionSpecTable != null) { - rcSection = simContextSection.addSection("Reaction Mapping For " + simContext.getName(), simContextSection.getNumberDepth() + 1); - rcSection.add(reactionSpecTable); - } - - //add species context specs - SpeciesContextSpec speciesContSpecs [] = rc.getSpeciesContextSpecs(); - Table speciesSpecTable = null; - int widths [] = {2, 2, 4, 4, 1}; - for (int i = 0; i < speciesContSpecs.length; i++) { - if (i == 0) { - speciesSpecTable = getTable(5, 100, 1, 3, 3); - speciesSpecTable.addCell(createCell("Initial Conditions", getBold(DEF_HEADER_FONT_SIZE), 5, 1, Element.ALIGN_CENTER, true)); - speciesSpecTable.addCell(createHeaderCell("Species", getBold(), 1)); - speciesSpecTable.addCell(createHeaderCell("Structure", getBold(), 1)); - speciesSpecTable.addCell(createHeaderCell("Initial Conc.", getBold(), 1)); - speciesSpecTable.addCell(createHeaderCell("Diffusion Const.", getBold(), 1)); - speciesSpecTable.addCell(createHeaderCell("Fixed (T/F)", getBold(), 1)); - speciesSpecTable.endHeaders(); - } - String speciesName = speciesContSpecs[i].getSpeciesContext().getSpecies().getCommonName(); - String structName = speciesContSpecs[i].getSpeciesContext().getStructure().getName(); - String diff = speciesContSpecs[i].getDiffusionParameter().getExpression().infix(); - VCUnitDefinition diffUnit = speciesContSpecs[i].getDiffusionParameter().getUnitDefinition(); - SpeciesContextSpecParameter initParam = speciesContSpecs[i].getInitialConditionParameter(); - String initConc = initParam == null? "" :initParam.getExpression().infix(); - VCUnitDefinition initConcUnit = initParam == null? null : initParam.getUnitDefinition(); - speciesSpecTable.addCell(createCell(speciesName, getFont())); - speciesSpecTable.addCell(createCell(structName, getFont())); - speciesSpecTable.addCell(createCell(initConc + (initConcUnit == null ? "": " " + initConcUnit.getSymbolUnicode()), getFont())); - speciesSpecTable.addCell(createCell(diff + (diffUnit == null ? "": " " + diffUnit.getSymbolUnicode()), getFont())); - speciesSpecTable.addCell(createCell((speciesContSpecs[i].isConstant() ? " T ": " F "), getFont())); - } - if (speciesSpecTable != null) { - if (rcSection == null) { - rcSection = simContextSection.addSection("Reaction Mapping For " + simContext.getName(), simContextSection.getNumberDepth() + 1); - } - speciesSpecTable.setWidths(widths); - rcSection.add(speciesSpecTable); - } - } + ReactionContext rc = simContext.getReactionContext(); + if (rc == null) { + return; + } + Section rcSection = null; + //add reaction specs + ReactionSpec[] reactionSpecs = rc.getReactionSpecs(); + Table reactionSpecTable = null; + for (int i = 0; i < reactionSpecs.length; i++) { + if (i == 0) { + reactionSpecTable = getTable(4, 100, 1, 3, 3); + //reactionSpecTable.setTableFitsPage(true); + reactionSpecTable.addCell(createCell("Reaction Mapping", getBold(DEF_HEADER_FONT_SIZE), 4, 1, Element.ALIGN_CENTER, true)); + reactionSpecTable.addCell(createHeaderCell("Name", getBold(), 1)); + reactionSpecTable.addCell(createHeaderCell("Type", getBold(), 1)); + reactionSpecTable.addCell(createHeaderCell("Enabled (T/F)", getBold(), 1)); + reactionSpecTable.addCell(createHeaderCell("Fast (T/F)", getBold(), 1)); + reactionSpecTable.endHeaders(); + } + String reactionName = reactionSpecs[i].getReactionStep().getName(); + String reactionType = reactionSpecs[i].getReactionStep().getDisplayType(); + reactionSpecTable.addCell(createCell(reactionName, getFont())); + reactionSpecTable.addCell(createCell(reactionType, getFont())); + reactionSpecTable.addCell(createCell((reactionSpecs[i].isExcluded() ? " F " : " T "), getFont())); + reactionSpecTable.addCell(createCell((reactionSpecs[i].isFast() ? " T " : " F "), getFont())); + } + if (reactionSpecTable != null) { + rcSection = simContextSection.addSection("Reaction Mapping For " + simContext.getName(), simContextSection.getNumberDepth() + 1); + rcSection.add(reactionSpecTable); + } + //add species context specs + SpeciesContextSpec[] speciesContSpecs = rc.getSpeciesContextSpecs(); + Table speciesSpecTable = null; + int[] widths = {2, 2, 4, 4, 1}; + for (int i = 0; i < speciesContSpecs.length; i++) { + if (i == 0) { + speciesSpecTable = getTable(5, 100, 1, 3, 3); + speciesSpecTable.addCell(createCell("Initial Conditions", getBold(DEF_HEADER_FONT_SIZE), 5, 1, Element.ALIGN_CENTER, true)); + speciesSpecTable.addCell(createHeaderCell("Species", getBold(), 1)); + speciesSpecTable.addCell(createHeaderCell("Structure", getBold(), 1)); + speciesSpecTable.addCell(createHeaderCell("Initial Conc.", getBold(), 1)); + speciesSpecTable.addCell(createHeaderCell("Diffusion Const.", getBold(), 1)); + speciesSpecTable.addCell(createHeaderCell("Fixed (T/F)", getBold(), 1)); + speciesSpecTable.endHeaders(); + } + String speciesName = speciesContSpecs[i].getSpeciesContext().getSpecies().getCommonName(); + String structName = speciesContSpecs[i].getSpeciesContext().getStructure().getName(); + String diff = speciesContSpecs[i].getDiffusionParameter().getExpression().infix(); + VCUnitDefinition diffUnit = speciesContSpecs[i].getDiffusionParameter().getUnitDefinition(); + SpeciesContextSpecParameter initParam = speciesContSpecs[i].getInitialConditionParameter(); + String initConc = initParam == null ? "" : initParam.getExpression().infix(); + VCUnitDefinition initConcUnit = initParam == null ? null : initParam.getUnitDefinition(); + speciesSpecTable.addCell(createCell(speciesName, getFont())); + speciesSpecTable.addCell(createCell(structName, getFont())); + speciesSpecTable.addCell(createCell(initConc + (initConcUnit == null ? "" : " " + initConcUnit.getSymbolUnicode()), getFont())); + speciesSpecTable.addCell(createCell(diff + (diffUnit == null ? "" : " " + diffUnit.getSymbolUnicode()), getFont())); + speciesSpecTable.addCell(createCell((speciesContSpecs[i].isConstant() ? " T " : " F "), getFont())); + } + if (speciesSpecTable != null) { + if (rcSection == null) { + rcSection = simContextSection.addSection("Reaction Mapping For " + simContext.getName(), simContextSection.getNumberDepth() + 1); + } + speciesSpecTable.setWidths(widths); + rcSection.add(speciesSpecTable); + } + } + + + private Cell getReactionArrowImageCell(boolean bReversible) { + // Create image for arrow(s) + int imageWidth = 150; + int imageHeight = 50; + BufferedImage bufferedImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR); + + Graphics2D g = (Graphics2D) bufferedImage.getGraphics(); + g.setClip(0, 0, imageWidth, imageHeight); + g.setColor(Color.white); + g.fillRect(0, 0, imageWidth, imageHeight); + g.setColor(Color.black); + int fontSize = 12; + g.setFont(new java.awt.Font("SansSerif", Font.BOLD, fontSize)); + + // get image for reaction equation arrows + // Draw the arrows on canvas/image + if (bReversible) { + // Forward *AND* Reverse (bi-directional) arrow + java.awt.Polygon arrow = new java.awt.Polygon(new int[]{20, 40, 40, 110, 110, 130, 110, 110, 40, 40}, + new int[]{25, 14, 22, 22, 14, 25, 36, 28, 28, 36}, 10); + g.fill(arrow); + } else { + // Only Forward Arrow + java.awt.Polygon arrow = new java.awt.Polygon(new int[]{20, 110, 110, 130, 110, 110, 20}, + new int[]{22, 22, 14, 25, 36, 28, 28}, 7); + g.fill(arrow); + } - private Cell getReactionArrowImageCell(boolean bReversible) throws DocumentException { - // Create image for arrow(s) - int imageWidth = 150; - int imageHeight = 50; - BufferedImage bufferedImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR); - - Graphics2D g = (Graphics2D)bufferedImage.getGraphics(); - g.setClip(0, 0, imageWidth, imageHeight); - g.setColor(Color.white); - g.fillRect(0, 0, imageWidth, imageHeight); - g.setColor(Color.black); - int fontSize = 12; - g.setFont(new java.awt.Font("SansSerif", Font.BOLD, fontSize)); - - // get image for reaction equation arrows - // Draw the arrows on canvas/image - if (bReversible){ - // Forward *AND* Reverse (bi-directional) arrow - java.awt.Polygon arrow = new java.awt.Polygon( new int[] {20, 40, 40, 110, 110, 130, 110, 110, 40, 40}, - new int[] {25, 14, 22, 22, 14, 25, 36, 28, 28, 36}, 10); - g.fill(arrow); - } else { - // Only Forward Arrow - java.awt.Polygon arrow = new java.awt.Polygon( new int[] {20, 110, 110, 130, 110, 110, 20}, - new int[] {22, 22, 14, 25, 36, 28, 28}, 7); - g.fill(arrow); - } - - Cell imageCell = null; - try { - com.lowagie.text.Image rpImage = com.lowagie.text.Image.getInstance(bufferedImage, null); - rpImage.setAlignment(com.lowagie.text.Image.MIDDLE); - imageCell = new Cell(); - imageCell.add(rpImage); - } catch (Exception e) { - lg.error("Unable to add structure mapping image to report.", e); - } + Cell imageCell = null; + try { + com.lowagie.text.Image rpImage = com.lowagie.text.Image.getInstance(bufferedImage, null); + rpImage.setAlignment(com.lowagie.text.Image.MIDDLE); + imageCell = new Cell(); + imageCell.add(rpImage); + } catch (Exception e) { + lg.error("Unable to add structure mapping image to report.", e); + } - return imageCell; - } + return imageCell; + } - -//each reaction has its own table, ordered by the structures. - protected void writeReactions(Chapter physioChapter, Model model) throws DocumentException { - if (model == null) { - return; - } - Paragraph reactionParagraph = new Paragraph(); - reactionParagraph.add(new Chunk("Structures and Reactions Diagram").setLocalDestination(model.getName())); - Section reactionDiagramSection = physioChapter.addSection(reactionParagraph, physioChapter.getNumberDepth() + 1); - try{ - addImage(reactionDiagramSection, encodeJPEG(generateDocReactionsImage(model,null))); - }catch(Exception e){ - lg.error(e.getMessage(), e); - throw new DocumentException(e.getClass().getName()+": "+e.getMessage()); - } + //each reaction has its own table, ordered by the structures. + protected void writeReactions(Chapter physioChapter, Model model) throws DocumentException { - - for (int i = 0; i < model.getNumStructures(); i++) { - ReactionStep[] reactionSteps = model.getReactionSteps(); - ReactionStep rs = null; - Table modifierTable = null; - Table reactionTable = null; - boolean firstTime = true; - Section reactStructSection = null; - for (int j = 0; j < reactionSteps.length; j++) { - if (reactionSteps[j].getStructure() == model.getStructure(i)) { //can also use structureName1.equals(structureName2) - if (firstTime) { - Paragraph linkParagraph = new Paragraph(); - linkParagraph.add(new Chunk("Reaction(s) in " + model.getStructure(i).getName()).setLocalDestination(model.getStructure(i).getName())); - reactStructSection = physioChapter.addSection(linkParagraph, physioChapter.getNumberDepth() + 1); - firstTime = false; - } - rs = reactionSteps[j]; - String type; - if (rs instanceof SimpleReaction) { - type = "Reaction"; - } else { - type = "Flux"; - } - //write Reaction equation as a table + if (model == null) { + return; + } + Paragraph reactionParagraph = new Paragraph(); + reactionParagraph.add(new Chunk("Structures and Reactions Diagram").setLocalDestination(model.getName())); + Section reactionDiagramSection = physioChapter.addSection(reactionParagraph, physioChapter.getNumberDepth() + 1); + try { + addImage(reactionDiagramSection, encodeJPEG(generateDocReactionsImage(model, null))); + } catch (Exception e) { + lg.error(e.getMessage(), e); + throw new DocumentException(e.getClass().getName() + ": " + e.getMessage()); + } - // Get the image arrow cell depending on type of reactionStep : MassAction => double arrow, otherwise, forward arrow - boolean bReversible = false; - if (rs.getKinetics() instanceof MassActionKinetics) { - bReversible = true; - } - Cell arrowImageCell = getReactionArrowImageCell(bReversible); - - // Get reactants and products strings - ReactionCanvas rc = new ReactionCanvas(); - rc.setReactionStep(rs); - ReactionCanvasDisplaySpec rcdSpec = rc.getReactionCanvasDisplaySpec(); - String reactants = rcdSpec.getLeftText(); - String products = rcdSpec.getRightText(); - - // Create table and add cells for reactants, arrow(s) images, products - int widths [] = {8, 1, 8}; - reactionTable = getTable(3, 100, 0, 2, 2); - - // Add reactants as cell - Cell tableCell = createCell(reactants, getBold()); - tableCell.setHorizontalAlignment(Cell.ALIGN_RIGHT); - tableCell.setBorderColor(Color.white); - reactionTable.addCell(tableCell); - // add arrow(s) image as cell - if (arrowImageCell != null) { - arrowImageCell.setHorizontalAlignment(Cell.ALIGN_CENTER); - arrowImageCell.setBorderColor(Color.white); - reactionTable.addCell(arrowImageCell); - } - // add products as cell - tableCell = createCell(products, getBold()); - tableCell.setBorderColor(Color.white); - reactionTable.addCell(tableCell); - - // reactionTable.setBorderColor(Color.white); - reactionTable.setWidths(widths); - - // Identify modifiers, - ReactionParticipant[] rpArr = rs.getReactionParticipants(); - Vector modifiersVector = new Vector(); - for(int k = 0; k < rpArr.length; k += 1){ - if (rpArr[k] instanceof Catalyst) { - modifiersVector.add(rpArr[k]); - } - } - // Write the modifiers in a separate table, if present - if (modifiersVector.size() > 0) { - modifierTable = getTable(1, 50, 0, 1, 1); - modifierTable.addCell(createCell("Modifiers List", getBold(DEF_HEADER_FONT_SIZE), 1, 1, Element.ALIGN_CENTER, true)); - StringBuffer modifierNames = new StringBuffer(); - for (int k = 0; k < modifiersVector.size(); k++) { - modifierNames.append(((Catalyst)modifiersVector.elementAt(k)).getName() + "\n"); - } - modifierTable.addCell(createCell(modifierNames.toString().trim(), getFont())); - modifiersVector.removeAllElements(); - } - - Section reactionSection = reactStructSection.addSection(type + " " + rs.getName(), reactStructSection.getNumberDepth() + 1); - //Annotation - VCMetaData vcMetaData = rs.getModel().getVcMetaData(); - if (vcMetaData.getFreeTextAnnotation(rs) != null) { - Table annotTable = getTable(1, 100, 1, 3, 3); - annotTable.addCell(createCell("Reaction Annotation", getBold(DEF_HEADER_FONT_SIZE), 1, 1, Element.ALIGN_CENTER, true)); - annotTable.addCell(createCell(vcMetaData.getFreeTextAnnotation(rs),getFont())); - reactionSection.add(annotTable); - //reactionSection.add(new Paragraph("\""+rs.getAnnotation()+"\"")); - } - // reaction table - if (reactionTable != null) { - reactionSection.add(reactionTable); - reactionTable = null; // re-set reactionTable - } - if (modifierTable != null) { - reactionSection.add(modifierTable); - modifierTable = null; - } - // Write kinetics parameters, etc. in a table - writeKineticsParams(reactionSection, rs); - } - } - } - } + for (int i = 0; i < model.getNumStructures(); i++) { + ReactionStep[] reactionSteps = model.getReactionSteps(); + ReactionStep rs; + Table modifierTable = null; + Table reactionTable; + boolean firstTime = true; + Section reactStructSection = null; + for (ReactionStep reactionStep : reactionSteps) { + if (reactionStep.getStructure() == model.getStructure(i)) { //can also use structureName1.equals(structureName2) + if (firstTime) { + Paragraph linkParagraph = new Paragraph(); + linkParagraph.add(new Chunk("Reaction(s) in " + model.getStructure(i).getName()).setLocalDestination(model.getStructure(i).getName())); + reactStructSection = physioChapter.addSection(linkParagraph, physioChapter.getNumberDepth() + 1); + firstTime = false; + } + rs = reactionStep; + String type; + if (rs instanceof SimpleReaction) { + type = "Reaction"; + } else { + type = "Flux"; + } + //write Reaction equation as a table + + // Get the image arrow cell depending on type of reactionStep : MassAction => double arrow, otherwise, forward arrow + boolean bReversible = rs.getKinetics() instanceof MassActionKinetics; + Cell arrowImageCell = getReactionArrowImageCell(bReversible); + + // Get reactants and products strings + ReactionCanvas rc = new ReactionCanvas(); + rc.setReactionStep(rs); + ReactionCanvasDisplaySpec rcdSpec = rc.getReactionCanvasDisplaySpec(); + String reactants = rcdSpec.getLeftText(); + String products = rcdSpec.getRightText(); + + // Create table and add cells for reactants, arrow(s) images, products + int[] widths = {8, 1, 8}; + reactionTable = getTable(3, 100, 0, 2, 2); + + // Add reactants as cell + Cell tableCell = createCell(reactants, getBold()); + tableCell.setHorizontalAlignment(Cell.ALIGN_RIGHT); + tableCell.setBorderColor(Color.white); + reactionTable.addCell(tableCell); + // add arrow(s) image as cell + if (arrowImageCell != null) { + arrowImageCell.setHorizontalAlignment(Cell.ALIGN_CENTER); + arrowImageCell.setBorderColor(Color.white); + reactionTable.addCell(arrowImageCell); + } + // add products as cell + tableCell = createCell(products, getBold()); + tableCell.setBorderColor(Color.white); + reactionTable.addCell(tableCell); + + // reactionTable.setBorderColor(Color.white); + reactionTable.setWidths(widths); + + // Identify modifiers, + ReactionParticipant[] rpArr = rs.getReactionParticipants(); + Vector modifiersVector = new Vector<>(); + for (ReactionParticipant reactionParticipant : rpArr) { + if (reactionParticipant instanceof Catalyst) { + modifiersVector.add(reactionParticipant); + } + } + + // Write the modifiers in a separate table, if present + if (!modifiersVector.isEmpty()) { + modifierTable = getTable(1, 50, 0, 1, 1); + modifierTable.addCell(createCell("Modifiers List", getBold(DEF_HEADER_FONT_SIZE), 1, 1, Element.ALIGN_CENTER, true)); + StringBuilder modifierNames = new StringBuilder(); + for (int k = 0; k < modifiersVector.size(); k++) { + modifierNames.append(modifiersVector.elementAt(k).getName()).append("\n"); + } + modifierTable.addCell(createCell(modifierNames.toString().trim(), getFont())); + modifiersVector.removeAllElements(); + } + + Section reactionSection = reactStructSection.addSection(type + " " + rs.getName(), reactStructSection.getNumberDepth() + 1); + //Annotation + VCMetaData vcMetaData = rs.getModel().getVcMetaData(); + if (vcMetaData.getFreeTextAnnotation(rs) != null) { + Table annotTable = getTable(1, 100, 1, 3, 3); + annotTable.addCell(createCell("Reaction Annotation", getBold(DEF_HEADER_FONT_SIZE), 1, 1, Element.ALIGN_CENTER, true)); + annotTable.addCell(createCell(vcMetaData.getFreeTextAnnotation(rs), getFont())); + reactionSection.add(annotTable); + //reactionSection.add(new Paragraph("\""+rs.getAnnotation()+"\"")); + } + // reaction table + if (reactionTable != null) { + reactionSection.add(reactionTable); + reactionTable = null; // re-set reactionTable + } + if (modifierTable != null) { + reactionSection.add(modifierTable); + modifierTable = null; + } + // Write kinetics parameters, etc. in a table + writeKineticsParams(reactionSection, rs); + } + } + } + } -//container can be a chapter or a section of a chapter. - protected void writeSimulation(Section container, Simulation sim) throws DocumentException { + //container can be a chapter or a section of a chapter. + protected void writeSimulation(Section container, Simulation sim) throws DocumentException { - if (sim == null) { - return; - } - Section simSection = container.addSection(sim.getName(), container.getNumberDepth() + 1); - writeMetadata(simSection, sim.getName(), sim.getDescription(), null, "Simulation "); - //add overriden params - Table overParamTable = null; - MathOverrides mo = sim.getMathOverrides(); - if (mo != null) { - String constants [] = mo.getOverridenConstantNames(); - for (int i = 0; i < constants.length; i++) { - String actualStr = "", defStr = ""; - Expression tempExp = mo.getDefaultExpression(constants[i]); - if (tempExp != null) { - defStr = tempExp.infix(); - } - if (mo.isScan(constants[i])) { - actualStr = mo.getConstantArraySpec(constants[i]).toString(); - } else { - tempExp = mo.getActualExpression(constants[i], MathOverrides.ScanIndex.ZERO); - if (tempExp != null) { - actualStr = tempExp.infix(); - } - } - if (overParamTable == null) { - overParamTable = getTable(3, 75, 1, 3, 3); - overParamTable.setAlignment(Table.ALIGN_LEFT); - overParamTable.addCell(createCell("Overriden Parameters", getBold(DEF_HEADER_FONT_SIZE), 3, 1, Element.ALIGN_CENTER, true)); - overParamTable.addCell(createHeaderCell("Name", getBold(), 1)); - overParamTable.addCell(createHeaderCell("Actual Value", getBold(), 1)); - overParamTable.addCell(createHeaderCell("Default Value", getBold(), 1)); - } - overParamTable.addCell(createCell(constants[i], getFont())); - overParamTable.addCell(createCell(actualStr, getFont())); - overParamTable.addCell(createCell(defStr, getFont())); - } - } - if (overParamTable != null) { - simSection.add(overParamTable); - } - //add spatial details - //sim.isSpatial(); - Table meshTable = null; - MeshSpecification mesh = sim.getMeshSpecification(); - if (mesh != null) { - Geometry geom = mesh.getGeometry(); - Extent extent = geom.getExtent(); - String extentStr = "(" + extent.getX() + ", " + extent.getY() + ", " + extent.getZ() + ")"; - ISize meshSize = mesh.getSamplingSize(); - String meshSizeStr = "(" + meshSize.getX() + ", " + meshSize.getY() + ", " + meshSize.getZ() + ")"; - meshTable = getTable(2, 75, 1, 3, 3); - meshTable.setAlignment(Table.ALIGN_LEFT); - meshTable.addCell(createCell("Geometry Setting", getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - meshTable.addCell(createCell("Geometry Size (um)", getFont())); - meshTable.addCell(createCell(extentStr, getFont())); - meshTable.addCell(createCell("Mesh Size (elements)", getFont())); - meshTable.addCell(createCell(meshSizeStr, getFont())); - } - if (meshTable != null) { - simSection.add(meshTable); - } - //write advanced sim settings - Table simAdvTable = null; - SolverTaskDescription solverDesc = sim.getSolverTaskDescription(); - if (solverDesc != null) { - String solverName = solverDesc.getSolverDescription().getDisplayLabel(); - simAdvTable = getTable(2, 75, 1, 3, 3); - simAdvTable.setAlignment(Table.ALIGN_LEFT); - simAdvTable.addCell(createCell("Advanced Settings", getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - simAdvTable.addCell(createCell("Solver Name", getFont())); - simAdvTable.addCell(createCell(solverName, getFont())); - simAdvTable.addCell(createCell("Time Bounds - Starting", getFont())); - simAdvTable.addCell(createCell("" + solverDesc.getTimeBounds().getStartingTime(), getFont())); - simAdvTable.addCell(createCell("Time Bounds - Ending", getFont())); - simAdvTable.addCell(createCell("" + solverDesc.getTimeBounds().getEndingTime(), getFont())); - simAdvTable.addCell(createCell("Time Step - Min", getFont())); - simAdvTable.addCell(createCell("" + solverDesc.getTimeStep().getMinimumTimeStep(), getFont())); - simAdvTable.addCell(createCell("Time Step - Default", getFont())); - simAdvTable.addCell(createCell("" + solverDesc.getTimeStep().getDefaultTimeStep(), getFont())); - simAdvTable.addCell(createCell("Time Step - Max", getFont())); - simAdvTable.addCell(createCell("" + solverDesc.getTimeStep().getMaximumTimeStep(), getFont())); - ErrorTolerance et = solverDesc.getErrorTolerance(); - if (et != null) { - simAdvTable.addCell(createCell("Error Tolerance - Absolute", getFont())); - simAdvTable.addCell(createCell("" + et.getAbsoluteErrorTolerance(), getFont())); - simAdvTable.addCell(createCell("Error Tolerance - Relative", getFont())); - simAdvTable.addCell(createCell("" + et.getRelativeErrorTolerance(), getFont())); - } - OutputTimeSpec ots = solverDesc.getOutputTimeSpec(); - if (ots.isDefault()) { - simAdvTable.addCell(createCell("Keep Every", getFont())); - simAdvTable.addCell(createCell("" + ((DefaultOutputTimeSpec)ots).getKeepEvery(), getFont())); - simAdvTable.addCell(createCell("Keep At Most", getFont())); - simAdvTable.addCell(createCell("" + ((DefaultOutputTimeSpec)ots).getKeepAtMost(), getFont())); - } else if (ots.isUniform()) { - simAdvTable.addCell(createCell("Output Time Step", getFont())); - simAdvTable.addCell(createCell("" + ((UniformOutputTimeSpec)ots).getOutputTimeStep(), getFont())); - } else if (ots.isExplicit()) { - simAdvTable.addCell(createCell("Output Time Points", getFont())); - simAdvTable.addCell(createCell("" + ((ExplicitOutputTimeSpec)ots).toCommaSeperatedOneLineOfString(), getFont())); - } - simAdvTable.addCell(createCell("Use Symbolic Jacobian (T/F)", getFont())); - simAdvTable.addCell(createCell((solverDesc.getUseSymbolicJacobian() ? " T ": " F "), getFont())); - Constant sp = solverDesc.getSensitivityParameter(); - if (sp != null) { - simAdvTable.addCell(createCell("Sensitivity Analysis Param", getFont())); - simAdvTable.addCell(createCell(sp.getName(), getFont())); - } - } - if (simAdvTable != null) { - simSection.add(simAdvTable); - } - } + if (sim == null) { + return; + } + Section simSection = container.addSection(sim.getName(), container.getNumberDepth() + 1); + writeMetadata(simSection, sim.getName(), sim.getDescription(), null, "Simulation "); + //add overriden params + Table overParamTable = null; + MathOverrides mo = sim.getMathOverrides(); + if (mo != null) { + String[] constants = mo.getOverridenConstantNames(); + for (String constant : constants) { + String actualStr = "", defStr = ""; + Expression tempExp = mo.getDefaultExpression(constant); + if (tempExp != null) { + defStr = tempExp.infix(); + } + if (mo.isScan(constant)) { + actualStr = mo.getConstantArraySpec(constant).toString(); + } else { + tempExp = mo.getActualExpression(constant, MathOverrides.ScanIndex.ZERO); + if (tempExp != null) { + actualStr = tempExp.infix(); + } + } + if (overParamTable == null) { + overParamTable = getTable(3, 75, 1, 3, 3); + overParamTable.setAlignment(Table.ALIGN_LEFT); + overParamTable.addCell(createCell("Overriden Parameters", getBold(DEF_HEADER_FONT_SIZE), 3, 1, Element.ALIGN_CENTER, true)); + overParamTable.addCell(createHeaderCell("Name", getBold(), 1)); + overParamTable.addCell(createHeaderCell("Actual Value", getBold(), 1)); + overParamTable.addCell(createHeaderCell("Default Value", getBold(), 1)); + } + overParamTable.addCell(createCell(constant, getFont())); + overParamTable.addCell(createCell(actualStr, getFont())); + overParamTable.addCell(createCell(defStr, getFont())); + } + } + if (overParamTable != null) { + simSection.add(overParamTable); + } + //add spatial details + //sim.isSpatial(); + Table meshTable = null; + MeshSpecification mesh = sim.getMeshSpecification(); + if (mesh != null) { + Geometry geom = mesh.getGeometry(); + Extent extent = geom.getExtent(); + String extentStr = "(" + extent.getX() + ", " + extent.getY() + ", " + extent.getZ() + ")"; + ISize meshSize = mesh.getSamplingSize(); + String meshSizeStr = "(" + meshSize.getX() + ", " + meshSize.getY() + ", " + meshSize.getZ() + ")"; + meshTable = getTable(2, 75, 1, 3, 3); + meshTable.setAlignment(Table.ALIGN_LEFT); + meshTable.addCell(createCell("Geometry Setting", getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + meshTable.addCell(createCell("Geometry Size (um)", getFont())); + meshTable.addCell(createCell(extentStr, getFont())); + meshTable.addCell(createCell("Mesh Size (elements)", getFont())); + meshTable.addCell(createCell(meshSizeStr, getFont())); + } + if (meshTable != null) { + simSection.add(meshTable); + } + //write advanced sim settings + Table simAdvTable = null; + SolverTaskDescription solverDesc = sim.getSolverTaskDescription(); + if (solverDesc != null) { + String solverName = solverDesc.getSolverDescription().getDisplayLabel(); + simAdvTable = getTable(2, 75, 1, 3, 3); + simAdvTable.setAlignment(Table.ALIGN_LEFT); + simAdvTable.addCell(createCell("Advanced Settings", getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + simAdvTable.addCell(createCell("Solver Name", getFont())); + simAdvTable.addCell(createCell(solverName, getFont())); + simAdvTable.addCell(createCell("Time Bounds - Starting", getFont())); + simAdvTable.addCell(createCell("" + solverDesc.getTimeBounds().getStartingTime(), getFont())); + simAdvTable.addCell(createCell("Time Bounds - Ending", getFont())); + simAdvTable.addCell(createCell("" + solverDesc.getTimeBounds().getEndingTime(), getFont())); + simAdvTable.addCell(createCell("Time Step - Min", getFont())); + simAdvTable.addCell(createCell("" + solverDesc.getTimeStep().getMinimumTimeStep(), getFont())); + simAdvTable.addCell(createCell("Time Step - Default", getFont())); + simAdvTable.addCell(createCell("" + solverDesc.getTimeStep().getDefaultTimeStep(), getFont())); + simAdvTable.addCell(createCell("Time Step - Max", getFont())); + simAdvTable.addCell(createCell("" + solverDesc.getTimeStep().getMaximumTimeStep(), getFont())); + ErrorTolerance et = solverDesc.getErrorTolerance(); + if (et != null) { + simAdvTable.addCell(createCell("Error Tolerance - Absolute", getFont())); + simAdvTable.addCell(createCell("" + et.getAbsoluteErrorTolerance(), getFont())); + simAdvTable.addCell(createCell("Error Tolerance - Relative", getFont())); + simAdvTable.addCell(createCell("" + et.getRelativeErrorTolerance(), getFont())); + } + OutputTimeSpec ots = solverDesc.getOutputTimeSpec(); + if (ots.isDefault()) { + simAdvTable.addCell(createCell("Keep Every", getFont())); + simAdvTable.addCell(createCell("" + ((DefaultOutputTimeSpec) ots).getKeepEvery(), getFont())); + simAdvTable.addCell(createCell("Keep At Most", getFont())); + simAdvTable.addCell(createCell("" + ((DefaultOutputTimeSpec) ots).getKeepAtMost(), getFont())); + } else if (ots.isUniform()) { + simAdvTable.addCell(createCell("Output Time Step", getFont())); + simAdvTable.addCell(createCell("" + ((UniformOutputTimeSpec) ots).getOutputTimeStep(), getFont())); + } else if (ots.isExplicit()) { + simAdvTable.addCell(createCell("Output Time Points", getFont())); + simAdvTable.addCell(createCell(((ExplicitOutputTimeSpec) ots).toCommaSeperatedOneLineOfString(), getFont())); + } + simAdvTable.addCell(createCell("Use Symbolic Jacobian (T/F)", getFont())); + simAdvTable.addCell(createCell((solverDesc.getUseSymbolicJacobian() ? " T " : " F "), getFont())); + Constant sp = solverDesc.getSensitivityParameter(); + if (sp != null) { + simAdvTable.addCell(createCell("Sensitivity Analysis Param", getFont())); + simAdvTable.addCell(createCell(sp.getName(), getFont())); + } + } + if (simAdvTable != null) { + simSection.add(simAdvTable); + } + } -//SimulationContext: ignored the constraints (steady/unsteady). + //SimulationContext: ignored the constraints (steady/unsteady). //Electrical Stimulus: ignored the Ground Electrode, -protected void writeSimulationContext(Chapter simContextsChapter, SimulationContext simContext, PublishPreferences preferences) throws Exception { - - Section simContextSection = simContextsChapter.addSection("Application: " + simContext.getName(), simContextsChapter.getNumberDepth() + 1); - writeMetadata(simContextSection, simContext.getName(), simContext.getDescription(), null, "Application "); - //add geometry context (structure mapping) - writeStructureMapping(simContextSection, simContext); - //add reaction context (Reaction Mapping) - writeReactionContext(simContextSection, simContext); - //add Membrane Mapping & electrical stimuli - writeMembraneMapping(simContextSection, simContext); - // - if (preferences.includeGeom()) { - writeGeom(simContextSection, simContext.getGeometry(), simContext.getGeometryContext()); - } - if (preferences.includeMathDesc()) { - writeMathDescAsText(simContextSection, simContext.getMathDescription()); - //writeMathDescAsImages(simContextSection, simContext.getMathDescription()); - } - if (preferences.includeSim()) { - Section simSection = simContextSection.addSection("Simulation(s)", simContextSection.getDepth() + 1); - Simulation sims [] = simContext.getSimulations(); - for (int i = 0; i < sims.length; i++) { - writeSimulation(simSection, sims[i]); - } - } -} - - -//not used for now... -protected void writeSpecies(Species[] species) throws DocumentException { - - if (species.length > 0) { - Table table = new Table(2); - table.setWidth(100); - table.setBorderWidth(0); //for now... - // - int[] widths = new int[] { 1, 1 }; - // - table.addCell(createHeaderCell("Species", getBold(), 2)); - for(int i = 0; i < species.length/2 + (species.length % 2); i++) { - int n = species.length/2 + (species.length % 2) + i; - table.addCell(createCell(species[i].getCommonName(), getFont())); - widths[0] = Math.max(widths[0], species[i].getCommonName().length()); - if (n < species.length) { - table.addCell(createCell(species[n].getCommonName(), getFont())); - widths[1] = Math.max(widths[1], species[n].getCommonName().length()); - } else { - table.addCell(createCell("", getFont())); - } - } - table.setWidths(widths); - document.add(table); - } -} - - - protected void writeStructure(Model model, Structure struct, Table structTable) throws DocumentException { - - //If this structure has any reactions in it, add its name as a hyperlink to the reactions' list. - if (hasReactions(model, struct)) { - Paragraph linkParagraph = new Paragraph(); - Font linkFont; - try { - BaseFont fontBaseFont = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); - linkFont = new Font(fontBaseFont, DEF_FONT_SIZE, Font.NORMAL, new java.awt.Color(0, 0, 255)); - } catch (Exception e) { - linkFont = getFont(); - lg.error(e.getMessage(), e); - } - linkParagraph.add(new Chunk(struct.getName(), linkFont).setLocalGoto(struct.getName())); - Cell structLinkCell = new Cell(linkParagraph); - structLinkCell.setBorderWidth(1); - structLinkCell.setHorizontalAlignment(Element.ALIGN_LEFT); - structTable.addCell(structLinkCell); - } else { - structTable.addCell(createCell(struct.getName(), getFont())); - } - StructureTopology structTopology = model.getStructureTopology(); - if (struct instanceof Membrane) { - structTable.addCell(createCell("Membrane", getFont())); - Feature outsideFeature = structTopology.getOutsideFeature((Membrane)struct); - Feature insideFeature = structTopology.getInsideFeature((Membrane)struct); - structTable.addCell(createCell((insideFeature != null ? insideFeature.getName() : "N/A"), getFont())); - structTable.addCell(createCell((outsideFeature != null ? outsideFeature.getName() : "N/A"), getFont())); - } else { - structTable.addCell(createCell("Feature", getFont())); - String outsideStr = "N/A", insideStr = "N/A"; - Membrane enclosingMem = (Membrane)structTopology.getParentStructure(struct); - if (enclosingMem != null) { - outsideStr = enclosingMem.getName(); - } - //To do: retrieve the 'child' membrane here... - structTable.addCell(createCell(insideStr, getFont())); - structTable.addCell(createCell(outsideStr, getFont())); - } - } + protected void writeSimulationContext(Chapter simContextsChapter, SimulationContext simContext, PublishPreferences preferences) throws DocumentException { + + Section simContextSection = simContextsChapter.addSection("Application: " + simContext.getName(), simContextsChapter.getNumberDepth() + 1); + writeMetadata(simContextSection, simContext.getName(), simContext.getDescription(), null, "Application "); + //add geometry context (structure mapping) + writeStructureMapping(simContextSection, simContext); + //add reaction context (Reaction Mapping) + writeReactionContext(simContextSection, simContext); + //add Membrane Mapping & electrical stimuli + writeMembraneMapping(simContextSection, simContext); + // + if (preferences.includeGeom()) { + writeGeom(simContextSection, simContext.getGeometry(), simContext.getGeometryContext()); + } + if (preferences.includeMathDesc()) { + writeMathDescAsText(simContextSection, simContext.getMathDescription()); + //writeMathDescAsImages(simContextSection, simContext.getMathDescription()); + } + if (preferences.includeSim()) { + Section simSection = simContextSection.addSection("Simulation(s)", simContextSection.getDepth() + 1); + Simulation[] sims = simContext.getSimulations(); + for (Simulation sim : sims) { + writeSimulation(simSection, sim); + } + } + } + + + //not used for now... + protected void writeSpecies(Species[] species) throws DocumentException { + + if (species.length > 0) { + Table table = new Table(2); + table.setWidth(100); + table.setBorderWidth(0); //for now... + // + int[] widths = new int[]{1, 1}; + // + table.addCell(createHeaderCell("Species", getBold(), 2)); + for (int i = 0; i < species.length / 2 + (species.length % 2); i++) { + int n = species.length / 2 + (species.length % 2) + i; + table.addCell(createCell(species[i].getCommonName(), getFont())); + widths[0] = Math.max(widths[0], species[i].getCommonName().length()); + if (n < species.length) { + table.addCell(createCell(species[n].getCommonName(), getFont())); + widths[1] = Math.max(widths[1], species[n].getCommonName().length()); + } else { + table.addCell(createCell("", getFont())); + } + } + table.setWidths(widths); + document.add(table); + } + } + + + protected void writeStructure(Model model, Structure struct, Table structTable) throws DocumentException { + + //If this structure has any reactions in it, add its name as a hyperlink to the reactions' list. + if (hasReactions(model, struct)) { + Paragraph linkParagraph = new Paragraph(); + Font linkFont; + try { + BaseFont fontBaseFont = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); + linkFont = new Font(fontBaseFont, DEF_FONT_SIZE, Font.NORMAL, new java.awt.Color(0, 0, 255)); + } catch (Exception e) { + linkFont = getFont(); + lg.error(e.getMessage(), e); + } + linkParagraph.add(new Chunk(struct.getName(), linkFont).setLocalGoto(struct.getName())); + Cell structLinkCell = new Cell(linkParagraph); + structLinkCell.setBorderWidth(1); + structLinkCell.setHorizontalAlignment(Element.ALIGN_LEFT); + structTable.addCell(structLinkCell); + } else { + structTable.addCell(createCell(struct.getName(), getFont())); + } + StructureTopology structTopology = model.getStructureTopology(); + if (struct instanceof Membrane) { + structTable.addCell(createCell("Membrane", getFont())); + Feature outsideFeature = structTopology.getOutsideFeature((Membrane) struct); + Feature insideFeature = structTopology.getInsideFeature((Membrane) struct); + structTable.addCell(createCell((insideFeature != null ? insideFeature.getName() : "N/A"), getFont())); + structTable.addCell(createCell((outsideFeature != null ? outsideFeature.getName() : "N/A"), getFont())); + } else { + structTable.addCell(createCell("Feature", getFont())); + String outsideStr = "N/A", insideStr = "N/A"; + Membrane enclosingMem = (Membrane) structTopology.getParentStructure(struct); + if (enclosingMem != null) { + outsideStr = enclosingMem.getName(); + } + //To do: retrieve the 'child' membrane here... + structTable.addCell(createCell(insideStr, getFont())); + structTable.addCell(createCell(outsideStr, getFont())); + } + } -//boundary types ignored. - protected void writeStructureMapping(Section simContextSection, SimulationContext sc) throws DocumentException { + //boundary types ignored. + protected void writeStructureMapping(Section simContextSection, SimulationContext sc) throws DocumentException { - GeometryContext gc = sc.getGeometryContext(); - if (gc == null) { - return; - } - Section structMappSection = null; + GeometryContext gc = sc.getGeometryContext(); + if (gc == null) { + return; + } + Section structMappSection = null; /*try { ByteArrayOutputStream bos = generateStructureMappingImage(sc); com.lowagie.text.Image structMapImage = com.lowagie.text.Image.getInstance(Toolkit.getDefaultToolkit().createImage(bos.toByteArray()), null); @@ -2203,117 +2214,116 @@ protected void writeStructureMapping(Section simContextSection, SimulationContex System.err.println("Unable to add structure mapping image to report."); lg.error(e); }*/ - StructureMapping structMap [] = gc.getStructureMappings(); - Table structMapTable = null; - ModelUnitSystem modelUnitSystem = sc.getModel().getUnitSystem(); - for (int i = 0; i < structMap.length; i++) { - if (!(structMap[i] instanceof FeatureMapping)) { - continue; - } - if (i == 0) { - structMapTable = getTable(5, 100, 1, 3, 3); - structMapTable.addCell(createCell("Structure Mapping", getBold(DEF_HEADER_FONT_SIZE), 5, 1, Element.ALIGN_CENTER, true)); - structMapTable.addCell(createHeaderCell("Structure", getBold(), 1)); - structMapTable.addCell(createHeaderCell("Subdomain", getBold(), 1)); - structMapTable.addCell(createHeaderCell("Resolved (T/F)", getBold(), 1)); - structMapTable.addCell(createHeaderCell("Surf/Vol", getBold(), 1)); - structMapTable.addCell(createHeaderCell("VolFract", getBold(), 1)); - structMapTable.endHeaders(); - } - String structName = structMap[i].getStructure().getName(); - SubVolume subVol = (SubVolume)((FeatureMapping)structMap[i]).getGeometryClass(); - String subDomain = ""; - if (subVol != null) { - subDomain = subVol.getName(); - } - boolean isResolved = false; // ((FeatureMapping)structMap[i]).getResolved(); - String surfVolStr = "", volFractStr = ""; - MembraneMapping mm = (MembraneMapping)gc.getStructureMapping(sc.getModel().getStructureTopology().getParentStructure(structMap[i].getStructure())); - if (mm != null) { - StructureMapping.StructureMappingParameter smp = mm.getSurfaceToVolumeParameter(); - if (smp != null) { - Expression tempExp = smp.getExpression(); - VCUnitDefinition tempUnit = smp.getUnitDefinition(); - if (tempExp != null) { - surfVolStr = tempExp.infix(); - if (tempUnit != null && !modelUnitSystem.getInstance_DIMENSIONLESS().compareEqual(tempUnit)) { //no need to add '1' for dimensionless unit - surfVolStr += " " + tempUnit.getSymbolUnicode(); - } - } - } - smp = mm.getVolumeFractionParameter(); - if (smp != null) { - Expression tempExp = smp.getExpression(); - VCUnitDefinition tempUnit = smp.getUnitDefinition(); - if (tempExp != null) { - volFractStr = tempExp.infix(); - if (tempUnit != null && !modelUnitSystem.getInstance_DIMENSIONLESS().compareEqual(tempUnit)) { - volFractStr += " " + tempUnit.getSymbolUnicode(); - } - } - } - } - structMapTable.addCell(createCell(structName, getFont())); - structMapTable.addCell(createCell(subDomain, getFont())); - structMapTable.addCell(createCell((isResolved ? " T ": " F "), getFont())); - structMapTable.addCell(createCell(surfVolStr, getFont())); - structMapTable.addCell(createCell(volFractStr, getFont())); - } - if (structMapTable != null) { - if (structMappSection == null) { - structMappSection = simContextSection.addSection("Structure Mapping For " + sc.getName(), simContextSection.getNumberDepth() + 1); - } - structMappSection.add(structMapTable); - } - } - - -//currently not used. - protected void writeSubDomainsEquationsAsImages(Section mathDescSection, MathDescription mathDesc) { - - Enumeration subDomains = mathDesc.getSubDomains(); - Expression expArray[]; - Section volDomains = mathDescSection.addSection("Volume Domains", mathDescSection.getDepth() + 1); - Section memDomains = mathDescSection.addSection("Membrane Domains", mathDescSection.getDepth() + 1); - int scale = 1, height = 200; //arbitrary - int viewableWidth = (int)(document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin()); - BufferedImage dummy = new BufferedImage(viewableWidth, height, BufferedImage.TYPE_3BYTE_BGR); - while(subDomains.hasMoreElements()) { - SubDomain subDomain = subDomains.nextElement(); - Enumeration equationsList = subDomain.getEquations(); - ArrayList expList = new ArrayList(); - while (equationsList.hasMoreElements()) { - Equation equ = equationsList.nextElement(); - try { - Enumeration enum_equ = equ.getTotalExpressions(); - while (enum_equ.hasMoreElements()){ - Expression exp = new Expression(enum_equ.nextElement()); - expList.add(exp.flatten()); - } - } catch (ExpressionException ee) { - lg.error("Unable to process the equation for subdomain: " + subDomain.getName(), ee); - continue; - } - } - expArray = (Expression [])expList.toArray(new Expression[expList.size()]); - Section tempSection = null; - if (subDomain instanceof CompartmentSubDomain) { - tempSection = volDomains.addSection(subDomain.getName(), volDomains.getDepth() + 1); - } else if (subDomain instanceof MembraneSubDomain) { - tempSection = memDomains.addSection(subDomain.getName(), memDomains.getDepth() + 1); - } - try { - Dimension dim = ExpressionCanvas.getExpressionImageSize(expArray, (Graphics2D)dummy.getGraphics()); - System.out.println("Image dim: " + dim.width + " " + dim.height); - BufferedImage bufferedImage = new BufferedImage((int)dim.getWidth()*scale, (int)dim.getHeight()*scale, BufferedImage.TYPE_3BYTE_BGR); - ExpressionCanvas.getExpressionAsImage(expArray, bufferedImage, scale); - //Table imageTable = null;; - com.lowagie.text.Image expImage = com.lowagie.text.Image.getInstance(bufferedImage, null); - expImage.setAlignment(com.lowagie.text.Image.LEFT); - if (viewableWidth < expImage.getScaledWidth()) { - expImage.scaleToFit(viewableWidth, expImage.getHeight()); - System.out.println("SubDomain expresions After scaling: " + expImage.getScaledWidth()); - } + StructureMapping[] structMap = gc.getStructureMappings(); + Table structMapTable = null; + ModelUnitSystem modelUnitSystem = sc.getModel().getUnitSystem(); + for (int i = 0; i < structMap.length; i++) { + if (!(structMap[i] instanceof FeatureMapping)) { + continue; + } + if (i == 0) { + structMapTable = getTable(5, 100, 1, 3, 3); + structMapTable.addCell(createCell("Structure Mapping", getBold(DEF_HEADER_FONT_SIZE), 5, 1, Element.ALIGN_CENTER, true)); + structMapTable.addCell(createHeaderCell("Structure", getBold(), 1)); + structMapTable.addCell(createHeaderCell("Subdomain", getBold(), 1)); + structMapTable.addCell(createHeaderCell("Resolved (T/F)", getBold(), 1)); + structMapTable.addCell(createHeaderCell("Surf/Vol", getBold(), 1)); + structMapTable.addCell(createHeaderCell("VolFract", getBold(), 1)); + structMapTable.endHeaders(); + } + String structName = structMap[i].getStructure().getName(); + SubVolume subVol = (SubVolume) structMap[i].getGeometryClass(); + String subDomain = ""; + if (subVol != null) { + subDomain = subVol.getName(); + } + boolean isResolved = false; // ((FeatureMapping)structMap[i]).getResolved(); + String surfVolStr = "", volFractStr = ""; + MembraneMapping mm = (MembraneMapping) gc.getStructureMapping(sc.getModel().getStructureTopology().getParentStructure(structMap[i].getStructure())); + if (mm != null) { + StructureMapping.StructureMappingParameter smp = mm.getSurfaceToVolumeParameter(); + if (smp != null) { + Expression tempExp = smp.getExpression(); + VCUnitDefinition tempUnit = smp.getUnitDefinition(); + if (tempExp != null) { + surfVolStr = tempExp.infix(); + if (tempUnit != null && !modelUnitSystem.getInstance_DIMENSIONLESS().compareEqual(tempUnit)) { //no need to add '1' for dimensionless unit + surfVolStr += " " + tempUnit.getSymbolUnicode(); + } + } + } + smp = mm.getVolumeFractionParameter(); + if (smp != null) { + Expression tempExp = smp.getExpression(); + VCUnitDefinition tempUnit = smp.getUnitDefinition(); + if (tempExp != null) { + volFractStr = tempExp.infix(); + if (tempUnit != null && !modelUnitSystem.getInstance_DIMENSIONLESS().compareEqual(tempUnit)) { + volFractStr += " " + tempUnit.getSymbolUnicode(); + } + } + } + } + structMapTable.addCell(createCell(structName, getFont())); + structMapTable.addCell(createCell(subDomain, getFont())); + structMapTable.addCell(createCell((isResolved ? " T " : " F "), getFont())); + structMapTable.addCell(createCell(surfVolStr, getFont())); + structMapTable.addCell(createCell(volFractStr, getFont())); + } + if (structMapTable != null) { + if (structMappSection == null) { + structMappSection = simContextSection.addSection("Structure Mapping For " + sc.getName(), simContextSection.getNumberDepth() + 1); + } + structMappSection.add(structMapTable); + } + } + + + //currently not used. + protected void writeSubDomainsEquationsAsImages(Section mathDescSection, MathDescription mathDesc) { + + Enumeration subDomains = mathDesc.getSubDomains(); + Expression[] expArray; + Section volDomains = mathDescSection.addSection("Volume Domains", mathDescSection.getDepth() + 1); + Section memDomains = mathDescSection.addSection("Membrane Domains", mathDescSection.getDepth() + 1); + int scale = 1, height = 200; //arbitrary + int viewableWidth = (int) (document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin()); + BufferedImage dummy = new BufferedImage(viewableWidth, height, BufferedImage.TYPE_3BYTE_BGR); + while (subDomains.hasMoreElements()) { + SubDomain subDomain = subDomains.nextElement(); + Enumeration equationsList = subDomain.getEquations(); + ArrayList expList = new ArrayList<>(); + while (equationsList.hasMoreElements()) { + Equation equ = equationsList.nextElement(); + try { + Enumeration enum_equ = equ.getTotalExpressions(); + while (enum_equ.hasMoreElements()) { + Expression exp = new Expression(enum_equ.nextElement()); + expList.add(exp.flatten()); + } + } catch (ExpressionException ee) { + lg.error("Unable to process the equation for subdomain: " + subDomain.getName(), ee); + } + } + expArray = expList.toArray(Expression[]::new); + Section tempSection = null; + if (subDomain instanceof CompartmentSubDomain) { + tempSection = volDomains.addSection(subDomain.getName(), volDomains.getDepth() + 1); + } else if (subDomain instanceof MembraneSubDomain) { + tempSection = memDomains.addSection(subDomain.getName(), memDomains.getDepth() + 1); + } + try { + Dimension dim = ExpressionCanvas.getExpressionImageSize(expArray, (Graphics2D) dummy.getGraphics()); + lg.debug("Image dim: {} {}", dim.width, dim.height); + BufferedImage bufferedImage = new BufferedImage((int) dim.getWidth() * scale, (int) dim.getHeight() * scale, BufferedImage.TYPE_3BYTE_BGR); + ExpressionCanvas.getExpressionAsImage(expArray, bufferedImage, scale); + //Table imageTable = null;; + com.lowagie.text.Image expImage = com.lowagie.text.Image.getInstance(bufferedImage, null); + expImage.setAlignment(com.lowagie.text.Image.LEFT); + if (viewableWidth < expImage.getScaledWidth()) { + expImage.scaleToFit(viewableWidth, expImage.getHeight()); + lg.debug("SubDomain expressions After scaling: {}", expImage.getScaledWidth()); + } /*Cell imageCell = new Cell(); imageCell.add(expImage); if (imageTable == null) { @@ -2324,110 +2334,110 @@ protected void writeSubDomainsEquationsAsImages(Section mathDescSection, MathDes imageTable.addCell(imageCell); imageTable.setWidth(100); tempSection.add(imageTable);*/ - tempSection.add(expImage); - } catch (Exception e) { - lg.error("Unable to add subdomain equation image to report.", e); - } - } - if (volDomains.isEmpty()) { - mathDescSection.remove(volDomains); - } - if (memDomains.isEmpty()) { - mathDescSection.remove(memDomains); - } - } - + tempSection.add(expImage); + } catch (Exception e) { + lg.error("Unable to add subdomain equation image to report.", e); + } + } + if (volDomains.isEmpty()) { + mathDescSection.remove(volDomains); + } + if (memDomains.isEmpty()) { + mathDescSection.remove(memDomains); + } + } + + + protected void writeSubDomainsEquationsAsText(Section mathDescSection, MathDescription mathDesc) throws DocumentException { + + Enumeration subDomains = mathDesc.getSubDomains(); + Section volDomains = mathDescSection.addSection("Volume Domains", mathDescSection.getDepth() + 1); + Section memDomains = mathDescSection.addSection("Membrane Domains", mathDescSection.getDepth() + 1); + Section filDomains = mathDescSection.addSection("Filament Domains", mathDescSection.getDepth() + 1); + while (subDomains.hasMoreElements()) { + Section tempSection = null; + SubDomain subDomain = subDomains.nextElement(); + if (subDomain instanceof CompartmentSubDomain) { + tempSection = volDomains.addSection(subDomain.getName(), volDomains.getDepth() + 1); + } else if (subDomain instanceof MembraneSubDomain) { + tempSection = memDomains.addSection(subDomain.getName(), memDomains.getDepth() + 1); + } else if (subDomain instanceof FilamentSubDomain) { + tempSection = filDomains.addSection(subDomain.getName(), filDomains.getDepth() + 1); + } + Enumeration equationsList = subDomain.getEquations(); + while (equationsList.hasMoreElements()) { + Equation equ = equationsList.nextElement(); + writeEquation(tempSection, equ); + } + if (subDomain.getFastSystem() != null) { + writeFastSystem(tempSection, subDomain.getFastSystem()); + } + if (subDomain instanceof MembraneSubDomain) { + Enumeration jcList = ((MembraneSubDomain) subDomain).getJumpConditions(); + while (jcList.hasMoreElements()) { + JumpCondition jc = jcList.nextElement(); + writeJumpCondition(tempSection, jc); + } + } + } + if (volDomains.isEmpty()) { + mathDescSection.remove(volDomains); + } + if (memDomains.isEmpty()) { + mathDescSection.remove(memDomains); + } + if (filDomains.isEmpty()) { + mathDescSection.remove(filDomains); + } + } - protected void writeSubDomainsEquationsAsText(Section mathDescSection, MathDescription mathDesc) throws DocumentException { - - Enumeration subDomains = mathDesc.getSubDomains(); - Section volDomains = mathDescSection.addSection("Volume Domains", mathDescSection.getDepth() + 1); - Section memDomains = mathDescSection.addSection("Membrane Domains", mathDescSection.getDepth() + 1); - Section filDomains = mathDescSection.addSection("Filament Domains", mathDescSection.getDepth() + 1); - while(subDomains.hasMoreElements()) { - Section tempSection = null; - SubDomain subDomain = subDomains.nextElement(); - if (subDomain instanceof CompartmentSubDomain) { - tempSection = volDomains.addSection(subDomain.getName(), volDomains.getDepth() + 1); - } else if (subDomain instanceof MembraneSubDomain) { - tempSection = memDomains.addSection(subDomain.getName(), memDomains.getDepth() + 1); - } else if (subDomain instanceof FilamentSubDomain) { - tempSection = filDomains.addSection(subDomain.getName(), filDomains.getDepth() + 1); - } - Enumeration equationsList = subDomain.getEquations(); - while (equationsList.hasMoreElements()) { - Equation equ = equationsList.nextElement(); - writeEquation(tempSection, equ); - } - if (subDomain.getFastSystem() != null) { - writeFastSystem(tempSection, subDomain.getFastSystem()); - } - if (subDomain instanceof MembraneSubDomain) { - Enumeration jcList = ((MembraneSubDomain)subDomain).getJumpConditions(); - while (jcList.hasMoreElements()) { - JumpCondition jc = jcList.nextElement(); - writeJumpCondition(tempSection, jc); - } - } - } - if (volDomains.isEmpty()) { - mathDescSection.remove(volDomains); - } - if (memDomains.isEmpty()) { - mathDescSection.remove(memDomains); - } - if (filDomains.isEmpty()) { - mathDescSection.remove(filDomains); - } - } + protected void writeVolumeRegionEquation(Section container, VolumeRegionEquation eq) throws DocumentException { - protected void writeVolumeRegionEquation(Section container, VolumeRegionEquation eq) throws DocumentException { + Table eqTable = getTable(2, 100, 1, 2, 2); + eqTable.addCell(createCell(VCML.VolumeRegionEquation + " " + eq.getVariable().getName(), + getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); + eqTable.endHeaders(); + String exp = "0.0"; + eqTable.addCell(createCell(VCML.UniformRate, getFont())); + if (eq.getUniformRateExpression() != null) { + exp = eq.getUniformRateExpression().infix(); + } + eqTable.addCell(createCell(exp, getFont())); + exp = "0.0"; + eqTable.addCell(createCell(VCML.VolumeRate, getFont())); + if (eq.getVolumeRateExpression() != null) { + exp = eq.getVolumeRateExpression().infix(); + } + eqTable.addCell(createCell(exp, getFont())); - Table eqTable = getTable(2, 100, 1, 2, 2); - eqTable.addCell(createCell(VCML.VolumeRegionEquation + " " + eq.getVariable().getName(), - getBold(DEF_HEADER_FONT_SIZE), 2, 1, Element.ALIGN_CENTER, true)); - eqTable.endHeaders(); - String exp = "0.0"; - eqTable.addCell(createCell(VCML.UniformRate, getFont())); - if (eq.getUniformRateExpression() != null) { - exp = eq.getUniformRateExpression().infix(); - } - eqTable.addCell(createCell(exp, getFont())); - exp = "0.0"; - eqTable.addCell(createCell(VCML.VolumeRate, getFont())); - if (eq.getVolumeRateExpression() != null) { - exp = eq.getVolumeRateExpression().infix(); - } - eqTable.addCell(createCell(exp, getFont())); - // exp = "0.0"; // eqTable.addCell(createCell(VCML.MembraneRate, getFont())); // if (eq.getMembraneRateExpression() != null) { // exp = eq.getMembraneRateExpression().infix(); // } // eqTable.addCell(createCell(exp, getFont())); - - if (eq.getInitialExpression() != null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); - } - int solutionType = eq.getSolutionType(); - switch (solutionType) { - case Equation.UNKNOWN_SOLUTION:{ - if (eq.getInitialExpression() == null) { - eqTable.addCell(createCell(VCML.Initial, getFont())); - eqTable.addCell(createCell("0.0", getFont())); - } - break; - } - case Equation.EXACT_SOLUTION:{ - eqTable.addCell(createCell(VCML.Exact, getFont())); - eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); - break; - } - } - - container.add(eqTable); - } + + if (eq.getInitialExpression() != null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell(eq.getInitialExpression().infix(), getFont())); + } + int solutionType = eq.getSolutionType(); + switch (solutionType) { + case Equation.UNKNOWN_SOLUTION: { + if (eq.getInitialExpression() == null) { + eqTable.addCell(createCell(VCML.Initial, getFont())); + eqTable.addCell(createCell("0.0", getFont())); + } + break; + } + case Equation.EXACT_SOLUTION: { + eqTable.addCell(createCell(VCML.Exact, getFont())); + eqTable.addCell(createCell(eq.getExactSolution().infix(), getFont())); + break; + } + } + + container.add(eqTable); + } } diff --git a/vcell-core/src/main/java/cbit/vcell/solver/SolverUtilities.java b/vcell-core/src/main/java/cbit/vcell/solver/SolverUtilities.java index 047f049b67..bf8a8b831c 100644 --- a/vcell-core/src/main/java/cbit/vcell/solver/SolverUtilities.java +++ b/vcell-core/src/main/java/cbit/vcell/solver/SolverUtilities.java @@ -162,10 +162,12 @@ public static SolverDescription matchSolverWithKisaoId(String originalId, boolea private static List matchByKisaoId(KisaoTerm candidate) { List solverDescriptions = new ArrayList<>(); for (SolverDescription sd : SolverDescription.values()) { - if(sd.getKisao().contains(":") || sd.getKisao().contains("_")) { + if (sd.getKisao().contains(":") || sd.getKisao().contains("_")) { logger.trace(sd.getKisao()); + } else if ("KISAO".equals(sd.getKisao())){ + logger.info("Skipping not-yet-created KiSAO term"); } else { - logger.warn(sd.getKisao() + " - bad format, skipping"); + logger.warn("`{}` is bad KiSAO formating, skipping", sd.getKisao()); continue; } String s1 = candidate.getId(); diff --git a/vcell-core/src/main/java/cbit/vcell/xml/XmlHelper.java b/vcell-core/src/main/java/cbit/vcell/xml/XmlHelper.java index 5f9ff632f8..4af94c50ac 100644 --- a/vcell-core/src/main/java/cbit/vcell/xml/XmlHelper.java +++ b/vcell-core/src/main/java/cbit/vcell/xml/XmlHelper.java @@ -501,7 +501,7 @@ public static List readOmex(File omexFile, VCLogger vcLogger) throws E public static List importSEDML(VCLogger transLogger, ExternalDocInfo externalDocInfo, SedML sedml, boolean exactMatchOnly) throws Exception { - SEDMLImporter sedmlImporter = new SEDMLImporter(transLogger, externalDocInfo, + SEDMLImporter sedmlImporter = new SEDMLImporter(transLogger, externalDocInfo.getFile(), sedml, exactMatchOnly); return sedmlImporter.getBioModels(); } diff --git a/vcell-core/src/main/java/cbit/vcell/xml/XmlReader.java b/vcell-core/src/main/java/cbit/vcell/xml/XmlReader.java index fdc9d4067a..43e664866f 100644 --- a/vcell-core/src/main/java/cbit/vcell/xml/XmlReader.java +++ b/vcell-core/src/main/java/cbit/vcell/xml/XmlReader.java @@ -4562,7 +4562,7 @@ public Model getModel(Element param) throws XmlParseException{ if(element != null){ getRbmModelContainer(element, newmodel); } else { - lg.info("RbmModelContainer is missing."); + lg.debug("RbmModelContainer is missing."); } //Add SpeciesContexts diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/SchemaValidatorImpl.java b/vcell-core/src/main/java/org/jlibsedml/validation/SchemaValidatorImpl.java index 2b10a4827b..7ec5444523 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/SchemaValidatorImpl.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/SchemaValidatorImpl.java @@ -88,9 +88,12 @@ private String getSchema(String xmlAsString) throws JDOMException, return SEDML_L1_V1_SCHEMA; } else if (level.equals("1") && version.equals("2")) { return SEDML_L1_V2_SCHEMA; + } else if (level.equals("1") && version.equals("3")) { + log.info("Version 3 support still in development."); + return SEDML_L1_V3_SCHEMA; } else { // probably level 3, but trying anyway to interpret with level 2 - System.out.println("SED-ML version level not supported, import may fail"); + log.info("WARNING: SED-ML L{}V{} not supported, import may fail", level, version); return SEDML_L1_V3_SCHEMA; // throw new IllegalArgumentException( // "Invalid level/version combingation - must be 1-1 or 1-2 but was " diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/SchematronValidator.java b/vcell-core/src/main/java/org/jlibsedml/validation/SchematronValidator.java index fd72478d4a..92450d345b 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/SchematronValidator.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/SchematronValidator.java @@ -116,7 +116,7 @@ private String getSchematronXSL() { } else if (sedml.isL1V2()) { return "validatorl1v2.xsl"; } else { - System.out.println("Unsupported version, import may fail"); + lg.warn("Unsupported sedml version `L{}V{}` detected, validating as L1V2", sedml.getLevel(), sedml.getVersion()); return "validatorl1v2.xsl"; // throw new UnsupportedOperationException(MessageFormat.format( // "Invalid level and version - {0}-{1}", sedml.getLevel(), diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java index 2a27d5a876..a58ee8ddbe 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java +++ b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java @@ -63,8 +63,7 @@ */ public class SEDMLImporter { private final static Logger logger = LogManager.getLogger(SEDMLImporter.class); - private final SedML sedml; - private final ExternalDocInfo externalDocInfo; + private SedML sedml; private final boolean exactMatchOnly; private final VCLogger transLogger; @@ -76,31 +75,47 @@ public class SEDMLImporter { private final HashMap importMap = new HashMap<>(); /** - * Prepares a sedml for import as biomodels + * Builds the importer for future initialization * * @param transLogger the VC logger to use - * @param externalDocInfo contextual information necessary for import - * @param sedml the sedml to import + * @param exactMatchOnly do not substitute for "compatible" kisao solvers, use the exact solver only. + */ + public SEDMLImporter(VCLogger transLogger, boolean exactMatchOnly) { + this.transLogger = transLogger; + this.sedml = null; + this.exactMatchOnly = exactMatchOnly; + } + + /** + * Prepares a sedml for import as biomodels + * + * @param transLogger the VC logger to use * @param exactMatchOnly do not substitute for "compatible" kisao solvers, use the exact solver only. * @throws FileNotFoundException if the sedml archive can not be found * @throws XMLException if the sedml has invalid xml. */ - public SEDMLImporter(VCLogger transLogger, ExternalDocInfo externalDocInfo, SedML sedml, boolean exactMatchOnly) + public SEDMLImporter(VCLogger transLogger, File fileWithSedmlToProcess, SedML sedml, boolean exactMatchOnly) throws XMLException, IOException { - this.transLogger = transLogger; - this.externalDocInfo = externalDocInfo; - this.sedml = sedml; - this.exactMatchOnly = exactMatchOnly; - - this.initialize(); + this(transLogger, exactMatchOnly); + this.initialize(fileWithSedmlToProcess, sedml); } - - private void initialize() throws XMLException, IOException { - // extract bioModel name from sedx (or sedml) file - this.bioModelBaseName = FileUtils.getBaseName(this.externalDocInfo.getFile().getAbsolutePath()); - if(this.externalDocInfo.getFile().getPath().toLowerCase().endsWith("sedx") - || this.externalDocInfo.getFile().getPath().toLowerCase().endsWith("omex")) { - this.ac = Libsedml.readSEDMLArchive(Files.newInputStream(this.externalDocInfo.getFile().toPath())); + + /** + * Initialize the importer to process a specific set of SedML within a document or archive. + * @param fileWithSedmlToProcess the file containing SedML + * @param sedml the SedML to be processed (since the file may have more than 1 sedml) + * @throws IOException if the sedml archive can not be found, or the IO stream reading it failed + * @throws XMLException if the sedml has invalid xml. + */ + public void initialize(File fileWithSedmlToProcess, SedML sedml) throws XMLException, IOException { + // extract bioModel name from sedml (or sedml) file + if (fileWithSedmlToProcess == null) throw new IllegalArgumentException("Source file of SedML can not be null!"); + if (sedml == null) throw new IllegalArgumentException("Provided SedML can not be null!"); + this.sedml = sedml; + this.bioModelBaseName = FileUtils.getBaseName(fileWithSedmlToProcess.getAbsolutePath()); + if(fileWithSedmlToProcess.getPath().toLowerCase().endsWith("sedx") + || fileWithSedmlToProcess.getPath().toLowerCase().endsWith("omex")) { + this.ac = Libsedml.readSEDMLArchive(Files.newInputStream(fileWithSedmlToProcess.toPath())); } this.resolver = new ModelResolver(this.sedml); if(this.ac != null) { @@ -109,7 +124,7 @@ private void initialize() throws XMLException, IOException { this.resolver.add(amr); } else { this.resolver.add(new FileModelResolver()); // assumes absolute paths - String sedmlRelativePrefix = this.externalDocInfo.getFile().getParent() + File.separator; + String sedmlRelativePrefix = fileWithSedmlToProcess.getParent() + File.separator; this.resolver.add(new RelativeFileModelResolver(sedmlRelativePrefix)); // in case model URIs are relative paths } this.sbmlSupport = new SBMLSupport(); @@ -426,10 +441,10 @@ private List mergeBioModels(List bioModels) { BioModel bm0 = bioModels.get(0); for (int i = 1; i < bioModels.size(); i++) { - System.out.println("----comparing model from----"+bioModels.get(i)+" with model from "+bm0); + logger.debug("--------------------\ncomparing model from `{}`\n with model from `{}`\n--------------------", bioModels.get(i), bm0); RelationVisitor rvNotStrict = new ModelRelationVisitor(false); boolean equivalent = bioModels.get(i).getModel().relate(bm0.getModel(),rvNotStrict); - System.out.println(equivalent); + logger.debug("Equivalent => {}", equivalent); if (!equivalent) return bioModels; } // all have matchable model, try to merge by pooling SimContexts @@ -1070,7 +1085,7 @@ private void translateAlgorithmParams(SolverTaskDescription simTaskDesc, org.jli NonspatialStochHybridOptions nonspatialSHO = simTaskDesc.getStochHybridOpt(); nonspatialSHO.setSDETolerance(Double.parseDouble(apValue)); } else { - logger.error("Algorithm parameter with kisao id '" + apKisaoID + "' not supported at this time, skipping."); + logger.warn("Algorithm parameter with kisao id '" + apKisaoID + "' not supported at this time, skipping."); } } simTaskDesc.setErrorTolerance(errorTolerance); diff --git a/vcell-core/src/main/java/org/vcell/sedml/log/BiosimulationLog.java b/vcell-core/src/main/java/org/vcell/sedml/log/BiosimulationLog.java index 87366a8506..7020ae1253 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/log/BiosimulationLog.java +++ b/vcell-core/src/main/java/org/vcell/sedml/log/BiosimulationLog.java @@ -22,7 +22,7 @@ import java.util.ArrayList; import java.util.List; -public class BiosimulationLog { +public class BiosimulationLog implements AutoCloseable { public static class LogValidationException extends RuntimeException { public LogValidationException(String message) { diff --git a/vcell-core/src/main/java/org/vcell/sedml/testsupport/FailureType.java b/vcell-core/src/main/java/org/vcell/sedml/testsupport/FailureType.java index c5e9c9ce59..42c5118aa6 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/testsupport/FailureType.java +++ b/vcell-core/src/main/java/org/vcell/sedml/testsupport/FailureType.java @@ -13,6 +13,7 @@ public enum FailureType { MATH_GENERATION_FAILURE, MATH_OVERRIDES_A_FUNCTION, MATH_OVERRIDES_INVALID, + NESTED_SEDML_REPEATED_TASK, // We can do a repeated task of a normal task, but not another repeated task. NULL_POINTER_EXCEPTION, OPERATION_NOT_SUPPORTED, // VCell simply doesn't have the necessary features to run this archive. SBML_IMPORT_FAILURE,