diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 69b44df79a..7add25c3e9 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -181,9 +181,9 @@ jobs: mvn --batch-mode clean install dependency:copy-dependencies -Dgroups="${{ matrix.test-group }}" fi -# - name: Setup tmate session -# uses: mxschmitt/action-tmate@v3 -# if: ${{ failure() }} + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: ${{ failure() }} CD: name: CD diff --git a/vcell-api/src/main/java/org/vcell/rest/server/RestDatabaseService.java b/vcell-api/src/main/java/org/vcell/rest/server/RestDatabaseService.java index 1a262d836c..e9f6ab2f57 100644 --- a/vcell-api/src/main/java/org/vcell/rest/server/RestDatabaseService.java +++ b/vcell-api/src/main/java/org/vcell/rest/server/RestDatabaseService.java @@ -40,7 +40,7 @@ import org.vcell.sbml.OmexPythonUtils; import org.vcell.sedml.ModelFormat; import org.vcell.sedml.PublicationMetadata; -import org.vcell.sedml.SEDMLExporter; +import org.vcell.sedml.SedMLExporter; import org.vcell.util.*; import org.vcell.util.document.*; @@ -466,7 +466,7 @@ public String query(BiomodelVCMLServerResource resource, User vcellUser) throws return vcmlBigString.toString(); } - public ByteArrayRepresentation query(BiomodelOMEXServerResource resource, User vcellUser, boolean bSkipUnsupported, StringBuffer suggestedProjectName) throws SQLException, DataAccessException, XmlParseException, IOException, SEDMLExporter.SEDMLExportException, OmexPythonUtils.OmexValidationException { + public ByteArrayRepresentation query(BiomodelOMEXServerResource resource, User vcellUser, boolean bSkipUnsupported, StringBuffer suggestedProjectName) throws SQLException, DataAccessException, XmlParseException, IOException, SedMLExporter.SEDMLExportException, OmexPythonUtils.OmexValidationException { if (vcellUser == null) { vcellUser = VCellApiApplication.DUMMY_USER; } @@ -487,7 +487,7 @@ public ByteArrayRepresentation query(BiomodelOMEXServerResource resource, User v Predicate simContextFilter = (SimulationContext sc) -> true; if (bSkipUnsupported) { - Map unsupportedApplications = SEDMLExporter.getUnsupportedApplicationMap(bioModel, modelFormat); + Map unsupportedApplications = SedMLExporter.getUnsupportedApplicationMap(bioModel, modelFormat); simContextFilter = (SimulationContext sc) -> !unsupportedApplications.containsKey(sc.getName()); } String filenamePrefix = "VCDB_"+bioModelKey; @@ -498,7 +498,7 @@ public ByteArrayRepresentation query(BiomodelOMEXServerResource resource, User v suggestedProjectName.append(filenamePrefix); try { boolean bCreateOmexArchive = true; - SEDMLExporter.writeBioModel(bioModel, publicationMetadata, exportOmexFile, modelFormat, simContextFilter, + SedMLExporter.writeBioModel(bioModel, publicationMetadata, exportOmexFile, modelFormat, simContextFilter, bHasPython, bRoundTripSBMLValidation, bCreateOmexArchive); byte[] omexFileBytes = Files.readAllBytes(exportOmexFile.toPath()); return new ByteArrayRepresentation(omexFileBytes, BiomodelOMEXResource.OMEX_MEDIATYPE); 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 9e811434e8..f56ce9945d 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 @@ -141,7 +141,8 @@ public void executeArchive(boolean isBioSimSedml) throws BiosimulationsHdfWriter private void executeSedmlDocument(String sedmlLocation, HDF5ExecutionResults cumulativeHdf5Results) throws IOException, PreProcessingException, ExecutionException { 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); + SedMLJob job = new SedMLJob(sedmlLocation, this.omexHandler, this.inputFile, this.outputDir, this.sedmlPath2d3d.toString(), this.cliRecorder, this.bKeepTempFiles, this.bExactMatchOnly, this.bSmallMeshOverride); + this.logOmexMessage.append("Processing ").append(job.SEDML_NAME).append(". "); SedmlStatistics stats = job.preProcessDoc(); boolean hasSucceeded = job.simulateSedml(cumulativeHdf5Results); this.anySedmlDocumentHasSucceeded |= hasSucceeded; 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 8e4ed71896..b3b7828dcd 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 @@ -14,8 +14,15 @@ import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jlibsedml.DataSet; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.output.DataSet; import org.jlibsedml.*; +import org.jlibsedml.components.output.Output; +import org.jlibsedml.components.output.Report; +import org.jlibsedml.components.simulation.UniformTimeCourse; import org.vcell.cli.run.results.ValueHolder; import org.vcell.sbml.vcell.lazy.LazySBMLNonSpatialDataAccessor; import org.vcell.util.DataAccessException; @@ -146,10 +153,11 @@ public static double[] interpLinear(double[] x, double[] y, double[] xi) throws return yi; } - public static HashMap generateReportsAsCSV(SedML sedml, Map> organizedNonSpatialResults, File outDirForCurrentSedml) { + public static HashMap generateReportsAsCSV(SedMLDataContainer sedmlContainer, Map> organizedNonSpatialResults, File outDirForCurrentSedml) { // finally, the real work - HashMap reportsHash = new HashMap<>(); - for (Output sedmlOutput : sedml.getOutputs()) { + SedML sedML = sedmlContainer.getSedML(); + HashMap reportsHash = new HashMap<>(); + for (Output sedmlOutput : sedML.getOutputs()) { // We only want Reports if (!(sedmlOutput instanceof Report sedmlReport)) { if (logger.isDebugEnabled()) logger.info("Ignoring unsupported output `" + sedmlOutput.getId() + "` while CSV generation."); @@ -168,11 +176,11 @@ public static HashMap generateReportsAsCSV(SedML sedml, Map datasets = sedmlReport.getListOfDataSets(); + List datasets = sedmlReport.getDataSets(); Map dataGeneratorMapping = new LinkedHashMap<>(); for (DataSet dataset : datasets) { - DataGenerator referencedGenerator = sedml.getDataGeneratorWithId(dataset.getDataReference()); - if (referencedGenerator == null) throw new NullPointerException("SedML DataGenerator referenced by report is missing!"); + DataGenerator referencedGenerator = sedmlContainer.findDataGeneratorById(dataset.getDataReference()); + if (null == referencedGenerator) throw new IllegalArgumentException("Unable to find data generator referenced in dataset: " + dataset.getDataReference()); if (!organizedNonSpatialResults.containsKey(referencedGenerator)) break; dataGeneratorMapping.put(dataset, referencedGenerator); } @@ -184,7 +192,7 @@ public static HashMap generateReportsAsCSV(SedML sedml, Map dataHolder = organizedNonSpatialResults.get(referencedGenerator); @@ -192,14 +200,14 @@ public static HashMap generateReportsAsCSV(SedML sedml, Map extensionListMap = new HashMap() {{ - put("csv", "reports.zip"); - put("pdf", "plots.zip"); + Map extensionListMap = new HashMap<>() {{ + this.put("csv", "reports.zip"); + this.put("pdf", "plots.zip"); }}; for (String ext : extensionListMap.keySet()) { 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 similarity index 69% rename from vcell-cli/src/main/java/org/vcell/cli/run/SedmlJob.java rename to vcell-cli/src/main/java/org/vcell/cli/run/SedMLJob.java index 2cf0a438ae..6367efe797 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 @@ -3,10 +3,24 @@ import cbit.vcell.resource.OperatingSystemInfo; import cbit.vcell.xml.ExternalDocInfo; import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.output.Output; +import org.jlibsedml.components.output.Plot2D; +import org.jlibsedml.components.output.Plot3D; +import org.jlibsedml.components.output.Report; +import org.jlibsedml.components.simulation.Simulation; +import org.jlibsedml.components.simulation.SteadyState; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.simulation.OneStep; +import org.jlibsedml.components.task.RepeatedTask; +import org.jlibsedml.components.task.SetValue; import org.vcell.cli.messaging.CLIRecordable; import org.vcell.cli.exceptions.ExecutionException; import org.vcell.cli.exceptions.PreProcessingException; @@ -22,7 +36,6 @@ import org.vcell.sedml.log.BiosimulationLog; import org.vcell.trace.Span; import org.vcell.trace.Tracer; -import org.vcell.util.DataAccessException; import org.vcell.util.FileUtils; import org.vcell.util.Pair; @@ -32,29 +45,28 @@ 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. */ -public class SedmlJob { +public class SedMLJob { + private final static Logger logger = LogManager.getLogger(SedMLJob.class); private final boolean SHOULD_KEEP_TEMP_FILES, ACCEPT_EXACT_MATCH_ONLY, SHOULD_OVERRIDE_FOR_SMALL_MESH; - private final String SEDML_NAME, SEDML_LOCATION, BIOMODEL_BASE_NAME, RESULTS_DIRECTORY_PATH; + private final String 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; // 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; - private SedML sedml; + private SedMLDataContainer sedml; + public final String SEDML_NAME; - private final static Logger logger = LogManager.getLogger(SedmlJob.class); /** * Constructor to provide all needed info for a SedML processing job. @@ -63,17 +75,15 @@ public class SedmlJob { * @param omexHandler object to deal with omex archive related utilities * @param masterOmexArchive the archive containing the sedml file * @param resultsDirPath path to where the results should be placed - * @param sedmlPath2d3dString path to where 2D and 3D plots are stored + * @param pathToPlotsDirectory path to where 2D and 3D plots are stored * @param cliRecorder recorder object used for CLI applications * @param bKeepTempFiles whether temp files shouldn't be deleted, or should. * @param bExactMatchOnly enforces a KISAO match, with no substitution * @param bSmallMeshOverride whether to use small meshes or standard meshes. - * @param logOmexMessage a string-builder to contain progress updates of omex execution */ - public SedmlJob(String sedmlLocation, OmexHandler omexHandler, File masterOmexArchive, - String resultsDirPath, String sedmlPath2d3dString, CLIRecordable cliRecorder, - boolean bKeepTempFiles, boolean bExactMatchOnly, boolean bSmallMeshOverride, - StringBuilder logOmexMessage){ + public SedMLJob(String sedmlLocation, OmexHandler omexHandler, File masterOmexArchive, + String resultsDirPath, String pathToPlotsDirectory, CLIRecordable cliRecorder, + boolean bKeepTempFiles, boolean bExactMatchOnly, boolean bSmallMeshOverride){ final String SAFE_WINDOWS_FILE_SEPARATOR = "\\\\"; final String SAFE_UNIX_FILE_SEPARATOR = "/"; this.MASTER_OMEX_ARCHIVE = masterOmexArchive; @@ -85,8 +95,7 @@ public SedmlJob(String sedmlLocation, OmexHandler omexHandler, File masterOmexAr 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; - this.PLOTS_DIRECTORY = new File(sedmlPath2d3dString); + this.PLOTS_DIRECTORY = new File(pathToPlotsDirectory); this.CLI_RECORDER = cliRecorder; this.SHOULD_KEEP_TEMP_FILES = bKeepTempFiles; this.ACCEPT_EXACT_MATCH_ONLY = bExactMatchOnly; @@ -107,81 +116,82 @@ public SedmlJob(String sedmlLocation, OmexHandler omexHandler, File masterOmexAr public SedmlStatistics preProcessDoc() throws IOException, PreProcessingException { BiosimulationLog biosimLog = BiosimulationLog.instance(); - Span span = Tracer.startSpan(Span.ContextType.PROCESSING_SEDML, "preProcessDoc", null); - RunUtils.removeAndMakeDirs(this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML); - - this.LOG_OMEX_MESSAGE.append("Processing ").append(this.SEDML_NAME).append(". "); - - // Load SedML - logger.info("Initializing and Pre-Processing SedML document: {}", this.SEDML_NAME); - biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.RUNNING); + Span span = null; try { - this.sedml = SedmlJob.getSedMLFile(this.SEDML_NAME_SPLIT, this.MASTER_OMEX_ARCHIVE); - } catch (Exception e) { - String prefix = "SedML pre-processing for " + this.SEDML_LOCATION + " failed"; - this.logDocumentError = prefix + ": " + e.getMessage(); - Tracer.failure(e, prefix); - this.reportProblem(e); - this.somethingFailed = SedmlJob.somethingDidFail(); - biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.FAILED); - span.close(); - throw new PreProcessingException(prefix, e); - } + span = Tracer.startSpan(Span.ContextType.PROCESSING_SEDML, "preProcessDoc", null); + RunUtils.removeAndMakeDirs(this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML); - // 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()); - for (Simulation simulation : this.sedml.getSimulations()){ - boolean isUTC = simulation instanceof UniformTimeCourse; - if (isUTC) this.DOC_STATISTICS.setNumUniformTimeCourseSimulations(this.DOC_STATISTICS.getNumUniformTimeCourseSimulations() + 1); - else if (simulation instanceof OneStep) this.DOC_STATISTICS.setNumOneStepSimulations(this.DOC_STATISTICS.getNumOneStepSimulations() + 1); - else if (simulation instanceof SteadyState) this.DOC_STATISTICS.setNumSteadyStateSimulations(this.DOC_STATISTICS.getNumSteadyStateSimulations() + 1); - else this.DOC_STATISTICS.setNumAnalysisSimulations(this.DOC_STATISTICS.getNumAnalysisSimulations() + 1); - } + // 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 = "SedML pre-processing for " + this.SEDML_LOCATION + " failed"; + this.logDocumentError = prefix + ": " + e.getMessage(); + Tracer.failure(e, prefix); + this.reportProblem(e); + this.somethingFailed = SedMLJob.somethingDidFail(); + biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.FAILED); + span.close(); + throw new PreProcessingException(prefix, e); + } + + // If we got here, we have a successful load!! + SedML sedML = this.sedml.getSedML(); + 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(sedML.getModels().size()); + this.DOC_STATISTICS.setNumSimulations(sedML.getSimulations().size()); + for (Simulation simulation : sedML.getSimulations()){ + boolean isUTC = simulation instanceof UniformTimeCourse; + if (isUTC) this.DOC_STATISTICS.setNumUniformTimeCourseSimulations(this.DOC_STATISTICS.getNumUniformTimeCourseSimulations() + 1); + else if (simulation instanceof OneStep) this.DOC_STATISTICS.setNumOneStepSimulations(this.DOC_STATISTICS.getNumOneStepSimulations() + 1); + else if (simulation instanceof SteadyState) this.DOC_STATISTICS.setNumSteadyStateSimulations(this.DOC_STATISTICS.getNumSteadyStateSimulations() + 1); + else this.DOC_STATISTICS.setNumAnalysisSimulations(this.DOC_STATISTICS.getNumAnalysisSimulations() + 1); + } + this.DOC_STATISTICS.setNumTasks(sedML.getTasks().size()); + this.DOC_STATISTICS.setNumOutputs(sedML.getOutputs().size()); + for (Output output : 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); + } - this.DOC_STATISTICS.setNumTasks(this.sedml.getTasks().size()); + // Check for overrides + for(Model m : sedML.getModels()) { + if (m.getChanges().isEmpty()) continue; + this.DOC_STATISTICS.setHasOverrides(this.hasOverrides = true); + break; + } - 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); - } + // Check for parameter scans + for(AbstractTask at : sedML.getTasks()) { + if (!(at instanceof RepeatedTask rt)) continue; + List changes = rt.getChanges(); + if(changes == null || changes.isEmpty()) continue; + this.DOC_STATISTICS.setHasScans(this.hasScans = true); + break; + } - // Check for overrides - for(Model m : this.sedml.getModels()) { - if (m.getListOfChanges().isEmpty()) continue; - this.DOC_STATISTICS.setHasOverrides(this.hasOverrides = true); - break; - } + logger.info("{}{}", resultString, this.DOC_STATISTICS.toFormattedString()); - // 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.DOC_STATISTICS.setHasScans(this.hasScans = true); - break; - } - logger.info("{}{}", resultString, this.DOC_STATISTICS.toFormattedString()); + // Before we leave, we need to throw an exception if we have any VCell Sims we can't run. + if (this.DOC_STATISTICS.getNumUniformTimeCourseSimulations() != this.DOC_STATISTICS.getNumSimulations()){ + biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.SKIPPED); + PreProcessingException exception = new PreProcessingException("There are SedML simulations VCell is not capable of running at this time!"); + Tracer.failure(exception, "Fatal discovery encountered while processing SedML: non-compatible SedML Simulations found."); + throw exception; + } - // Before we leave, we need to throw an exception if we have any VCell Sims we can't run. - if (this.DOC_STATISTICS.getNumUniformTimeCourseSimulations() != this.DOC_STATISTICS.getNumSimulations()){ - biosimLog.updateSedmlDocStatusYml(this.SEDML_LOCATION, BiosimulationLog.Status.SKIPPED); - PreProcessingException exception = new PreProcessingException("There are SedML simulations VCell is not capable of running at this time!"); - Tracer.failure(exception, "Fatal discovery encountered while processing SedML: non-compatible SedML Simulations found."); - throw exception; + } finally { + if (span != null) span.close(); } - - span.close(); return this.DOC_STATISTICS; } @@ -191,14 +201,14 @@ public SedmlStatistics preProcessDoc() throws IOException, PreProcessingExceptio * * @throws IOException if there are system I/O issues */ - public boolean simulateSedml(HDF5ExecutionResults masterHdf5File) throws ExecutionException, IOException { + public boolean simulateSedml(HDF5ExecutionResults resultsCollection) throws ExecutionException, IOException { /* * - Run solvers and generate outputs * - XmlHelper code uses two types of resolvers to handle absolute or relative paths */ SolverHandler solverHandler = new SolverHandler(); this.runSimulations(solverHandler); - this.processOutputs(solverHandler, masterHdf5File); + this.processOutputs(solverHandler, resultsCollection); return this.evaluateResults(); } @@ -217,14 +227,17 @@ private void runSimulations(SolverHandler solverHandler) throws ExecutionExcepti RunUtils.drawBreakLine("-", 100); try { span = Tracer.startSpan(Span.ContextType.SIMULATIONS_RUN, "runSimulations", null); - Map taskResults = 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, - this.ACCEPT_EXACT_MATCH_ONLY, this.SHOULD_OVERRIDE_FOR_SMALL_MESH); + SolverHandler.Configuration initializedModelPair = solverHandler.initialize(externalDocInfo, this.sedml, this.ACCEPT_EXACT_MATCH_ONLY); + if (!this.sedml.equals(initializedModelPair.postInitializedSedml())){ + logger.warn("Importer returned modified SedML to process; now using modified SedML"); + this.sedml = initializedModelPair.postInitializedSedml(); + } + Map taskResults = solverHandler.simulateAllTasks(this.CLI_RECORDER, + this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML, this.SEDML_LOCATION, this.SHOULD_KEEP_TEMP_FILES, this.SHOULD_OVERRIDE_FOR_SMALL_MESH); int numSimulationsUnsuccessful = 0; StringBuilder executionSummary = new StringBuilder("Summary of Task Results\n"); for (AbstractTask sedmlTask : taskResults.keySet()){ - String sedmlTaskName = (sedmlTask.getName() == null || sedmlTask.getName().isBlank()) ? sedmlTask.getId() : sedmlTask.getName() + " (" + sedmlTask.getId() + ")" ; + String sedmlTaskName = (sedmlTask.getName() == null || sedmlTask.getName().isBlank()) ? sedmlTask.getId().string() : sedmlTask.getName() + " (" + sedmlTask.getId() + ")" ; executionSummary.append("\t> ").append(sedmlTaskName).append("::").append(taskResults.get(sedmlTask).name()).append("\n"); if (!taskResults.get(sedmlTask).equals(BiosimulationLog.Status.SUCCEEDED)) numSimulationsUnsuccessful++; } @@ -268,8 +281,8 @@ private void processOutputs(SolverHandler solverHandler, HDF5ExecutionResults ma SpatialResultsConverter.organizeSpatialResultsBySedmlDataGenerator( this.sedml, solverHandler.spatialResults, 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(); + boolean hasReports = !this.sedml.getSedML().getOutputs().stream().filter(Report.class::isInstance).map(Report.class::cast).toList().isEmpty(); + boolean has2DPlots = !this.sedml.getSedML().getOutputs().stream().filter(Plot2D.class::isInstance).map(Plot2D.class::cast).toList().isEmpty(); if (!solverHandler.nonSpatialResults.isEmpty()) { if (hasReports) this.generateCSV(organizedNonSpatialResults); if (has2DPlots) this.generatePlots(organizedNonSpatialResults); @@ -336,7 +349,7 @@ private boolean declarePassedResult() throws IOException { } private void generateCSV(Map> organizedNonSpatialResults) { - Map csvReports; + Map csvReports; this.logDocumentMessage += "Generating CSV file... "; logger.info("Generating CSV file... "); @@ -356,12 +369,12 @@ private void generatePlots(Map> plot2Ds = plotExtractor.extractPlotRelevantData(organizedNonSpatialResults); + Map> plot2Ds = plotExtractor.extractPlotRelevantData(organizedNonSpatialResults); for (Results2DLinePlot plotToExport : plot2Ds.keySet()){ try { plotToExport.generatePng(plot2Ds.get(plotToExport).one + ".png", this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML); plotToExport.generatePdf(plot2Ds.get(plotToExport).one + ".pdf", this.OUTPUT_DIRECTORY_FOR_CURRENT_SEDML); - BiosimulationLog.instance().updatePlotStatusYml(this.SEDML_NAME, plot2Ds.get(plotToExport).two, BiosimulationLog.Status.SUCCEEDED); + BiosimulationLog.instance().updatePlotStatusYml(this.SEDML_NAME, plot2Ds.get(plotToExport).two.string(), BiosimulationLog.Status.SUCCEEDED); } catch (ChartCouldNotBeProducedException e){ logger.error("Failed creating plot:", e); throw new ExecutionException("Failed to create plot: " + plotToExport.getTitle(), e); @@ -380,13 +393,13 @@ private void indexHDF5Data(Map nonSpatialResults = new LinkedHashMap<>(); + // Initialization Vars + private String modelReportingName; + private SedMLDataContainer initializedSedMLContainer; + private Map bioModelToSBMLMapping; + // // // // + Map nonSpatialResults = new LinkedHashMap<>(); Map spatialResults = new LinkedHashMap<>(); Map tempSimulationToTaskMap = new LinkedHashMap<> (); // key = vcell simulation, value = sedml topmost task (the imported task id) Map taskToTempSimulationMap = new LinkedHashMap<> (); // the opposite Map origSimulationToTempSimulationMap = new LinkedHashMap<> (); // the opposite - Map> taskToListOfSubTasksMap = new LinkedHashMap> (); // key = topmost AbstractTask, value = recursive list of subtasks - Map> taskToVariableMap = new LinkedHashMap> (); // key = AbstractTask, value = list of variables calculated by this task - Map> taskToChangeTargetMap = new LinkedHashMap> (); // key = RepeatedTask, value = list of the parameters that are being changed - Map> taskToChildRepeatedTasks = new LinkedHashMap> (); // key = Task, value = list of RepeatedTasks ending with this task - Map topTaskToBaseTask = new LinkedHashMap (); // key = TopmostTaskId, value = Tasks at the bottom of the SubTasks chain OR the topmost task itself if instanceof Task + Map> taskToListOfSubTasksMap = new LinkedHashMap<> (); // key = topmost AbstractTask, value = recursive list of subtasks + Map> taskToVariableMap = new LinkedHashMap<> (); // key = AbstractTask, value = list of variables calculated by this task + Map> taskToChangeTargetMap = new LinkedHashMap<> (); // key = RepeatedTask, value = list of the parameters that are being changed + Map topTaskToBaseTask = new LinkedHashMap<> (); // key = TopmostTaskId, value = Tasks at the bottom of the SubTasks chain OR the topmost task itself if instanceof Task private static void sanityCheck(BioModel bioModel) throws SEDMLImportException { if (bioModel == null) throw new SEDMLImportException("Imported BioModel is null."); @@ -96,9 +99,42 @@ private static void sanityCheck(BioModel bioModel) throws SEDMLImportException { } } - public void initialize(List bioModelList, SedML sedml) throws ExpressionException { - Set topmostTasks = new LinkedHashSet<> (); - for(BioModel bioModel : bioModelList) { + public Configuration initialize(ExternalDocInfo externalDocInfo, SedMLDataContainer initialSedmlContainer, boolean exactMatchOnly) + throws ExpressionException, SEDMLImportException { + cbit.util.xml.VCLogger sedmlImportLogger = new LocalLogger(); + + //String outDirRoot = outputDirForSedml.toString().substring(0, outputDirForSedml.toString().lastIndexOf(System.getProperty("file.separator"))); + SedMLDataContainer actionableSedmlContainer; + Map bioModelMapping; + SedMLImporter sedmlImporter = new SedMLImporter(sedmlImportLogger, new SedMLImporter.StrictnessPolicy( + true, + SedMLImporter.StrictnessPolicy.MultipleSubTaskPolicy.CONDENSE_ELSE_REMOVE, + exactMatchOnly ? SedMLImporter.StrictnessPolicy.SolverMatchPolicy.STRICT_MATCH_OR_REJECT : SedMLImporter.StrictnessPolicy.SolverMatchPolicy.SUNDIALS_AS_LAST_RESORT + )); + this.modelReportingName = org.vcell.util.FileUtils.getBaseName(externalDocInfo.getFile().getAbsolutePath()); + + try { + actionableSedmlContainer = sedmlImporter.initialize(externalDocInfo.getFile(), initialSedmlContainer); + } catch (Exception e) { + String errMessage = "Unable to prepare SED-ML for conversion into BioModel(s)"; + String formattedError = String.format("%s, failed with error: %s", errMessage, e.getMessage()); + logger.error(formattedError); + throw new SEDMLImportException(e); + } + + try { + bioModelMapping = sedmlImporter.getBioModels(); + } catch (Exception e) { + logger.error("Unable to Parse SED-ML into Bio-Model, failed with err: {}", e.getMessage(), e); + throw e; + } + for (BioModel generatedBioModel : bioModelMapping.keySet()) SolverHandler.sanityCheck(generatedBioModel); + + this.countBioModels = bioModelMapping.size(); + + SedML sedML = initialSedmlContainer.getSedML(); + Set topmostTasks = new LinkedHashSet<> (); + for(BioModel bioModel : bioModelMapping.keySet()) { Simulation[] sims = bioModel.getSimulations(); for(Simulation sim : sims) { if(sim.getImportedTaskID() == null) { @@ -106,137 +142,92 @@ public void initialize(List bioModelList, SedML sedml) throws Expressi } TempSimulation tempSimulation = new TempSimulation(sim,false); String importedTaskId = tempSimulation.getImportedTaskID(); - AbstractTask at = sedml.getTaskWithId(importedTaskId); - tempSimulationToTaskMap.put(tempSimulation, at); - taskToTempSimulationMap.put(at, tempSimulation); - origSimulationToTempSimulationMap.put(sim, tempSimulation); - topmostTasks.add(at); // all the tasks referred by an importedTaskId are supposed to be topmost + AbstractTask abstractTask = initialSedmlContainer.findAbstractTaskById(new SId(importedTaskId)); + if (null == abstractTask) throw new RuntimeException("Imported task id " + importedTaskId + " is not an AbstractTask."); + this.tempSimulationToTaskMap.put(tempSimulation, abstractTask); + this.taskToTempSimulationMap.put(abstractTask, tempSimulation); + this.origSimulationToTempSimulationMap.put(sim, tempSimulation); + topmostTasks.add(abstractTask); // all the tasks referred by an importedTaskId are supposed to be topmost } } - { - // we first make a list of all the sub tasks (sub tasks themselves may be instanceof Task or another RepeatedTask) - Set subTasks = new LinkedHashSet<> (); - for(AbstractTask at : sedml.getTasks()) { - if(!(at instanceof RepeatedTask rt)) continue; - Map subTasksOfRepeatedTask = rt.getSubTasks(); - for (Map.Entry entry : subTasksOfRepeatedTask.entrySet()) { - String subTaskId = entry.getKey(); - AbstractTask subTask = sedml.getTaskWithId(subTaskId); - subTasks.add(subTask); - } - } - // then we make a list of all topmost tasks (Task or RepeatedTask that are not a subtask) - // the topmost task is the "actual" task at the end of a chain of subtasks - Set topmostTasks2 = new LinkedHashSet<> (); // topmost tasks, different way to calculate (they are not in the list of subtasks above) - for(AbstractTask at : sedml.getTasks()) { - if(!subTasks.contains(at)) { - topmostTasks2.add(at); - } - } - if(topmostTasks.size() != topmostTasks2.size()) { - logger.error("TopmostTasks lists sizes are different."); -// throw new RuntimeException("TopmostTasks lists sizes are different."); - } - for (AbstractTask task : topmostTasks) { // we have higher confidence that topmostTask is correct - List subTasksList = new ArrayList<> (); - AbstractTask referredTask; - RepeatedTask rt; - Task actualTask; - // find the actual Task and extract the simulation - if(task instanceof RepeatedTask repeatedTask) { - rt = repeatedTask; - do { - SubTask st = rt.getSubTasks().entrySet().iterator().next().getValue(); // single subtask - String taskId = st.getTaskId(); - referredTask = sedml.getTaskWithId(taskId); - if (referredTask instanceof RepeatedTask repeatedReferredTask) rt = repeatedReferredTask; - subTasksList.add(referredTask); // last entry added will be a instanceof Task - } while (referredTask instanceof RepeatedTask); - actualTask = (Task)referredTask; - } else { - actualTask = (Task)task; - } - taskToListOfSubTasksMap.put(task, subTasksList); // subTasksList may be empty if task instanceof Task - topTaskToBaseTask.put(task.getId(), actualTask); + { // sub scope to keep names limited + // we first make a list of all the subtasks (subtasks themselves may be instanceof Task or another RepeatedTask) + Set subTasks = new LinkedHashSet<> (); + for(AbstractTask at : sedML.getTasks()) { + if(!(at instanceof RepeatedTask rt)) continue; + for (SubTask entry : rt.getSubTasks()) { + AbstractTask subTaskTarget = initialSedmlContainer.findAbstractTaskById(entry.getTask()); + if (null == subTaskTarget) throw new RuntimeException("Subtask (id=" + entry.getId().string() + " ) does not reference an AbstractTask."); + subTasks.add(subTaskTarget); + } + } + // then we make a list of all topmost tasks (Task or RepeatedTask that are not a subtask) + // the topmost task is the "actual" task at the end of a chain of subtasks + Set topmostTasks2 = new LinkedHashSet<> (); // topmost tasks, different way to calculate (they are not in the list of subtasks above) + for (AbstractTask at : sedML.getTasks()) { + if(!subTasks.contains(at)) { + topmostTasks2.add(at); + } + } + if(topmostTasks.size() != topmostTasks2.size()) { + logger.error("TopmostTasks lists sizes are different."); + // throw new RuntimeException("TopmostTasks lists sizes are different."); + } + for (AbstractTask abstractTask : topmostTasks) { // we have higher confidence that topmostTask is correct + List subTasksList = new ArrayList<> (); + Task baseTask; + if(abstractTask instanceof RepeatedTask repeatedTask) { + subTasksList.addAll(initialSedmlContainer.getActualSubTasks(repeatedTask.getId())); + baseTask = initialSedmlContainer.getBaseTask(repeatedTask.getId()); + if (baseTask == null) throw new RuntimeException("Unable to find base task of repeated task: " + repeatedTask.getId().string() + "."); + } else if (abstractTask instanceof Task task) { + baseTask = task; + } else { + throw new RuntimeException(String.format("Task (id=%s) has unknown type: %s.", abstractTask.getId().string(), abstractTask.getClass().getName())); + } - Set childRepeatedTasks = new LinkedHashSet<> (); - taskToChildRepeatedTasks.put(actualTask, childRepeatedTasks); // list of all Tasks, the set is only initialized here - } - for(Map.Entry> entry : taskToListOfSubTasksMap.entrySet()) { // populate the taskToChildRepeatedTasks map - AbstractTask topmostTask = entry.getKey(); - List dependingTasks = entry.getValue(); - if(topmostTask instanceof Task) { - // nothing to do except some sanity checks maybe - // the taskToChildRepeatedTasks contains this key and the associated set should be empty -// assert dependingTasks.isEmpty() == true; // the dependingTasks list should be empty -// assert taskToChildRepeatedTasks.containsKey(topmostTask) == true; // the Task should be a key in the map -// assert taskToChildRepeatedTasks.get(topmostTask).isEmpty() == true; // the set of repeated tasks associated to this task should be empty - } else { // this is a RepeatedTask - // or use Task actualTask = topTaskToBaseTask.get(topmostTask.getId()); - Task actualTask = null; - for(AbstractTask dependingTask : dependingTasks) { - if(dependingTask instanceof Task) { // should always be one Task at the end of the list - actualTask = (Task)dependingTask; - break; // we found the only Task - } - } -// assert rootTask != null; - Set childRepeatedTasks = taskToChildRepeatedTasks.get(actualTask); -// assert childRepeatedTasks.isEmpty() == true; - childRepeatedTasks.add((RepeatedTask)topmostTask); - for(AbstractTask dependingTask : dependingTasks) { - if(dependingTask instanceof RepeatedTask) { - childRepeatedTasks.add((RepeatedTask)dependingTask); - } - } - } - } - } + this.taskToListOfSubTasksMap.put(abstractTask, subTasksList); // subTasksList may be empty if task instanceof Task + this.topTaskToBaseTask.put(abstractTask.getId(), baseTask); + } + } // End of sub scope to keep names limited { - // - // key = tasks that are used for generating some output - // - Map variableToTaskMap = new LinkedHashMap<> (); // temporary use - List ooo = sedml.getOutputs(); - for(Output oo : ooo) { - if(oo instanceof Report) { - // TODO: check if multiple reports may use different tasks for the same variable - // here we assume that each variable may only be the result of one task - // the variable id we produce in vcell is definitely correct since the - // variable id is constructed based on the task id - List datasets = ((Report) oo).getListOfDataSets(); - for (DataSet dataset : datasets) { - DataGenerator datagen = sedml.getDataGeneratorWithId(dataset.getDataReference()); -// assert datagen != null; - List vars = new ArrayList<>(datagen.getListOfVariables()); - for(Variable var : vars) { - AbstractTask task = sedml.getTaskWithId(var.getReference()); - variableToTaskMap.put(var, task); + // + // key = tasks that are used for generating some output + // + Map variableToTaskMap = new LinkedHashMap<> (); // temporary use + for(Output oo : sedML.getOutputs()) { + if(oo instanceof Report rep) { + // TODO: check if multiple reports may use different tasks for the same variable + // here we assume that each variable may only be the result of one task + // the variable id we produce in vcell is definitely correct since the + // variable id is constructed based on the task id + List datasets = rep.getDataSets(); + for (DataSet dataset : datasets) { + DataGenerator dataGen = initialSedmlContainer.findDataGeneratorById(dataset.getDataReference()); + if (null == dataGen) throw new IllegalArgumentException("Unable to find data generator referenced in dataset: " + dataset.getDataReference()); + for(Variable var : dataGen.getVariables()) { + AbstractTask task = initialSedmlContainer.findAbstractTaskById(var.getTaskReference()); + if (null == task) throw new IllegalArgumentException("Unable to find task referenced by variable: " + var.getTaskReference()); + variableToTaskMap.put(var, task); + } } } - } - } - for(Map.Entry entry : variableToTaskMap.entrySet()) { - Variable var = entry.getKey(); - AbstractTask task = entry.getValue(); - if(!taskToVariableMap.containsKey(task)) { - List vars = new ArrayList<> (); - vars.add(var); - taskToVariableMap.put(task, vars); - } else { - List vars = taskToVariableMap.get(task); - vars.add(var); - taskToVariableMap.put(task, vars); - } - } + } + for (Map.Entry entry : variableToTaskMap.entrySet()) { + Variable var = entry.getKey(); + AbstractTask task = entry.getValue(); + List vars = this.taskToVariableMap.containsKey(task) ? this.taskToVariableMap.get(task) : new ArrayList<>(); + vars.add(var); + this.taskToVariableMap.put(task, vars); + } } - for (Map.Entry> entry : taskToListOfSubTasksMap.entrySet()) { - AbstractTask topTask = entry.getKey(); - Task actualTask = topTaskToBaseTask.get(topTask.getId()); - TempSimulation tempSimulation = taskToTempSimulationMap.get(topTask); + for (AbstractTask topTask : this.taskToListOfSubTasksMap.keySet()) { + List subTasks = this.taskToListOfSubTasksMap.get(topTask); + Task baseTask = this.topTaskToBaseTask.get(topTask.getId()); + TempSimulation tempSimulation = this.taskToTempSimulationMap.get(topTask); int scanCount = tempSimulation.getScanCount(); if(scanCount > 1) { // we know that topTask is a RepeatedTask @@ -256,7 +247,7 @@ public void initialize(List bioModelList, SedML sedml) throws Expressi Range range = rt.getRange(change.getRangeReference()); ASTNode math = change.getMath(); Expression exp = new ExpressionMathMLParser(null).fromMathML(math, "t"); - if (exp.infix().equals(range.getId())) { + if (exp.infix().equals(range.getId().string())) { String targetID = sbmlSupport.getIdFromXPathIdentifer(starget); Enumeration overridesHashKeys = tempSimulation.getMathOverrides().getOverridesHashKeys(); boolean found = false; @@ -271,13 +262,15 @@ public void initialize(List bioModelList, SedML sedml) throws Expressi // assert found == true; } } - taskToChangeTargetMap.put(rt, targetIdSet); + this.taskToChangeTargetMap.put(rt, targetIdSet); } } + 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()); } + return new Configuration(this.initializedSedMLContainer = actionableSedmlContainer, this.bioModelToSBMLMapping = bioModelMapping); } private static class TempSimulationJob extends SimulationJob { @@ -286,12 +279,12 @@ private static class TempSimulationJob extends SimulationJob { * Insert the method's description here. * Creation date: (10/7/2005 4:50:05 PM) * - * @param argSim - * @param jobIndex int - * @param argFDIS + * @param sim the {@link TempSimulation} this job refers to + * @param jobIndex int for parameters scans, what the job index is + * @param fdis field data identification specifications */ - public TempSimulationJob(TempSimulation argSim, int jobIndex, FieldDataIdentifierSpec[] argFDIS) { - super(argSim, jobIndex, argFDIS); + public TempSimulationJob(TempSimulation sim, int jobIndex, FieldDataIdentifierSpec[] fdis) { + super(sim, jobIndex, fdis); } @Override @@ -300,44 +293,30 @@ public TempSimulation getSimulation() { } public Simulation getOrigSimulation() { - return getSimulation().getOriginalSimulation(); + return this.getSimulation().getOriginalSimulation(); } public TempSimulation getTempSimulation() { - return getSimulation(); + return this.getSimulation(); } } - public Map simulateAllTasks(ExternalDocInfo externalDocInfo, SedML sedmlRequested, CLIRecordable cliLogger, - File outputDirForSedml, String outDir, String sedmlLocation, - boolean keepTempFiles, boolean exactMatchOnly, boolean bSmallMeshOverride) - throws XMLException, IOException, SEDMLImportException, ExpressionException, PropertyVetoException { + public Map simulateAllTasks(CLIRecordable cliLogger, File outputDirForSedml, String sedmlLocation, boolean keepTempFiles, boolean bSmallMeshOverride) + throws IOException, PropertyVetoException { + // Input state validation + if (this.initializedSedMLContainer == null) throw new IllegalStateException("Importer has not yet been initialized!"); + if (this.bioModelToSBMLMapping == null) throw new IllegalStateException("Importer has not yet been initialized!"); + if (this.bioModelToSBMLMapping.isEmpty()) throw new IllegalStateException("Importer failed to create biomodels for initialized SedML!"); // create the VCDocument(s) (bioModel(s) + application(s) + simulation(s)), do sanity checks Map biosimStatusMap = new LinkedHashMap<>(); - cbit.util.xml.VCLogger sedmlImportLogger = new LocalLogger(); - String inputFile = externalDocInfo.getFile().getAbsolutePath(); - String bioModelBaseName = org.vcell.util.FileUtils.getBaseName(inputFile); //String outDirRoot = outputDirForSedml.toString().substring(0, outputDirForSedml.toString().lastIndexOf(System.getProperty("file.separator"))); - this.sedmlImporter = new SEDMLImporter(sedmlImportLogger, externalDocInfo.getFile(), sedmlRequested, exactMatchOnly); - List bioModelList; - try { - bioModelList = this.sedmlImporter.getBioModels(); - } catch (Exception e) { - logger.error("Unable to Parse SED-ML into Bio-Model, failed with err: {}", e.getMessage(), e); - throw e; - } - for (BioModel generatedBioModel : bioModelList) SolverHandler.sanityCheck(generatedBioModel); - - this.countBioModels = bioModelList.size(); - - this.initialize(bioModelList, sedmlRequested); int simulationJobCount = 0; int bioModelCount = 0; boolean hasSomeSpatial = false; boolean bTimeoutFound = false; - for (BioModel bioModel : bioModelList) { + for (BioModel bioModel : this.bioModelToSBMLMapping.keySet()) { Span biomodel_span = null; try { biomodel_span = Tracer.startSpan(Span.ContextType.BioModel, bioModel.getName(), Map.of("bioModelName", bioModel.getName())); @@ -345,9 +324,9 @@ public Map simulateAllTasks(ExternalDocIn Map vCellTempSimStatusMap = new LinkedHashMap<>(); Map simDurationMap_ms = new LinkedHashMap<>(); - List simJobsList = preProcessTempSimulations(sedmlLocation, bSmallMeshOverride, bioModel, vCellTempSimStatusMap, simDurationMap_ms); + List simJobsList = this.preProcessTempSimulations(sedmlLocation, bSmallMeshOverride, bioModel, vCellTempSimStatusMap, simDurationMap_ms); for (TempSimulationJob tempSimulationJob : simJobsList) { - AbstractTask task = tempSimulationToTaskMap.get(tempSimulationJob.getTempSimulation()); + AbstractTask task = this.tempSimulationToTaskMap.get(tempSimulationJob.getTempSimulation()); biosimStatusMap.put(task, BiosimulationLog.Status.QUEUED); String paramScanIndex = task instanceof RepeatedTask ? ":" + tempSimulationJob.getJobIndex() : ""; String tempSimJobLabel = tempSimulationJob.getSimulationJobID() + tempSimulationJob.getJobIndex(); @@ -405,8 +384,11 @@ public Map simulateAllTasks(ExternalDocIn if (SolverStatus.SOLVER_FINISHED == abstractJavaSolver.getSolverStatus().getStatus()){ odeSolverResultSet = ((ODESolver) solver).getODESolverResultSet(); // must interpolate data for uniform time course which is not supported natively by the Java solvers - org.jlibsedml.Simulation sedmlSim = sedmlRequested.getSimulation(task.getSimulationReference()); - if (sedmlSim instanceof UniformTimeCourse utcSedmlSim) { + Task baseTask = this.initializedSedMLContainer.getBaseTask(task.getId()); + if (baseTask == null) throw new RuntimeException("Unable to find base task"); + org.jlibsedml.components.simulation.Simulation sedmlSim = this.initializedSedMLContainer.findSimulationById(baseTask.getSimulationReference()); + if (null == sedmlSim) throw new RuntimeException("Unable to find simulation for base task"); + if (sedmlSim instanceof UniformTimeCourse utcSedmlSim) { odeSolverResultSet = RunUtils.interpolate(odeSolverResultSet, utcSedmlSim); logTaskMessage += "done. Interpolating... "; } @@ -447,7 +429,7 @@ public Map simulateAllTasks(ExternalDocIn logger.info("Successful execution ({}s): Model '{}' Task '{}' ({}).", ((double)elapsedTime_ms)/1000, bioModel.getName(), tempSimulationJobSim.getDescription(), simTask.getSimulation().getName()); - countSuccessfulSimulationRuns++; // we only count the number of simulations (tasks) that succeeded + this.countSuccessfulSimulationRuns++; // we only count the number of simulations (tasks) that succeeded if (vCellTempSimStatusMap.get(originalSim) != BiosimulationLog.Status.ABORTED && vCellTempSimStatusMap.get(originalSim) != BiosimulationLog.Status.FAILED) { vCellTempSimStatusMap.put(originalSim, BiosimulationLog.Status.SUCCEEDED); } @@ -494,11 +476,11 @@ public Map simulateAllTasks(ExternalDocIn if (!bTimeoutFound) { // don't repeat this for each task String str = logTaskError.substring(0, logTaskError.indexOf("Process timed out")); str += "Process timed out"; // truncate the rest of the spam - cliLogger.writeDetailedErrorList(e, bioModelBaseName + ", solver: " + sdl + ": " + type + ": " + str); + cliLogger.writeDetailedErrorList(e, this.modelReportingName + ", solver: " + sdl + ": " + type + ": " + str); bTimeoutFound = true; } } else { - cliLogger.writeDetailedErrorList(e,bioModelBaseName + ", solver: " + sdl + ": " + type + ": " + logTaskError); + cliLogger.writeDetailedErrorList(e,this.modelReportingName + ", solver: " + sdl + ": " + type + ": " + logTaskError); } } finally { if (sim_span != null) { @@ -507,7 +489,7 @@ public Map simulateAllTasks(ExternalDocIn } MathSymbolMapping mathMapping = (MathSymbolMapping) simTask.getSimulation().getMathDescription().getSourceSymbolMapping(); - SBMLSymbolMapping sbmlMapping = this.sedmlImporter.getSBMLSymbolMapping(bioModel); + SBMLSymbolMapping sbmlMapping = this.bioModelToSBMLMapping.get(bioModel); TaskJob taskJob = new TaskJob(task.getId(), tempSimulationJob.getJobIndex()); if (sd.isSpatial()) { logger.info("Processing spatial results of execution..."); @@ -532,17 +514,17 @@ public Map simulateAllTasks(ExternalDocIn if (status == BiosimulationLog.Status.RUNNING) { continue; // if this happens somehow, we just don't write anything } - AbstractTask task = tempSimulationToTaskMap.get(tempSimulation); + AbstractTask task = this.tempSimulationToTaskMap.get(tempSimulation); // assert task != null; double duration_s = simDurationMap_ms.get(tempSimulation)/1000.0; SolverTaskDescription std = tempSimulation.getSolverTaskDescription(); SolverDescription sd = std.getSolverDescription(); String kisao = sd.getKisao(); - BiosimulationLog.instance().updateTaskStatusYml(sedmlLocation, task.getId(), status, duration_s, kisao); + BiosimulationLog.instance().updateTaskStatusYml(sedmlLocation, task.getId().string(), status, duration_s, kisao); - List children = taskToListOfSubTasksMap.get(task); + List children = this.taskToListOfSubTasksMap.get(task); for (AbstractTask rt : children) { - BiosimulationLog.instance().updateTaskStatusYml(sedmlLocation, rt.getId(), status, duration_s, kisao); + BiosimulationLog.instance().updateTaskStatusYml(sedmlLocation, rt.getId().string(), status, duration_s, kisao); } } bioModelCount++; @@ -555,7 +537,7 @@ public Map simulateAllTasks(ExternalDocIn } logger.info("Ran " + simulationJobCount + " simulation jobs for " + bioModelCount + " biomodels."); if(hasSomeSpatial) { - cliLogger.writeSpatialList(bioModelBaseName); + cliLogger.writeSpatialList(this.modelReportingName); } RunUtils.drawBreakLine("-", 100); return biosimStatusMap; @@ -563,14 +545,14 @@ public Map simulateAllTasks(ExternalDocIn private List preProcessTempSimulations(String sedmlLocation, boolean bSmallMeshOverride, BioModel bioModel, Map vCellTempSimStatusMap, Map simDurationMap_ms) throws PropertyVetoException { List simJobsList = new ArrayList<>(); - for (TempSimulation tempSimulation : Arrays.stream(bioModel.getSimulations()).map(s -> origSimulationToTempSimulationMap.get(s)).toList()) { + for (TempSimulation tempSimulation : Arrays.stream(bioModel.getSimulations()).map(s -> this.origSimulationToTempSimulationMap.get(s)).toList()) { if (tempSimulation.getImportedTaskID() == null) { continue; // this is a simulation not matching the imported task, so we skip it } AbstractTask task = tempSimulationToTaskMap.get(tempSimulation); vCellTempSimStatusMap.put(tempSimulation, BiosimulationLog.Status.RUNNING); - BiosimulationLog.instance().updateTaskStatusYml(sedmlLocation, task.getId(), BiosimulationLog.Status.RUNNING, + BiosimulationLog.instance().updateTaskStatusYml(sedmlLocation, task.getId().string(), BiosimulationLog.Status.RUNNING, 0.0, tempSimulation.getSolverTaskDescription().getSolverDescription().getKisao()); simDurationMap_ms.put(tempSimulation, 0); @@ -616,8 +598,7 @@ public void sendMessage(Priority p, ErrorType et, String message) throws VCLogge } } - public void sendAllMessages() { - } + public void sendAllMessages() {} public boolean hasMessages() { return false; @@ -756,5 +737,7 @@ public static void main(String[] args) throws Exception { */ } + + public record Configuration(SedMLDataContainer postInitializedSedml, Map bioModelsToSymbolMappings){} } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/TaskJob.java b/vcell-cli/src/main/java/org/vcell/cli/run/TaskJob.java index 5600ae6e5b..f1cf1fe4a1 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/TaskJob.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/TaskJob.java @@ -1,11 +1,13 @@ package org.vcell.cli.run; +import org.jlibsedml.components.SId; + public class TaskJob { final int jobID; - final String taskID; // base task for a simulation (instance of Task) + final SId taskID; // base task for a simulation (instance of Task) - public TaskJob(String taskID, int jobID) { + public TaskJob(SId taskID, int jobID) { this.taskID = taskID; this.jobID = jobID; } @@ -14,7 +16,7 @@ public String getId() { return taskID + "_" + jobID; } - public String getTaskId() { + public SId getTaskId() { return taskID; } 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 cad25a5d40..efa35d6f17 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 @@ -6,8 +6,8 @@ import io.jhdf.api.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jlibsedml.Report; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.components.output.Report; import org.vcell.util.DataAccessException; import org.vcell.util.trees.GenericStringTree; import org.vcell.util.trees.Tree; @@ -42,7 +42,7 @@ public static void writeHdf5(HDF5ExecutionResults hdf5ExecutionResults, File out try { try (WritableHdfFile hdf5File = HdfFile.write(tempFile.toPath())){ // Sanity Check - for (SedML sedml : hdf5ExecutionResults){ + for (SedMLDataContainer sedml : hdf5ExecutionResults){ Hdf5DataContainer hdf5DataWrapper = hdf5ExecutionResults.getData(sedml); Set uriKeySet = hdf5DataWrapper.reportToUriMap.keySet(), resultsSet = hdf5DataWrapper.reportToResultsMap.keySet(); diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/HDF5ExecutionResults.java b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/HDF5ExecutionResults.java index 058aeaca26..0056c9f100 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/HDF5ExecutionResults.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/HDF5ExecutionResults.java @@ -2,15 +2,15 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -public class HDF5ExecutionResults implements Iterable{ +public class HDF5ExecutionResults implements Iterable{ private final static Logger logger = LogManager.getLogger(HDF5ExecutionResults.class); - private final Map executionResultsMapping; + private final Map executionResultsMapping; public boolean isBioSimHdf5; public HDF5ExecutionResults(boolean isBioSimHdf5){ @@ -18,18 +18,18 @@ public HDF5ExecutionResults(boolean isBioSimHdf5){ this.isBioSimHdf5 = isBioSimHdf5; } - public void addResults(SedML sedml, Hdf5DataContainer dataContainer){ + public void addResults(SedMLDataContainer sedml, Hdf5DataContainer dataContainer){ if (this.executionResultsMapping.containsKey(sedml)) logger.warn("Overwriting Results..."); this.executionResultsMapping.put(sedml, dataContainer); } - public Hdf5DataContainer getData(SedML sedml){ + public Hdf5DataContainer getData(SedMLDataContainer sedml){ if (!this.executionResultsMapping.containsKey(sedml)) throw new RuntimeException("No data for requested SED-ML!"); return this.executionResultsMapping.get(sedml); } @Override - public Iterator iterator() { + public Iterator iterator() { return this.executionResultsMapping.keySet().iterator(); } } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataContainer.java b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataContainer.java index 73b86ba2d8..9d22a25e2d 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataContainer.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataContainer.java @@ -1,6 +1,6 @@ package org.vcell.cli.run.hdf5; -import org.jlibsedml.Report; +import org.jlibsedml.components.output.Report; import java.util.LinkedHashMap; import java.util.List; 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 55fee6582f..afc137b5cd 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,10 +2,10 @@ import cbit.vcell.solver.TempSimulation; -import org.jlibsedml.DataGenerator; -import org.jlibsedml.Report; -import org.jlibsedml.SedML; -import org.jlibsedml.AbstractTask; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.output.Report; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.components.task.AbstractTask; import org.vcell.cli.run.results.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -19,7 +19,7 @@ * Factory class to create Hdf5DataWrappers from a sedml object and simulation data. */ public class Hdf5DataExtractor { - private final SedML sedml; + private final SedMLDataContainer sedml; private final Map taskToSimulationMap; private final String sedmlLocation; @@ -31,7 +31,7 @@ public class Hdf5DataExtractor { * @param sedml the sedml object to get outputs, datasets, and data generators from. * @param taskToSimulationMap mapping of task to its simulation data */ - public Hdf5DataExtractor(SedML sedml, Map taskToSimulationMap){ + public Hdf5DataExtractor(SedMLDataContainer sedml, Map taskToSimulationMap){ this.sedml = sedml; this.taskToSimulationMap = taskToSimulationMap; this.sedmlLocation = Paths.get(sedml.getPathForURI(), sedml.getFileName()).toString(); diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataPreparer.java b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataPreparer.java index e5645a8354..63bb8af6de 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataPreparer.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataPreparer.java @@ -2,13 +2,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jlibsedml.DataSet; -import org.jlibsedml.Report; +import org.jlibsedml.components.output.DataSet; +import org.jlibsedml.components.output.Report; import org.vcell.sbml.vcell.SBMLDataRecord; import org.vcell.sbml.vcell.lazy.LazySBMLDataAccessor; -import org.vcell.sbml.vcell.lazy.LazySBMLNonSpatialDataAccessor; import org.vcell.util.DataAccessException; -import org.vcell.util.Pair; import java.util.*; diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataSourceSpatialSimMetadata.java b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataSourceSpatialSimMetadata.java index c65166784c..20e5b34b6b 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataSourceSpatialSimMetadata.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataSourceSpatialSimMetadata.java @@ -2,7 +2,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jlibsedml.Variable; +import org.jlibsedml.components.Variable; import java.util.*; diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataSourceSpatialSimVars.java b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataSourceSpatialSimVars.java index 6624c5ac93..b142e7c198 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataSourceSpatialSimVars.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5DataSourceSpatialSimVars.java @@ -2,7 +2,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jlibsedml.*; +import org.jlibsedml.components.Variable; +import org.jlibsedml.components.dataGenerator.DataGenerator; + import java.util.*; public class Hdf5DataSourceSpatialSimVars { @@ -160,8 +162,8 @@ private static Map> convertMapToUseArrayList(Map> extractPlotRelevantData(Map> organizedNonSpatialResults) { - Map> plots = new LinkedHashMap<>(); - Set xAxisNames = new LinkedHashSet<>(); + public Map> extractPlotRelevantData(Map> organizedNonSpatialResults) { + Map> plots = new LinkedHashMap<>(); + Set xLabelNames = new LinkedHashSet<>(); if (organizedNonSpatialResults.isEmpty()) return plots; - for (Plot2D requestedPlot : this.sedml.getOutputs().stream().filter(Plot2D.class::isInstance).map(Plot2D.class::cast).toList()){ - BiosimulationLog.instance().updatePlotStatusYml(this.sedmlName, requestedPlot.getId(), BiosimulationLog.Status.RUNNING); + SedML sedML = this.sedml.getSedML(); + for (Plot2D requestedPlot : sedML.getOutputs().stream().filter(Plot2D.class::isInstance).map(Plot2D.class::cast).toList()){ + BiosimulationLog.instance().updatePlotStatusYml(this.sedmlName, requestedPlot.getIdAsString(), BiosimulationLog.Status.RUNNING); Results2DLinePlot plot = new Results2DLinePlot(); plot.setTitle(requestedPlot.getName()); - for (Curve curve : requestedPlot.getListOfCurves()){ + + for (AbstractCurve abstractCurve: requestedPlot.getCurves()){ + if (!(abstractCurve instanceof Curve curve )){ + logger.warn("AbstractCurve `{}` is not a Curve for PlottingDataExtractor; skipping...", abstractCurve); + continue; + } ValueHolder xResults, yResults; - BiosimulationLog.instance().updateCurveStatusYml(this.sedmlName, requestedPlot.getId(), curve.getId(), BiosimulationLog.Status.RUNNING); - DataGenerator requestedXGenerator = this.sedml.getDataGeneratorWithId(curve.getXDataReference()); - DataGenerator requestedYGenerator = this.sedml.getDataGeneratorWithId(curve.getYDataReference()); - if (requestedXGenerator == null || requestedYGenerator == null) - throw this.logBeforeThrowing(new RuntimeException("Unexpected null returns"), requestedPlot.getId(), curve.getId()); + BiosimulationLog.instance().updateCurveStatusYml(this.sedmlName, requestedPlot.getIdAsString(), curve.getIdAsString(), BiosimulationLog.Status.RUNNING); + DataGenerator requestedXGenerator = this.sedml.findDataGeneratorById(curve.getXDataReference()); + if (null == requestedXGenerator) throw new RuntimeException("Unable to retrieve x data reference!"); + DataGenerator requestedYGenerator = this.sedml.findDataGeneratorById(curve.getYDataReference()); + if (null == requestedYGenerator) throw new RuntimeException("Unable to retrieve y data reference!"); if (null == (xResults = PlottingDataExtractor.simplifyRedundantSets(organizedNonSpatialResults.get(requestedXGenerator)))) - throw this.logBeforeThrowing(new RuntimeException("Unexpected lack of x-axis results!"), requestedPlot.getId(), curve.getId()); + throw this.logBeforeThrowing(new RuntimeException("Unexpected lack of x-axis results!"), requestedPlot.getIdAsString(), curve.getIdAsString()); if (null == (yResults = organizedNonSpatialResults.get(requestedYGenerator))) - throw this.logBeforeThrowing(new RuntimeException("Unexpected lack of y-axis results!"), requestedPlot.getId(), curve.getId()); + throw this.logBeforeThrowing(new RuntimeException("Unexpected lack of y-axis results!"), requestedPlot.getIdAsString(), curve.getIdAsString()); // There's two cases: 1 x-axis, n y-axes; or n x-axes, n y-axes. final boolean hasSingleXSeries = xResults.listOfResultSets.size() == 1; @@ -65,14 +80,11 @@ public Map> extractPlotRelevantData(Map< boolean hasPairsOfSeries = xResults.listOfResultSets.size() == yResults.listOfResultSets.size(); if (!hasSingleXSeries && !hasPairsOfSeries){ RuntimeException exception = new RuntimeException("Unexpected mismatch between number of x data sets, and y data sets!"); - throw this.logBeforeThrowing(exception, requestedPlot.getId(), curve.getId()); + throw this.logBeforeThrowing(exception, requestedPlot.getIdAsString(), curve.getIdAsString()); } - 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); + Pair labelPair = this.getXYAxisLabel(curve); + xLabelNames.add(labelPair.one); for (int i = 0; i < yResults.listOfResultSets.size(); i++){ SBMLDataRecord xLazyResults, yLazyResults; @@ -89,16 +101,16 @@ public Map> extractPlotRelevantData(Map< List xData = Arrays.stream(xDataArray).boxed().toList(); List yData = Arrays.stream(yDataArray).boxed().toList(); - String xSeriesLabel = xLabel + (hasSingleXSeries ? "" : " (" + i + ")"); - String ySeriesLabel = yLabel + (hasSingleYSeries ? "" : " (" + i + ")"); + String xSeriesLabel = labelPair.one + (hasSingleXSeries ? "" : " (" + i + ")"); + String ySeriesLabel = labelPair.two + (hasSingleYSeries ? "" : " (" + i + ")"); SingleAxisSeries xSeries = new SingleAxisSeries(xSeriesLabel, xData); SingleAxisSeries ySeries = new SingleAxisSeries(ySeriesLabel, yData); plot.addXYData(xSeries, ySeries); } - BiosimulationLog.instance().updateCurveStatusYml(this.sedmlName, requestedPlot.getId(), curve.getId(), BiosimulationLog.Status.SUCCEEDED); + BiosimulationLog.instance().updateCurveStatusYml(this.sedmlName, requestedPlot.getIdAsString(), curve.getIdAsString(), BiosimulationLog.Status.SUCCEEDED); } - plot.setXAxisTitle(String.join("/", xAxisNames)); + plot.setXAxisTitle(PlottingDataExtractor.getXAxisName(xLabelNames, requestedPlot)); String plotFileName = getValidPlotName(requestedPlot); plots.put(plot, new Pair<>(plotFileName, requestedPlot.getId())); } @@ -111,7 +123,7 @@ private static String getValidPlotName(Plot2D requestedPlot) { String illegalRegex = ".*[" + illegalChars + "].*"; // Need to make sure the file can be made in the OS String requestedPlotName = requestedPlot.getName(); boolean hasBadPlotName = requestedPlotName == null || requestedPlotName.isBlank() || requestedPlotName.matches(illegalRegex); - return hasBadPlotName ? requestedPlot.getId() : requestedPlot.getName(); + return hasBadPlotName ? requestedPlot.getIdAsString() : requestedPlot.getName(); } private RuntimeException logBeforeThrowing(RuntimeException e, String plotId, String curveId) { @@ -134,4 +146,36 @@ private static ValueHolder simplifyRedundantSets adjustedSets.listOfResultSets.addAll(setOfData); return adjustedSets; } + + private static String getXAxisName(Set xLabelNames, Plot plot){ + if (plot.getXAxis() != null){ + if (plot.getXAxis().getName() != null) return plot.getXAxis().getName(); + if (plot.getXAxis().getId() != null) return plot.getXAxis().getId().string(); + } + String allXNames = String.join("/", xLabelNames); + return (allXNames.length() > 50) ? allXNames.substring(0, 50) + "..." : allXNames; + } + + private Pair getXYAxisLabel(Curve curve){ + String yLabel; + DataGenerator requestedXGenerator = this.sedml.findDataGeneratorById(curve.getXDataReference()); + String xLabel = (null == requestedXGenerator) ? "" : PlottingDataExtractor.getBestLabel(requestedXGenerator); + if (curve.getName() != null) yLabel = curve.getName(); + else if (curve.getId() != null) yLabel = curve.getId().string(); + else { + DataGenerator requestedYGenerator = this.sedml.findDataGeneratorById(curve.getYDataReference()); + yLabel = (null == requestedYGenerator) ? "" : PlottingDataExtractor.getBestLabel(requestedYGenerator); + } + return new Pair<>(xLabel, yLabel); + } + + private static String getBestLabel(DataGenerator dataGenerator){ + // Check if time first + if (dataGenerator.getVariables().size() == 1){ + Variable targetVar = dataGenerator.getVariables().get(0); + if (targetVar.isSymbol() && "urn:sedml:symbol:time".equals(targetVar.getSymbol().getUrn())) return "Time"; + } + if (dataGenerator.getName() != null) return dataGenerator.getName(); + return dataGenerator.getIdAsString(); + } } 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 index b04a69b837..17e381cc7c 100644 --- 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 @@ -4,10 +4,10 @@ import com.lowagie.text.DocumentException; -import org.jfree.chart.ChartFactory; -import org.jfree.chart.JFreeChart; +import org.jfree.chart.*; import org.jfree.chart.labels.StandardXYItemLabelGenerator; import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.title.LegendTitle; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; @@ -84,12 +84,14 @@ public int getLargestSeriesSize(){ * @throws IllegalArgumentException if the length of data on each axis doesn't match */ public void addXYData(SingleAxisSeries xData, SingleAxisSeries yData){ + // input validation 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()); @@ -210,7 +212,7 @@ public SingleAxisSeries getYAxisSeries(String label){ } public int getNumXSeries(){ - return this.dataSetMappings.keySet().size(); + return this.dataSetMappings.size(); } public int getNumYSeries(){ @@ -284,6 +286,20 @@ private JFreeChart createChart(){ XYDataset dataset2D = this.generateChartLibraryDataset(); JFreeChart chart = ChartFactory.createXYLineChart(this.plotTitle, this.xAxisTitle, yAxisLabel, dataset2D); + // Attempt to manage legend so that it doesn't take up the whole plot + LegendTitle legend = chart.getLegend(); + Font legendFont = legend.getItemFont(); + int fontSize = legendFont.getSize(); + int totalChars = 0; + for (LegendItemSource itemSource : legend.getSources()){ + Iterator legendItemsIterator = itemSource.getLegendItems().iterator(); + while (legendItemsIterator.hasNext()) { + totalChars += legendItemsIterator.next().getLabel().length(); + } + } + int adjustedFontSize = Math.max(fontSize - (totalChars / 100), 8); // for every 100 characters, we scale down by 1pt; minimum of 8pt. + legend.setItemFont(new Font(legendFont.getName(), legendFont.getStyle(), adjustedFontSize)); + // Tweak Chart so it looks better chart.setBorderVisible(true); chart.getPlot().setBackgroundPaint(Color.white); diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/results/DataMapping.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/DataMapping.java index ad2e301a81..0156cab8f3 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/results/DataMapping.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/DataMapping.java @@ -1,7 +1,7 @@ package org.vcell.cli.run.results; -import org.jlibsedml.DataSet; -import org.jlibsedml.Report; +import org.jlibsedml.components.output.DataSet; +import org.jlibsedml.components.output.Report; import org.vcell.sbml.vcell.lazy.LazySBMLDataAccessor; import java.util.ArrayList; 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 index e058ee5626..99becc8cee 100644 --- 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 @@ -8,6 +8,16 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.Variable; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.output.*; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.task.RepeatedTask; +import org.jlibsedml.components.task.Task; import org.vcell.cli.run.TaskJob; import org.vcell.cli.run.hdf5.Hdf5SedmlResults; import org.vcell.cli.run.hdf5.Hdf5SedmlResultsNonSpatial; @@ -24,25 +34,33 @@ public class NonSpatialResultsConverter extends ResultsConverter { private final static Logger logger = LogManager.getLogger(NonSpatialResultsConverter.class); - public static Map> organizeNonSpatialResultsBySedmlDataGenerator(SedML sedml, Map nonSpatialResultsHash, Map taskToSimulationMap) throws ExpressionException, SEDMLImportException { + public static Map> organizeNonSpatialResultsBySedmlDataGenerator(SedMLDataContainer sedmlContainer, Map nonSpatialResultsHash, Map taskToSimulationMap) throws ExpressionException, SEDMLImportException { Map> nonSpatialOrganizedResultsMap = new HashMap<>(); if (nonSpatialResultsHash.isEmpty()) return nonSpatialOrganizedResultsMap; - for (Output output : ResultsConverter.getValidOutputs(sedml)){ + SedML sedML = sedmlContainer.getSedML(); + for (Output output : ResultsConverter.getValidOutputs(sedmlContainer)){ Set dataGeneratorsToProcess; if (output instanceof Report report){ dataGeneratorsToProcess = new LinkedHashSet<>(); - for (DataSet dataSet : report.getListOfDataSets()){ + for (DataSet dataSet : report.getDataSets()){ // 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); + DataGenerator dg = sedmlContainer.findDataGeneratorById(dataSet.getDataReference()); + if (dg == null) throw new RuntimeException("Requested data generator does not exist"); + dataGeneratorsToProcess.add(dg); + BiosimulationLog.instance().updateDatasetStatusYml(Paths.get(sedmlContainer.getPathForURI(), sedmlContainer.getFileName()).toString(), output.getIdAsString(), dataSet.getIdAsString(), 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())); + for (AbstractCurve abstractCurve : plot2D.getCurves()){ + if (!(abstractCurve instanceof Curve curve)) continue; + DataGenerator xDataGen = sedmlContainer.findDataGeneratorById(curve.getXDataReference()); + if (xDataGen == null) throw new RuntimeException("Non-data-generator found"); + uniqueDataGens.add(xDataGen); + DataGenerator yDataGen = sedmlContainer.findDataGeneratorById(curve.getYDataReference()); + if (yDataGen == null) throw new RuntimeException("Non-data-generator found"); + uniqueDataGens.add(yDataGen); } dataGeneratorsToProcess = uniqueDataGens; } else { @@ -52,18 +70,17 @@ else if (output instanceof Plot2D plot2D){ // We need to determine the "correct" number of data points to pad correctly. int maxTimeLength = 0; for (DataGenerator dataGenerator : dataGeneratorsToProcess){ - for (Variable variable : dataGenerator.getListOfVariables()){ - AbstractTask task = sedml.getTaskWithId(variable.getReference()); - AbstractTask derivedTask = ResultsConverter.getBaseTask(task, sedml); - if (!(derivedTask instanceof Task baseTask)) throw new SEDMLImportException("Unable to find base task referred to by var `" + variable.getId() + "`"); - org.jlibsedml.Simulation sim = sedml.getSimulation(baseTask.getSimulationReference()); - if (!(sim instanceof UniformTimeCourse utcSim)) throw new SEDMLImportException("Unable to find utc sim referred to by var `" + variable.getId() + "`"); + for (Variable variable : dataGenerator.getVariables()){ + Task baseTask = sedmlContainer.findBaseTaskByAbstractTaskId(variable.getTaskReference()); + if (baseTask == null) throw new SEDMLImportException("Unable to find base task referred to by var `" + variable.getId() + "`"); + UniformTimeCourse utcSim = sedmlContainer.findUniformTimeCourseById(baseTask.getSimulationReference()); + if (utcSim == null) throw new SEDMLImportException("Unable to find utc sim referred to by var `" + variable.getId() + "`"); maxTimeLength = Math.max(utcSim.getNumberOfSteps() + 1, maxTimeLength); } } for (DataGenerator dataGen : dataGeneratorsToProcess) { - ValueHolder valueHolder = NonSpatialResultsConverter.getNonSpatialValueHolderForDataGenerator(sedml, dataGen, nonSpatialResultsHash, taskToSimulationMap, maxTimeLength); + ValueHolder valueHolder = NonSpatialResultsConverter.getNonSpatialValueHolderForDataGenerator(sedmlContainer, dataGen, nonSpatialResultsHash, taskToSimulationMap, maxTimeLength); if (valueHolder == null) continue; nonSpatialOrganizedResultsMap.put(dataGen, valueHolder); } @@ -73,29 +90,29 @@ else if (output instanceof Plot2D plot2D){ } - public static Map> prepareNonSpatialDataForHdf5(SedML sedml, Map> nonSpatialResultsMapping, + public static Map> prepareNonSpatialDataForHdf5(SedMLDataContainer sedmlContainer, Map> nonSpatialResultsMapping, Set allValidDataGenerators, String sedmlLocation, boolean isBioSimMode) { Map> results = new LinkedHashMap<>(); if (nonSpatialResultsMapping.isEmpty()){ logger.debug("No non-spatial data generated; No need to prepare non-existent data!"); return results; } - List modifiedList = new ArrayList<>(sedml.getOutputs().stream().filter(Report.class::isInstance).map(Report.class::cast).toList()); + SedML sedML = sedmlContainer.getSedML(); + List modifiedList = new ArrayList<>(sedML.getOutputs().stream().filter(Report.class::isInstance).map(Report.class::cast).toList()); Map> generalizedResultsMapping = new LinkedHashMap<>(); for (var set : nonSpatialResultsMapping.entrySet()) generalizedResultsMapping.put(set.getKey(), new ValueHolder<>(set.getValue())); // If we're not doing biosimulators mode, we need to record the plot2D as reports as well. - if (isBioSimMode) ResultsConverter.add2DPlotsAsReports(sedml, generalizedResultsMapping, modifiedList); + if (isBioSimMode) ResultsConverter.add2DPlotsAsReports(sedmlContainer, generalizedResultsMapping, modifiedList); for (Report report : modifiedList){ Map> dataSetValues = new LinkedHashMap<>(); - for (DataSet dataset : report.getListOfDataSets()) { + for (DataSet dataset : report.getDataSets()) { // use the data reference to obtain the data generator - DataGenerator dataGen = sedml.getDataGeneratorWithId(dataset.getDataReference()); - if (dataGen == null) - throw new RuntimeException("No data for Data Generator `" + dataset.getDataReference() + "` can be found!"); + DataGenerator dataGen = sedmlContainer.findDataGeneratorById(dataset.getDataReference()); + if (dataGen == null) throw new RuntimeException("No data for Data Generator `" + dataset.getDataReference() + "` can be found!");; if (!generalizedResultsMapping.containsKey(dataGen)){ if (allValidDataGenerators.contains(dataGen)) continue; throw new RuntimeException("No data for Data Generator `" + dataset.getDataReference() + "` can be found!"); @@ -115,10 +132,10 @@ public static Map> prepareNonSpatialDataForHdf5(S 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 = ResultsConverter.removeVCellPrefixes(report.getId(), report.getId()); + hdf5DatasetWrapper.datasetMetadata._type = NonSpatialResultsConverter.getKind(report.getIdAsString()); + hdf5DatasetWrapper.datasetMetadata.sedmlId = ResultsConverter.removeVCellPrefixes(report.getIdAsString(), report.getIdAsString()); hdf5DatasetWrapper.datasetMetadata.sedmlName = report.getName(); - hdf5DatasetWrapper.datasetMetadata.uri = Paths.get(sedmlLocation, report.getId()).toString(); + hdf5DatasetWrapper.datasetMetadata.uri = Paths.get(sedmlLocation, report.getIdAsString()).toString(); for (DataSet dataSet : dataSetValues.keySet()){ ValueHolder dataSetValuesSource = dataSetValues.get(dataSet); @@ -148,7 +165,7 @@ public static Map> prepareNonSpatialDataForHdf5(S hdf5DatasetWrapper.dataSource = dataSourceNonSpatial; // Using upcasting hdf5DatasetWrapper.datasetMetadata.sedmlDataSetDataTypes.add("float64"); hdf5DatasetWrapper.datasetMetadata.sedmlDataSetIds.add( - ResultsConverter.removeVCellPrefixes(dataSet.getId(), hdf5DatasetWrapper.datasetMetadata.sedmlId)); + ResultsConverter.removeVCellPrefixes(dataSet.getIdAsString(), hdf5DatasetWrapper.datasetMetadata.sedmlId)); hdf5DatasetWrapper.datasetMetadata.sedmlDataSetLabels.add(dataSet.getLabel()); hdf5DatasetWrapper.datasetMetadata.sedmlDataSetNames.add(dataSet.getName()); hdf5DatasetWrapper.datasetMetadata.sedmlDataSetShapes = shapes; @@ -159,21 +176,23 @@ public static Map> prepareNonSpatialDataForHdf5(S return results; } - private static ValueHolder getNonSpatialValueHolderForDataGenerator(SedML sedml, DataGenerator dataGen, - Map nonSpatialResultsHash, - Map taskToSimulationMap, int padToLength) throws ExpressionException { + private static ValueHolder getNonSpatialValueHolderForDataGenerator(SedMLDataContainer sedmlContainer, DataGenerator dataGen, + Map nonSpatialResultsHash, + Map taskToSimulationMap, int padToLength) throws ExpressionException { if (dataGen == null) throw new IllegalArgumentException("Provided Data Generator can not be null!"); Map> resultsByVariable = new HashMap<>(); + SedML sedml = sedmlContainer.getSedML(); // get the list of variables associated with the data reference - for (Variable var : dataGen.getListOfVariables()) { + for (Variable var : dataGen.getVariables()) { // for each variable we recover the task - AbstractTask topLevelTask = sedml.getTaskWithId(var.getReference()); - AbstractTask baseTask = ResultsConverter.getBaseTask(topLevelTask, sedml); // if !RepeatedTask, baseTask == topLevelTask + AbstractTask topLevelTask = sedmlContainer.findAbstractTaskById(var.getTaskReference()); + if (topLevelTask == null) throw new RuntimeException("Task referenced by variable could not be found!"); + Task baseTask = sedmlContainer.findBaseTaskByAbstractTaskId(var.getTaskReference()); // from the task we get the sbml model - org.jlibsedml.Simulation sedmlSim = sedml.getSimulation(baseTask.getSimulationReference()); - + org.jlibsedml.components.simulation.Simulation sedmlSim = sedmlContainer.findSimulationById(baseTask.getSimulationReference()); + if (null == sedmlSim) throw new RuntimeException("Simulation referenced by task could not be found!"); if (!(sedmlSim instanceof UniformTimeCourse utcSim)){ logger.error("only uniform time course simulations are supported"); continue; @@ -218,9 +237,11 @@ private static ValueHolder getNonSpatialValueHol // We now need to condense the multiple variables into a single resolved value - String exampleReference = resultsByVariable.keySet().iterator().next().getReference(); + SId exampleTaskReference = resultsByVariable.keySet().iterator().next().getTaskReference(); int numJobs = resultsByVariable.values().iterator().next().listOfResultSets.size(); - ValueHolder synthesizedResults = new ValueHolder<>(taskToSimulationMap.get(sedml.getTaskWithId(exampleReference))); + AbstractTask abstractTask = sedmlContainer.findAbstractTaskById(exampleTaskReference); + if (null == abstractTask) throw new RuntimeException("Task referenced by variable could not be found!"); + ValueHolder synthesizedResults = new ValueHolder<>(taskToSimulationMap.get(abstractTask)); SimpleDataGenCalculator calc = new SimpleDataGenCalculator(dataGen); // Perform the math! @@ -245,7 +266,7 @@ private static SBMLDataRecord getSynthesizedDataSet(SimpleDataGenCalculator calc LazySBMLNonSpatialDataAccessor specificJobDataSet = nonSpatialValue.listOfResultSets.get(jobNum); double[] lazyData = specificJobDataSet.getData().data(); double datum = datumIndex >= lazyData.length ? Double.NaN : lazyData[datumIndex]; - calc.setArgument(var.getId(), datum); + calc.setArgument(var.getId().string(), datum); if (!vcSimulation.equals(nonSpatialValue.vcSimulation)){ logger.warn("Simulations differ across variables; need to fix data structures to accommodate?"); } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/results/ReorganizedSpatialResults.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/ReorganizedSpatialResults.java index a5a76412a0..790c55cc89 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/results/ReorganizedSpatialResults.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/ReorganizedSpatialResults.java @@ -6,7 +6,8 @@ import io.jhdf.api.Node; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jlibsedml.AbstractTask; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.task.AbstractTask; import org.vcell.cli.run.TaskJob; import org.vcell.cli.run.hdf5.Hdf5DataSourceSpatialSimMetadata; import org.vcell.cli.run.hdf5.Hdf5DataSourceSpatialSimVars; @@ -19,8 +20,8 @@ public class ReorganizedSpatialResults { private final static Logger lg = LogManager.getLogger(Hdf5DataSourceSpatialSimVars.class); - private final Map taskGroupToMetadata; - private final Map taskGroupToMapOfVariableNameToListOfLocations; + private final Map taskGroupToMetadata; + private final Map taskGroupToMapOfVariableNameToListOfLocations; public ReorganizedSpatialResults(Map spatialVcellJobToResultsMap, Map sedmlTaskToVCellSim) { this.taskGroupToMetadata = new HashMap<>(); @@ -28,17 +29,17 @@ public ReorganizedSpatialResults(Map spatialVcellJobToResultsMap, this.populate(spatialVcellJobToResultsMap, sedmlTaskToVCellSim); } - public Set getTaskGroupSet(){ + public Set getTaskGroupSet(){ return this.taskGroupToMetadata.keySet(); } - public Hdf5DataSourceSpatialSimMetadata getMetadataFromTaskGroup(String taskGroup){ + public Hdf5DataSourceSpatialSimMetadata getMetadataFromTaskGroup(SId taskGroup){ if (this.taskGroupToMetadata.containsKey(taskGroup)) return this.taskGroupToMetadata.get(taskGroup); throw new IllegalArgumentException("`" + taskGroup + "` is not a valid task group (key miss)"); } - public Hdf5DataSourceSpatialSimVars getVarsFromTaskGroup(String taskGroup){ + public Hdf5DataSourceSpatialSimVars getVarsFromTaskGroup(SId taskGroup){ if (this.taskGroupToMapOfVariableNameToListOfLocations.containsKey(taskGroup)) return this.taskGroupToMapOfVariableNameToListOfLocations.get(taskGroup); throw new IllegalArgumentException("`" + taskGroup + "` is not a valid task group (key miss)"); @@ -109,7 +110,7 @@ private void populate(Map spatialVcellJobToResultsMap, Map taskJobSet1, taskJobSet2; + Set taskJobSet1, taskJobSet2; taskJobSet1 = new HashSet<>(this.taskGroupToMapOfVariableNameToListOfLocations.keySet()); taskJobSet2 = new HashSet<>(this.taskGroupToMetadata.keySet()); // If they're not the same size, we have a mismatch; that's a problem @@ -118,7 +119,7 @@ private void populate(Map spatialVcellJobToResultsMap, Map getValidOutputs(SedML sedml){ + protected static List getValidOutputs(SedMLDataContainer sedml){ List nonPlot3DOutputs = new ArrayList<>(); List plot3DOutputs = new ArrayList<>(); - for (Output output : sedml.getOutputs()){ + for (Output output : sedml.getSedML().getOutputs()){ if (output instanceof Plot3D plot3D) plot3DOutputs.add(plot3D); else nonPlot3DOutputs.add(output); } @@ -27,9 +37,9 @@ protected static List getValidOutputs(SedML sedml){ /** * 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. */ protected static String removeVCellPrefixes(String s, String sedmlId){ @@ -58,21 +68,25 @@ protected static String removeVCellPrefixes(String s, String sedmlId){ return s; } - protected static void add2DPlotsAsReports(SedML sedml, Map> organizedNonSpatialResults, List listToModify){ - for (Plot2D plot2D: sedml.getOutputs().stream().filter(Plot2D.class::isInstance).map(Plot2D.class::cast).toList()){ - Set addedDataGenIDs = new LinkedHashSet<>(); + protected static void add2DPlotsAsReports(SedMLDataContainer sedmlContainer, Map> organizedNonSpatialResults, List listToModify){ + SedML sedML = sedmlContainer.getSedML(); + for (Plot2D plot2D: sedML.getOutputs().stream().filter(Plot2D.class::isInstance).map(Plot2D.class::cast).toList()){ + Set addedDataGenIDs = new LinkedHashSet<>(); Report fakeReport = new Report(plot2D.getId(), plot2D.getName()); - for (Curve curve: plot2D.getListOfCurves()){ - DataGenerator dataGenX = sedml.getDataGeneratorWithId(curve.getXDataReference()); - if (dataGenX == null || !organizedNonSpatialResults.containsKey(dataGenX)) + for (AbstractCurve abstractCurve: plot2D.getCurves()){ + if (!(abstractCurve instanceof Curve curve)) continue; + DataGenerator dataGenX = sedmlContainer.findDataGeneratorById(curve.getXDataReference()); + if (null == dataGenX) continue; + if (!organizedNonSpatialResults.containsKey(dataGenX)) throw new RuntimeException("No data for Data Generator `" + curve.getXDataReference() + "` can be found!"); if (!addedDataGenIDs.contains(dataGenX.getId())) { addedDataGenIDs.add(dataGenX.getId()); String fakeLabel = String.format("%s.%s::X", plot2D.getId(), curve.getId()); fakeReport.addDataSet(new DataSet(dataGenX.getId(), dataGenX.getName(), fakeLabel, dataGenX.getId())); } - DataGenerator dataGenY = sedml.getDataGeneratorWithId(curve.getYDataReference()); - if (dataGenY == null || !organizedNonSpatialResults.containsKey(dataGenY)) + DataGenerator dataGenY = sedmlContainer.findDataGeneratorById(curve.getYDataReference()); + if (null == dataGenY) continue; + if (!organizedNonSpatialResults.containsKey(dataGenY)) throw new RuntimeException("No data for Data Generator `" + curve.getYDataReference() + "` can be found!"); if (!addedDataGenIDs.contains(dataGenY.getId())) { addedDataGenIDs.add(dataGenY.getId()); @@ -84,17 +98,6 @@ protected static void add2DPlotsAsReports(SedML sedml, Map bindingMap; + private final Expression equation; + private final Map bindingMap; public SimpleDataGenCalculator(DataGenerator dataGen) throws ExpressionException { this.equation = new Expression(dataGen.getMathAsString()); this.bindingMap = new LinkedHashMap<>(); // LinkedHashMap preserves insertion order - String[] variableArray = dataGen.getListOfVariables().stream().map(Variable::getId).toArray(String[]::new); + String[] variableArray = dataGen.getVariables().stream().map(Variable::getId).map(SId::string).toArray(String[]::new); SymbolTable symTable = new SimpleSymbolTable(variableArray); this.equation.bindExpression(symTable); for (String var : variableArray){ - bindingMap.put(var, this.EMPTY_VALUE); + this.bindingMap.put(var, this.EMPTY_VALUE); } } @@ -47,7 +48,7 @@ public void setArguments(Map parameterToArgumentMap){ public double evaluateWithCurrentArguments(boolean shouldClear) throws ExpressionException, DivideByZeroException { Double[] args = this.bindingMap.values().toArray(new Double[0]); double answer = this.equation.evaluateVector(Stream.of(args).mapToDouble(Double::doubleValue).toArray()); - if (shouldClear) for (String key : this.bindingMap.keySet()) this.bindingMap.put(key, EMPTY_VALUE); + if (shouldClear) this.bindingMap.replaceAll((k, v) -> this.EMPTY_VALUE); return answer; } diff --git a/vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialResultsConverter.java b/vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialResultsConverter.java index 744b54f328..0e50491c19 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialResultsConverter.java +++ b/vcell-cli/src/main/java/org/vcell/cli/run/results/SpatialResultsConverter.java @@ -8,6 +8,17 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jlibsedml.*; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.Variable; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.output.*; +import org.jlibsedml.components.simulation.Simulation; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.task.RepeatedTask; +import org.jlibsedml.components.task.Task; import org.vcell.cli.exceptions.ExecutionException; import org.vcell.cli.run.TaskJob; import org.vcell.cli.run.hdf5.Hdf5SedmlResults; @@ -25,25 +36,34 @@ public class SpatialResultsConverter extends ResultsConverter { private final static Logger logger = LogManager.getLogger(SpatialResultsConverter.class); - public static Map> organizeSpatialResultsBySedmlDataGenerator(SedML sedml, Map spatialResultsHash, Map taskToSimulationMap) throws ExpressionException, MathException, IOException, ExecutionException, DataAccessException { + public static Map> organizeSpatialResultsBySedmlDataGenerator(SedMLDataContainer sedmlContainer, Map spatialResultsHash, Map taskToSimulationMap) throws ExpressionException, MathException, IOException, ExecutionException, DataAccessException { Map> spatialOrganizedResultsMap = new HashMap<>(); if (spatialResultsHash.isEmpty()) return spatialOrganizedResultsMap; - for (Output output : ResultsConverter.getValidOutputs(sedml)){ + SedML sedML = sedmlContainer.getSedML(); + for (Output output : ResultsConverter.getValidOutputs(sedmlContainer)){ Set dataGeneratorsToProcess; if (output instanceof Report report){ dataGeneratorsToProcess = new LinkedHashSet<>(); - for (DataSet dataSet : report.getListOfDataSets()){ + for (DataSet dataSet : report.getDataSets()){ // 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); + DataGenerator dataGenerator = sedmlContainer.findDataGeneratorById(dataSet.getDataReference()); + if (dataGenerator == null) throw new RuntimeException("Non-data-generator found"); + dataGeneratorsToProcess.add(dataGenerator); + BiosimulationLog.instance().updateDatasetStatusYml(Paths.get(sedmlContainer.getPathForURI(), sedmlContainer.getFileName()).toString(), output.getId().string(), dataSet.getId().string(), 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())); + for (AbstractCurve abstractCurve : plot2D.getCurves()){ + if (!(abstractCurve instanceof Curve curve)) continue; + DataGenerator xDataGen = sedmlContainer.findDataGeneratorById(curve.getXDataReference()); + if (xDataGen == null) throw new RuntimeException("Non-data-generator found"); + uniqueDataGens.add(xDataGen); + + DataGenerator yDataGen = sedmlContainer.findDataGeneratorById(curve.getYDataReference()); + if (yDataGen == null) throw new RuntimeException("Non-data-generator found"); + uniqueDataGens.add(yDataGen); } dataGeneratorsToProcess = uniqueDataGens; } else { @@ -52,7 +72,7 @@ else if (output instanceof Plot2D plot2D){ } for (DataGenerator dataGen : dataGeneratorsToProcess) { - ValueHolder valueHolder = SpatialResultsConverter.getSpatialValueHolderForDataGenerator(sedml, dataGen, spatialResultsHash, taskToSimulationMap); + ValueHolder valueHolder = SpatialResultsConverter.getSpatialValueHolderForDataGenerator(sedmlContainer, dataGen, spatialResultsHash, taskToSimulationMap); // if (valueHolder == null) continue; // We don't want this, we want nulls to pass through for later processing. spatialOrganizedResultsMap.put(dataGen, valueHolder); } @@ -61,15 +81,16 @@ else if (output instanceof Plot2D plot2D){ return spatialOrganizedResultsMap; } - public static Map> prepareSpatialDataForHdf5(SedML sedml, Map> spatialResultsMapping, - Set allValidDataGenerators, String sedmlLocation, boolean isBioSimMode) { + public static Map> prepareSpatialDataForHdf5(SedMLDataContainer sedml, Map> spatialResultsMapping, + Set allValidDataGenerators, String sedmlLocation, boolean isBioSimMode) { Map> results = new LinkedHashMap<>(); if (spatialResultsMapping.isEmpty()){ logger.debug("No spatial data generated; No need to prepare non-existent data!"); return results; } - List modifiedList = new ArrayList<>(sedml.getOutputs().stream().filter(Report.class::isInstance).map(Report.class::cast).toList()); + SedML sedML = sedml.getSedML(); + List modifiedList = new ArrayList<>(sedML.getOutputs().stream().filter(Report.class::isInstance).map(Report.class::cast).toList()); // We can generalize the results now! Map> generalizedResultsMapping = new LinkedHashMap<>(); @@ -81,17 +102,16 @@ public static Map> prepareSpatialDataForHdf5(SedM for (Report report : modifiedList){ Map> dataSetValues = new LinkedHashMap<>(); - for (DataSet dataset : report.getListOfDataSets()) { + for (DataSet dataSet : report.getDataSets()) { // use the data reference to obtain the data generator - DataGenerator dataGen = sedml.getDataGeneratorWithId(dataset.getDataReference()); - if (dataGen == null) - throw new RuntimeException("No data for Data Generator `" + dataset.getDataReference() + "` can be found!"); + DataGenerator dataGen = sedml.findDataGeneratorById(dataSet.getDataReference()); + if (dataGen == null) throw new RuntimeException("No valid Data Generator `" + dataSet.getDataReference() + "` can be found!"); if (!generalizedResultsMapping.containsKey(dataGen)){ if (allValidDataGenerators.contains(dataGen)) continue; - throw new RuntimeException("No data for Data Generator `" + dataset.getDataReference() + "` can be found!"); + throw new RuntimeException("No data for Data Generator `" + dataSet.getDataReference() + "` can be found!"); } ValueHolder value = generalizedResultsMapping.get(dataGen); - dataSetValues.put(dataset, value); + dataSetValues.put(dataSet, value); } // end of current dataset processing if (dataSetValues.isEmpty()) { @@ -103,10 +123,10 @@ public static Map> prepareSpatialDataForHdf5(SedM Hdf5SedmlResultsSpatial dataSourceSpatial = new Hdf5SedmlResultsSpatial(); Hdf5SedmlResults hdf5DatasetWrapper = new Hdf5SedmlResults(); - hdf5DatasetWrapper.datasetMetadata._type = SpatialResultsConverter.getKind(report.getId()); - hdf5DatasetWrapper.datasetMetadata.sedmlId = ResultsConverter.removeVCellPrefixes(report.getId(), report.getId()); + hdf5DatasetWrapper.datasetMetadata._type = SpatialResultsConverter.getKind(report.getId().string()); + hdf5DatasetWrapper.datasetMetadata.sedmlId = ResultsConverter.removeVCellPrefixes(report.getId().string(), report.getId().string()); hdf5DatasetWrapper.datasetMetadata.sedmlName = report.getName(); - hdf5DatasetWrapper.datasetMetadata.uri = Paths.get(sedmlLocation, report.getId()).toString(); + hdf5DatasetWrapper.datasetMetadata.uri = Paths.get(sedmlLocation, report.getId().string()).toString(); Set refinedDataSets = new LinkedHashSet<>(); for (DataSet dataSet : dataSetValues.keySet()){ @@ -144,7 +164,7 @@ public static Map> prepareSpatialDataForHdf5(SedM hdf5DatasetWrapper.dataSource = dataSourceSpatial; // Using upcasting hdf5DatasetWrapper.datasetMetadata.sedmlDataSetDataTypes.add("float64"); hdf5DatasetWrapper.datasetMetadata.sedmlDataSetIds.add( - ResultsConverter.removeVCellPrefixes(dataSet.getId(), hdf5DatasetWrapper.datasetMetadata.sedmlId)); + ResultsConverter.removeVCellPrefixes(dataSet.getId().string(), hdf5DatasetWrapper.datasetMetadata.sedmlId)); hdf5DatasetWrapper.datasetMetadata.sedmlDataSetLabels.add(dataSet.getLabel()); hdf5DatasetWrapper.datasetMetadata.sedmlDataSetNames.add(dataSet.getName()); @@ -157,21 +177,25 @@ public static Map> prepareSpatialDataForHdf5(SedM return results; } - private static ValueHolder getSpatialValueHolderForDataGenerator(SedML sedml, DataGenerator dataGen, - Map spatialResultsHash, - Map taskToSimulationMap) throws ExpressionException, ExecutionException, MathException, IOException, DataAccessException { + private static ValueHolder getSpatialValueHolderForDataGenerator(SedMLDataContainer sedml, DataGenerator dataGen, + Map spatialResultsHash, + Map taskToSimulationMap) throws ExpressionException, ExecutionException, MathException, IOException, DataAccessException { if (dataGen == null) throw new IllegalArgumentException("Provided Data Generator can not be null!"); Map> resultsByVariable = new HashMap<>(); int maxLengthOfData = 0; + SedML sedML = sedml.getSedML(); // get the list of variables associated with the data reference - for (Variable var : dataGen.getListOfVariables()) { + for (Variable var : dataGen.getVariables()) { // for each variable we recover the task - AbstractTask topLevelTask = sedml.getTaskWithId(var.getReference()); - AbstractTask baseTask = ResultsConverter.getBaseTask(topLevelTask, sedml); // if !RepeatedTask, baseTask == topLevelTask + AbstractTask topLevelTask = sedml.findAbstractTaskById(var.getTaskReference()); + if (null == topLevelTask) throw new RuntimeException("Task referenced by variable could not be found!"); + Task baseTask = sedml.findBaseTaskByAbstractTaskId(topLevelTask.getId()); + if (baseTask == null) throw new RuntimeException("Unable to find task referenced by var: " + var.getId().string()); // from the task we get the sbml model - org.jlibsedml.Simulation sedmlSim = sedml.getSimulation(baseTask.getSimulationReference()); + Simulation sedmlSim = sedml.findSimulationById(var.getTaskReference()); + if (null == sedmlSim) throw new RuntimeException("Unable to find simulation referenced by task: " + baseTask.getId().string()); if (!(sedmlSim instanceof UniformTimeCourse utcSim)){ logger.error("only uniform time course simulations are supported"); diff --git a/vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java b/vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java index 3d45ea519c..46cca84bbc 100644 --- a/vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java +++ b/vcell-cli/src/main/java/org/vcell/cli/vcml/VcmlOmexConverter.java @@ -38,7 +38,7 @@ public static void convertOneFile(File input, boolean bValidateOmex, boolean bSkipUnsupportedApps, boolean bAddPublicationInfo) - throws SEDMLExporter.SEDMLExportException, OmexPythonUtils.OmexValidationException, IOException { + throws SedMLExporter.SEDMLExportException, OmexPythonUtils.OmexValidationException, IOException { if (input == null || !input.isFile() || !input.toString().endsWith(".vcml")) { throw new RuntimeException("expecting inputFilePath '"+input+"' to be an existing .vcml file"); @@ -53,7 +53,7 @@ public static void convertOneFile(File input, sedmlEventLog = (String entry) -> {}; } boolean bHasPython = true; - List sedmlTaskRecords = SEDMLExporter.writeBioModel( + List sedmlTaskRecords = SedMLExporter.writeBioModel( input, bioModelInfo, outputDir, @@ -104,7 +104,7 @@ public static void convertFilesNoDatabase(File inputDir, File outputDir, ModelFo Span omexExportSpan = Tracer.startSpan(Span.ContextType.OMEX_EXPORT, "convertOneFile for "+inputFileName, null); try { boolean bHasPython = true; - List sedmlTaskRecords = SEDMLExporter.writeBioModel( + List sedmlTaskRecords = SedMLExporter.writeBioModel( inputFile, bioModelInfo, outputDir, diff --git a/vcell-cli/src/test/java/org/vcell/cli/run/hdf5/BiosimulationsHdf5WriterTest.java b/vcell-cli/src/test/java/org/vcell/cli/run/hdf5/BiosimulationsHdf5WriterTest.java index 70c52fb3d8..3e65ca3938 100644 --- a/vcell-cli/src/test/java/org/vcell/cli/run/hdf5/BiosimulationsHdf5WriterTest.java +++ b/vcell-cli/src/test/java/org/vcell/cli/run/hdf5/BiosimulationsHdf5WriterTest.java @@ -2,8 +2,9 @@ import cbit.vcell.resource.PropertyLoader; import com.google.common.io.Files; -import org.jlibsedml.DataSet; -import org.jlibsedml.Report; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.output.DataSet; +import org.jlibsedml.components.output.Report; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.vcell.sbml.vcell.SBMLDataRecord; @@ -22,12 +23,12 @@ public class BiosimulationsHdf5WriterTest { public static HDF5ExecutionResults createExampleData() { - Report plotReport = new Report("report0", "Plot Report"); - Report reportReport = new Report("report1", "Report Report"); + Report plotReport = new Report(new SId("report0"), "Plot Report"); + Report reportReport = new Report(new SId("report1"), "Report Report"); - DataSet t = new DataSet("t","t","t","#null"); - DataSet s0 = new DataSet("s0","s0","s0","#null"); - DataSet s1 = new DataSet("s1", "s1", "s1","#null"); + DataSet t = new DataSet(new SId("t"),"t","t",new SId("null")); + DataSet s0 = new DataSet(new SId("s0"),"s0","s0",new SId("null")); + DataSet s1 = new DataSet(new SId("s1"), "s1", "s1",new SId("null")); plotReport.addDataSet(t); plotReport.addDataSet(s0); plotReport.addDataSet(s1); reportReport.addDataSet(t); reportReport.addDataSet(s0); reportReport.addDataSet(s1); 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 index 23d5edb9f9..1fd1a08571 100644 --- 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 @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import org.vcell.cli.commands.execution.BiosimulationsCommand; +import org.vcell.cli.testsupport.SSIMComparisonTool; import org.vcell.util.Pair; import org.vcell.util.VCellUtilityHub; @@ -29,7 +30,11 @@ @Tag("Fast") public class TestResults2DLinePlot { - private static List paraData = List.of( + private static final double PIXEL_DIFF_HIGH = 0.2; // 20% + private static final double PIXEL_DIFF_LOW = -0.2; // 20% + private static final double ACCURACY_THRESHOLD = 0.90; // 97% + + private static final List paraData = List.of( new XYDataItem(0.0, 0.0), new XYDataItem(0.5, 0.25), new XYDataItem(1.0, 1.0), @@ -44,21 +49,32 @@ public class TestResults2DLinePlot { new XYDataItem(5.5, 30.25) ); - private static Pair parabolicData = new Pair<>( + private static final 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<>( + private static final 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<>( + private static final 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 ensureSSIMWorks() 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); + SSIMComparisonTool comparisonTool = new SSIMComparisonTool(); + Assertions.assertEquals (1.0, comparisonTool.performModifiedSSIMComparison(standardImage, standardImage).product()); + } + @Test public void testConstructors(){ Results2DLinePlot[] testInstances = { @@ -152,16 +168,13 @@ public void pngRoundTripTest() throws IOException { 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)); - } - } + double accuracy = TestResults2DLinePlot.getAccuracy(originalImage, roundTrippedImage).product(); + Assertions.assertTrue(accuracy > ACCURACY_THRESHOLD, String.format("accuracy: %f !> %f; file: %s", accuracy, ACCURACY_THRESHOLD, dupe.getCanonicalPath())); } @Test public void pngLibraryLevelTest() throws IOException { - String STANDARD_IMAGE_LOCAL_PATH = "Parabolic.png"; + 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)); @@ -176,11 +189,8 @@ public void pngLibraryLevelTest() throws IOException { 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)); - } - } + double accuracy = TestResults2DLinePlot.getAccuracy(standardImage, currentImage).product(); + Assertions.assertTrue(accuracy > ACCURACY_THRESHOLD, String.format("accuracy: %f !> %f", accuracy, ACCURACY_THRESHOLD)); } @Test @@ -211,8 +221,8 @@ public void pngExecutionLevelTest() throws IOException { 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"; + String PLOT_0_PATH = "plot_result_0.png"; + String PLOT_1_PATH = "plot_result_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)); @@ -227,17 +237,17 @@ public void pngExecutionLevelTest() throws IOException { Assertions.assertEquals(standardImage1.getWidth(), generatedImage1.getWidth()); Assertions.assertEquals(standardImage1.getHeight(), generatedImage1.getHeight()); + SSIMComparisonTool.Results results0 = TestResults2DLinePlot.getAccuracy(standardImage0, generatedImage0); + SSIMComparisonTool.Results results1 = TestResults2DLinePlot.getAccuracy(standardImage1, generatedImage1); + String errMsg = String.format("Values Threshold (%f):\n\tTest0 - accuracy: %f (%f/%f/%f); file: %s\n\tTest1 - accuracy: %f ((%f/%f/%f)); file: %s", + ACCURACY_THRESHOLD, + results0.product(), results0.luminanceComponent(), results0.contrastComponent(), results0.structureComponent(), generatedPlot0.getCanonicalPath(), + results1.product(), results1.luminanceComponent(), results1.contrastComponent(), results1.structureComponent(), generatedPlot1.getCanonicalPath()); + Assertions.assertTrue(results0.product() > ACCURACY_THRESHOLD && results1.product() > ACCURACY_THRESHOLD, errMsg); + } - 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)); - } - } + private static SSIMComparisonTool.Results getAccuracy(BufferedImage original, BufferedImage generated){ + SSIMComparisonTool ssim = new SSIMComparisonTool(); + return ssim.performModifiedSSIMComparison(original, generated); } } diff --git a/vcell-cli/src/test/java/org/vcell/cli/testsupport/SSIMComparisonTool.java b/vcell-cli/src/test/java/org/vcell/cli/testsupport/SSIMComparisonTool.java new file mode 100644 index 0000000000..3c55272201 --- /dev/null +++ b/vcell-cli/src/test/java/org/vcell/cli/testsupport/SSIMComparisonTool.java @@ -0,0 +1,197 @@ +package org.vcell.cli.testsupport; + +import java.awt.image.BufferedImage; + +public class SSIMComparisonTool { + private static final double S_WEIGHT = 0.03, M_WEIGHT = 0.05, L_WEIGHT = 0.15; + + private static final double[][] blurWeights = { + {S_WEIGHT, S_WEIGHT, S_WEIGHT, S_WEIGHT, S_WEIGHT}, + {S_WEIGHT, M_WEIGHT, M_WEIGHT, M_WEIGHT, S_WEIGHT}, + {S_WEIGHT, M_WEIGHT, L_WEIGHT, M_WEIGHT, S_WEIGHT}, + {S_WEIGHT, M_WEIGHT, M_WEIGHT, M_WEIGHT, S_WEIGHT}, + {S_WEIGHT, S_WEIGHT, S_WEIGHT, S_WEIGHT, S_WEIGHT} + }; + private static final int blurOffset = blurWeights.length / 2; + private static final int blurStartOffset = -blurOffset; + private static final int blurEndOffset = blurStartOffset + blurWeights.length; + + private static final double STANDARD_RED_WEIGHT = 0.299; + private static final double STANDARD_GREEN_WEIGHT = 0.587; + private static final double STANDARD_BLUE_WEIGHT = 0.114; + private static final double DYNAMIC_RANGE = 0xFF; // 2^8 - 1 + + private static final double STANDARD_LUMINANCE_CONSTANT = 0.01; + private static final double STANDARD_CONTRAST_CONSTANT = 0.03; + //private double STANDARD_STRUCTURE_CONSTANT = CONTRAST_CONSTANT / Math.sqrt(2); + + private static final double ALPHA_WEIGHT = 1; + private static final double BETA_WEIGHT = 1; + private static final double GAMMA_WEIGHT = 1; + + private final double LUMINANCE_STABILIZER; + private final double CONTRAST_STABILIZER; + private final double STRUCTURE_STABILIZER; + + public SSIMComparisonTool(){ + this(STANDARD_LUMINANCE_CONSTANT, STANDARD_CONTRAST_CONSTANT, STANDARD_CONTRAST_CONSTANT / 2); + } + + /** + * Builds the stabilizer variables from luminance and contrast constants + * @param luminanceConstant + * @param contrastConstant + */ + public SSIMComparisonTool(double luminanceConstant, double contrastConstant){ + this( + DYNAMIC_RANGE * DYNAMIC_RANGE * luminanceConstant * luminanceConstant, + DYNAMIC_RANGE * DYNAMIC_RANGE * contrastConstant * contrastConstant, + (DYNAMIC_RANGE * DYNAMIC_RANGE * contrastConstant * contrastConstant) / 2 + ); + } + + /** + * Assign pre-computed stabilizers for SSIM calculations + * @param luminanceStabilizer + * @param contrastStabilizer + * @param structureStabilizer + */ + public SSIMComparisonTool(double luminanceStabilizer, double contrastStabilizer, double structureStabilizer){ + this.LUMINANCE_STABILIZER = luminanceStabilizer; + this.CONTRAST_STABILIZER = contrastStabilizer; + this.STRUCTURE_STABILIZER = structureStabilizer; + } + + /** + * Returns a modified version of SSIM analysis, where structure calculations are done on blurred variants. + * This is done to reduce "error" based on shifted pixels and different fonts. + * @param original + * @param contender + * @return + */ + public Results performModifiedSSIMComparison(BufferedImage original, BufferedImage contender){ + double[][] originalGrayscaleData = SSIMComparisonTool.getGrayScaleData(original); + double[][] contenderGrayscaleData = SSIMComparisonTool.getGrayScaleData(contender); + Results regularResults = this.performSSIMComparison(originalGrayscaleData, contenderGrayscaleData); + double[][] blurredOGD = doubleWideBlur(originalGrayscaleData); + double[][] blurredCGD = doubleWideBlur(contenderGrayscaleData); + Results blurredResults = this.performSSIMComparison(blurredOGD, blurredCGD); + double newProduct = regularResults.luminanceComponent() * regularResults.contrastComponent() * blurredResults.structureComponent(); + return new Results(newProduct, regularResults.luminanceComponent(), regularResults.contrastComponent(), blurredResults.structureComponent()); + + } + + public Results performSSIMComparison(BufferedImage original, BufferedImage contender){ + double[][] originalGrayscaleData = SSIMComparisonTool.getGrayScaleData(original); + double[][] contenderGrayscaleData = SSIMComparisonTool.getGrayScaleData(contender); + return this.performSSIMComparison(originalGrayscaleData, contenderGrayscaleData); + } + + + private Results performSSIMComparison(double[][] originalGrayscaleData, double[][] contenderGrayscaleData){ + double originalPSM = SSIMComparisonTool.pixelSampleMean(originalGrayscaleData); + double contenderPSM = SSIMComparisonTool.pixelSampleMean(contenderGrayscaleData); + double originalVariance = SSIMComparisonTool.sampleVariance(originalGrayscaleData, originalPSM); + double contenderVariance = SSIMComparisonTool.sampleVariance(contenderGrayscaleData, contenderPSM); + double coVariance = SSIMComparisonTool.sampleCovariance(originalGrayscaleData, originalPSM, contenderGrayscaleData, contenderPSM); + double luminance = this.luminanceCalculation(originalPSM, contenderPSM); + double contrast = this.contrastCalculation(originalVariance, contenderVariance); + double structure = this.structureCalculation(originalVariance, contenderVariance, coVariance); + double weightedLuminance = Math.pow(luminance, SSIMComparisonTool.ALPHA_WEIGHT); + double weightedContrast = Math.pow(contrast, SSIMComparisonTool.BETA_WEIGHT); + double weightedStructure = Math.pow(structure, SSIMComparisonTool.GAMMA_WEIGHT); + double product = weightedLuminance * weightedContrast * weightedStructure; + return new Results(product, weightedLuminance, weightedContrast, weightedStructure); + } + + private double luminanceCalculation(double originalPSM, double contenderPSM){ + double opsmSquared = originalPSM * originalPSM; + double cpsmSquared = contenderPSM * contenderPSM; + double numerator = 2 * originalPSM * contenderPSM + this.LUMINANCE_STABILIZER; + double denominator = opsmSquared + cpsmSquared + this.LUMINANCE_STABILIZER; + return numerator / denominator; + } + + private double contrastCalculation(double originalVariance, double contenderVariance){ + double originalSD = Math.sqrt(originalVariance); + double contenderSD = Math.sqrt(contenderVariance); + double numerator = 2 * originalSD * contenderSD + this.CONTRAST_STABILIZER; + double denominator = originalVariance + contenderVariance + this.CONTRAST_STABILIZER; + return numerator / denominator; + } + + private double structureCalculation(double originalVariance, double contenderVariance, double coVariance) { + double numerator = coVariance + this.STRUCTURE_STABILIZER; + double denominator = Math.sqrt(originalVariance * contenderVariance) + this.STRUCTURE_STABILIZER; + return numerator / denominator; + } + + private static double pixelSampleMean(double[][] grayscaleData){ + int numRows = grayscaleData.length; + int numCols = grayscaleData[0].length; + double sum = 0; + for (var row : grayscaleData) for (double v : row) sum += v; + return sum / (numRows * numCols); + } + + private static double sampleVariance(double[][] grayscaleData, double mean) { + int numRows = grayscaleData.length; + int numCols = grayscaleData[0].length; + double sum = 0; + for (var row : grayscaleData) for (double v : row) sum += (v - mean) * (v - mean); + return sum / (numRows * numCols); + } + + private static double sampleCovariance(double[][] originalGrayscaleData, double originalMean, + double[][] contenderGrayscaleData, double contenderMean) { + double sum = 0; + int originalNumRows = originalGrayscaleData.length, originalNumCols = originalGrayscaleData[0].length; + for (int i = 0; i < originalNumRows; i++) for (int j = 0; j < originalNumCols; j++) + sum += (originalGrayscaleData[i][j] - originalMean) * (contenderGrayscaleData[i][j] - contenderMean); + return sum / (originalNumRows * originalNumCols); + } + + // The idea for SSIM is based on human perception, so grayscale works well + private static double[][] getGrayScaleData(BufferedImage image){ + int w = image.getWidth(); + int h = image.getHeight(); + double[][] convertedData = new double[h][w]; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int rgb = image.getRGB(x, y); + double blue = STANDARD_BLUE_WEIGHT * (0xff & rgb); + rgb = (rgb >> 8); + double green = STANDARD_GREEN_WEIGHT * (0xff & rgb); + rgb = (rgb >> 8); + double red = STANDARD_RED_WEIGHT * (0xff & rgb); + + convertedData[y][x] = red + green + blue; + } + } + return convertedData; + } + + private static double[][] doubleWideBlur(double[][] grayscaleData){ + return wideBlur(wideBlur(grayscaleData)); + } + + private static double[][] wideBlur(double[][] grayscaleData){ + double[][] convertedData = new double[grayscaleData.length][grayscaleData[0].length]; + for (int y = 0; y < grayscaleData.length; y++) { + for (int x = 0; x < grayscaleData[0].length; x++) { + double newValue = 0.0; + for (int i = blurStartOffset; i < blurEndOffset; i++) { + for (int j = blurStartOffset; j < blurEndOffset; j++) { + if (y + i < 0 || x + j < 0 || y + i >= grayscaleData.length || x + j >= grayscaleData[0].length) continue; + newValue += grayscaleData[y + i][x + j] * blurWeights[i + blurOffset][j + blurOffset]; + } + } + convertedData[y][x] = newValue; + } + } + return convertedData; + } + + public record Results (double product, double luminanceComponent, double contrastComponent, double structureComponent) {} +} 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 deleted file mode 100644 index 9b72945444..0000000000 Binary files a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/Parabolic.png and /dev/null 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..b1af7d0a89 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/plot_0.png b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_0.png deleted file mode 100644 index a709083200..0000000000 Binary files a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_0.png and /dev/null 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 deleted file mode 100644 index d1a380f496..0000000000 Binary files a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_1.png and /dev/null differ diff --git a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_result_0.png b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_result_0.png new file mode 100644 index 0000000000..9035f67950 Binary files /dev/null and b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_result_0.png differ diff --git a/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_result_1.png b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_result_1.png new file mode 100644 index 0000000000..e6eab6f570 Binary files /dev/null and b/vcell-cli/src/test/resources/org/vcell/cli/run/plotting/plot_result_1.png differ diff --git a/vcell-client/src/main/java/cbit/vcell/client/ClientRequestManager.java b/vcell-client/src/main/java/cbit/vcell/client/ClientRequestManager.java index d4ccbbd4ee..7cf6b2c136 100644 --- a/vcell-client/src/main/java/cbit/vcell/client/ClientRequestManager.java +++ b/vcell-client/src/main/java/cbit/vcell/client/ClientRequestManager.java @@ -82,8 +82,8 @@ import org.jdom2.Namespace; import org.jlibsedml.ArchiveComponents; import org.jlibsedml.Libsedml; -import org.jlibsedml.SEDMLDocument; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.SedMLDocument; import org.vcell.api.server.AsynchMessageManager; import org.vcell.api.server.ClientServerManager; import org.vcell.api.server.ConnectionStatus; @@ -3275,17 +3275,17 @@ private void openAfterChecking(VCDocumentInfo documentInfo, final TopLevelWindow try { ArchiveComponents ac = null; ac = Libsedml.readSEDMLArchive(new FileInputStream(file)); - List docs = ac.getSedmlDocuments(); + List docs = ac.getSedmlDocuments(); if (docs.isEmpty()) { throw new RuntimeException("Did not find any supported SEDML files in archive " + file.getName()); } - List sedmls = new ArrayList<>(); - for (SEDMLDocument doc : docs) { - SedML sedml =doc.getSedMLModel(); + List sedmls = new ArrayList<>(); + for (SedMLDocument doc : docs) { + SedMLDataContainer sedml =doc.getSedMLModel(); if (sedml == null) { throw new RuntimeException("Failed importing " + file.getName()); } - if (sedml.getModels().isEmpty()) { + if (sedml.getSedML().getModels().isEmpty()) { throw new RuntimeException("Unable to find any model in " + file.getName()); } sedmls.add(sedml); @@ -3333,8 +3333,8 @@ public void run(Hashtable hashTable) throws Exception { || file.getName().toLowerCase().endsWith(".omex"))) { TranslationLogger transLogger = new TranslationLogger(requester); // iterate through one or more SEDML objects - List sedmls = (List) hashTable.get(SEDML_MODELS); - for (SedML sedml : sedmls) { + List sedmls = (List) hashTable.get(SEDML_MODELS); + for (SedMLDataContainer sedml : sedmls) { // default to import all tasks List vcdocs = XmlHelper.importSEDML(transLogger, externalDocInfo, sedml, false); for (VCDocument vcdoc : vcdocs) { @@ -3477,14 +3477,14 @@ public void run(Hashtable hashTable) throws Exception { requester); } else if (xmlType.equals(XMLTags.SedMLTypeTag)) { File sedmlFile = xmlSource.getXmlFile(); - SedML sedml = Libsedml.readDocument(sedmlFile).getSedMLModel(); + SedMLDataContainer sedml = Libsedml.readDocument(sedmlFile).getSedMLModel(); if (sedml == null) { throw new RuntimeException("Failed importing " + file.getName()); } - if (sedml.getModels().isEmpty()) { + if (sedml.getSedML().getModels().isEmpty()) { throw new RuntimeException("Unable to find any model in " + file.getName()); } - List sedmls = new ArrayList<>(); + List sedmls = new ArrayList<>(); sedmls.add(sedml); hashTable.put(SEDML_MODELS, sedmls); diff --git a/vcell-client/src/main/java/org/vcell/sedml/gui/SEDMLChooserPanel.java b/vcell-client/src/main/java/org/vcell/sedml/gui/SEDMLChooserPanel.java index 29bffd46df..250995f30d 100644 --- a/vcell-client/src/main/java/org/vcell/sedml/gui/SEDMLChooserPanel.java +++ b/vcell-client/src/main/java/org/vcell/sedml/gui/SEDMLChooserPanel.java @@ -14,14 +14,17 @@ import javax.swing.JRadioButton; import javax.swing.JToggleButton.ToggleButtonModel; -import org.jlibsedml.AbstractTask; -import org.jlibsedml.Change; -import org.jlibsedml.RepeatedTask; -import org.jlibsedml.SEDMLTags; -import org.jlibsedml.SedML; -import org.jlibsedml.SubTask; -import org.jlibsedml.Task; -import org.vcell.sedml.SEDMLUtil; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.model.Change; +import org.jlibsedml.components.task.RepeatedTask; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.task.SubTask; +import org.jlibsedml.components.task.Task; +import org.vcell.sedml.SedMLUtil; import org.vcell.util.UserCancelException; import org.vcell.util.gui.DialogUtils; @@ -30,7 +33,7 @@ // ask the user to choose one task only (we only support importing of one task) public class SEDMLChooserPanel extends JPanel { - private SedML sedml; + private SedMLDataContainer sedml; public ButtonGroup group = new ButtonGroup(); public class SEDMLRadioButtonModel extends ToggleButtonModel { @@ -46,7 +49,7 @@ public AbstractTask getTask() { } } - public SEDMLChooserPanel(SedML sedml) { + public SEDMLChooserPanel(SedMLDataContainer sedml) { super(); this.sedml = sedml; initialize(); @@ -61,31 +64,38 @@ private void initialize() { // build and present to the user all the tasks that we can import; we skip those that we can't import for some reason // (incompatibility with vCell for example) and for them we create a list of problems which we show to the user - for(AbstractTask at : sedml.getTasks()) { + for(AbstractTask at : this.sedml.getSedML().getTasks()) { String text = ""; String tooltip = ""; boolean issueFound = false; - if(at instanceof Task) { - Task t = (Task)at; - text = " Simple task '" + t.getId() + "' - " + - sedml.getModelWithId(t.getModelReference()).getClass().getSimpleName() + " '" + // model class - SEDMLUtil.getName(sedml.getModelWithId(t.getModelReference())) + "' : " + - sedml.getSimulation(t.getSimulationReference()).getClass().getSimpleName() + " '" + // simulation class - SEDMLUtil.getName(sedml.getSimulation(t.getSimulationReference())) + "' "; - tooltip = "The model has " + sedml.getModelWithId(t.getModelReference()).getListOfChanges().size() + " changes."; - } else if(at instanceof RepeatedTask) { - RepeatedTask rt = (RepeatedTask)at; - - // TODO: we issue warning that importing repeated task is not implemented yet + if(at instanceof Task t) { + Model model = this.sedml.findModelById(t.getModelReference()); + org.jlibsedml.components.simulation.Simulation sim = this.sedml.findSimulationById(t.getSimulationReference()); + String modelSimpleName = model == null ? "" : model.getClass().getSimpleName(); + String simSimpleName = sim == null ? "" : sim.getClass().getSimpleName(); + text = " Simple task '" + t.getId() + "' - " + + modelSimpleName + " '" + // model class + SedMLUtil.getName(model) + "' : " + + simSimpleName + " '" + // simulation class + SedMLUtil.getName(sim) + "' "; + if (model != null){ + tooltip = "The model has " + model.getListOfChanges().size() + " changes."; + } else { + tooltip = ""; + } + + } else if(at instanceof RepeatedTask rt) { + + // TODO: we issue warning that importing repeated task is not implemented yet // but we have still can import it as simple task, so we don't set issueFound to true - issues.add("Importing a RepeatedTask is not implemented yet, '" + SEDMLUtil.getName(rt) + "' may be imported as SimpleTask."); + issues.add("Importing a RepeatedTask is not implemented yet, '" + SedMLUtil.getName(rt) + "' may be imported as SimpleTask."); // Verify that all the changes are supported (setValue only, for now) and if anything else is found // add an error message to the list of errors and skip the task for(Change c : rt.getChanges()) { - if(!c.getChangeKind().equals(SEDMLTags.SET_VALUE_KIND)) { + if(!c.getChangeKind().equals(SedMLTags.SET_VALUE_KIND)) { issues.add("The '" + c.getChangeKind() + "' change kind is not supported."); issueFound = true; } @@ -96,22 +106,34 @@ private void initialize() { issues.add("At least one subtask is required within a repeated task: " + rt.getId()); issueFound = true; case 1: - SubTask st = rt.getSubTasks().entrySet().iterator().next().getValue(); // first (and only) element - String taskId = st.getTaskId(); - AbstractTask t = sedml.getTaskWithId(taskId); - text = " Repeated task '" + rt.getId() + "' - " + - sedml.getModelWithId(t.getModelReference()).getClass().getSimpleName() + " '" + // model class - SEDMLUtil.getName(sedml.getModelWithId(t.getModelReference())) + "' : " + - sedml.getSimulation(t.getSimulationReference()).getClass().getSimpleName() + " '" + // simulation class - SEDMLUtil.getName(sedml.getSimulation(t.getSimulationReference())) + "' "; - tooltip = "The repeated task has " + rt.getChanges().size() + " changes and " + rt.getRanges().size() + " ranges."; + SubTask st = rt.getSubTasks().iterator().next(); // first (and only) element + SId taskId = st.getTask(); + AbstractTask abstractTask = this.sedml.findAbstractTaskById(taskId); + Task rtBaseTask; + if (null != abstractTask && null != (rtBaseTask = this.sedml.getBaseTask(abstractTask.getId()))){ + Model model = this.sedml.findModelById(rtBaseTask.getModelReference()); + org.jlibsedml.components.simulation.Simulation sim = this.sedml.findSimulationById(rtBaseTask.getSimulationReference()); + String rtModelSimpleName = model == null ? "" : model.getClass().getSimpleName(); + String rtSimSimpleName = sim == null ? "" : sim.getClass().getSimpleName(); + text = " Repeated task '" + rt.getId() + "' - " + + rtModelSimpleName + " '" + // model class + SedMLUtil.getName(model) + "' : " + + rtSimSimpleName + " '" + // simulation class + SedMLUtil.getName(sim) + "' "; + tooltip = "The repeated task has " + rt.getChanges().size() + " changes and " + rt.getRanges().size() + " ranges."; + } else { + text = "Unknown task"; + tooltip = "Unable to determine task type or details"; + } + + break; default: issues.add("Multiple subtasks within a repeated task '" + rt.getId() + "' are not supported."); issueFound = true; } } else { - issues.add("The task class '" + SEDMLUtil.getName(at) + "' is not supported."); + issues.add("The task class '" + SedMLUtil.getName(at) + "' is not supported."); issueFound = true; } @@ -176,7 +198,7 @@ private void initialize() { add(new JLabel(""), gbc); } - public static AbstractTask chooseTask(SedML sedml, Component requester, String name) { + public static AbstractTask chooseTask(SedMLDataContainer sedml, Component requester, String name) { SEDMLChooserPanel panel = new SEDMLChooserPanel(sedml); int oKCancel = DialogUtils.showComponentOKCancelDialog(requester, panel, "Import Sed-ML file: " + name); diff --git a/vcell-client/src/main/java/org/vcell/util/gui/exporter/OmexExtensionFilter.java b/vcell-client/src/main/java/org/vcell/util/gui/exporter/OmexExtensionFilter.java index 7bf20a0742..41c6037c0a 100644 --- a/vcell-client/src/main/java/org/vcell/util/gui/exporter/OmexExtensionFilter.java +++ b/vcell-client/src/main/java/org/vcell/util/gui/exporter/OmexExtensionFilter.java @@ -6,7 +6,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.vcell.sedml.PublicationMetadata; -import org.vcell.sedml.SEDMLExporter; +import org.vcell.sedml.SedMLExporter; import org.vcell.util.FileUtils; import org.vcell.util.document.BioModelInfo; @@ -46,9 +46,9 @@ public void writeBioModel(DocumentManager _documentManager, BioModel bioModel, F } } } - Map unsupportedApplications = SEDMLExporter.getUnsupportedApplicationMap(bioModel, modelFormat); + Map unsupportedApplications = SedMLExporter.getUnsupportedApplicationMap(bioModel, modelFormat); Predicate simContextFilter = (SimulationContext sc) -> !unsupportedApplications.containsKey(sc.getName()); - SEDMLExporter.writeBioModel(bioModel, publicationInfo, exportFile, modelFormat, simContextFilter, bHasPython, bRoundTripSBMLValidation, bCreateOmexArchive); + SedMLExporter.writeBioModel(bioModel, publicationInfo, exportFile, modelFormat, simContextFilter, bHasPython, bRoundTripSBMLValidation, bCreateOmexArchive); } } } diff --git a/vcell-client/src/main/java/org/vcell/util/gui/exporter/SedmlExtensionFilter.java b/vcell-client/src/main/java/org/vcell/util/gui/exporter/SedmlExtensionFilter.java index 77c5521e57..7991c3c911 100644 --- a/vcell-client/src/main/java/org/vcell/util/gui/exporter/SedmlExtensionFilter.java +++ b/vcell-client/src/main/java/org/vcell/util/gui/exporter/SedmlExtensionFilter.java @@ -5,7 +5,7 @@ import cbit.vcell.clientdb.DocumentManager; import cbit.vcell.mapping.SimulationContext; import org.vcell.sedml.ModelFormat; -import org.vcell.sedml.SEDMLExporter; +import org.vcell.sedml.SedMLExporter; import org.vcell.util.FileUtils; import org.vcell.util.UserCancelException; @@ -57,11 +57,11 @@ public void writeBioModel(DocumentManager documentManager, BioModel bioModel, Fi String sFile = FileUtils.getBaseName(exportFile.getAbsolutePath()); String sExt = FileUtils.getExtension(exportFile.getAbsolutePath()); - SEDMLExporter sedmlExporter = null; + SedMLExporter sedmlExporter = null; if (bioModel != null) { - sedmlExporter = new SEDMLExporter(sFile, bioModel, sedmlLevel, sedmlVersion, null); + sedmlExporter = new SedMLExporter(sFile, bioModel, sedmlLevel, sedmlVersion, null); boolean bRoundTripSBMLValidation = true; - Map unsupportedApplications = SEDMLExporter.getUnsupportedApplicationMap(bioModel, modelFormat); + Map unsupportedApplications = SedMLExporter.getUnsupportedApplicationMap(bioModel, modelFormat); Predicate simContextFilter = (SimulationContext sc) -> !unsupportedApplications.containsKey(sc.getName()); resultString = sedmlExporter.getSEDMLDocument(sPath, sFile, modelFormat, bRoundTripSBMLValidation, simContextFilter).writeDocumentToString(); // gather unsupported applications with messages @@ -77,7 +77,7 @@ public void writeBioModel(DocumentManager documentManager, BioModel bioModel, Fi } } - public void doSpecificWork(SEDMLExporter sedmlExporter, String resultString, String sPath, String sFile) throws Exception { + public void doSpecificWork(SedMLExporter sedmlExporter, String resultString, String sPath, String sFile) throws Exception { String sedmlFileName = Paths.get(sPath, sFile + ".sedml").toString(); XmlUtil.writeXMLStringToFile(resultString, sedmlFileName, true); sedmlExporter.addSedmlFileToList(sFile + ".sedml"); diff --git a/vcell-client/src/main/java/org/vcell/util/gui/exporter/SpringSaLaDExtensionFilter.java b/vcell-client/src/main/java/org/vcell/util/gui/exporter/SpringSaLaDExtensionFilter.java index f6cd7a1011..f369ac603b 100644 --- a/vcell-client/src/main/java/org/vcell/util/gui/exporter/SpringSaLaDExtensionFilter.java +++ b/vcell-client/src/main/java/org/vcell/util/gui/exporter/SpringSaLaDExtensionFilter.java @@ -2,27 +2,16 @@ import java.awt.Component; import java.io.File; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import javax.swing.JFrame; -import javax.swing.JOptionPane; - import org.vcell.sedml.ModelFormat; -import org.vcell.sedml.SEDMLExporter; import org.vcell.util.FileUtils; import org.vcell.util.UserCancelException; import org.vcell.util.gui.DialogUtils; -import org.vcell.util.gui.exporter.ExtensionFilter.ChooseContext; -import cbit.util.xml.XmlUtil; import cbit.vcell.biomodel.BioModel; -import cbit.vcell.client.PopupGenerator; import cbit.vcell.clientdb.DocumentManager; import cbit.vcell.export.SpringSaLaDExporter; import cbit.vcell.mapping.SimulationContext; diff --git a/vcell-core/src/main/java/cbit/vcell/math/Variable.java b/vcell-core/src/main/java/cbit/vcell/math/Variable.java index 9081105218..199a8aeae8 100644 --- a/vcell-core/src/main/java/cbit/vcell/math/Variable.java +++ b/vcell-core/src/main/java/cbit/vcell/math/Variable.java @@ -27,24 +27,33 @@ */ @SuppressWarnings("serial") public abstract class Variable extends CommentedObject implements SymbolTableEntry, Serializable, Matchable, VCMLProvider, IssueSource { - private String name = null; + private String name; private transient int symbolTableIndex = -1; private Domain domain = null; // global if null public static final String COMBINED_IDENTIFIER_SEPARATOR = "::"; void rename(String newName) { - this.name = newName; + if (newName == null) + throw new IllegalArgumentException("Variable does not have a name"); + + String nameWithPeriodsMangled = newName.replace('.','_'); + if (!nameWithPeriodsMangled.equals(TokenMangler.fixTokenStrict(nameWithPeriodsMangled))){ + throw new RuntimeException("unexpected character sequence in variable name " + newName); + } + this.name = newName; } public static class Domain implements Matchable, Serializable { - private String name = null; + private final String name; - public Domain(String argName){ - String nameWithPeriodsMangled = argName.replace('.','_'); + public Domain(String name){ + if (name == null) + throw new IllegalArgumentException("Variable does not have a name"); + String nameWithPeriodsMangled = name.replace('.','_'); if (!nameWithPeriodsMangled.equals(TokenMangler.fixTokenStrict(nameWithPeriodsMangled))){ - throw new RuntimeException("unexpected character sequence in domain name "+argName); + throw new RuntimeException("unexpected character sequence in domain name "+name); } - this.name = argName; + this.name = name; } public Domain(GeometryClass geometryClass){ this(geometryClass.getName()); @@ -75,16 +84,9 @@ public String toString(){ /** * This method was created by a SmartGuide. */ -protected Variable (String argName, Domain argDomain ) { - if (argName == null) { - throw new IllegalArgumentException("Variable does not have a name"); - } - String nameWithPeriodsMangled = argName.replace('.','_'); - if (!nameWithPeriodsMangled.equals(TokenMangler.fixTokenStrict(nameWithPeriodsMangled))){ - throw new RuntimeException("unexpected character sequence in variable name "+argName); - } - this.name = argName; - this.domain = argDomain; +protected Variable (String name, Domain domain ) { + this.rename(name); + this.domain = domain; } /** * This method was created in VisualAge. 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 5c395d0957..73c4be4e0a 100644 --- a/vcell-core/src/main/java/cbit/vcell/xml/XmlHelper.java +++ b/vcell-core/src/main/java/cbit/vcell/xml/XmlHelper.java @@ -16,7 +16,6 @@ import cbit.util.xml.VCLoggerException; import cbit.util.xml.XmlUtil; import cbit.vcell.biomodel.BioModel; -import cbit.vcell.biomodel.BioModelTransforms; import cbit.vcell.biomodel.ModelUnitConverter; import cbit.vcell.biomodel.meta.IdentifiableProvider; import cbit.vcell.biomodel.meta.VCMetaData; @@ -25,18 +24,11 @@ import cbit.vcell.field.FieldDataIdentifierSpec; import cbit.vcell.geometry.Geometry; import cbit.vcell.geometry.GeometryException; -import cbit.vcell.mapping.MappingException; -import cbit.vcell.mapping.MathMapping; -import cbit.vcell.mapping.MathSymbolMapping; import cbit.vcell.mapping.SimulationContext; import cbit.vcell.math.MathDescription; import cbit.vcell.mathmodel.MathModel; import cbit.vcell.messaging.server.SimulationTask; -import cbit.vcell.model.Kinetics; import cbit.vcell.model.ModelUnitSystem; -import cbit.vcell.model.Parameter; -import cbit.vcell.model.ReactionStep; -import cbit.vcell.parser.Expression; import cbit.vcell.parser.ExpressionException; import cbit.vcell.resource.PropertyLoader; import cbit.vcell.solver.Simulation; @@ -54,29 +46,16 @@ import org.vcell.sbml.vcell.MathModel_SBMLExporter; import org.vcell.sbml.vcell.SBMLAnnotationUtil; import org.vcell.sbml.vcell.SBMLExporter; -import org.vcell.sbml.vcell.SBMLImportException; import org.vcell.sbml.vcell.SBMLImporter; -import org.vcell.sedml.SEDMLImporter; +import org.vcell.sedml.SedMLImporter; import org.vcell.util.Extent; import org.vcell.util.Pair; import org.vcell.util.TokenMangler; import org.vcell.util.document.VCDocument; import org.vcell.util.document.VCellSoftwareVersion; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.stream.XMLStreamException; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathFactory; - -import java.beans.PropertyVetoException; + import java.io.*; import java.util.*; @@ -474,21 +453,21 @@ public static List readOmex(File omexFile, VCLogger vcLogger) throws E // iterate through one or more SEDML objects ArchiveComponents ac = null; ac = Libsedml.readSEDMLArchive(new FileInputStream(omexFile)); - List sedmlDocs = ac.getSedmlDocuments(); + List sedmlDocs = ac.getSedmlDocuments(); - List sedmls = new ArrayList<>(); - for (SEDMLDocument sedmlDoc : sedmlDocs) { - SedML sedml = sedmlDoc.getSedMLModel(); + List sedmls = new ArrayList<>(); + for (SedMLDocument sedmlDoc : sedmlDocs) { + SedMLDataContainer sedml = sedmlDoc.getSedMLModel(); if (sedml == null) { throw new RuntimeException("Failed importing " + omexFile.getName()); } - if (sedml.getModels().isEmpty()) { + if (sedml.getSedML().getModels().isEmpty()) { throw new RuntimeException("There are no models in " + omexFile.getName()); } sedmls.add(sedml); } - for (SedML sedml : sedmls) { + for (SedMLDataContainer sedml : sedmls) { // default to import all tasks ExternalDocInfo externalDocInfo = new ExternalDocInfo(omexFile,true); List biomodels = importSEDML(vcLogger, externalDocInfo, sedml, false); @@ -501,10 +480,14 @@ 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.getFile(), - sedml, exactMatchOnly); - return sedmlImporter.getBioModels(); + SedMLDataContainer sedml, boolean exactMatchOnly) throws Exception { + SedMLImporter sedmlImporter = new SedMLImporter(transLogger, new SedMLImporter.StrictnessPolicy( + true, + SedMLImporter.StrictnessPolicy.MultipleSubTaskPolicy.CONDENSE_ELSE_REMOVE, + exactMatchOnly ? SedMLImporter.StrictnessPolicy.SolverMatchPolicy.STRICT_MATCH_OR_REJECT : SedMLImporter.StrictnessPolicy.SolverMatchPolicy.SUNDIALS_AS_LAST_RESORT + )); + sedmlImporter.initialize(externalDocInfo.getFile(), sedml); + return new ArrayList<>(sedmlImporter.getBioModels().keySet()); } public static BioModel XMLToBioModel(XMLSource xmlSource) throws XmlParseException { diff --git a/vcell-core/src/main/java/org/jlibsedml/AbstractIdentifiableElement.java b/vcell-core/src/main/java/org/jlibsedml/AbstractIdentifiableElement.java deleted file mode 100644 index eed7d45746..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/AbstractIdentifiableElement.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.jlibsedml; - -/** - * Base class for all elements that have a mandatory Id attribute and a human readable name. - * Object equality is based on the ID element. Default sorting is also based on the sort-order of the - * String id attribute. - * Clients should note this is NOT an API class and is not intended to be sub-classed or referenced by clients. - * @author radams - * - */ - public abstract class AbstractIdentifiableElement extends SEDBase implements IIdentifiable, Comparable { - - private SId id; - private String name; - - - - /** - * Setter for the name of this object. - * @param name A short human-readable String. Can be null. - * @since 1.2.0 - */ - public void setName(String name) { - this.name = name; - } - - /** - * @return the name for this element, may be null or empty. - */ - public String getName() { - return name; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((id == null) ? 0 : id.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AbstractIdentifiableElement other = (AbstractIdentifiableElement) obj; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - return true; - } - - /** - * - * @param id A non-null, non empty String. - * @param name An optional String for a human readable descriptor of the element. This can be - * null or empty. - */ - public AbstractIdentifiableElement(String id, String name) { - super(); - if(SEDMLElementFactory.getInstance().isStrictCreation()) - Assert.checkNoNullArgs(id); - this.id = new SId(id); - this.name=name; - } - - /** - * Getter for the id attribute. - * @return A non-null, non-empty ID string. - */ - public String getId() { - return id.getString(); - } - - /** - * Compares identifiable elements based on String comparison - * of their identifiers. - */ - public int compareTo(AbstractIdentifiableElement o) { - return id.getString().compareTo(o.getId()); - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/AbstractTask.java b/vcell-core/src/main/java/org/jlibsedml/AbstractTask.java deleted file mode 100644 index d0b5fbe250..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/AbstractTask.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.jlibsedml; - -public abstract class AbstractTask extends AbstractIdentifiableElement { - - public AbstractTask(String id, String name) { - super(id, name); - } - public abstract String getModelReference() ; - public abstract String getSimulationReference() ; -} diff --git a/vcell-core/src/main/java/org/jlibsedml/AddXML.java b/vcell-core/src/main/java/org/jlibsedml/AddXML.java deleted file mode 100644 index d0a15e1dac..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/AddXML.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.jlibsedml; - -/** - * Encapsulates an AddXML element in SED-ML. This requires the value of the - * 'target' attribute to refer to a parent or container element into which the XML will be added. - * * @author anu/radams - * - */ -public final class AddXML extends Change { - @Override - public String toString() { - return "AddXML [AddXML=" + newXML + ", getTarget()=" + getTargetXPath() - + "]"; - } - - public boolean accept(SEDMLVisitor visitor) { - - return visitor.visit(this); - - } - - private NewXML newXML = null; - - /** - * - * @param target - * A non-null XPathTarget of the XPath target - * @param newXML - * A non-null NewXML of new XML - * @throws IllegalArgumentException - * if either argument is null. - */ - public AddXML(XPathTarget target, NewXML newXML) { - super(target); - if(SEDMLElementFactory.getInstance().isStrictCreation()) - Assert.checkNoNullArgs(newXML); - this.newXML = newXML; - } - - /** - * Getter for the change kind. - * @return SEDMLTags.ADD_XML_KIND - */ - @Override - public String getChangeKind() { - return SEDMLTags.ADD_XML_KIND; - } - - /** - * Getter for the new XML to be added to the target. - * - * @return the {@link NewXML} to be added. - */ - public NewXML getNewXML() { - return newXML; - } - - @Override - public String getElementName() { - return SEDMLTags.ADD_XML; - } - - /** - * Sets the NewXML for this element. - * @param newXML A non-null {@link NewXML} object. - * @since 1.2.0 - */ - public void setNewXML(NewXML newXML) { - this.newXML = newXML; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Algorithm.java b/vcell-core/src/main/java/org/jlibsedml/Algorithm.java deleted file mode 100644 index b91c58eee7..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Algorithm.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.List; - -/** - * Encapsulates an Algorithm element in SED-ML. Equality of objects is based on the KISAO ID. - * @author radams - * @link http://www.ebi.ac.uk/compneur-srv/kisao/ - * - */ -public final class Algorithm extends SEDBase { - private String kisaoID; - private List listOfAlgorithmParameters = new ArrayList(); - - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } - - /** - * Getter for the KisaoID of the algorithm. - * @return the Kisao ID - */ - public final String getKisaoID() { - return kisaoID; - } - - public void addAlgorithmParameter(AlgorithmParameter algorithmParameter) { - listOfAlgorithmParameters.add(algorithmParameter); - } - public List getListOfAlgorithmParameters() { - return listOfAlgorithmParameters; - } - - - /** - * Takes a non-null, non empty KisaoID. - * @param kisaoID A String. - * @throws IllegalArgumentException if kisaoID is null or empty. - */ - public Algorithm(String kisaoID) { - super(); - Assert.checkNoNullArgs(kisaoID); - Assert.stringsNotEmpty(kisaoID); - this.kisaoID = kisaoID; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((kisaoID == null) ? 0 : kisaoID.hashCode()); - return result; - } - - // We'll assume that Algorithms with the same kisaoID are equal regardless of the list of parameters - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Algorithm other = (Algorithm) obj; - if (kisaoID == null) { - if (other.kisaoID != null) - return false; - } else if (!kisaoID.equals(other.kisaoID)) - return false; - return true; - } - - @Override - public String toString() { - String s = "Algorithm [kisaoID=" + kisaoID; - for(AlgorithmParameter ap : listOfAlgorithmParameters) { - s += " " + ap.toString() + " "; - } - s += "]"; - return s; - } - - - - @Override - public String getElementName() { - return SEDMLTags.ALGORITHM_TAG; - } - - - - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/AlgorithmParameter.java b/vcell-core/src/main/java/org/jlibsedml/AlgorithmParameter.java deleted file mode 100644 index f6d087d8e1..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/AlgorithmParameter.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.jlibsedml; -/** - * An ALgorithm Parameter for a specific simulation algorithm. - *
- * This class does not currently verify the validity of a parameter for the - * given algorithm. - */ -public class AlgorithmParameter { - - - private String kisaoID; - private String value; - - public AlgorithmParameter(String kisaoID, String value) { - super(); - Assert.checkNoNullArgs(kisaoID); - Assert.stringsNotEmpty(kisaoID); - this.kisaoID = kisaoID; - this.setValue(value); - } - - public void setKisaoID(String kisaoID) { - this.kisaoID = kisaoID; - } - public String getKisaoID() { - return kisaoID; - } - public void setValue(String value) { - this.value = value; - } - public String getValue() { - return value; - } - public String toString() { - String s = "AlgorithmParameter ["; - s += "kisaoID=" + kisaoID; - s += " value=" + value; - s += "]"; - return s; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((kisaoID == null) ? 0 : kisaoID.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - AlgorithmParameter other = (AlgorithmParameter) obj; - if (kisaoID == null) { - if (other.kisaoID != null) - return false; - } else if (!kisaoID.equals(other.kisaoID)) - return false; - return true; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Annotation.java b/vcell-core/src/main/java/org/jlibsedml/Annotation.java deleted file mode 100644 index 60be0ac5af..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Annotation.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.jdom2.Element; -import org.jdom2.Namespace; - -/** - * Encapsulates an annotation element which can be used for software tools to - * annotate a SED-ML document. - * The content of an annotation need not be human-readable.
- * For human readable content, the 'Notes' elements should be used.
- * Elements added to this Annotation should be in their own XML namespace. - * - */ -public final class Annotation { - - private List elements = new ArrayList(); - - - /** - * Accepts an annotation element and adds it as the first XML element in the List - * of XML elements. - * @param argAnnotElement a non-null {@link Element} - */ - public Annotation(Element argAnnotElement) { - if(SEDMLElementFactory.getInstance().isStrictCreation()) - Assert.checkNoNullArgs(argAnnotElement); - elements.add(0, argAnnotElement); - } - - - - /** - * Retrieves the first annotation element for this object. - * @return a non-null {@link Element} - * @deprecated Use get getAnnotationElementList() from now on. - */ - public Element getAnnotationElement() { - - return elements.get(0); - - } - - /** - * Retrieves the first annotation element for this object. - * @return a non-null unmodifiable List{@link Element}. - */ - public List getAnnotationElementsList() { - return Collections.unmodifiableList(elements); - } - - /** - * Will remove this element based on its namespace. In section 2.3.3.3 of the L1V1 specification, - * it is recommended that an Annotation only has one top-level element in a particular - * namespace. - * @param el The Element to remove. - * @return true if an element in the same namespace as this was removed. - */ - public boolean removeElement (Element el){ - Namespace ns = el.getNamespace(); - for (Element child: elements) { - if (child.getNamespace().equals(el.getNamespace())){ - return elements.remove(child); - } - } - return false; - } - - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/ArchiveComponents.java b/vcell-core/src/main/java/org/jlibsedml/ArchiveComponents.java index 8eb127d348..9fcac115f8 100644 --- a/vcell-core/src/main/java/org/jlibsedml/ArchiveComponents.java +++ b/vcell-core/src/main/java/org/jlibsedml/ArchiveComponents.java @@ -11,15 +11,15 @@ public class ArchiveComponents { private List modelFiles; - private List sedmlDocs; + private List sedmlDocs; /** * * @param modelFiles a non-null, but possible empty list of IModelContent objects. - * @param sedmlDoc A {@link SEDMLDocument} + * @param sedmlDoc A {@link SedMLDocument} * @throws IllegalArgumentException if either parameter is null. */ - public ArchiveComponents(List modelFiles, List sedmlDocs) { + public ArchiveComponents(List modelFiles, List sedmlDocs) { super(); if(modelFiles ==null || sedmlDocs==null){ throw new IllegalArgumentException(); @@ -56,9 +56,9 @@ public boolean addModelContent (IModelContent toAdd){ /** * Gets the SED-ML document stored in this archive. - * @return A {@link SEDMLDocument}, which will not be null. + * @return A {@link SedMLDocument}, which will not be null. */ - public List getSedmlDocuments() { + public List getSedmlDocuments() { return sedmlDocs; } diff --git a/vcell-core/src/main/java/org/jlibsedml/Assert.java b/vcell-core/src/main/java/org/jlibsedml/Assert.java deleted file mode 100644 index 11b67b73ce..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Assert.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.jlibsedml; - -/* - * Utility class for argument checking, package scoped. - */ -class Assert { - // prevent subclassing - private Assert (){} - - /* - * Checks any argument list for null and throws an IllegalArgumentException if they are. - */ - static void checkNoNullArgs (Object ... args) { - for (Object o : args) { - if (o == null){ - throw new IllegalArgumentException(); - } - } - } - - /* - * Throws IllegalArgumentException if strings are empty. - */ - static void stringsNotEmpty(String ...args) { - for (String o : args) { - if (o.length()==0){ - throw new IllegalArgumentException(); - } - } - - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Change.java b/vcell-core/src/main/java/org/jlibsedml/Change.java deleted file mode 100644 index 0ce888f3e8..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Change.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.jlibsedml; - -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; - -/** - * Abstract class for ChangeXXX classes that manipulate a {@link Model}. - * This class is not intended to be sub-classed by clients. - * @author anu/radams - * - */ -public abstract class Change extends SEDBase { - private XPathTarget target = null; - - /** - * Sorts a list of Changes into the correct order specified in the schema. - * @author radams - * - */ - static class ChangeComparator implements Comparator{ - static Map changeKindOrder; - static { - changeKindOrder = new HashMap(); - changeKindOrder.put(SEDMLTags.CHANGE_ATTRIBUTE_KIND, 1); - changeKindOrder.put(SEDMLTags.CHANGE_XML_KIND, 2); - changeKindOrder.put(SEDMLTags.ADD_XML_KIND, 3); - changeKindOrder.put(SEDMLTags.REMOVE_XML_KIND, 4); - changeKindOrder.put(SEDMLTags.COMPUTE_CHANGE_KIND, 5); - changeKindOrder.put(SEDMLTags.SET_VALUE_KIND, 6); - } - public int compare(Change o1, Change o2) { - return changeKindOrder.get(o1.getChangeKind()).compareTo(changeKindOrder.get(o2.getChangeKind())); - } - } - - /** - * - * @param target A non-null, non-empty String - * that identifies an XPath expression to change. - */ - public Change(XPathTarget target) { - if(SEDMLElementFactory.getInstance().isStrictCreation()) - Assert.checkNoNullArgs(target); - - this.target = target; - } - - /** - * Gets the XPath expression that identifies the target XML to which the change will be applied. - * @return An XPath String. - */ - public final XPathTarget getTargetXPath() { - return target; - } - - /** - * Type definition of the type of change to be applied.
- * Returns one of: - *
    - *
  • SEDMLTags.CHANGE_ATTRIBUTE_KIND - *
  • SEDMLTags.CHANGE_XML_KIND - *
  • SEDMLTags.ADD_XML_KIND - *
  • SEDMLTags.REMOVE_XML_KIND - *
  • SEDMLTags.COMPUTE_CHANGE_KIND - *
  • SET_VALUE_KIND - *
- * @return a String for the type of change element. - */ - public abstract String getChangeKind(); - - /** - * Boolean test for whether this object is of type ChangeAttribute. - * @return a boolean - */ - public boolean isChangeAttribute(){ - return SEDMLTags.CHANGE_ATTRIBUTE_KIND.equals(getChangeKind()); - } - /** - * Boolean test for whether this object is of type ChangeXML. - * @return a boolean - */ - public boolean isChangeXML(){ - return SEDMLTags.CHANGE_XML_KIND.equals(getChangeKind()); - } - /** - * Boolean test for whether this object is of type AddXML. - * @return a boolean - */ - public boolean isAddXML(){ - return SEDMLTags.ADD_XML_KIND.equals(getChangeKind()); - } - /** - * Boolean test for whether this object is of type RemoveXML. - * @return a boolean - */ - public boolean isRemoveXML(){ - return SEDMLTags.REMOVE_XML_KIND.equals(getChangeKind()); - } - /** - * Boolean test for whether this object is of type ComputeChange. - * @return a boolean - */ - public boolean isComputeChange(){ - return SEDMLTags.COMPUTE_CHANGE_KIND.equals(getChangeKind()); - } - public boolean isSetValue() { - return SEDMLTags.SET_VALUE_KIND.equals(getChangeKind()); - } - - /** - * Setter for the target XPath expression that identifies where the change should be - * applied. - * @param target A non-null {@link XPathTarget} - * @since 1.2.0 - */ - public void setTarget(XPathTarget target) { - if(SEDMLElementFactory.getInstance().isStrictCreation()) - Assert.checkNoNullArgs(target); - this.target = target; - } - - -} \ No newline at end of file diff --git a/vcell-core/src/main/java/org/jlibsedml/ChangeAttribute.java b/vcell-core/src/main/java/org/jlibsedml/ChangeAttribute.java deleted file mode 100644 index 2a91f661bf..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/ChangeAttribute.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.jlibsedml; - -/** - * Class for manipulating the value of an attribute via XPath. - * @author anu/radams - * - */ -public final class ChangeAttribute extends Change { - - - private String newValue = null; - - /** - * - * @param target An {@link XPathTarget} to an attribute whose value is to be changed. - * @param newValue The new value of target attribute. - * @throws IllegalArgumentException if either argument is null or empty. - */ - public ChangeAttribute(XPathTarget target, String newValue) { - super(target); - if(SEDMLElementFactory.getInstance().isStrictCreation()) { - Assert.checkNoNullArgs(newValue); - Assert.stringsNotEmpty(newValue); - } - this.newValue = newValue; - } - - public boolean accept(SEDMLVisitor visitor) { - - return visitor.visit(this); - - } - - /** - * Getter for the change kind. - * @return SEDMLTags.CHANGE_ATTRIBUTE_KIND; - */ - @Override - public String getChangeKind() { - return SEDMLTags.CHANGE_ATTRIBUTE_KIND; - } - - /** - * Getter for the new attribute value to apply to the target expression. - * @return A non-null, non-empty String. - */ - public String getNewValue() { - return newValue; - } - - @Override - public String toString() { - return "ChangeAttribute [newValue=" + newValue + ", getTarget()=" - + getTargetXPath() + "]"; - } - - /** - * Setter for the new value of the this object. - * @param newValue A non-null, non-empty String. - * @throws IllegalArgumentException if newValue is null or empty. - * @since 1.2.0 - */ - public void setNewValue(String newValue) { - if(SEDMLElementFactory.getInstance().isStrictCreation()) { - Assert.checkNoNullArgs(newValue); - Assert.stringsNotEmpty(newValue); - } - this.newValue = newValue; - } - - @Override - public String getElementName() { - // TODO Auto-generated method stub - return SEDMLTags.CHANGE_ATTRIBUTE; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/ChangeXML.java b/vcell-core/src/main/java/org/jlibsedml/ChangeXML.java deleted file mode 100644 index 5b2c251763..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/ChangeXML.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.jlibsedml; - -/** - * Encapsulates a changeXML element in SED-ML. Currently this is achieved by replacing - * the 'target' content element with the 'newXML' content. - * * @author anu/radams - * - */ -public final class ChangeXML extends Change { - @Override - public String toString() { - return "ChangeXML [newXML=" + newXML + ", getTarget()=" + getTargetXPath() - + "]"; - } - - /** - * Setter for the {@link NewXML} for this object. - * @param newXML A non-null {@link NewXML} object. - * @throws IllegalArgumentException if newXML is null. - * @since 1.2.0 - */ - public void setNewXML(NewXML newXML) { - this.newXML = newXML; - } - - private NewXML newXML = null; - - - /** - * - * @param target A XPathTargetobject - * @param newXML A String of new XML - */ - public ChangeXML(XPathTarget target, NewXML newXML) { - super(target); - this.newXML = newXML; - } - - public boolean accept(SEDMLVisitor visitor) { - - return visitor.visit(this); - - } - /** - * Getter for the change kind. - * @return SEDMLTags.CHANGE_XML_KIND; - */ - @Override - public String getChangeKind() { - return SEDMLTags.CHANGE_XML_KIND; - } - - /** - * Getter for the new XML that replaces the old XML. - * @return a NewXML object - */ - public NewXML getNewXML() { - return newXML; - } - - @Override - public String getElementName() { - return SEDMLTags.CHANGE_XML; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/ComputeChange.java b/vcell-core/src/main/java/org/jlibsedml/ComputeChange.java deleted file mode 100644 index b5e68411ef..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/ComputeChange.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.List; - -import org.jmathml.ASTNode; -import org.jmathml.ASTRootNode; - -/** - * Encapsulates the ComputeChange element of SEDML. - * - * * @author anu/radams - * - */ -public class ComputeChange extends Change implements IMathContainer { - - private ASTNode math = null; - private List listOfVariables; - private List listOfParameters; - - /** - * - * @param target - * A non-null XPathTarget to which the change should be - * applied. - * @param math - * An {@link ASTRootNode} used to compute the new value of the target element. - */ - public ComputeChange(XPathTarget target, ASTNode math) { - super(target); - this.setMath(math); - listOfVariables = new ArrayList(); - listOfParameters = new ArrayList(); - } - public ComputeChange(XPathTarget target) { - this(target, null); - } - - public void setMath(ASTNode math) { - this.math = math; - } - public ASTNode getMath() { - return math; - } - - /** - * Getter for the change kind. - * @return SEDMLTags.COMPUTE_CHANGE_KIND; - */ - @Override - public String getChangeKind() { - return SEDMLTags.COMPUTE_CHANGE_KIND; - } - - public boolean accept(SEDMLVisitor visitor) { - - if(visitor.visit(this)){ - for (Variable var :getListOfVariables()){ - if( !var.accept(visitor)) { - return false; - } - - } - for (Parameter p :getListOfParameters()){ - if(! p.accept(visitor)){ - return false; - } - } - return true; - }else { - return false; - } - } - - /** - * Returns a possible empty but non-null list of {@link Variable} objects - * @return a list of {@link Variable} - */ - public List getListOfVariables() { - return listOfVariables; - } - - @Override - public String toString() { - return "ComputeChange [math=" + getMath() + ", getTarget()=" + getTargetXPath() - + "]"; - } - - void setListOfVariables(List listOfVariables) { - this.listOfVariables = listOfVariables; - } - - void setListOfParameters(List listOfParameters) { - this.listOfParameters = listOfParameters; - } - - /** - * Returns a possible empty but non-null list of {@link Parameter} objects - * @return a list of {@link Parameter} - */ - public List getListOfParameters() { - return listOfParameters; - } - - /** - * Adds a parameter to this element - * @param param - * @return true if param was successfully added. - */ - public boolean addParameter(Parameter param) { - return listOfParameters.add(param); - } - - /** - * Adds a variable to this element - * @param var a {@link Variable} - * @return true if var was successfully added. - */ - public boolean addVariable(Variable var) { - return listOfVariables.add(var); - } - - public String getId() { - return SEDMLTags.COMPUTE_CHANGE_KIND; - } - - @Override - public String getElementName() { - return SEDMLTags.COMPUTE_CHANGE; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Curve.java b/vcell-core/src/main/java/org/jlibsedml/Curve.java deleted file mode 100644 index f707ca9c8b..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Curve.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.jlibsedml; - -/** - * Supports the SED-ML 'Curve' element representing a trace on a 2D Plot. - */ -public class Curve extends AbstractIdentifiableElement{ - - @Override - public String toString() { - return "Curve [id=" + getId() + ", logX=" + logX + ", logY=" + logY - + ", name=" + getName() + ", xDataReference=" + xDataReference - + ", yDataReference=" + yDataReference + "]"; - } - - - /** - * Setter for whether the x-axis of this curve is on a log scale. - * @param logX A boolean. - * @since 1.2.0 - */ - public void setLogX(boolean logX) { - this.logX = logX; - } - - /** - * Setter for whether the y-axis of this curve is on a log scale. - * @param logY A boolean. - * @since 1.2.0 - */ - public void setLogY(boolean logY) { - this.logY = logY; - } - - /** - * Setter for the x-axis data generator. - * @param xDataReference A non-null String that is an identifier of a {@link DataGenerator} - * element. - * @since 1.2.0 - */ - public void setxDataReference(String xDataReference) { - if(SEDMLElementFactory.getInstance().isStrictCreation()){ - Assert.checkNoNullArgs( xDataReference); - Assert.stringsNotEmpty( xDataReference); - } - this.xDataReference = xDataReference; - } - - /** - * Setter for the y-axis data generator. - * @param yDataReference A non-null String that is an identifier of a {@link DataGenerator} - * element. - * @since 1.2.0 - */ - public void setyDataReference(String yDataReference) { - this.yDataReference = yDataReference; - } - - - - private boolean logX = false; - private boolean logY = false; - - private String xDataReference = null; // DataGenerator.id - private String yDataReference = null; // DataGenerator.id - - - /** - * - * @param argId An identifier that is unique in this document. - * @param argName An optional name - * @param logX boolean as to whether x-axis is a log scale. - * @param logY boolean as to whether y-axis is a log scale. - * @param xDataReference A String reference to the {@link DataGenerator} for the x-axis. - * @param yDataReference A String reference to the {@link DataGenerator} for the y-axis. - * @throws IllegalArgumentException if any argument except name is null or empty. - */ - public Curve(String argId, String argName, boolean logX, boolean logY, String xDataReference, String yDataReference) { - super(argId,argName); - if(SEDMLElementFactory.getInstance().isStrictCreation()){ - Assert.checkNoNullArgs(argId, logX, logY, xDataReference, yDataReference); - Assert.stringsNotEmpty(argId, xDataReference, yDataReference); - } - this.logX = logX; - this.logY = logY; - this.xDataReference = xDataReference; - this.yDataReference = yDataReference; - } - - /** - * @return the reference to the {@link DataGenerator} for the x-axis - */ - public String getXDataReference() { - return xDataReference; - } - - /** - * @return the reference to the {@link DataGenerator} for the y-axis - */ - public String getYDataReference() { - return yDataReference; - } - - /** - * @return true if the x-axis is a log scale, false otherwise. - */ - public boolean getLogX() { - return logX; - } - - /** - * @return true if the y-axis is a log scale, false otherwise. - */ - public boolean getLogY() { - return logY; - } - - @Override - public String getElementName() { - return SEDMLTags.OUTPUT_CURVE; - } - - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/DataGenerator.java b/vcell-core/src/main/java/org/jlibsedml/DataGenerator.java deleted file mode 100644 index 77a7f162bf..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/DataGenerator.java +++ /dev/null @@ -1,181 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.jmathml.ASTNode; -import org.jmathml.FormulaFormatter; - -/** - * Encapsulates a DataGenerator element in SED-ML. - * - * @author anu/radams - * - */ -public final class DataGenerator extends AbstractIdentifiableElement implements - IMathContainer { - - private ASTNode math = null; - - private ArrayList listOfVariables = new ArrayList(); - private ArrayList listOfParameters = new ArrayList(); - private FormulaFormatter formulaFormatter=new FormulaFormatter(); - - /** - * - * @param id - * A unique identifier for this element. - * @param name - * An optional name, can be null. - * @param math An ASTNode - * @throws IllegalArgumentException - * if id is null or empty string, or math is null. - */ - public DataGenerator(String id, String name, ASTNode math) { - super(id, name); - if (SEDMLElementFactory.getInstance().isStrictCreation()) { - Assert.checkNoNullArgs(math); - } - this.math = math; - } - - /* - * Package scoped constructor for reading from XML - */ - DataGenerator(String id, String name) { - super(id, name); - } - - @Override - public String toString() { - return "DataGenerator [math=" + math + ", name=" + getName() - + ", getId()=" + getId() + "]"; - } - - /** - * Returns a read-only list of this element's List of - * variables. - * - * @return A possibly empty but non-null List of {@link Variable} - * objects. - */ - public List getListOfVariables() { - return Collections.unmodifiableList(listOfVariables); - } - - /** - * Adds a {@link Variable} to this object's list of Variables, if not - * already present. - * - * @param variable - * A non-null {@link Variable} element - * @return true if variable added, false - * otherwise. - */ - public boolean addVariable(Variable variable) { - if (!listOfVariables.contains(variable)) - return listOfVariables.add(variable); - return false; - } - - /** - * Removes a {@link Variable} from this object's list of Variables. - * - * @param variable - * A non-null {@link Variable} element - * @return true if variable removed, false - * otherwise. - */ - public boolean removeVariable(Variable variable) { - - return listOfVariables.remove(variable); - - } - - /** - * Getter for a read-only list of parameters. - * - * @return A possibly empty but non-null List of {@link Parameter} - * objects. - */ - public List getListOfParameters() { - return Collections.unmodifiableList(listOfParameters); - } - - /** - * Adds a {@link Parameter} to this object's list of Parameters, if not - * already present. - * - * @param parameter - * A non-null {@link Parameter} element - * @return true if parameter added, false - * otherwise. - */ - public boolean addParameter(Parameter parameter) { - if (!listOfParameters.contains(parameter)) - return listOfParameters.add(parameter); - return false; - } - - /** - * Removes a {@link Parameter} from this object's list of Parameters. - * - * @param parameter - * A non-null {@link Parameter} element. - * @return true if parameter removed, false - * otherwise. - */ - public boolean removeParameter(Parameter parameter) { - - return listOfParameters.remove(parameter); - - } - - void setMathML(ASTNode math) { - this.math = math; - } - - /** - * Gets the {@link ASTNode} maths for this object - * @return an {@link ASTNode}. - */ - public ASTNode getMath() { - return math; - } - - /** - * Convenience function to return the maths expression as a C-style string. - * @return A String representation of the maths of this DataGenerator. - */ - public String getMathAsString(){ - return formulaFormatter.formulaToString(math); - } - - @Override - public String getElementName() { - return SEDMLTags.DATAGENERATOR_TAG; - } - - public boolean accept(SEDMLVisitor visitor){ - if(visitor.visit(this)){ - for (Variable var :getListOfVariables()){ - if( !var.accept(visitor)) { - return false; - } - - } - for (Parameter p :getListOfParameters()){ - if(! p.accept(visitor)){ - return false; - } - } - return true; - }else { - return false; - } - - - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/DataSet.java b/vcell-core/src/main/java/org/jlibsedml/DataSet.java deleted file mode 100644 index 5cf5430fbd..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/DataSet.java +++ /dev/null @@ -1,89 +0,0 @@ -package org.jlibsedml; - -/** - * Encapsulates the representation of a data set output from a task. * @author - * anu/radams - * - */ -public final class DataSet extends AbstractIdentifiableElement { - - @Override - public String toString() { - return "DataSet [dataReference=" + dataReference + ", id=" + getId() - + ", label=" + label + ", name=" + getName() + "]"; - } - - /** - * Sets the label used to identify this DataSet. - * @param label A String. - * @since 1.2.0 - */ - public void setLabel(String label) { - this.label = label; - } - - /** - * Sets the dataReference. This should be a DataGenerator reference. - * @param dataReference A non-nullString. - * @since 1.2.0 - */ - public void setDataReference(String dataReference) { - this.dataReference = dataReference; - } - - private String label = null; - private String dataReference = null; // DataGenerator.id - - /** - * - * @param argId - * An identifier that is unique in this document. - * @param argName - * An optional name. - * @param label - * to identify the data set in a report. - * @param dataRef - * A String reference to the {@link DataGenerator} - * for this data set. - * @throws IllegalArgumentException - * if any argument except name is null or empty. - */ - public DataSet(String argId, String argName, String label, String dataRef) { - super(argId, argName); - if (SEDMLElementFactory.getInstance().isStrictCreation()) { - Assert.checkNoNullArgs(argId, label, dataRef); - Assert.stringsNotEmpty(argId, label, dataRef); - } - this.dataReference = dataRef; - - this.label = label; - - } - - /** - * @return the label for this element. - */ - public String getLabel() { - return label; - } - - /** - * @return the reference to the {@link DataGenerator} used to create this - * data set. - */ - public final String getDataReference() { - return dataReference; - } - - @Override - public String getElementName() { - return SEDMLTags.OUTPUT_DATASET; - } - - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } - - - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Function.java b/vcell-core/src/main/java/org/jlibsedml/Function.java index 21da0f8c7f..3ffcb22e85 100644 --- a/vcell-core/src/main/java/org/jlibsedml/Function.java +++ b/vcell-core/src/main/java/org/jlibsedml/Function.java @@ -27,7 +27,7 @@ public String toString() { } public String getElementName() { - return SEDMLTags.FUNCTIONAL_RANGE_TAG; + return SedMLTags.FUNCTIONAL_RANGE_TAG; } } diff --git a/vcell-core/src/main/java/org/jlibsedml/FunctionalRange.java b/vcell-core/src/main/java/org/jlibsedml/FunctionalRange.java deleted file mode 100644 index ae0982cf12..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/FunctionalRange.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.jlibsedml; - -import java.util.HashMap; -import java.util.Map; - -import org.jmathml.ASTNode; - -public class FunctionalRange extends Range { - - private String range = ""; - private Map variables = new HashMap (); - private Map parameters = new HashMap (); - private ASTNode math = null; - - - public FunctionalRange(String id, String range) { - super(id); - if(range != null) { - this.range = range; - } - } - public FunctionalRange(String id, String index, Map variables, Map parameters, ASTNode mathAsNode) { - super(id); - if(index != null) { - this.range = index; - } - if(variables != null) { - this.variables = variables; - } - if(parameters != null) { - this.parameters = parameters; - } - this.math = mathAsNode; - } - -/* - - - - - - - - - w - index - - - - -*/ - public String getRange() { - return range; - } - public void addVariable(AbstractIdentifiableElement var) { - if(!variables.containsKey(var.getId())) { - variables.put(var.getId(), var); - } - } - // can be Variable or Parameter - public Map getVariables() { - return variables; - } - public void addParameter(AbstractIdentifiableElement var) { - if(!parameters.containsKey(var.getId())) { - parameters.put(var.getId(), var); - } - } - // can be Variable or Parameter - public Map getParameters() { - return parameters; - } - public void setMath(ASTNode math) { - this.math = math; - } - public ASTNode getMath() { - return math; - } - - @Override - public String toString() { - return "Functional Range [" - + "getId()=" + getId() - + ", getIndex()=" + getRange() - + ", getMath()=" + getMath() - + ", variables.size()=" + variables.size() - + ", parameters.size()=" + parameters.size() - + "]"; - } - /** - * This method is not supported yet. - * @throws UnsupportedOperationException - */ - @Override - public int getNumElements() { - throw new UnsupportedOperationException("Unsupported method getNumElements() for " + getElementName()); - } - - /** - * This method is not supported yet. - * @throws UnsupportedOperationException - */ - @Override - public double getElementAt(int index) { - throw new UnsupportedOperationException("Unsupported method getElementAt() for " + getElementName()); - } - - @Override - public String getElementName() { - return SEDMLTags.FUNCTIONAL_RANGE_TAG; - } - - @Override - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/IIdentifiable.java b/vcell-core/src/main/java/org/jlibsedml/IIdentifiable.java index c0393aee18..34fcd478ad 100644 --- a/vcell-core/src/main/java/org/jlibsedml/IIdentifiable.java +++ b/vcell-core/src/main/java/org/jlibsedml/IIdentifiable.java @@ -1,5 +1,7 @@ package org.jlibsedml; +import org.jlibsedml.components.SId; + /** * Interface for typing any object in SED-ML that can be identified. * @author radams @@ -11,5 +13,6 @@ public interface IIdentifiable { * Gets a non-null, non-empty identifier for this object. * @return a String of a unique identifier for this object. */ - public String getId() ; + SId getId() ; + String getIdAsString(); } diff --git a/vcell-core/src/main/java/org/jlibsedml/IMathContainer.java b/vcell-core/src/main/java/org/jlibsedml/IMathContainer.java deleted file mode 100644 index 9a1b5ad2c4..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/IMathContainer.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.jlibsedml; - -import java.util.List; - -import org.jmathml.ASTNode; - -/** - * Convenience interface for representing elements in SED-ML that contain math expressions. - * @author radams - * - */ -public interface IMathContainer { - - - /** - * Gets list of identifiable variables. Implementing classes can define - * their own sub-type of {@link IIdentifiable}. - * @return A List. - */ - List getListOfVariables(); - - /** - * Gets list of identifiable parameters. Implementing classes can define - * their sub-type. - * @return A List. - */ - List getListOfParameters(); - - /** - * Gets an ASTNode for this object. - * @return An {@link ASTNode}. - */ - ASTNode getMath(); - - /** - * Gets an identifier for this object. - * @return A non-null string. - */ - String getId(); - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Libsedml.java b/vcell-core/src/main/java/org/jlibsedml/Libsedml.java index f1c034c09b..3bf97cea85 100644 --- a/vcell-core/src/main/java/org/jlibsedml/Libsedml.java +++ b/vcell-core/src/main/java/org/jlibsedml/Libsedml.java @@ -74,12 +74,12 @@ private Libsedml() {} * * @param file * A non-null, readable file of a SED-ML xml file - * @return A non null {@link SEDMLDocument} + * @return A non null {@link SedMLDocument} * @throws XMLException * for parsing errors, or if file doesn't exist or is not * readable */ - public static SEDMLDocument readDocument(File file) throws XMLException { + public static SedMLDocument readDocument(File file) throws XMLException { String fileContents = null; try { fileContents = FileUtils.readFileToString(file); @@ -93,12 +93,12 @@ public static SEDMLDocument readDocument(File file) throws XMLException { * Reads SEDML from an input stream * @param is * @param encoding - the character encoding. Defaults to default encoding if this is null. - * @return A {@link SEDMLDocument} + * @return A {@link SedMLDocument} * @throws XMLException * @throws IOException if stream cannot be read * @since 2.2.3 */ - public static SEDMLDocument readDocument(InputStream is, String encoding) throws XMLException, IOException { + public static SedMLDocument readDocument(InputStream is, String encoding) throws XMLException, IOException { if(encoding == null) { encoding = Charset.defaultCharset().name(); } @@ -142,10 +142,10 @@ public static boolean isSEDML(InputStream inputStream) { } catch (XMLException e) { return false; } - return SEDMLTags.SEDML_L1V1_NS.equalsIgnoreCase(doc.getRootElement() - .getNamespaceURI()) || SEDMLTags.SEDML_L1V2_NS.equalsIgnoreCase(doc.getRootElement() - .getNamespaceURI()) || SEDMLTags.SEDML_L1V3_NS.equalsIgnoreCase(doc.getRootElement() - .getNamespaceURI()) || SEDMLTags.SEDML_L1V4_NS.equalsIgnoreCase(doc.getRootElement() + return SedMLTags.SEDML_L1V1_NS.equalsIgnoreCase(doc.getRootElement() + .getNamespaceURI()) || SedMLTags.SEDML_L1V2_NS.equalsIgnoreCase(doc.getRootElement() + .getNamespaceURI()) || SedMLTags.SEDML_L1V3_NS.equalsIgnoreCase(doc.getRootElement() + .getNamespaceURI()) || SedMLTags.SEDML_L1V4_NS.equalsIgnoreCase(doc.getRootElement() .getNamespaceURI()); } @@ -188,30 +188,30 @@ private static boolean hasExtraSurroundingParentheses(String text) { && text.trim().indexOf(")") == (text.length() - 1); } - private static SEDMLDocument buildDocumentFromXMLTree(Document doc, - List errs) throws XMLException { + private static SedMLDocument buildDocumentFromXMLTree(Document doc, + List errs) throws XMLException { Element sedRoot = doc.getRootElement(); - SEDMLReader reader = new SEDMLReader(); + SedMLReader reader = new SedMLReader(); try { - SEDMLElementFactory.getInstance().setStrictCreation(false); - SedML sedML = reader.getSedDocument(sedRoot); - SEDMLDocument sedmlDoc = new SEDMLDocument(sedML, errs); + SedMLElementFactory.getInstance().setStrictCreation(false); + SedMLDataContainer sedMLDataContainer = reader.getSedDocument(sedRoot); + SedMLDocument sedmlDoc = new SedMLDocument(sedMLDataContainer, errs); sedmlDoc.validate(); return sedmlDoc; } finally { - SEDMLElementFactory.getInstance().setStrictCreation(true); + SedMLElementFactory.getInstance().setStrictCreation(true); } } /** * Creates a new, empty SED-ML document * - * @return A non-null, empty {@link SEDMLDocument}. This document is not + * @return A non-null, empty {@link SedMLDocument}. This document is not * free of errors after creation, as a valid document contains at * least one task, for example. */ - public static SEDMLDocument createDocument() { - return new SEDMLDocument(); + public static SedMLDocument createDocument() { + return new SedMLDocument(); } /** @@ -293,24 +293,22 @@ public static ArchiveComponents readSEDMLArchive(final InputStream archive) throw new IllegalArgumentException(); } - ZipInputStream zis = new ZipInputStream(archive); - ZipEntry entry = null; - - int read; - List contents = new ArrayList(); - List docs = new ArrayList(); - try { + try (ZipInputStream zis = new ZipInputStream(archive)) { + ZipEntry entry; + int read; + List contents = new ArrayList<>(); + List docs = new ArrayList<>(); while ((entry = zis.getNextEntry()) != null) { - if(entry.getName().endsWith(".rdf")) { - continue; // we skip rdf files, otherwise isSEDML() below fails - } + if (entry.getName().endsWith(".rdf")) { + continue; // we skip rdf files, otherwise isSEDML() below fails + } ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[4096]; while ((read = zis.read(buf)) != -1) { baos.write(buf, 0, read); } if (isSEDML(new ByteArrayInputStream(baos.toByteArray()))) { - SEDMLDocument doc = readDocumentFromString(baos.toString()); + SedMLDocument doc = readDocumentFromString(baos.toString()); String fullPath = entry.getName(); int lastSeparator = Math.max(fullPath.lastIndexOf("\\"), fullPath.lastIndexOf("/")) + 1; String path = fullPath.substring(0, lastSeparator); @@ -326,12 +324,9 @@ public static ArchiveComponents readSEDMLArchive(final InputStream archive) } return new ArchiveComponents(contents, docs); } catch (Exception e) { - throw new XMLException("Error reading archive: " + e.getMessage()); - } finally { - try { - zis.close(); - } catch (IOException e) {}// ignore + throw new XMLException("Error reading archive: ", e); } + // ignore } /** @@ -344,7 +339,7 @@ public static ArchiveComponents readSEDMLArchive(final InputStream archive) * @throws XMLException * if the XML was malformed or could not otherwise be parsed. */ - public static SEDMLDocument readDocumentFromString(String sedmlString) + public static SedMLDocument readDocumentFromString(String sedmlString) throws XMLException { SAXBuilder builder = new SAXBuilder(); List errs = new RawContentsSchemaValidator(sedmlString) @@ -357,7 +352,7 @@ public static SEDMLDocument readDocumentFromString(String sedmlString) } catch (IOException e) { throw new XMLException("Error reading file", e); } - SEDMLDocument sedmlDoc = buildDocumentFromXMLTree(doc, errs); + SedMLDocument sedmlDoc = buildDocumentFromXMLTree(doc, errs); return sedmlDoc; } diff --git a/vcell-core/src/main/java/org/jlibsedml/ModelTransformationUtils.java b/vcell-core/src/main/java/org/jlibsedml/ModelTransformationUtils.java index 3dfaca213f..1a661d2127 100644 --- a/vcell-core/src/main/java/org/jlibsedml/ModelTransformationUtils.java +++ b/vcell-core/src/main/java/org/jlibsedml/ModelTransformationUtils.java @@ -26,6 +26,9 @@ import org.jdom2.Namespace; import org.jdom2.output.XMLOutputter; +import org.jlibsedml.components.model.Change; +import org.jlibsedml.components.model.ChangeAttribute; +import org.jlibsedml.components.model.NewXML; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -81,7 +84,7 @@ static void changeXMLElement(final Document doc, NewXML newXML, for (int i = 0; i < nodes.getLength(); i++) { Node parent = nodes.item(i).getParentNode(); removeChild(nodes); - for (org.jdom2.Element el : newXML.getXml()) { + for (org.jdom2.Element el : newXML.xml()) { el.setNamespace(Namespace.NO_NAMESPACE); String elAsString = new XMLOutputter().outputString(el); Node imported = doc.importNode( diff --git a/vcell-core/src/main/java/org/jlibsedml/NamespaceContextHelper.java b/vcell-core/src/main/java/org/jlibsedml/NamespaceContextHelper.java index b85b212e89..75265123bd 100644 --- a/vcell-core/src/main/java/org/jlibsedml/NamespaceContextHelper.java +++ b/vcell-core/src/main/java/org/jlibsedml/NamespaceContextHelper.java @@ -25,13 +25,8 @@ */ - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.net.URI; +import java.util.*; import javax.xml.namespace.NamespaceContext; @@ -44,11 +39,11 @@ /** * Implementation of the javax.xml.namespace.NamespaceContext interface. - + * *

* There are four ways to instantiate this class: *

- * + * *
    *
  1. The no-argument constructor produces an initially empty namespace * context.
  2. @@ -59,13 +54,13 @@ *
  3. A constructor taking an org.jdom2.Document and an XPathTarget. This constructor tries * to match XPath prefixes to namespaces and add them to the Namespace lookup.
  4. *
- * + * *

* After the object has been instantiated, you can call the - * {@link #add(String,String)} method to add additional bindings to the + * {@link #add(String, String)} method to add additional bindings to the * namespace context. A number of rules are designed to make sure that the context remains coherent: *

- * + * *
    *
  • Namespace bindings can only be added, not removed.
  • *
  • Once a prefix is bound, its binding cannot be changed.
  • @@ -76,292 +71,306 @@ * expression can never match a name that's in a namespace. In * particular, setting the default namespace won't have that effect. *
- * + * *

* Even with these rules, you can't assume that the context is thread safe. * Don't allow it to be changed while someone else is reading it. *

- * - - * + * * @author Norman Walsh * @author Richard Adams * - */ - public class NamespaceContextHelper implements NamespaceContext { - Logger log = LoggerFactory.getLogger(NamespaceContextHelper.class); - private Map ns = new HashMap(); - - /** - * Creates a new instance of NamespaceContextHelper. - * - *

- * Creates an empty namespace context. - *

- */ - NamespaceContextHelper() { - } - - /** - * Creates a new instance of NamespaceContextHelper. - * - *

- * Creates a namespace context with the bindings specified in - * initialNamespaces. - *

- */ - NamespaceContextHelper( Map initialNamespaces) { - for (String key: initialNamespaces.keySet()) { - String uri = initialNamespaces.get(key); - add(key, uri); - } - } - - /** - * Creates a new instance of NamespaceContextHelper. - * - *

- * Creates a namespace context with the specified prefix bound - * to uri. - *

- */ - NamespaceContextHelper(String prefix, String uri) { - add(prefix, uri); - } - - /** - * Adds a new prefix/uri binding to the namespace context. - * - * @throws NullPointerException - * if the prefix or uri is - * null. - * @throws IllegalArgumentException - * if the caller attempts to change the binding of - * prefix, if the caller attempts to bind the - * prefix "xml" or the namespace " - * http://www.w3.org/XML/1998/namespace" - * incorrectly, if the caller attempts to bind the prefix " - * xmlns" or the namespace " - * http://www.w3.org/2000/xmlns", or if the - * prefix is not a valid NCName. - */ - void add(String prefix, String uri) { - if (prefix == null || uri == null) { - throw new NullPointerException( - "Null prefix or uri passed to NamespaceContextHelper"); - } - - // VCell Hacky-fix (for a hacky implementation) - //if (uri.equals("http://www.copasi.org/static/sbml")){ - // log.warn("jlibsedml just tried to map a COPASI RDF namespace! Don't worry, we stopped it."); - // return; // We have no need to consider the COPASI RDF namespace in VCell - //} - - if (ns.containsKey(prefix)) { - String curURI = (String) ns.get(prefix); - if (uri.equals(curURI)) { - return; - } - throw new IllegalArgumentException( - "Attempt to change binding in NamespaceContextHelper"); - } - - if ("xml".equals(prefix) - && !"http://www.w3.org/XML/1998/namespace".equals(uri)) { - throw new IllegalArgumentException( - "The prefix 'xml' can only be bound to 'http://www.w3.org/XML/1998/namespace' in NamespaceContextHelper"); - } - - if ("http://www.w3.org/XML/1998/namespace".equals(uri) - && !"xml".equals(prefix)) { - throw new IllegalArgumentException( - "The namespace 'http://www.w3.org/XML/1998/namespace' can only have the prefix 'xml' in NamespaceContextHelper"); - } - - if ("xmlns".equals(prefix) - || "http://www.w3.org/2000/xmlns".equals(uri)) { - throw new IllegalArgumentException( - "Neither the prefix 'xmlns' nor the URI 'http://www.w3.org/2000/xmlns' can be bound in NamespaceContextHelper"); - } - - if ("".equals(prefix)) { - ns.put(prefix, uri); - } else { - if (prefix.matches("\\w[^ :/]*")) { - this.ns.put(prefix, uri); - } else { - throw new IllegalArgumentException( - "Prefix is not a valid NCName in NamespaceContextHelper"); - } - } - } - - /** Implements the NamespaceContext getNamespaceURI method. */ - public String getNamespaceURI(String prefix) { - String s = ns.get(prefix); - return s; - } - private Set nss; - - /** - * Attempts to match up prefixes in target with URIs in the document. It does this by: - *
    - *
  • If a namespace prefix in the model matches the XPath prefix, then that namespace is used. - *
  • A model namespace with no prefix is used if the URI contains the XPath prefix as a subsequence. - *
- * @param doc A JDOM document - */ - public NamespaceContextHelper(org.jdom2.Document doc) { - Iterator it = doc.getDescendants(new ElementFilter()); - nss = new HashSet(); - while (it.hasNext()) { - Element el = (Element) it.next(); - Namespace ns = el.getNamespace(); - - if (ns != null && !nss.contains(ns)) { - if (ns.getURI().equals("http://www.copasi.org/static/sbml")){ - ns = Namespace.getNamespace("COPASI", "http://www.copasi.org/static/sbml"); - } - nss.add(ns); - } - List atts = el.getAttributes(); - for ( Attribute att: atts){ - if(!att.getNamespace().equals(Namespace.NO_NAMESPACE)){ - nss.add(att.getNamespace()); - } - } - } - for (Namespace ns : nss) { - log.debug("Namespace {}", ns.getURI()); - } - return; - } - - public void process (XPathTarget target) { - Set xpathPrefixes = target.getXPathPrefixes(); - for (String prefix : xpathPrefixes) { - for (Namespace ns : nss) { - if ( ((ns.getPrefix().toLowerCase().equals(prefix.toLowerCase()) - || "".equals(ns.getPrefix()) - && ns.getURI().toLowerCase().contains(prefix.toLowerCase())) - || - "".equals(ns.getPrefix()) - && replace(ns.getURI().toLowerCase()).contains(prefix.toLowerCase())) - && getNamespaceURI(prefix) == null) { - add(prefix, ns.getURI()); - } - } - } - - } - - private String replace(String lowerCase) { - return lowerCase.replaceAll("/", "_"); - } - - public boolean isAllXPathPrefixesMapped(XPathTarget target){ - Set xpathPrefixes = target.getXPathPrefixes(); - for (String prefix : xpathPrefixes) { - if(ns.get(prefix)==null){ - return false; - } - } - return true; - } - - - /** Implements the NamespaceContext getPrefix method. */ - public String getPrefix(String namespaceURI) { - if (ns.containsValue(namespaceURI)) { - Iterator keys = ns.keySet().iterator(); - while (keys.hasNext()) { - String pfx = keys.next(); - String uri = ns.get(pfx); - if (namespaceURI.equals(uri)) { - return pfx; - } - } - } - return null; - } - - /** - * Implements a NON STANDARD method for finding all of the - * prefixes in the namespace context. - * - *

- * Returns an iterator over all of the prefixes in the namespace context. - * Note that multiple prefixes may be bound to the same URI. - *

- */ - public Iterator getPrefixes() { - return getPrefixes(null); - } - - /** Implements the NamespaceContext getPrefixes method. */ - public Iterator getPrefixes(String namespaceURI) { - return new NSIterator(ns, namespaceURI); - } - - /** - * Implements a NON STANDARD method for finding all of the - * namespace URIs in the namespace context. - * - *

- * Returns an iterator over all of the namespace URIs in the namespace - * context. Note that each namespace URI is returned exactly once, even if - * it is bound to several different prefixes. - *

- */ - public Iterator getNamespaceURIs() { - // Make sure each URI is returned at most once... - Map uriHash = new HashMap(); - - for (String pfx:ns.keySet()) { - - String uri = ns.get(pfx); - if (!uriHash.containsKey(uri)) { - uriHash.put(uri, pfx); - } - } - - return new NSIterator(uriHash, null); - } - - /** Implements the Iterator interface over namespace bindings. */ - private class NSIterator implements Iterator { - private Iterator keys; - - public NSIterator(Map hash, String value) { - keys = hash.keySet().iterator(); - if (value != null) { - // We have to copy the hash to get only the keys that have the - // specified value - Map vHash = new HashMap(); - while (keys.hasNext()) { - String key = keys.next(); - String val = hash.get(key); - if (val.equals(value)) { - vHash.put(key, val); - } - } - keys = vHash.keySet().iterator(); - } - } - - public boolean hasNext() { - return keys.hasNext(); - } - - public String next() { - return keys.next(); - } - - public void remove() { - throw new UnsupportedOperationException( - "Cannot remove prefix in NamespaceContextHelper"); - } - } +public class NamespaceContextHelper implements NamespaceContext { + private static final Logger log = LoggerFactory.getLogger(NamespaceContextHelper.class); + private final Map namespaceMapping; + private final Set prefixlessNamespaces; + + /** + * Creates a new instance of NamespaceContextHelper. + * + *

+ * Creates an empty namespace context. + *

+ */ + NamespaceContextHelper() { + this.namespaceMapping = new HashMap<>(); + this.prefixlessNamespaces = new HashSet<>(); + } + + /** + * Creates a new instance of NamespaceContextHelper. + * + *

+ * Creates a namespace context with the bindings specified in + * initialNamespaces. + *

+ */ + NamespaceContextHelper(Map initialNamespaces) { + this(); + for (String prefix : initialNamespaces.keySet()) { + this.add(prefix, initialNamespaces.get(prefix)); + } + } + + /** + * Creates a new instance of NamespaceContextHelper. + * + *

+ * Creates a namespace context with the specified prefix bound + * to uri. + *

+ */ + NamespaceContextHelper(String prefix, String uri) { + this(); + this.add(prefix, uri); + } + + /** + * Creates a new instance of NamespaceContextHelper. + * + *

+ * Creates a namespace context with the specified prefix bound + * to uri. + *

+ */ + NamespaceContextHelper(Namespace namespace) { + this(); + this.add(namespace.getPrefix(), namespace.getURI()); + } + + + /** + * Attempts to match up prefixes in target with URIs in the document. It does this by: + *
    + *
  • If a namespace prefix in the model matches the XPath prefix, then that namespace is used. + *
  • A model namespace with no prefix is used if the URI contains the XPath prefix as a subsequence. + *
+ * + * @param doc A JDOM document + */ + public NamespaceContextHelper(org.jdom2.Document doc) throws XMLException { + this(); + for (Element el : doc.getDescendants(new ElementFilter())) { + Namespace ns = el.getNamespace(); + if (ns != null) { + if (!"".equals(ns.getPrefix()) && this.namespaceMapping.containsKey(ns.getPrefix()) && !this.namespaceMapping.get(ns.getPrefix()).equals(ns.getURI())) + throw new XMLException("duplicate prefixes"); + if (ns.getURI().equals("http://www.copasi.org/static/sbml")) { + ns = Namespace.getNamespace("COPASI", "http://www.copasi.org/static/sbml"); + } + this.add(ns); + } + + for (Attribute att : el.getAttributes()) { + if (!att.getNamespace().equals(Namespace.NO_NAMESPACE)) { + ns = att.getNamespace(); + this.add(ns); + } + } + } + } + + /** + * Adds a new prefix/uri binding to the namespace context. + * + * @throws NullPointerException if the prefix or uri is + * null. + * @throws IllegalArgumentException if the caller attempts to change the binding of + * prefix, if the caller attempts to bind the + * prefix "xml" or the namespace " + * http://www.w3.org/XML/1998/namespace" + * incorrectly, if the caller attempts to bind the prefix " + * xmlns" or the namespace " + * http://www.w3.org/2000/xmlns", or if the + * prefix is not a valid NCName. + */ + void add(Namespace ns) { + this.add(ns.getPrefix(), ns.getURI()); + } + + /** + * Adds a new prefix/uri binding to the namespace context. + * + * @throws NullPointerException if the prefix or uri is + * null. + * @throws IllegalArgumentException if the caller attempts to change the binding of + * prefix, if the caller attempts to bind the + * prefix "xml" or the namespace " + * http://www.w3.org/XML/1998/namespace" + * incorrectly, if the caller attempts to bind the prefix " + * xmlns" or the namespace " + * http://www.w3.org/2000/xmlns", or if the + * prefix is not a valid NCName. + */ + void add(String prefix, String uri) { + if (prefix == null || uri == null) { + throw new NullPointerException("Null prefix or uri passed to NamespaceContextHelper"); + } + + // VCell Hacky-fix (for a hacky implementation) + //if (uri.equals("http://www.copasi.org/static/sbml")){ + // log.warn("jlibsedml just tried to map a COPASI RDF namespace! Don't worry, we stopped it."); + // return; // We have no need to consider the COPASI RDF namespace in VCell + //} + + if ("xml".equals(prefix) && !"http://www.w3.org/XML/1998/namespace".equals(uri)) { + throw new IllegalArgumentException("The prefix 'xml' can only be bound to 'http://www.w3.org/XML/1998/namespace' in NamespaceContextHelper"); + } + + if (!"xml".equals(prefix) && "http://www.w3.org/XML/1998/namespace".equals(uri)) { + throw new IllegalArgumentException("The namespace 'http://www.w3.org/XML/1998/namespace' can only have the prefix 'xml' in NamespaceContextHelper"); + } + + if ("xmlns".equals(prefix) || "http://www.w3.org/2000/xmlns".equals(uri)) { + throw new IllegalArgumentException("Neither the prefix 'xmlns' nor the URI 'http://www.w3.org/2000/xmlns' can be bound in NamespaceContextHelper"); + } + + if (prefix.isEmpty()) { + if (uri.contains("www.sbml.org/sbml/level")){ + this.add("sbml", uri); + return; + } + this.prefixlessNamespaces.add(uri); + } else { + if (this.namespaceMapping.containsKey(prefix)) { + String curURI = this.namespaceMapping.get(prefix); + if (uri.equals(curURI)) return; + throw new IllegalArgumentException("Attempt to change binding in NamespaceContextHelper"); + } + if (prefix.matches("\\w[^ :/]*")) { + this.namespaceMapping.put(prefix, uri); + } else { + throw new IllegalArgumentException("Prefix is not a valid NCName in NamespaceContextHelper"); + } + } + } + + /** + * Implements the NamespaceContext getNamespaceURI method. + */ + public String getNamespaceURI(String prefix) { + if (prefix == null || prefix.isEmpty()) throw new NullPointerException("Null prefix or uri passed to NamespaceContextHelper"); + return this.namespaceMapping.get(prefix); + } + + public Set getAllPrefixlessURIs() { + return Collections.unmodifiableSet(this.prefixlessNamespaces); + } + + public void process(XPathTarget target) { + Set xpathPrefixes = new HashSet<>(target.getXPathPrefixes()); + xpathPrefixes.retainAll(this.namespaceMapping.keySet()); + for (String prefix : xpathPrefixes) { + Namespace ns = Namespace.getNamespace(prefix, this.namespaceMapping.get(prefix)); + boolean prefixesMatch = ns.getPrefix().equalsIgnoreCase(prefix); + boolean nsPrefixIsEmpty = ns.getPrefix().isEmpty(); + boolean prefixFoundInNsURI = ns.getURI().toLowerCase().contains(prefix.toLowerCase()); + boolean modifiedNsURIContainsPrefix = this.replace(ns.getURI().toLowerCase()).contains(prefix.toLowerCase()); + boolean mappingFound = (prefixesMatch || nsPrefixIsEmpty && (prefixFoundInNsURI || modifiedNsURIContainsPrefix)); + if (mappingFound && this.getNamespaceURI(prefix) == null) this.add(prefix, ns.getURI()); + } + } + + private String replace(String lowerCase) { + return lowerCase.replaceAll("/", "_"); + } + + public boolean areAllXPathPrefixesMapped(XPathTarget target) { + Set xpathPrefixes = target.getXPathPrefixes(); + for (String prefix : xpathPrefixes) { + if ("".equals(prefix) && !this.prefixlessNamespaces.isEmpty()) continue; + if (this.namespaceMapping.get(prefix) != null) continue; + return false; + } + return true; + } + + + /** + * Implements the NamespaceContext getPrefix method. + */ + public String getPrefix(String namespaceURI) { + if (!this.namespaceMapping.containsValue(namespaceURI)) return ""; + for (String pfx : this.namespaceMapping.keySet()) { + String uri = this.namespaceMapping.get(pfx); + if (namespaceURI.equals(uri)) return pfx; + } + return ""; + } + + /** + * Implements a NON STANDARD method for finding all of the + * prefixes in the namespace context. + * + *

+ * Returns an iterator over all of the prefixes in the namespace context. + * Note that multiple prefixes may be bound to the same URI. + *

+ */ + public Iterator getPrefixes() { + return this.getPrefixes(null); + } + + /** + * Implements the NamespaceContext getPrefixes method. + */ + public Iterator getPrefixes(String namespaceURI) { + return new NSIterator(this.namespaceMapping, namespaceURI); + } + + /** + * Implements a NON STANDARD method for finding all of the + * namespace URIs in the namespace context. + * + *

+ * Returns an iterator over all of the namespace URIs in the namespace + * context. Note that each namespace URI is returned exactly once, even if + * it is bound to several different prefixes. + *

+ */ + public List getNamespaceURIs() { + // Make sure each URI is returned at most once... + Set uriHash = new HashSet<>(this.prefixlessNamespaces); + uriHash.addAll(this.namespaceMapping.values()); + return uriHash.stream().toList(); + } + + /** + * Implements the Iterator interface over namespace bindings. + */ + private static class NSIterator implements Iterator { + private Iterator keys; + + public NSIterator(Map hash, String value) { + this.keys = hash.keySet().iterator(); + if (value != null) { + // We have to copy the hash to get only the keys that have the + // specified value + Map vHash = new HashMap<>(); + while (this.keys.hasNext()) { + String key = this.keys.next(); + String val = hash.get(key); + if (val.equals(value)) { + vHash.put(key, val); + } + } + this.keys = vHash.keySet().iterator(); + } + } + + public boolean hasNext() { + return this.keys.hasNext(); + } + + public String next() { + return this.keys.next(); + } + + public void remove() { + throw new UnsupportedOperationException( + "Cannot remove prefix in NamespaceContextHelper"); + } + } } diff --git a/vcell-core/src/main/java/org/jlibsedml/NewXML.java b/vcell-core/src/main/java/org/jlibsedml/NewXML.java deleted file mode 100644 index c26e3ad00a..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/NewXML.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.jlibsedml; - -import java.util.Collections; -import java.util.List; - -import org.jdom2.Element; - -/** - * Encapsulates a NewXML element in the SED-ML specification. This object can contain one or more XML elements. - * For example, - * *
-   <addXML target="/sbml:sbml/sbml:model/sbml:listOfParameters">
- *  <newXML>
- *    <parameter metaid="metaid_0000010"id="V_mT"value="0.7"/>
- *    <parameter metaid="metaid_0000011"id="V_mT2"value="0.71"/>
-*    </newXML>
-*  </addXML>
-*
-will produce a NewXML object with a List of two {@link Element}s, all of which will be added as children of the - the target element. - * @author radams - * - */ -public final class NewXML { - - public NewXML( List xml) { - super(); - this.xml = xml; - } - - /** - * Getter for the model XML fragments to be inserted into the model XML. - * @return An unmodifiable - */ - public List getXml() { - return Collections.unmodifiableList(xml); - } - /** - * Gets the number of top-level XML elements contained in this object. - * @return an integer, >= 0 - */ - public int numElements() { - return xml.size(); - } - - private final List xml; - - - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Notes.java b/vcell-core/src/main/java/org/jlibsedml/Notes.java deleted file mode 100644 index e872ee3386..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Notes.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.jlibsedml; - -import org.jdom2.Element; -import org.jdom2.Namespace; - -/** - * Contains notes elements to display human readable information to the user. - * The content should be XHTML.
- * Usage is as follows:
- * Create a new Notes element - * - *
- * // create some xhtml. E.g.,
- * Element para = new Element("p");
- * para.setText("some comment");
- * 
- * // create a notes element
- * Notes note = new Notes(para);
- * 
- * - * Clients do not have to set the namespace of the XHTML contents, this is performed by this class. - * - */ -public final class Notes { - - private Element notesElement = null; - - /** - * @param argNotesElement - * A non-null Element. This element will have its namespace set - * to "http://www.w3.org/1999/xhtml" - */ - public Notes(Element argNotesElement) { - if (SEDMLElementFactory.getInstance().isStrictCreation()) { - Assert.checkNoNullArgs(argNotesElement); - } - this.notesElement = argNotesElement; - notesElement.setNamespace(Namespace.getNamespace(SEDMLTags.XHTML_NS)); - } - - /** - * Get the Notes element for this object, will not return null. - * - * @return An {@link Element} - */ - public Element getNotesElement() { - return notesElement; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/OneStep.java b/vcell-core/src/main/java/org/jlibsedml/OneStep.java deleted file mode 100644 index 5e475aaf0b..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/OneStep.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.jlibsedml; -/** - * - * Represents the OneStep class in SED-ML. This defines the next output point - * that should be reached by the simulation, as an increment from the current point. - * @since 2.1.0 - */ -public class OneStep extends Simulation { - - - private double step; - - public OneStep(String id, String name, Algorithm algorithm, double step) { - super(id, name, algorithm); - this.setStep(step); - } - - @Override - public String toString() { - return "OneStep [" + getAlgorithm() - + ", name=" + getName() - + ", getId()=" + getId() - + ", getStep()=" + getStep() - + "]"; - } - - @Override - public String getSimulationKind() { - return SEDMLTags.SIMUL_OS_KIND; - } - - @Override - public String getElementName() { - return SEDMLTags.SIM_OS; - } - /** - * Sets the step. - * @param step - */ - public void setStep(double step) { - this.step = step; - } - - /** - * Gets the step - * @return a double. - */ - public double getStep() { - return step; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Output.java b/vcell-core/src/main/java/org/jlibsedml/Output.java deleted file mode 100644 index 3195246cff..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Output.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.jlibsedml; - -import java.util.List; - -/** - * Base class for any kind of SED-ML output - e.g., a plot or report. - * @author anu/radams - * - */ -public abstract class Output extends AbstractIdentifiableElement{ - - public boolean accept(SEDMLVisitor visitor){ - if(visitor.visit(this)){ - if(isPlot2d()){ - for (Curve c: ((Plot2D)this).getListOfCurves()) { - if(! c.accept(visitor)){ - return false; - } - } - return true; - } - else if(isPlot3d()){ - for (Surface sf: ((Plot3D)this).getListOfSurfaces()) { - if(! sf.accept(visitor)){ - return false; - } - } - return true; - } - else if(isReport()){ - for (DataSet sds: ((Report)this).getListOfDataSets()) { - if(! sds.accept(visitor)){ - return false; - } - } - return true; - }else { - return false; - } - }else { - return false; - } - } - - /** - * - * @param id - non null or non-empty String. - * @param name - optional, can be null - * @throws IllegalArgumentException if id is null. - */ - public Output(String id, String name) { - super(id,name); - - } - - /** - * Gets the type of this output (Plot2D, Plot3D, Report) - * @return A non-null String. - */ - public abstract String getKind(); - - /** - * Boolean test for whether this output is a Plot2d description. - * @return true if this is a Plot2d description, false otherwise. - */ - public boolean isPlot2d(){ - return getKind().equals(SEDMLTags.PLOT2D_KIND); - } - - /** - * Boolean test for whether this output is a Plot3d description. - * @return true if this is a Plot3d description, false otherwise. - */ - public boolean isPlot3d(){ - return getKind().equals(SEDMLTags.PLOT3D_KIND); - } - - - /** - * Boolean test for whether this output is a report description. - * @return true if this is a report description, false otherwise. - */ - public boolean isReport(){ - return getKind().equals(SEDMLTags.REPORT_KIND); - } - - /** - * Gets a {@link List} of all {@link DataGenerator} identifiers used in this output.
- * This list will contain only unique entries; the same {@link DataGenerator} id will not appear - * twice in this output. - * @return A possibly empty but non-null {@link List} of {@link DataGenerator} id values. - */ - public abstract List getAllDataGeneratorReferences (); - - /** - * Calculates and returns a non-redundant List of data generator references listed as being independent variables on the output. - * (i.e., the xDataReference). For {@link Report}s, (with no concept of independent/dependent variables), an empty list is returned. - * @return A non-null but possibly empty List of {@link DataGenerator} references. - */ - public abstract List getAllIndependentDataGeneratorReferences(); - - - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Plot2D.java b/vcell-core/src/main/java/org/jlibsedml/Plot2D.java deleted file mode 100644 index 22c377ee10..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Plot2D.java +++ /dev/null @@ -1,140 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -/** - * Encapsulates the Plot2d Sed-ML element. - * @author anu/radams - * - */ -public class Plot2D extends Output { - - @Override - public String toString() { - return "Plot2D [listOfCurves=" + listOfCurves + ", name=" + getName() + "]"; - } - - private ArrayList listOfCurves= new ArrayList(); - - - /** - * - * @param id A unique id for this element in the document. - * @param name An optional name for this element. - */ - public Plot2D(String id, String name) { - super(id, name); - } - - /** - * Gets a read-only list of Curves contained in this element. - * @return A possibly empty but non-null {@link List} of {@link Curve} elements. - */ - public List getListOfCurves() { - return Collections.unmodifiableList(listOfCurves); - } - - /** - * Gets the type of this output. - * @return SEDMLTags.PLOT2D_KIND - */ - public String getKind() { - return SEDMLTags.PLOT2D_KIND; - } - - /** - * Adds a {@link Curve} to this object's list of Curves, if not already present. - * @param curve A non-null {@link Curve} element - * @return true if curve added, false otherwise. - */ - public boolean addCurve(Curve curve ){ - if(!listOfCurves.contains(curve)) { - return listOfCurves.add(curve); - } else { - // TODO: add to error list - } - return false; - } - - /** - * Removes a {@link Curve} from this object's list of Curves, if not already present. - * @param curve A non-null {@link Curve} element - * @return true if curve added, false otherwise. - */ - public boolean removeCurve(Curve curve ){ - - return listOfCurves.remove(curve); - - } - - /** - * Returns a sublist of the {@link List} of Curves for this plot, that use the output of the specified {@link DataGenerator} - * for the X-axis. - * @param dg A non-null {@link DataGenerator} - * @return A possibly empty but non-null {@link List} of {@link Curve} elements. - */ - public List getCurvesUsingDataGeneratorAsXAxis(DataGenerator dg){ - List rc = new ArrayList(); - for (Curve cv: listOfCurves){ - if(cv.getXDataReference().equals(dg.getId())){ - rc.add(cv); - } - } - return rc; - } - - - /** - * Returns a sublist of the {@link List} of Curves for this plot, that use the output of the specified {@link DataGenerator} - * for the Y-axis. - * @param dg A non-null {@link DataGenerator} - * @return A possibly empty but non-null {@link List} of {@link Curve} elements. - */ - public List getCurvesUsingDataGeneratorAsYAxis(DataGenerator dg){ - List rc = new ArrayList(); - for (Curve cv: listOfCurves){ - if(cv.getYDataReference().equals(dg.getId())){ - rc.add(cv); - } - } - return rc; - } - - @Override - public List getAllDataGeneratorReferences() { - Set rc = new TreeSet(); - for (Curve c: listOfCurves){ - rc.add(c.getXDataReference()); - rc.add(c.getYDataReference()); - } - List rc2 = new ArrayList(); - for (String id:rc){ - rc2.add(id); - } - return rc2; - - } - - @Override - public List getAllIndependentDataGeneratorReferences() { - Set rc = new TreeSet(); - for (Curve c: listOfCurves){ - rc.add(c.getXDataReference()); - } - List rc2 = new ArrayList(); - for (String id:rc){ - rc2.add(id); - } - return rc2; - } - - @Override - public String getElementName() { - return SEDMLTags.OUTPUT_P2D; - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Plot3D.java b/vcell-core/src/main/java/org/jlibsedml/Plot3D.java deleted file mode 100644 index 443450b680..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Plot3D.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -/** - * Encapsulates the information required for a 3d plot in SED-ML. - * - * @author anu/radams - * - */ -public class Plot3D extends Output { - - @Override - public String getElementName() { - return SEDMLTags.OUTPUT_P3D; - } - - @Override - public String toString() { - return "Plot3D [listOfSurfaces=" + listOfSurfaces + "]"; - } - - private ArrayList listOfSurfaces; - - /** - * - * @param id - * A unique String identifier for this element. - * @param name - * An optional name. - */ - public Plot3D(String id, String name) { - super(id, name); - listOfSurfaces = new ArrayList(); - } - - /** - * Getter for a read-only list of Surfaces of this object. - * @return list of {@link Surface} - */ - public List getListOfSurfaces() { - return Collections.unmodifiableList(listOfSurfaces); - } - - /** - * Adds a {@link Surface} to this object's list of Surfaces, if not already - * present. - * - * @param surface - * A non-null {@link Surface} element - * @return true if surface added, false - * otherwise. - */ - public boolean addSurface(Surface surface) { - if (!listOfSurfaces.contains(surface)) - return listOfSurfaces.add(surface); - return false; - } - - /** - * REmoves a {@link Surface} from this object's list of Surfaces, if not - * already present. - * - * @param surface - * A non-null {@link Surface} element - * @return true if surface removed, false - * otherwise. - */ - public boolean removeSurface(Surface surface) { - - return listOfSurfaces.remove(surface); - - } - - /** - * Gets the type of this output. - * - * @return SEDMLTags.PLOT3D_KIND - */ - public String getKind() { - return SEDMLTags.PLOT3D_KIND; - } - - @Override - public List getAllDataGeneratorReferences() { - - Set rc = new TreeSet(); - for (Surface c : listOfSurfaces) { - rc.add(c.getXDataReference()); - rc.add(c.getYDataReference()); - rc.add(c.getZDataReference()); - } - List rc2 = new ArrayList(); - for (String id : rc) { - rc2.add(id); - } - return rc2; - - } - - @Override - public List getAllIndependentDataGeneratorReferences() { - Set rc = new TreeSet(); - for (Surface c : listOfSurfaces) { - rc.add(c.getXDataReference()); - } - List rc2 = new ArrayList(); - for (String id : rc) { - rc2.add(id); - } - return rc2; - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/RemoveXML.java b/vcell-core/src/main/java/org/jlibsedml/RemoveXML.java deleted file mode 100644 index cf804d08eb..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/RemoveXML.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.jlibsedml; - -/** - * Class encapsulating a RemoveXML element to be applied to a model. - * @author radams - * - */ -public class RemoveXML extends Change { - - /** - * - * @param target A non-null {@link XPathTarget} object - */ - public RemoveXML(XPathTarget target) { - super(target); - // TODO Auto-generated constructor stub - } - - @Override - public String getElementName() { - return SEDMLTags.REMOVE_XML; - } - - @Override - public final String getChangeKind() { - return SEDMLTags.REMOVE_XML_KIND; - } - - public boolean accept(SEDMLVisitor visitor) { - - return visitor.visit(this); - - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/RepeatedTask.java b/vcell-core/src/main/java/org/jlibsedml/RepeatedTask.java deleted file mode 100644 index 837aac08d4..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/RepeatedTask.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RepeatedTask extends AbstractTask { - private static final Logger logger = LoggerFactory.getLogger(RepeatedTask.class); - private boolean resetModel = false; - private String range = new String(); - - private Map ranges = new HashMap (); - private List changes = new ArrayList (); - private Map subTasks = new HashMap (); - - public boolean getResetModel() { - return resetModel; - } - public void setResetModel(boolean resetModel) { - this.resetModel = resetModel; - } - public String getRange() { - return range; - } - public void setRange(String range) { - this.range = range; - } - public Range getRange(String rangeId) { - return ranges.get(rangeId); - } - public void addRange(Range range) { - if(!ranges.containsKey(range.getId())) { - ranges.put(range.getId(), range); - } else { - logger.warn("range already in ranges list"); - logger.warn(" ...range " + range.getId() + " not added to list"); - } - } - - public Map getRanges() { - return ranges; - } - public void addChange(SetValue change) { - changes.add(change); - } - public List getChanges() { - return changes; - } - public void addSubtask(SubTask subTask) { - if(subTask == null || subTask.getTaskId() == null || subTask.getTaskId().equals("")) { - logger.warn("subtask cant't be null, key can't be null, key can't be empty string"); - logger.warn(" ...subtask " + subTask.getTaskId() + " not added to list"); - return; // subtask can't be null, key can't be null, key can't be "" - } - if(this.getId().equals(subTask.getTaskId())) { - logger.warn("'this' repeated task cannot be a subtask for itself"); - logger.warn(" ...subtask " + subTask.getTaskId() + " not added to list"); - return; // "this" repeated task cannot be a subtask for itself - } - if(!subTasks.containsKey(subTask.getTaskId())) { // no duplicates - subTasks.put(subTask.getTaskId(), subTask); - subTask.removeOwnerFromDependentTasksList(this); // this repeated task cannot depend on itself - } else { - logger.warn("subtask already in subtasks list"); - logger.warn("...subtask {} not added to list",subTask.getTaskId()); - return; - } - } - public Map getSubTasks() { - return subTasks; - } - - public RepeatedTask(String id, String name, boolean resetModel, String range) { - super(id, name); - this.resetModel = resetModel; - this.range = range; - } - - @Override - public String toString() { - return "Repeated Task [" - + "name=" + getName() - + ", getId()=" + getId() - + ", resetModel=" + resetModel - + ", ranges.size()=" + ranges.size() - + ", changes.size()=" + changes.size() - + ", subTasks.size()=" + subTasks.size() - + "]"; - } - - @Override - public String getElementName() { - return SEDMLTags.REPEATED_TASK_TAG; - } - - @Override - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } - - @Override - public String getModelReference() { - throw new UnsupportedOperationException("Not supported by RepeatedTask"); - } - - @Override - public String getSimulationReference() { - throw new UnsupportedOperationException("Not supported by Repeated task"); - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Report.java b/vcell-core/src/main/java/org/jlibsedml/Report.java deleted file mode 100644 index 02fe9e4f11..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Report.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Represents the SED-ML 'Report' element for describing textual output of - * a simulation. - * @author radams - * - */ -public final class Report extends Output { - - private ArrayList listOfDataSets; - - /** - * - * @param id A unique String identifier for this object. - * @param name An optional String name for this object. - */ - public Report(String id, String name) { - super(id, name); - listOfDataSets = new ArrayList(); - } - - @Override - public String getElementName() { - return SEDMLTags.OUTPUT_REPORT; - } - - /** - * Getter for a read-only list of {@link DataSet} objects contained in this report. - * @return non-null but possibly empty List . - */ - public List getListOfDataSets() { - return Collections.unmodifiableList(listOfDataSets); - } - - /** - * Adds a {@link DataSet} to this object's list of DataSets, if not already present. - * @param dataSet A non-null {@link DataSet} element - * @return true if dataSet added, false otherwise. - */ - public boolean addDataSet(DataSet dataSet ){ - if(!listOfDataSets.contains(dataSet)) - return listOfDataSets.add(dataSet); - return false; - } - - /** - * Removes a {@link DataSet} from this object's list of DataSets. - * @param dataSet A non-null {@link DataSet} element - * @return true if dataSet removed, false otherwise. - */ - public boolean removeDataSet(DataSet dataSet ){ - - return listOfDataSets.remove(dataSet); - - } - - /** - * Gets the type of this output. - * - * @return SEDMLTags.REPORT_KIND - */ - public String getKind() { - return SEDMLTags.REPORT_KIND; - } - - @Override - public List getAllDataGeneratorReferences() { - List rc = new ArrayList(); - for (DataSet d : listOfDataSets){ - rc.add(d.getDataReference()); - } - return rc; - } - - @Override - public List getAllIndependentDataGeneratorReferences() { - return Collections.emptyList(); - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDBase.java b/vcell-core/src/main/java/org/jlibsedml/SEDBase.java deleted file mode 100644 index dac57d05d8..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/SEDBase.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Base class of SEDML elements that can be annotated or contain notes. - * @author anu/radams - * - */ -public abstract class SEDBase { - - private String metaId = null; - // this is deprecated from v2 onwards. - private List notes = new ArrayList(); - - // this is deprecated from v2 onwards. - private List annotation = new ArrayList(); - - private Notes note; - - public Notes getNote() { - return note; - } - - /** - * Setter for a {@link Notes}. - * @param note Can be null, if the Notes are to be removed from this object. - */ - public void setNote(Notes note) { - this.note = note; - } - - /** - * Getter for the metaid attribute of this element. - * @return A String of the meta_id attribute, or an empty String if not set. - */ - public String getMetaId() { - return metaId; - } - - /** - * Setter for the metaid attribute of this element. - * @param metaId A non-null String of the meta_id attribute. - */ - public void setMetaId(String metaId) { - this.metaId = metaId; - } - - /** - * Gets a read-only view of the Notes for this element. - * @return An unmodifiable List of Notes. - * @deprecated Use getNote() from now on. - */ - public List getNotes() { - return Collections.unmodifiableList(notes); - } - - /** - * Adds a Note, if this note does not already exist. - * @param note A non-null {@link Notes} - * @return true if note added, false otherwise. - * @deprecated From now on, setNote(Note note) should be used. - */ - public boolean addNote(Notes note) { - if (!notes.contains(note)){ - return notes.add(note); - } - return false; - } - - /** - * Removes a Note from this element's list of {@link Notes} objects. - * @param note A non-null {@link Notes} - * @return true if note removed, false otherwise. - * @deprecated Use setNote(null) from now on. - */ - public boolean removeNote(Notes note) { - return notes.remove(note); - } - - /** - * Directly sets a list of Notes into this element. - * @param notes A non-null List of {@link Notes} objects. - * @throws IllegalArgumentException if notes is null. - * @deprecated Use setNote(Note n) from now on. - */ - public void setNotes(List notes) { - Assert.checkNoNullArgs(notes); - this.notes = notes; - } - - /** - * Gets a read-only view of the {@link Annotation}s for this element. - * @return An unmodifiable List of Annotation. - * @deprecated - */ - public List getAnnotation() { - return Collections.unmodifiableList(annotation); - } - - /** - * Adds a Annotation, if this annotation does not already exist in this element's annotations. - * @param ann A non-null {@link Annotation} - * @return true if ann added, false otherwise. - */ - public boolean addAnnotation(Annotation ann) { - if (!annotation.contains(ann)){ - return annotation.add(ann); - } - return false; - } - - /** - * Removes a Annotation from this element's list of {@link Annotation} objects. - * @param ann A non-null {@link Annotation} - * @return true if ann removed, false otherwise. - */ - public boolean removeAnnotation(Annotation ann) { - return annotation.remove(ann); - } - - /** - * Directly sets a list of Annotations into this element. - * @param annotations A non-null List of {@link Annotation} objects. - * @throws IllegalArgumentException if annotations is null. - */ - public void setAnnotation( List annotations) { - Assert.checkNoNullArgs(annotations); - this.annotation = annotations; - } - - /** - * Provides a link between the object model and the XML element names - * @return A non-null String of the XML element name of the object. - */ - public abstract String getElementName(); - - public abstract boolean accept(SEDMLVisitor visitor); - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDMLReader.java b/vcell-core/src/main/java/org/jlibsedml/SEDMLReader.java deleted file mode 100644 index 17ccd83e53..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/SEDMLReader.java +++ /dev/null @@ -1,845 +0,0 @@ -package org.jlibsedml; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.jdom2.DataConversionException; -import org.jdom2.Document; -import org.jdom2.Element; -import org.jdom2.JDOMException; -import org.jdom2.Namespace; -import org.jdom2.input.SAXBuilder; -import org.jlibsedml.UniformRange.UniformType; -import org.jlibsedml.mathsymbols.SedMLSymbolFactory; -import org.jmathml.ASTNode; -import org.jmathml.ASTRootNode; -import org.jmathml.MathMLReader; -import org.jmathml.SymbolRegistry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -@SuppressWarnings("unchecked") -class SEDMLReader { - - Namespace sedNS = null; - Logger log = LoggerFactory.getLogger(SEDMLReader.class); - Model getModel(Element modelElement) throws DataConversionException { - Model m = new Model(modelElement.getAttributeValue(SEDMLTags.MODEL_ATTR_ID), - modelElement.getAttributeValue(SEDMLTags.MODEL_ATTR_NAME), - modelElement.getAttributeValue(SEDMLTags.MODEL_ATTR_LANGUAGE), - modelElement.getAttributeValue(SEDMLTags.MODEL_ATTR_SOURCE)); - List lModelChildren = modelElement.getChildren(); - Iterator iModelChildren = lModelChildren.iterator(); - while (iModelChildren.hasNext()) { - Element eModelChild = (Element) iModelChildren.next(); - if (eModelChild.getName().equals(SEDMLTags.CHANGES)) { - List lChanges = eModelChild.getChildren(); - Iterator iChanges = lChanges.iterator(); - while (iChanges.hasNext()) { - Element aChange = (Element) iChanges.next(); - Change c = getChange(aChange); - m.addChange(c); - } - } - } - addNotesAndAnnotation(m, modelElement); - return m; - } - - Change getChange(Element aChange) throws DataConversionException { - Change rc = null; - if (aChange.getName().equals(SEDMLTags.CHANGE_ATTRIBUTE)) { - rc = new ChangeAttribute(new XPathTarget(aChange - .getAttributeValue(SEDMLTags.CHANGE_ATTR_TARGET)), aChange - .getAttributeValue(SEDMLTags.CHANGE_ATTR_NEWVALUE)); - - } else if (aChange.getName().equals(SEDMLTags.CHANGE_XML) - || aChange.getName().equals(SEDMLTags.ADD_XML)) { - Iterator changeChildren = aChange.getChildren().iterator(); - while (changeChildren.hasNext()) { - Element el = (Element) changeChildren.next(); - if (el.getName().equals(SEDMLTags.NEW_XML)) { - List xml = getNewXML(el); - NewXML newxml = new NewXML(xml); - if (aChange.getName().equals(SEDMLTags.CHANGE_XML)) - rc = new ChangeXML(new XPathTarget(aChange.getAttributeValue(SEDMLTags.CHANGE_ATTR_TARGET)), newxml); - else - rc = new AddXML(new XPathTarget(aChange.getAttributeValue(SEDMLTags.CHANGE_ATTR_TARGET)), newxml); - } - } - - } else if (aChange.getName().equals(SEDMLTags.REMOVE_XML)) { - rc = new RemoveXML(new XPathTarget(aChange.getAttributeValue(SEDMLTags.CHANGE_ATTR_TARGET))); - - } else if (aChange.getName().equals(SEDMLTags.COMPUTE_CHANGE)) { - ASTRootNode math = null; - Element toAdd = null; - List vars = new ArrayList(); - List params = new ArrayList(); - Iterator changeChildren = aChange.getChildren().iterator(); - while (changeChildren.hasNext()) { - Element el = (Element) changeChildren.next(); - - if (el.getName().equals(SEDMLTags.CHANGE_ATTR_MATH)) { - math = (ASTRootNode) new MathMLReader().parseMathML(el); - } else if (el.getName().equals(SEDMLTags.DATAGEN_ATTR_VARS_LIST)) { - List lVariables = el.getChildren(); - Iterator iVariables = lVariables.iterator(); - while (iVariables.hasNext()) { - Element eVariable = (Element) iVariables.next(); - if (eVariable.getName().equals(SEDMLTags.DATAGEN_ATTR_VARIABLE)) { - vars.add(createVariable(eVariable, true)); - } - } - } else if (el.getName().equals(SEDMLTags.DATAGEN_ATTR_PARAMS_LIST)) { - List lParameters = el.getChildren(); - Iterator iParameters = lParameters.iterator(); - while (iParameters.hasNext()) { - Element eParameter = (Element) iParameters.next(); - if (eParameter.getName().equals(SEDMLTags.DATAGEN_ATTR_PARAMETER)) { - params.add(createParameter(eParameter)); - } - } - } - - // TODO: variable and parameter need to be also - // loaded here - } - ComputeChange cc = new ComputeChange(new XPathTarget(aChange - .getAttributeValue(SEDMLTags.CHANGE_ATTR_TARGET)), math); - cc.setListOfParameters(params); - cc.setListOfVariables(vars); - rc = cc; - - } - addNotesAndAnnotation(rc, aChange); - return rc; - } - - // The Change within a repeated task (SetChange) - private void addChanges(RepeatedTask t, Element element) throws DataConversionException { - SetValue sv = null; - String changeChildName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - changeChildName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.SET_VALUE)) { - String xPathString = eChild.getAttributeValue(SEDMLTags.SET_VALUE_ATTR_TARGET); - XPathTarget target = new XPathTarget(xPathString); - String rangeReference = eChild.getAttributeValue(SEDMLTags.SET_VALUE_ATTR_RANGE_REF); - String modelReference = eChild.getAttributeValue(SEDMLTags.SET_VALUE_ATTR_MODEL_REF); - sv = new SetValue(target, rangeReference, modelReference); - getSetValueContent(sv, eChild); - t.addChange(sv); - } else { - log.warn("Unexpected " + eChild); - } - log.debug("sv " + sv.toString()); - } - } - private SetValue getSetValueContent(SetValue c, Element element) throws DataConversionException { - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - if (eChild.getName().equals(SEDMLTags.CHANGE_ATTR_MATH)) { - ASTNode math = (ASTRootNode) new MathMLReader().parseMathML(eChild); - c.setMath(math); - } else if (eChild.getName().equals(SEDMLTags.DATAGEN_ATTR_VARS_LIST)) { - List lVariables = eChild.getChildren(); - Iterator iVariables = lVariables.iterator(); - while (iVariables.hasNext()) { - Element eVariable = (Element) iVariables.next(); - if (eVariable.getName().equals(SEDMLTags.DATAGEN_ATTR_VARIABLE)) { - c.addVariable(createVariable(eVariable, true)); - } - } - } else if (eChild.getName().equals(SEDMLTags.DATAGEN_ATTR_PARAMS_LIST)) { - List lParameters = eChild.getChildren(); - Iterator iParameters = lParameters.iterator(); - while (iParameters.hasNext()) { - Element eParameter = (Element) iParameters.next(); - if (eParameter.getName().equals(SEDMLTags.DATAGEN_ATTR_PARAMETER)) { - c.addParameter(createParameter(eParameter)); - } - } - } else { - log.warn("Unexpected " + eChild); - } - } - return c; - } - - - private void addNotesAndAnnotation(SEDBase sedbase, Element xmlElement) { - List children = xmlElement.getChildren(); - Iterator ichildren = children.iterator(); - Listanns = new ArrayList(); - while (ichildren.hasNext()) { - Element element = (Element) ichildren.next(); - if (element.getName().equals(SEDMLTags.NOTES)) { - Notes n = getNotes(element); - if (n != null) - sedbase.addNote(n); - } else if (element.getName().equals(SEDMLTags.ANNOTATION)) { - anns.add(element); - - } - } - for (Element ann: anns){ - sedbase.addAnnotation(getAnnotation(ann)); - } - sedbase.setMetaId(xmlElement.getAttributeValue(SEDMLTags.META_ID_ATTR_NAME)); - } - - Simulation getSimulation(Element simElement) { - Simulation s = null; - List children = simElement.getChildren(); - Algorithm alg = null; - for (Element el : children) { - if (el.getName().equals(SEDMLTags.ALGORITHM_TAG)) { - alg = getAlgorithm(el); - } - } - if (simElement.getName().equals(SEDMLTags.SIM_UTC)) { - int numberOf; - if (simElement.getAttributeValue(SEDMLTags.UTCA_POINTS_NUM) != null) { - // deprecated in version 4 - numberOf = Integer.parseInt(simElement.getAttributeValue(SEDMLTags.UTCA_POINTS_NUM)); - } else { - numberOf = Integer.parseInt(simElement.getAttributeValue(SEDMLTags.UTCA_STEPS_NUM)); - } - s = new UniformTimeCourse(simElement.getAttributeValue(SEDMLTags.SIM_ATTR_ID), - simElement.getAttributeValue(SEDMLTags.SIM_ATTR_NAME), - Double.parseDouble(simElement.getAttributeValue(SEDMLTags.UTCA_INIT_T)), - Double.parseDouble(simElement.getAttributeValue(SEDMLTags.UTCA_OUT_START_T)), - Double.parseDouble(simElement.getAttributeValue(SEDMLTags.UTCA_OUT_END_T)), - numberOf, alg); - } else if(simElement.getName().equals(SEDMLTags.SIM_OS)) { - s = new OneStep(simElement.getAttributeValue(SEDMLTags.SIM_ATTR_ID), - simElement.getAttributeValue(SEDMLTags.SIM_ATTR_NAME), alg, - Double.parseDouble(simElement.getAttributeValue(SEDMLTags.OS_STEP))); - } else if(simElement.getName().equals(SEDMLTags.SIM_SS)) { - s = new SteadyState(simElement.getAttributeValue(SEDMLTags.SIM_ATTR_ID), - simElement.getAttributeValue(SEDMLTags.SIM_ATTR_NAME), alg); - } else if(simElement.getName().equals(SEDMLTags.SIM_ANY)) { // we don't know what "any" means, we do SteadyState as it's the simplest - s = new SteadyState(simElement.getAttributeValue(SEDMLTags.SIM_ATTR_ID), - simElement.getAttributeValue(SEDMLTags.SIM_ATTR_NAME), alg); - } - addNotesAndAnnotation(s, simElement); - - return s; - } - - Algorithm getAlgorithm(Element algorithmElement) { - Algorithm alg = new Algorithm(algorithmElement.getAttributeValue(SEDMLTags.ALGORITHM_ATTR_KISAOID)); - addNotesAndAnnotation(alg, algorithmElement); - - List children = algorithmElement.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - if (eChild.getName().equals(SEDMLTags.ALGORITHM_PARAMETER_LIST)) { - addAlgorithmParameters(alg, eChild); - } else { - log.warn("Unexpected " + eChild); - } - } - return alg; - } - private void addAlgorithmParameters(Algorithm a, Element element) { - String childName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - childName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.ALGORITHM_PARAMETER_TAG)) { - AlgorithmParameter ap = new AlgorithmParameter( - eChild.getAttributeValue(SEDMLTags.ALGORITHM_PARAMETER_KISAOID), - eChild.getAttributeValue(SEDMLTags.ALGORITHM_PARAMETER_VALUE)); - a.addAlgorithmParameter(ap); - } else { - log.warn("Unexpected " + eChild); - } - } - } - - Task getTask(Element taskElement) { - Task t = null; - t = new Task( - taskElement.getAttributeValue(SEDMLTags.TASK_ATTR_ID), // Task - // Attribute - // "id" - taskElement.getAttributeValue(SEDMLTags.TASK_ATTR_NAME), - taskElement.getAttributeValue(SEDMLTags.TASK_ATTR_MODELREF), - taskElement.getAttributeValue(SEDMLTags.TASK_ATTR_SIMREF)); - // notes and annotations - addNotesAndAnnotation(t, taskElement); - - return t; - } - RepeatedTask getRepeatedTask(Element taskElement) - throws DataConversionException { - RepeatedTask t = null; - String resetModel = taskElement.getAttributeValue(SEDMLTags.REPEATED_TASK_RESET_MODEL); - boolean bResetModel = resetModel == null || resetModel.equals("true") ? true : false; - t = new RepeatedTask( - taskElement.getAttributeValue(SEDMLTags.TASK_ATTR_ID), - taskElement.getAttributeValue(SEDMLTags.TASK_ATTR_NAME), - bResetModel, - taskElement.getAttributeValue(SEDMLTags.REPEATED_TASK_ATTR_RANGE)); - - addNotesAndAnnotation(t, taskElement); // notes and annotations - - List children = taskElement.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - String repeatedTaskChildName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.REPEATED_TASK_RANGES_LIST)) { - addRanges(t, eChild); - } else if(eChild.getName().equals(SEDMLTags.REPEATED_TASK_CHANGES_LIST)) { - addChanges(t, eChild); - } else if(eChild.getName().equals(SEDMLTags.REPEATED_TASK_SUBTASKS_LIST)) { - addSubTasks(t, eChild); - } else { - log.warn("Unexpected " + eChild); - } - } - return t; - } - - private void addSubTasks(RepeatedTask t, Element element) { - SubTask s = null; - String subTaskChildName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while(iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - subTaskChildName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.SUBTASK_TAG)) { - String order = eChild.getAttributeValue(SEDMLTags.SUBTASK_ATTR_ORDER); - String taskId = eChild.getAttributeValue(SEDMLTags.SUBTASK_ATTR_TASK); - s = new SubTask(order, taskId); - addDependTasks(s, eChild); - t.addSubtask(s); - } else { - log.warn("Unexpected " + eChild); - } - log.debug("s " + s.toString()); - } - } - - private void addDependTasks(SubTask t, Element element) { - - String subTaskChildName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while(iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - subTaskChildName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.DEPENDENT_TASK_SUBTASKS_LIST)) { - addDependTask(t, eChild); - } else { - log.warn("Unexpected " + eChild); - } - } - } - - private void addDependTask(SubTask t, Element element) { - SubTask s = null; - String subTaskChildName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while(iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - subTaskChildName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.DEPENDENTTASK_TAG)) { - String taskId = eChild.getAttributeValue(SEDMLTags.SUBTASK_ATTR_TASK); - s = new SubTask(taskId); - t.addDependentTask(s); - } else { - log.warn("Unexpected " + eChild); - } - log.debug("s " + s.toString()); - } - } - - private void addRanges(RepeatedTask task, Element element) - throws DataConversionException { - Range range = null; - String childName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - childName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.VECTOR_RANGE_TAG)) { - String id = eChild.getAttributeValue(SEDMLTags.RANGE_ATTR_ID); - range = new VectorRange(id); - addVectorRangeValues(range, eChild); - task.addRange(range); - } else if(eChild.getName().equals(SEDMLTags.UNIFORM_RANGE_TAG)) { - String id = eChild.getAttributeValue(SEDMLTags.RANGE_ATTR_ID); - Double start = Double.parseDouble(eChild.getAttributeValue(SEDMLTags.UNIFORM_RANGE_ATTR_START)); - Double end = Double.parseDouble(eChild.getAttributeValue(SEDMLTags.UNIFORM_RANGE_ATTR_END)); - int numberOfPoints; - if (eChild.getAttributeValue(SEDMLTags.UNIFORM_RANGE_ATTR_NUMP) != null) { - numberOfPoints = Integer.parseInt(eChild.getAttributeValue(SEDMLTags.UNIFORM_RANGE_ATTR_NUMP)); - } else { - numberOfPoints = Integer.parseInt(eChild.getAttributeValue(SEDMLTags.UNIFORM_RANGE_ATTR_NUMS)); - } - String type = eChild.getAttributeValue(SEDMLTags.UNIFORM_RANGE_ATTR_TYPE); - range = new UniformRange(id, start, end, numberOfPoints, UniformType.fromString(type)); - task.addRange(range); - } else if(eChild.getName().equals(SEDMLTags.FUNCTIONAL_RANGE_TAG)) { - String id = eChild.getAttributeValue(SEDMLTags.RANGE_ATTR_ID); - String index = eChild.getAttributeValue(SEDMLTags.FUNCTIONAL_RANGE_INDEX); - range = new FunctionalRange(id, index); - addFunctionalRangeLists(range, eChild); - task.addRange(range); - } else { - log.warn("Unexpected range type {}", eChild); - } - log.debug("range is {}", range); - } - } - - private void addFunctionalRangeLists(Range r, Element element) - throws DataConversionException { - String childName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - childName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.FUNCTIONAL_RANGE_VAR_LIST)) { - addFunctionalRangeVariable(r, eChild); - } else if(eChild.getName().equals(SEDMLTags.FUNCTIONAL_RANGE_PAR_LIST)) { - addFunctionalRangeParameter(r, eChild); - } else if(eChild.getName().equals(SEDMLTags.FUNCTION_MATH_TAG)) { - ASTNode math = (ASTRootNode) new MathMLReader().parseMathML(eChild); - log.debug("r " + math.toString()); - ((FunctionalRange) r).setMath(math); - } else { - log.warn("Unexpected " + eChild); - } - } - } - - private void addFunctionalRangeVariable(Range r, Element element) - throws DataConversionException { - String childName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - childName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.DATAGEN_ATTR_VARIABLE)) { - Variable v = createVariable(eChild, true); - log.debug("r Functional Range " + v.toString()); - ((FunctionalRange) r).addVariable(v); - } else { - log.warn("Unexpected " + eChild); - } - } - } - private void addFunctionalRangeParameter(Range r, Element element) - throws DataConversionException { - String childName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - childName = eChild.getName(); - if(eChild.getName().equals(SEDMLTags.DATAGEN_ATTR_PARAMETER)) { - Parameter p = createParameter(eChild); - log.debug("r Functional Range " + p.toString()); - ((FunctionalRange) r).addParameter(p); - } else { - log.warn("Unexpected " + eChild); - } - } - } - - private void addVectorRangeValues(Range r, Element element) { - String childName = null; - List children = element.getChildren(); - Iterator iChildren = children.iterator(); - while (iChildren.hasNext()) { - Element eChild = (Element) iChildren.next(); - childName = eChild.getName(); - if (eChild.getName().equals(SEDMLTags.VECTOR_RANGE_VALUE_TAG)) { - Double value = Double.parseDouble(eChild.getText()); - ((VectorRange) r).addValue(value); - log.debug("r Vector Range: addValue(" + value + ")"); - } else { - log.warn("Unexpected " + eChild); - } - } - } - - DataGenerator getDataGenerator(Element dataGenElement) - throws DataConversionException { - DataGenerator d = null; - ASTNode math = null; - d = new DataGenerator(dataGenElement - .getAttributeValue(SEDMLTags.DATAGEN_ATTR_ID), dataGenElement - .getAttributeValue(SEDMLTags.DATAGEN_ATTR_NAME)); - // eDataGenerator.getAttributeValue(MiaseMLTags.DGA_MATH)); - List lDataGeneratorChildren = dataGenElement.getChildren(); - Iterator iDataGeneratorChildren = lDataGeneratorChildren - .iterator(); - while (iDataGeneratorChildren.hasNext()) { - Element eDataGeneratorChild = (Element) iDataGeneratorChildren.next(); - - if (eDataGeneratorChild.getName().equals( - SEDMLTags.DATAGEN_ATTR_VARS_LIST)) { - List lVariables = eDataGeneratorChild.getChildren(); - Iterator iVariables = lVariables.iterator(); - while (iVariables.hasNext()) { - Element eVariable = (Element) iVariables.next(); - - if (eVariable.getName().equals( - SEDMLTags.DATAGEN_ATTR_VARIABLE)) { - // task - d.addVariable(createVariable(eVariable, false)); - } - } - } else if (eDataGeneratorChild.getName().equals( - SEDMLTags.DATAGEN_ATTR_PARAMS_LIST)) { - List lParameters = eDataGeneratorChild.getChildren(); - Iterator iParameters = lParameters.iterator(); - while (iParameters.hasNext()) { - Element eParameter = (Element) iParameters.next(); - if (eParameter.getName().equals( - SEDMLTags.DATAGEN_ATTR_PARAMETER)) { - - d.addParameter(createParameter(eParameter)); - } - } - } else if (eDataGeneratorChild.getName().equals( - SEDMLTags.DATAGEN_ATTR_MATH)) { - math = (ASTRootNode) new MathMLReader() - .parseMathML(eDataGeneratorChild); - - } - } - d.setMathML(math); - // notes and annotations - addNotesAndAnnotation(d, dataGenElement); - - return d; - } - - Parameter createParameter(Element eParameter) - throws DataConversionException { - Parameter p = new Parameter(eParameter - .getAttributeValue(SEDMLTags.PARAMETER_ID), eParameter - .getAttributeValue(SEDMLTags.PARAMETER_NAME), eParameter - .getAttribute(SEDMLTags.PARAMETER_VALUE).getDoubleValue()); - addNotesAndAnnotation(p, eParameter); - return p; - } - - Variable createVariable(Element eVariable, boolean isModel) { - if (eVariable.getAttribute(SEDMLTags.VARIABLE_SYMBOL) == null) { - Variable v = new Variable( - eVariable.getAttributeValue(SEDMLTags.VARIABLE_ID), - eVariable.getAttributeValue(SEDMLTags.VARIABLE_NAME), - isModel ? eVariable - .getAttributeValue(SEDMLTags.VARIABLE_MODEL) - : eVariable - .getAttributeValue(SEDMLTags.VARIABLE_TASK), - eVariable.getAttributeValue(SEDMLTags.VARIABLE_TARGET)); - addNotesAndAnnotation(v, eVariable); - return v; - } else { - Variable v = new Variable( - eVariable.getAttributeValue(SEDMLTags.VARIABLE_ID), - eVariable.getAttributeValue(SEDMLTags.VARIABLE_NAME), - isModel ? eVariable - .getAttributeValue(SEDMLTags.VARIABLE_MODEL) - : eVariable - .getAttributeValue(SEDMLTags.VARIABLE_TASK), - VariableSymbol.getVariableSymbolFor(eVariable - .getAttributeValue(SEDMLTags.VARIABLE_SYMBOL))); - addNotesAndAnnotation(v, eVariable); - return v; - } - } - - Output getOutput(Element outputElement) { - if (outputElement.getName().equals(SEDMLTags.OUTPUT_P2D)) { - Plot2D p2d = new Plot2D(outputElement - .getAttributeValue(SEDMLTags.OUTPUT_ID), outputElement - .getAttributeValue(SEDMLTags.OUTPUT_NAME)); - List lPlot2DChildren = outputElement.getChildren(); - Iterator iPlot2DChildren = lPlot2DChildren.iterator(); - while (iPlot2DChildren.hasNext()) { - Element ePlot2DChild = (Element) iPlot2DChildren.next(); - - // "listOfCurves" - if (ePlot2DChild.getName().equals(SEDMLTags.OUTPUT_CURVES_LIST)) { - List lCurves = ePlot2DChild.getChildren(); - Iterator iCurves = lCurves.iterator(); - while (iCurves.hasNext()) { - Element aCurve = (Element) iCurves.next(); - - if (aCurve.getName().equals(SEDMLTags.OUTPUT_CURVE)) { - Curve c = getCurve(aCurve); - p2d.addCurve(c); - } - } - } - } - // notes and annotations - addNotesAndAnnotation(p2d, outputElement); - - return p2d; - } else if (outputElement.getName().equals(SEDMLTags.OUTPUT_P3D)) { // ex: - // "plot3D" - Plot3D p3d = new Plot3D(outputElement - .getAttributeValue(SEDMLTags.OUTPUT_ID), outputElement - .getAttributeValue(SEDMLTags.OUTPUT_NAME)); - List lPlot3DChildren = outputElement.getChildren(); - Iterator iPlot3DChildren = lPlot3DChildren.iterator(); - while (iPlot3DChildren.hasNext()) { - Element ePlot3DChild = (Element) iPlot3DChildren.next(); - - // "listOfSurfaces" - if (ePlot3DChild.getName().equals( - SEDMLTags.OUTPUT_SURFACES_LIST)) { - List lSurfaces = ePlot3DChild.getChildren(); - Iterator iSurfaces = lSurfaces.iterator(); - while (iSurfaces.hasNext()) { - Element aSurface = (Element) iSurfaces.next(); - - if (aSurface.getName().equals(SEDMLTags.OUTPUT_SURFACE)) { - Surface s = getSurface(aSurface); - p3d.addSurface(s); - } - } - } - } - // notes and annotations - addNotesAndAnnotation(p3d, outputElement); - - return p3d; - } else if (outputElement.getName().equals(SEDMLTags.OUTPUT_REPORT)) { // ex: - // "report" - Report r = new Report(outputElement - .getAttributeValue(SEDMLTags.OUTPUT_ID), outputElement - .getAttributeValue(SEDMLTags.OUTPUT_NAME)); - List lReportChildren = outputElement.getChildren(); - Iterator iReportChildren = lReportChildren.iterator(); - while (iReportChildren.hasNext()) { - Element eReportDChild = (Element) iReportChildren.next(); - - // "listOfDataSets" - if (eReportDChild.getName().equals( - SEDMLTags.OUTPUT_DATASETS_LIST)) { - List lDataSets = eReportDChild.getChildren(); - Iterator iDataSets = lDataSets.iterator(); - while (iDataSets.hasNext()) { - Element aDataSet = (Element) iDataSets.next(); - - if (aDataSet.getName().equals(SEDMLTags.OUTPUT_DATASET)) { - DataSet ds = getDataset(aDataSet); - r.addDataSet(ds); - } - } - } - } - // notes and annotations - addNotesAndAnnotation(r, outputElement); - - return r; - } - return null; - } - - DataSet getDataset(Element aDataSet) { - DataSet ds = new DataSet(aDataSet - .getAttributeValue(SEDMLTags.OUTPUT_ID), aDataSet - .getAttributeValue(SEDMLTags.OUTPUT_NAME), aDataSet - .getAttributeValue(SEDMLTags.OUTPUT_DATASET_LABEL), aDataSet - .getAttributeValue(SEDMLTags.OUTPUT_DATA_REFERENCE)); - addNotesAndAnnotation(ds, aDataSet); - return ds; - } - - Surface getSurface(Element aSurface) { - Surface s = new Surface( - aSurface.getAttributeValue(SEDMLTags.OUTPUT_ID), aSurface - .getAttributeValue(SEDMLTags.OUTPUT_NAME), Boolean - .parseBoolean(aSurface - .getAttributeValue(SEDMLTags.OUTPUT_LOG_X)), - Boolean.parseBoolean(aSurface - .getAttributeValue(SEDMLTags.OUTPUT_LOG_Y)), Boolean - .parseBoolean(aSurface - .getAttributeValue(SEDMLTags.OUTPUT_LOG_Z)), - aSurface.getAttributeValue(SEDMLTags.OUTPUT_DATA_REFERENCE_X), - aSurface.getAttributeValue(SEDMLTags.OUTPUT_DATA_REFERENCE_Y), - aSurface.getAttributeValue(SEDMLTags.OUTPUT_DATA_REFERENCE_Z)); - addNotesAndAnnotation(s, aSurface); - return s; - } - - Curve getCurve(Element aCurve) { - - Curve c = new Curve(aCurve.getAttributeValue(SEDMLTags.OUTPUT_ID), - aCurve.getAttributeValue(SEDMLTags.OUTPUT_NAME), Boolean - .parseBoolean(aCurve - .getAttributeValue(SEDMLTags.OUTPUT_LOG_X)), - Boolean.parseBoolean(aCurve - .getAttributeValue(SEDMLTags.OUTPUT_LOG_Y)), aCurve - .getAttributeValue(SEDMLTags.OUTPUT_DATA_REFERENCE_X), - aCurve.getAttributeValue(SEDMLTags.OUTPUT_DATA_REFERENCE_Y)); - addNotesAndAnnotation(c, aCurve); - return c; - - } - - List getNewXML(Element newXMLElement) { - List rc = new ArrayList(); - int numEl = newXMLElement.getChildren().size(); - for (int i = 0; i < numEl; i++) { - rc.add((Element) ((Element) newXMLElement.getChildren().get(0)) - .detach()); - } - return rc; - } - - Annotation getAnnotation(Element annotationElement) { - return new Annotation((Element) ((Element)annotationElement.getChildren().get(0)).detach()); - } - - Notes getNotes(Element noteElement) { - if (noteElement.getChildren().size() > 0) { - return new Notes((Element) ((Element) noteElement.getChildren() - .get(0)).detach()); - } - return null; - } - - /* - * returns a SedML model given an Element of xml for a complete model non - - * api method - */ - public SedML getSedDocument(Element sedRoot) throws XMLException { - SedML sedDoc = null; - SymbolRegistry.getInstance().addSymbolFactory(new SedMLSymbolFactory()); - try { - Namespace sedNS = sedRoot.getNamespace(); - String verStr = sedRoot.getAttributeValue(SEDMLTags.VERSION_TAG); - String lvlStr = sedRoot.getAttributeValue(SEDMLTags.LEVEL_TAG); - if (verStr != null && lvlStr != null) { - int sedVersion = Integer.parseInt(verStr); - int sedLevel = Integer.parseInt(lvlStr); - sedDoc = new SedML(sedLevel, sedVersion, sedNS); - } else { - sedDoc = new SedML(sedNS); - } - - // Get additional namespaces if mentioned : mathml, sbml, etc. - List additionalNamespaces = sedRoot.getAdditionalNamespaces(); - sedDoc.setAdditionalNamespaces(additionalNamespaces); - - // notes and annotations - addNotesAndAnnotation(sedDoc, sedRoot); - Iterator elementsIter = null; - // models - Element el = sedRoot.getChild(SEDMLTags.MODELS, sedNS); - if (el != null) { - List elementsList = el.getChildren(); - - elementsIter = elementsList.iterator(); - - while (elementsIter.hasNext()) { - Element modelElement = elementsIter.next(); - if (modelElement.getName().equals(SEDMLTags.MODEL_TAG)) { - sedDoc.addModel(getModel(modelElement)); - } - } - } - - // simulations - el = sedRoot.getChild(SEDMLTags.SIMS, sedNS); - if (el != null) { - List elementsList = el.getChildren(); - elementsIter = elementsList.iterator(); - while (elementsIter.hasNext()) { - Element simElement = elementsIter.next(); - sedDoc.addSimulation(getSimulation(simElement)); - } - } - - el = sedRoot.getChild(SEDMLTags.TASKS, sedNS); - if (el != null) { - List elementsList = el.getChildren(); - elementsIter = elementsList.iterator(); - while (elementsIter.hasNext()) { - Element taskElement = elementsIter.next(); - if (taskElement.getName().equals(SEDMLTags.TASK_TAG)) { - sedDoc.addTask(getTask(taskElement)); - } else if(taskElement.getName().equals(SEDMLTags.REPEATED_TASK_TAG)) { - sedDoc.addTask(getRepeatedTask(taskElement)); - } - } - } - - el = sedRoot.getChild(SEDMLTags.DATAGENERATORS, sedNS); - if (el != null) { - List elementsList = el.getChildren(); - elementsIter = elementsList.iterator(); - while (elementsIter.hasNext()) { - Element dataGenElement = elementsIter.next(); - if (dataGenElement.getName().equals(SEDMLTags.DATAGENERATOR_TAG)) { - sedDoc.addDataGenerator(getDataGenerator(dataGenElement)); - } - } - } - - el = sedRoot.getChild(SEDMLTags.OUTPUTS, sedNS); - if (el != null) { - List elementsList = el.getChildren(); - elementsIter = elementsList.iterator(); - while (elementsIter.hasNext()) { - Element outputElement = elementsIter.next(); - if (outputElement.getName().equals(SEDMLTags.OUTPUT_P2D) - || outputElement.getName().equals(SEDMLTags.OUTPUT_P3D) - || outputElement.getName().equals(SEDMLTags.OUTPUT_REPORT)) { - sedDoc.addOutput(getOutput(outputElement)); - } - } - } - - } catch (Exception e) { - throw new XMLException("Error loading sed-ml document : " - + e.getMessage(), e); - // return sedDoc; - } - return sedDoc; - } - - public static SedML readFile (File file) throws JDOMException, IOException, XMLException{ - SAXBuilder builder = new SAXBuilder(); - Document doc = builder.build(file); - Element sedRoot = doc.getRootElement(); - try { - SEDMLElementFactory.getInstance().setStrictCreation(false); - SEDMLReader reader = new SEDMLReader(); - SedML sedML = reader.getSedDocument(sedRoot); - return sedML; - } finally { - SEDMLElementFactory.getInstance().setStrictCreation(true); - } - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDMLUtils.java b/vcell-core/src/main/java/org/jlibsedml/SEDMLUtils.java index 814e7c42f1..3c1dda44a7 100644 --- a/vcell-core/src/main/java/org/jlibsedml/SEDMLUtils.java +++ b/vcell-core/src/main/java/org/jlibsedml/SEDMLUtils.java @@ -79,15 +79,15 @@ private static String parseXPath(String xPathExpr, File xmlFile) { * @param fileName * @return */ - static SedML readSedDocument(String fileName) { + static SedMLDataContainer readSedDocument(String fileName) { try { SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(new File(fileName)); Element sedRoot = doc.getRootElement(); - SEDMLReader reader = new SEDMLReader(); + SedMLReader reader = new SedMLReader(); - SedML sedDoc = reader.getSedDocument(sedRoot); + SedMLDataContainer sedDoc = reader.getSedDocument(sedRoot); return sedDoc; } catch (Exception e) { throw new RuntimeException("Could not create SedMLDocument from file '" + fileName + "'", e); diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDMLVisitor.java b/vcell-core/src/main/java/org/jlibsedml/SEDMLVisitor.java index 887b0c7588..05421003d1 100644 --- a/vcell-core/src/main/java/org/jlibsedml/SEDMLVisitor.java +++ b/vcell-core/src/main/java/org/jlibsedml/SEDMLVisitor.java @@ -1,52 +1,26 @@ package org.jlibsedml; +import org.jlibsedml.components.Notes; +import org.jlibsedml.components.Parameter; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.Variable; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.components.algorithm.AlgorithmParameter; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.model.*; +import org.jlibsedml.components.output.Curve; +import org.jlibsedml.components.output.DataSet; +import org.jlibsedml.components.output.Output; +import org.jlibsedml.components.output.Surface; +import org.jlibsedml.components.simulation.Simulation; +import org.jlibsedml.components.task.*; + /** * Abstract class for any visitor to extend. * @author radams * */ public abstract class SEDMLVisitor { - - - public abstract boolean visit (SedML sedml); - - public abstract boolean visit (Simulation sim); - - public abstract boolean visit (Model model); - - public abstract boolean visit (Task task); - public abstract boolean visit (RepeatedTask repeatedTask); - - public abstract boolean visit (AddXML change); - - public abstract boolean visit (RemoveXML change); - - public abstract boolean visit (ChangeXML change); - - public abstract boolean visit (ChangeAttribute change); - - public abstract boolean visit (ComputeChange change); - - public abstract boolean visit(SetValue setValue) ; - - public abstract boolean visit (DataGenerator dg); - - public abstract boolean visit (Variable var); - - public abstract boolean visit (Parameter model); - - public abstract boolean visit (Output output); - - public abstract boolean visit(Algorithm algorithm); - - public abstract boolean visit(Curve curve) ; - - public abstract boolean visit(DataSet dataSet) ; - - public abstract boolean visit(Surface surface) ; - - public abstract boolean visit(UniformRange uniformRange) ; - public abstract boolean visit(VectorRange vectorRange) ; - public abstract boolean visit(FunctionalRange functionalRange) ; + public abstract boolean visit(SedBase sedBase); } diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDMLWriter.java b/vcell-core/src/main/java/org/jlibsedml/SEDMLWriter.java deleted file mode 100644 index fcab0f8c67..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/SEDMLWriter.java +++ /dev/null @@ -1,848 +0,0 @@ -package org.jlibsedml; - -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.jdom2.Element; -import org.jdom2.Namespace; -import org.jmathml.ASTToXMLElementVisitor; - -class SEDMLWriter { - - enum VariableType { - COMPUTE_CHANGE, DATA_GENERATOR - }; - - Element getXML(SedML sedmlObject) { - Element sedDocElement = new Element(SEDMLTags.ROOT_NODE_TAG); - sedDocElement.setAttribute(SEDMLTags.LEVEL_TAG, - "" + sedmlObject.getLevel()); - sedDocElement.setAttribute(SEDMLTags.VERSION_TAG, - "" + sedmlObject.getVersion()); - - // sedDocElement.setNamespace(sedmlDoc.getNamespace()); - List additionalNSs = sedmlObject.getAdditionalNamespaces(); - for (int i = 0; i < additionalNSs.size(); i++) { - sedDocElement.addNamespaceDeclaration((Namespace) additionalNSs - .get(i)); - } - - List tempArrayList; - - addNotesAndAnnotation(sedmlObject, sedDocElement); - - // add 'simulation' elements from sedDocument - tempArrayList = sedmlObject.getSimulations(); - Element listOfSimsElement = new Element(SEDMLTags.SIMS); // create list - // of - // simulations - // element - for (int i = 0; i < tempArrayList.size(); i++) { - listOfSimsElement.addContent(getXML((Simulation) tempArrayList - .get(i))); - } - sedDocElement.addContent(listOfSimsElement); - - // add 'model' elements from sedDocument - tempArrayList = sedmlObject.getModels(); - Element listOfModelsElement = new Element(SEDMLTags.MODELS); // create - // list of - // models - // element - for (int i = 0; i < tempArrayList.size(); i++) { - listOfModelsElement - .addContent(getXML((Model) tempArrayList.get(i))); - } - sedDocElement.addContent(listOfModelsElement); - - // add 'tasks' elements from sedDocument - tempArrayList = sedmlObject.getTasks(); - Element listOfTasksElement = new Element(SEDMLTags.TASKS); // create - // list of - // tasks - // element - for (int i = 0; i < tempArrayList.size(); i++) { - listOfTasksElement.addContent(getXML((AbstractTask) tempArrayList - .get(i))); - } - sedDocElement.addContent(listOfTasksElement); - - // add 'dataGenerators' elements from sedDocument - tempArrayList = sedmlObject.getDataGenerators(); - Element listOfDataGeneratorElement = new Element( - SEDMLTags.DATAGENERATORS); // create list of data generator - // element - for (int i = 0; i < tempArrayList.size(); i++) { - listOfDataGeneratorElement - .addContent(getXML((DataGenerator) tempArrayList.get(i))); - } - sedDocElement.addContent(listOfDataGeneratorElement); - - // add 'outputs' elements from sedDocument - tempArrayList = sedmlObject.getOutputs(); - Element listOfOutputsElement = new Element(SEDMLTags.OUTPUTS); // create - // list - // of - // outputs - // element - for (int i = 0; i < tempArrayList.size(); i++) { - listOfOutputsElement.addContent(getXML((Output) tempArrayList - .get(i))); - } - sedDocElement.addContent(listOfOutputsElement); - - // set sedML namespace for sedMLElement - sedDocElement = setDefaultNamespace(sedDocElement, - sedmlObject.getNamespace()); - - return sedDocElement; - } - - // ================= Models - Element getXML(Model sedmlModel) { - Element node = new Element(SEDMLTags.MODEL_TAG); - addNotesAndAnnotation(sedmlModel, node); - String s = null; - // Add Attributes to - s = sedmlModel.getId(); - if (s != null) - node.setAttribute(SEDMLTags.MODEL_ATTR_ID, sedmlModel.getId()); // insert - // 'id' - // attribute - s = sedmlModel.getName(); - if (s != null) - node.setAttribute(SEDMLTags.MODEL_ATTR_NAME, s); // insert 'name' - // attribute - s = sedmlModel.getLanguage(); - if (s != null) - node.setAttribute(SEDMLTags.MODEL_ATTR_LANGUAGE, s); // insert - // 'type' - // attribute - s = sedmlModel.getSourcePathOrURIString(); - if (s != null) - node.setAttribute(SEDMLTags.MODEL_ATTR_SOURCE, s); // insert - // 'source' - // attribute - - if (sedmlModel.getListOfChanges() != null - && sedmlModel.getListOfChanges().size() > 0) { - node.addContent(getXML(sedmlModel.getListOfChanges())); - } - - return node; - } - - org.jdom2.Element getXML(List sedModelChanges) { - Element list = new Element(SEDMLTags.CHANGES); - for (int i = 0; i < sedModelChanges.size(); i++) { - list.addContent(getXML((Change) sedModelChanges.get(i))); - } - return list; - } - - org.jdom2.Element getXML(Change sedmlChange) { - Element node = null; - String s = null; - // Add Changes to list of changes - - if (sedmlChange.getChangeKind().equals(SEDMLTags.CHANGE_ATTRIBUTE_KIND)) { - node = new Element(SEDMLTags.CHANGE_ATTRIBUTE);// various attributes - // depending on kind - addNotesAndAnnotation(sedmlChange, node); - s = ((ChangeAttribute) sedmlChange).getNewValue(); - if (s != null) - node.setAttribute(SEDMLTags.CHANGE_ATTR_NEWVALUE, s); - - } else if (sedmlChange.getChangeKind() - .equals(SEDMLTags.CHANGE_XML_KIND)) { - node = new Element(SEDMLTags.CHANGE_XML); - addNotesAndAnnotation(sedmlChange, node); - Element newxml = new Element(SEDMLTags.NEW_XML); - node.addContent(newxml); - for (Element el : ((ChangeXML) sedmlChange).getNewXML().getXml()) { - newxml.addContent(el.detach()); - } - - } else if (sedmlChange.getChangeKind().equals(SEDMLTags.ADD_XML_KIND)) { - node = new Element(SEDMLTags.ADD_XML); - addNotesAndAnnotation(sedmlChange, node); - Element newxml = new Element(SEDMLTags.NEW_XML); - node.addContent(newxml); - for (Element el : ((AddXML) sedmlChange).getNewXML().getXml()) { - newxml.addContent(el.detach()); - } - - } else if (sedmlChange.getChangeKind() - .equals(SEDMLTags.REMOVE_XML_KIND)) { - node = new Element(SEDMLTags.REMOVE_XML); - addNotesAndAnnotation(sedmlChange, node); - - } else if (sedmlChange.getChangeKind().equals(SEDMLTags.SET_VALUE_KIND)) { // SetValue - node = new Element(SEDMLTags.SET_VALUE); - SetValue c = (SetValue) sedmlChange; - addNotesAndAnnotation(c, node); - s = c.getRangeReference(); - if (s != null) - node.setAttribute(SEDMLTags.SET_VALUE_ATTR_RANGE_REF, s); - s = c.getModelReference(); - if (s != null) - node.setAttribute(SEDMLTags.SET_VALUE_ATTR_MODEL_REF, s); - List vars = c.getListOfVariables(); - if(vars.size() > 0) { - Element varList = new Element(SEDMLTags.COMPUTE_CHANGE_VARS); - node.addContent(varList); - for (Variable var : vars) { - varList.addContent(getXML(var, VariableType.COMPUTE_CHANGE)); - } - } - List params = c.getListOfParameters(); - if(params.size() > 0) { - Element paramList = new Element(SEDMLTags.COMPUTE_CHANGE_PARAMS); - node.addContent(paramList); - for (Parameter param : params) { - paramList.addContent(getXML(param)); - } - } - ASTToXMLElementVisitor astElementVisitor = new ASTToXMLElementVisitor(); - c.getMath().accept(astElementVisitor); - node.addContent(astElementVisitor.getElement()); - - } else if (sedmlChange.getChangeKind().equals( - SEDMLTags.COMPUTE_CHANGE_KIND)) { // ComputeChange - node = new Element(SEDMLTags.COMPUTE_CHANGE); - addNotesAndAnnotation(sedmlChange, node); - ComputeChange computeChange = (ComputeChange) sedmlChange; - if(!computeChange.getListOfVariables().isEmpty()) { - Element varList = new Element(SEDMLTags.COMPUTE_CHANGE_VARS); - node.addContent(varList); - List vars = computeChange.getListOfVariables(); - for (Variable var : vars) { - varList.addContent(getXML(var, VariableType.COMPUTE_CHANGE)); - } - } - if(!computeChange.getListOfParameters().isEmpty()) { - Element paramList = new Element(SEDMLTags.COMPUTE_CHANGE_PARAMS); - node.addContent(paramList); - List params = computeChange.getListOfParameters(); - for (Parameter param : params) { - paramList.addContent(getXML(param)); - } - } - ASTToXMLElementVisitor astElementVisitor = new ASTToXMLElementVisitor(); - computeChange.getMath().accept(astElementVisitor); - node.addContent(astElementVisitor.getElement()); - - } - node.setAttribute(SEDMLTags.CHANGE_ATTR_TARGET, sedmlChange - .getTargetXPath().getTargetAsString()); // insert 'target' - // attribute - return node; - } - - // ============= SetValue - - // =============== Simulations - org.jdom2.Element getXML(Simulation sedmlSim) { - Element node = null; - String s = null; - // Add simulations to list of simulations - if (sedmlSim.getSimulationKind().equals(SEDMLTags.SIMUL_UTC_KIND)) { // various - // attributes - // depending - // on - // kind - node = new Element(SEDMLTags.SIM_UTC); - addNotesAndAnnotation(sedmlSim, node); - s = sedmlSim.getId(); - if (s != null) - node.setAttribute(SEDMLTags.SIM_ATTR_ID, s); - s = sedmlSim.getName(); - if (s != null) - node.setAttribute(SEDMLTags.SIM_ATTR_NAME, s); - node.setAttribute(SEDMLTags.UTCA_INIT_T, Double - .toString(((UniformTimeCourse) sedmlSim).getInitialTime())); - node.setAttribute(SEDMLTags.UTCA_OUT_START_T, Double - .toString(((UniformTimeCourse) sedmlSim) - .getOutputStartTime())); - node.setAttribute(SEDMLTags.UTCA_OUT_END_T, - Double.toString(((UniformTimeCourse) sedmlSim) - .getOutputEndTime())); - node.setAttribute(SEDMLTags.UTCA_POINTS_NUM, Integer - .toString(((UniformTimeCourse) sedmlSim) - .getNumberOfSteps())); - } else if (sedmlSim.getSimulationKind().equals(SEDMLTags.SIMUL_OS_KIND)) { - node = new Element(SEDMLTags.SIM_OS); - addNotesAndAnnotation(sedmlSim, node); - s = sedmlSim.getId(); - if (s != null) - node.setAttribute(SEDMLTags.SIM_ATTR_ID, s); - s = sedmlSim.getName(); - if (s != null) - node.setAttribute(SEDMLTags.SIM_ATTR_NAME, s); - node.setAttribute(SEDMLTags.OS_STEP, - Double.toString(((OneStep) sedmlSim).getStep())); - } else if (sedmlSim.getSimulationKind().equals(SEDMLTags.SIMUL_SS_KIND)) { - node = new Element(SEDMLTags.SIM_SS); - addNotesAndAnnotation(sedmlSim, node); - s = sedmlSim.getId(); - if (s != null) - node.setAttribute(SEDMLTags.SIM_ATTR_ID, s); - s = sedmlSim.getName(); - if (s != null) - node.setAttribute(SEDMLTags.SIM_ATTR_NAME, s); - } else if (sedmlSim.getSimulationKind() - .equals(SEDMLTags.SIMUL_ANY_KIND)) { - node = new Element(SEDMLTags.SIM_ANY); - addNotesAndAnnotation(sedmlSim, node); - s = sedmlSim.getId(); - if (s != null) - node.setAttribute(SEDMLTags.SIM_ATTR_ID, s); - s = sedmlSim.getName(); - if (s != null) - node.setAttribute(SEDMLTags.SIM_ATTR_NAME, s); - } else { - throw new RuntimeException( - "Simulation must be uniformTimeCourse, oneStep or steadyState or any '" - + sedmlSim.getId()); - } - if (sedmlSim.getAlgorithm() != null) { - node.addContent(getXML(sedmlSim.getAlgorithm())); - } - return node; - } - - org.jdom2.Element getXML(Algorithm algorithm) { - String s = null; - Element node = new Element(SEDMLTags.ALGORITHM_TAG); - addNotesAndAnnotation(algorithm, node); - // Add Attributes to tasks - s = algorithm.getKisaoID(); - if (s != null) - node.setAttribute(SEDMLTags.ALGORITHM_ATTR_KISAOID, s); - - // list of algorithm parameters - List aps = algorithm.getListOfAlgorithmParameters(); - if (aps != null && aps.size() > 0) { - Element apList = new Element(SEDMLTags.ALGORITHM_PARAMETER_LIST); - for (int i = 0; i < aps.size(); i++) { - apList.addContent(getXML(aps.get(i))); - } - node.addContent(apList); - } - return node; - } - - org.jdom2.Element getXML(AlgorithmParameter ap) { - String s = null; - Element node = new Element(SEDMLTags.ALGORITHM_PARAMETER_TAG); - s = ap.getKisaoID(); - if (s != null) - node.setAttribute(SEDMLTags.ALGORITHM_PARAMETER_KISAOID, s); - s = ap.getValue(); - if (s != null) - node.setAttribute(SEDMLTags.ALGORITHM_PARAMETER_VALUE, s); - return node; - } - - // ============ Ranges - private Element getXML(Range range) { - String s = null; - if (range instanceof VectorRange) { - VectorRange vecRange = (VectorRange) range; - Element node = new Element(SEDMLTags.VECTOR_RANGE_TAG); - s = vecRange.getId(); - if (s != null) - node.setAttribute(SEDMLTags.RANGE_ATTR_ID, s); - for (int i = 0; i < vecRange.getNumElements(); i++) { - double n = vecRange.getElementAt(i); - Element v = new Element(SEDMLTags.VECTOR_RANGE_VALUE_TAG); - v.setText(Double.toString(n)); - node.addContent(v); - } - return node; - } else if (range instanceof UniformRange) { - UniformRange ur = (UniformRange) range; - Element node = new Element(SEDMLTags.UNIFORM_RANGE_TAG); - s = ur.getId(); - if (s != null) - node.setAttribute(SEDMLTags.RANGE_ATTR_ID, s); - s = Double.toString(((UniformRange) ur).getStart()); - if (s != null) - node.setAttribute(SEDMLTags.UNIFORM_RANGE_ATTR_START, s); - s = Double.toString(((UniformRange) ur).getEnd()); - if (s != null) - node.setAttribute(SEDMLTags.UNIFORM_RANGE_ATTR_END, s); - s = Integer.toString(((UniformRange) ur).getNumberOfPoints()); - if (s != null) - node.setAttribute(SEDMLTags.UNIFORM_RANGE_ATTR_NUMP, s); - s = ((UniformRange) ur).getType().getText(); - if (s != null) - node.setAttribute(SEDMLTags.UNIFORM_RANGE_ATTR_TYPE, s); - return node; - } else { // FunctionalRange - FunctionalRange fr = (FunctionalRange) range; - Element node = new Element(SEDMLTags.FUNCTIONAL_RANGE_TAG); - s = fr.getId(); - if (s != null) - node.setAttribute(SEDMLTags.RANGE_ATTR_ID, s); - s = fr.getRange(); - if (s != null) - node.setAttribute(SEDMLTags.FUNCTIONAL_RANGE_INDEX, s); - // list of variables - if(!fr.getVariables().isEmpty()) { - Element varList = new Element(SEDMLTags.FUNCTIONAL_RANGE_VAR_LIST); - node.addContent(varList); - Map vars = fr.getVariables(); - for (AbstractIdentifiableElement var : vars.values()) { - if (var instanceof Variable) { - varList.addContent(getXML((Variable) var, - VariableType.COMPUTE_CHANGE)); - } else { - throw new RuntimeException("Entity must be a Variable " - + var); - } - } - } - if(!fr.getParameters().isEmpty()) { - Element parList = new Element(SEDMLTags.FUNCTIONAL_RANGE_PAR_LIST); - node.addContent(parList); - Map pars = fr.getParameters(); - for (AbstractIdentifiableElement par : pars.values()) { - if (par instanceof Parameter) { - parList.addContent(getXML((Parameter) par)); - } else { - throw new RuntimeException("Entity must be a Parameter " - + par); - } - } - } - if (fr.getMath() != null) { - try { - ASTToXMLElementVisitor astElementVisitor = new ASTToXMLElementVisitor(); - fr.getMath().accept(astElementVisitor); - node.addContent(astElementVisitor.getElement()); // insert - // 'math' - // attribute - } catch (Exception e) { - throw new RuntimeException( - "Unable to process mathML for functional range '" - + fr.getId() + "' : " + e.getMessage(), e); - } - } - return node; - } - } - - // ============== SubTasks - private Element getXML(SubTask t) { - Element node = new Element(SEDMLTags.SUBTASK_TAG); - String s = null; - s = t.getOrder(); - if (s != null) - node.setAttribute(SEDMLTags.SUBTASK_ATTR_ORDER, s); - s = t.getTaskId(); - if (s != null) - node.setAttribute(SEDMLTags.SUBTASK_ATTR_TASK, s); - // Add list of dependent tasks - Map dependentTasks = ((SubTask) t).getDependentTasks(); - if (dependentTasks != null && !dependentTasks.isEmpty()) { - Element subTasksListElement = new Element( - SEDMLTags.DEPENDENT_TASK_SUBTASKS_LIST); - for (SubTask st : dependentTasks.values()) { - // we avoid recursion by NOT calling here - // subTasksListElement.addContent(getXML(st)) - // otherwise we might show dependent tasks of dependent tasks - Element dt = new Element(SEDMLTags.DEPENDENTTASK_TAG); - String s1 = null; - s1 = st.getTaskId(); - if (s1 != null) - dt.setAttribute(SEDMLTags.SUBTASK_ATTR_TASK, s1); - subTasksListElement.addContent(dt); - } - node.addContent(subTasksListElement); - } - return node; - } - - // ============== Tasks - org.jdom2.Element getXML(AbstractTask sedmlTask) { - - if (sedmlTask instanceof RepeatedTask) { - Element node = new Element(SEDMLTags.REPEATED_TASK_TAG); - addNotesAndAnnotation(sedmlTask, node); - String s = null; - // Add Attributes to tasks - s = sedmlTask.getId(); - if (s != null) - node.setAttribute(SEDMLTags.TASK_ATTR_ID, s); // insert 'id' - // attribute - s = sedmlTask.getName(); - if (s != null) { - node.setAttribute(SEDMLTags.TASK_ATTR_NAME, s); // insert 'name' - // attribute - } - // s = sedmlTask.getModelReference();if(s != - // null)node.setAttribute(SEDMLTags.TASK_ATTR_MODELREF, s); // - // insert 'model' reference - // s = sedmlTask.getSimulationReference();if(s != - // null)node.setAttribute(SEDMLTags.TASK_ATTR_SIMREF, s); - s = Boolean.toString(((RepeatedTask) sedmlTask).getResetModel()); - node.setAttribute(SEDMLTags.REPEATED_TASK_RESET_MODEL, s); - s = ((RepeatedTask) sedmlTask).getRange(); - if (s != null) - node.setAttribute(SEDMLTags.REPEATED_TASK_ATTR_RANGE, s); // "range" - // attribute - // Add list of ranges - Map mr = ((RepeatedTask) sedmlTask).getRanges(); - if (mr != null && !mr.isEmpty()) { - Element rangesListElement = new Element( - SEDMLTags.REPEATED_TASK_RANGES_LIST); - for (Range r : mr.values()) { - rangesListElement.addContent(getXML(r)); - } - node.addContent(rangesListElement); - } - // Add list of changes - List lcs = ((RepeatedTask) sedmlTask).getChanges(); - if (lcs != null && !lcs.isEmpty()) { - Element changesListElement = new Element( - SEDMLTags.REPEATED_TASK_CHANGES_LIST); - for (SetValue sv : lcs) { - changesListElement.addContent(getXML(sv)); - } - node.addContent(changesListElement); - } - // Add list of subtasks - Map mt = ((RepeatedTask) sedmlTask).getSubTasks(); - if (mt != null && !mt.isEmpty()) { - Element subTasksListElement = new Element( - SEDMLTags.REPEATED_TASK_SUBTASKS_LIST); - for (SubTask st : mt.values()) { - subTasksListElement.addContent(getXML(st)); - } - node.addContent(subTasksListElement); - } - - return node; - } else { - Element node = new Element(SEDMLTags.TASK_TAG); - addNotesAndAnnotation(sedmlTask, node); - String s = null; - // Add Attributes to tasks - s = sedmlTask.getId(); - if (s != null) - node.setAttribute(SEDMLTags.TASK_ATTR_ID, s); // insert 'id' - // attribute - s = sedmlTask.getName(); - if (s != null) { - node.setAttribute(SEDMLTags.TASK_ATTR_NAME, s); // insert 'name' - // attribute - } - s = sedmlTask.getModelReference(); - if (s != null) - node.setAttribute(SEDMLTags.TASK_ATTR_MODELREF, s); // insert - // 'model' - // reference - s = sedmlTask.getSimulationReference(); - if (s != null) - node.setAttribute(SEDMLTags.TASK_ATTR_SIMREF, s); - return node; - } - } - - private void addNotesAndAnnotation(SEDBase sedbase, Element node) { - - // add 'notes' elements from sedml - Notes note = sedbase.getNote(); - if(note != null) { - Element notes = new Element(SEDMLTags.NOTES); - notes.addContent(note.getNotesElement().detach()); - node.addContent(notes); - } - - // add 'annotation' elements from sedml - for (Annotation ann : sedbase.getAnnotation()) { - Element annEl = new Element(SEDMLTags.ANNOTATION); - annEl.addContent(ann.getAnnotationElement().detach()); - node.addContent(annEl); - } - if (sedbase.getMetaId() != null) { - node.setAttribute(SEDMLTags.META_ID_ATTR_NAME, sedbase.getMetaId()); - } - } - - // =============== DataGenerators - org.jdom2.Element getXML(DataGenerator sedmlDataGen) { - Element node = new Element(SEDMLTags.DATAGENERATOR_TAG); - String s = null; - addNotesAndAnnotation(sedmlDataGen, node); - // Add Attributes to data generators - s = sedmlDataGen.getId(); - if (s != null) - node.setAttribute(SEDMLTags.DATAGEN_ATTR_ID, sedmlDataGen.getId()); // insert - // 'id' - // attribute - s = sedmlDataGen.getName(); - if (s != null) { - node.setAttribute(SEDMLTags.DATAGEN_ATTR_NAME, s); // insert 'name' - // attribute - } - List listOfVariables = sedmlDataGen.getListOfVariables(); - if (listOfVariables != null && listOfVariables.size() > 0) { - Element list = new Element(SEDMLTags.DATAGEN_ATTR_VARS_LIST); - for (int i = 0; i < listOfVariables.size(); i++) { - list.addContent(getXML(listOfVariables.get(i), - VariableType.DATA_GENERATOR)); - } - node.addContent(list); - } - List listOfParameters = sedmlDataGen.getListOfParameters(); - if (listOfParameters != null && listOfParameters.size() > 0) { - Element list = new Element(SEDMLTags.DATAGEN_ATTR_PARAMS_LIST); - for (int i = 0; i < listOfParameters.size(); i++) { - list.addContent(getXML(listOfParameters.get(i))); - } - node.addContent(list); - } - if (sedmlDataGen.getMath() != null) { - try { - ASTToXMLElementVisitor astElementVisitor = new ASTToXMLElementVisitor(); - sedmlDataGen.getMath().accept(astElementVisitor); - node.addContent(astElementVisitor.getElement()); // insert - // 'math' - // attribute - } catch (Exception e) { - throw new RuntimeException( - "Unable to process mathML for datagenerator '" - + sedmlDataGen.getId() + "' : " - + e.getMessage(), e); - } - } - - return node; - } - - // TODO: need to add another getXML(Variable...) for the "change math" - // variables - org.jdom2.Element getXML(Variable variable, VariableType varType) { - Element node = new Element(SEDMLTags.DATAGEN_ATTR_VARIABLE); - addNotesAndAnnotation(variable, node);// Add Variables to list of - // variables - String s = null; - s = variable.getId(); - if (s != null) - node.setAttribute(SEDMLTags.VARIABLE_ID, variable.getId()); // insert - // 'id' - // attribute - s = variable.getName(); - if (s != null) { - node.setAttribute(SEDMLTags.VARIABLE_NAME, s); - } - s = variable.getReference(); - if (s != null && s.length() > 0 - && varType.equals(VariableType.COMPUTE_CHANGE)) { - node.setAttribute(SEDMLTags.VARIABLE_MODEL, variable.getReference()); // we - // know - // it's - // a - // task - // reference - } else if (s != null && s.length() > 0 - && varType.equals(VariableType.DATA_GENERATOR)) { - node.setAttribute(SEDMLTags.VARIABLE_TASK, variable.getReference()); - } - if (variable.isVariable()) { - s = variable.getTarget(); - if (s != null) - node.setAttribute(SEDMLTags.VARIABLE_TARGET, s); - } else if (variable.isSymbol()) { - s = variable.getSymbol().getUrn(); - if (s != null) - node.setAttribute(SEDMLTags.VARIABLE_SYMBOL, s); - } - - return node; - } - - org.jdom2.Element getXML(Parameter parameter) { - Element node = new Element(SEDMLTags.DATAGEN_ATTR_PARAMETER); - String s = null; - - s = parameter.getId(); - if (s != null) - node.setAttribute(SEDMLTags.PARAMETER_ID, parameter.getId()); // insert - // 'id' - // attribute - s = parameter.getName(); - if (s != null) - node.setAttribute(SEDMLTags.PARAMETER_NAME, s); - node.setAttribute(SEDMLTags.PARAMETER_VALUE, - Double.toString(parameter.getValue())); - addNotesAndAnnotation(parameter, node); - return node; - } - - // ============ Outputs - org.jdom2.Element getXML(Output sedmlOutput) { - Element node = null; // Add outputs to list of outputs - String s = null; - if (sedmlOutput.getKind().equals(SEDMLTags.PLOT2D_KIND)) { // various - // attributes - // depending - // on kind - node = new Element(SEDMLTags.OUTPUT_P2D); - addNotesAndAnnotation(sedmlOutput, node); - s = sedmlOutput.getId(); - if (s != null) - node.setAttribute(SEDMLTags.OUTPUT_ID, sedmlOutput.getId()); - s = sedmlOutput.getName(); - if (s != null) - node.setAttribute(SEDMLTags.OUTPUT_NAME, s); - List listOfCurves = ((Plot2D) sedmlOutput).getListOfCurves(); - if (listOfCurves != null && listOfCurves.size() > 0) { - Element list = new Element(SEDMLTags.OUTPUT_CURVES_LIST); - for (int i = 0; i < listOfCurves.size(); i++) { - list.addContent(getXML((Curve) listOfCurves.get(i))); - } - node.addContent(list); - } - } else if (sedmlOutput.getKind().equals(SEDMLTags.PLOT3D_KIND)) { - node = new Element(SEDMLTags.OUTPUT_P3D); - addNotesAndAnnotation(sedmlOutput, node); - s = sedmlOutput.getId(); - if (s != null) - node.setAttribute(SEDMLTags.OUTPUT_ID, sedmlOutput.getId()); - s = sedmlOutput.getName(); - if (s != null) - node.setAttribute(SEDMLTags.OUTPUT_NAME, s); - List listOfSurfaces = ((Plot3D) sedmlOutput) - .getListOfSurfaces(); - if (listOfSurfaces != null && listOfSurfaces.size() > 0) { - Element list = new Element(SEDMLTags.OUTPUT_SURFACES_LIST); - for (int i = 0; i < listOfSurfaces.size(); i++) { - list.addContent(getXML(listOfSurfaces.get(i))); - } - node.addContent(list); - } - } else if (sedmlOutput.getKind().equals(SEDMLTags.REPORT_KIND)) { - node = new Element(SEDMLTags.OUTPUT_REPORT); - addNotesAndAnnotation(sedmlOutput, node); - s = sedmlOutput.getId(); - if (s != null) - node.setAttribute(SEDMLTags.OUTPUT_ID, sedmlOutput.getId()); - s = sedmlOutput.getName(); - if (s != null) - node.setAttribute(SEDMLTags.OUTPUT_NAME, s); - List listOfDataSets = ((Report) sedmlOutput) - .getListOfDataSets(); - if (listOfDataSets != null && listOfDataSets.size() > 0) { - Element list = new Element(SEDMLTags.OUTPUT_DATASETS_LIST); - for (int i = 0; i < listOfDataSets.size(); i++) { - list.addContent(getXML(listOfDataSets.get(i))); - } - node.addContent(list); - } - } - - return node; - } - - org.jdom2.Element getXML(Curve sedCurve) { - String val = null;// Curves - Element node = new Element(SEDMLTags.OUTPUT_CURVE); - val = sedCurve.getId(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_ID, val); - val = sedCurve.getName(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_NAME, val); - node.setAttribute(SEDMLTags.OUTPUT_LOG_X, - String.valueOf(sedCurve.getLogX())); - node.setAttribute(SEDMLTags.OUTPUT_LOG_Y, - String.valueOf(sedCurve.getLogY())); - val = sedCurve.getXDataReference(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_DATA_REFERENCE_X, val); // insert - // 'xDataReference' - // attribute - val = sedCurve.getYDataReference(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_DATA_REFERENCE_Y, val); // insert - // 'yDataReference' - // attribute - addNotesAndAnnotation(sedCurve, node); - return node; - } - - org.jdom2.Element getXML(Surface sedSurface) { - // Surfaces - Element node = new Element(SEDMLTags.OUTPUT_SURFACE); - String val = null;// Curves - node.setAttribute(SEDMLTags.OUTPUT_LOG_Z, - String.valueOf(sedSurface.getLogZ())); - - val = sedSurface.getId(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_ID, val); - val = sedSurface.getName(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_NAME, val); - node.setAttribute(SEDMLTags.OUTPUT_LOG_X, - String.valueOf(sedSurface.getLogX())); - node.setAttribute(SEDMLTags.OUTPUT_LOG_Y, - String.valueOf(sedSurface.getLogY())); - val = sedSurface.getXDataReference(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_DATA_REFERENCE_X, val); // insert - // 'xDataReference' - // attribute - val = sedSurface.getYDataReference(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_DATA_REFERENCE_Y, val); // insert - // 'yDataReference' - // attribute - val = sedSurface.getZDataReference(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_DATA_REFERENCE_Z, val); - addNotesAndAnnotation(sedSurface, node); - return node; - } - - org.jdom2.Element getXML(DataSet sedDataSet) { // DataSets - String val = null; - Element node = new Element(SEDMLTags.OUTPUT_DATASET); - val = sedDataSet.getDataReference(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_DATA_REFERENCE, val); - val = sedDataSet.getId(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_ID, val); - val = sedDataSet.getName(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_NAME, val); - val = sedDataSet.getLabel(); - if (val != null) - node.setAttribute(SEDMLTags.OUTPUT_DATASET_LABEL, val); - addNotesAndAnnotation(sedDataSet, node); - - return node; - } - - private org.jdom2.Element setDefaultNamespace(org.jdom2.Element rootNode, - org.jdom2.Namespace namespace) { - // only if there is a node and it has no default namespace! - if (rootNode != null && rootNode.getNamespaceURI().length() == 0) { - // set namespace for this node - rootNode.setNamespace(namespace); - Iterator childIterator = rootNode.getChildren().iterator(); - while (childIterator.hasNext()) { - org.jdom2.Element child = childIterator.next(); - // check children - setDefaultNamespace(child, namespace); - } - } - return rootNode; - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/SId.java b/vcell-core/src/main/java/org/jlibsedml/SId.java deleted file mode 100644 index aee2cd41d5..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/SId.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.jlibsedml; - -public class SId { - - private final String string; - - public SId(String string) { - if(!isValidSId(string)) { - throw new IllegalArgumentException("Invalid SId: " + string); - } - this.string = string; - } - - public String getString() { - return string; - } - public static String unwrap(SId sid) { - if(sid == null) { - return null; - } - return sid.getString(); - } - public int length() { - return string.length(); - } - /* - * letter ::= 'a'..'z','A'..'Z' - * digit ::= '0'..'9' - * idChar ::= letter | digit | '_' - * SId ::= ( letter | '_' ) idChar* - */ - public static boolean isValidSId(String string) { - if(string == null) { - return false; - } - if(string.equals("")) { - return false; - } - if(!string.substring(0,1).matches("[a-zA-Z_]")) { - return false; - } - if(string.length() > 1) { - if(!string.substring(1).matches("[\\da-zA-Z_]*")) { - return false; - } - } - return true; - } - - @Override - public String toString() { - return "SId [" - + "getString()=" + getString() - + "]"; - } - @Override - public boolean equals(Object other) { - if(other == null) { - return false; - } - if(other instanceof SId) { - return getString().equals(((SId) other).getString()); - } else { - return false; - } - } - @Override - public int hashCode() { - return getString().hashCode(); - } - - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/SedML.java b/vcell-core/src/main/java/org/jlibsedml/SedML.java deleted file mode 100644 index 742e521f9f..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/SedML.java +++ /dev/null @@ -1,795 +0,0 @@ -package org.jlibsedml; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.jdom2.Namespace; -import org.jlibsedml.execution.IModelResolver; -import org.jlibsedml.execution.ModelResolver; -import org.jlibsedml.extensions.ElementSearchVisitor; -import org.jmathml.ASTNode; - -/** - * The top level object in a SED-ML document. - *

- * To create a SedML object, create a SED-ML document first. - *

- * - *

- * This class is basically a container element for the 5 different parts of the - * SED-ML - the model, simulation, task description, data generator, and output. - *

- * - * Elements can be added in two ways: - *
    - *
  • Either pass in a previously created list, e.g., - * - *
    - * List<Simulation> simulations = createListOfSimulationsSomewhereElse();
    - * sedml.setSimulations(simulations);
    - * 
    - * - * or - *
  • Add simulations one at a time: - * - *
    - * Simulation sim = createASimulation();
    - * sedml.addSimulation(sim);
    - * 
    - * - *
- * - * Elements can be added to a list that has previously been set, however setting - * in a list will overwrite any collections generated from either of the two - * approaches above.

- * - * All getListOfXXX() methods will return read-only lists. Attempting to modify - * these lists will generate runtime exceptions. To manipulate the lists, then, - * use the add/remove/set methods. - *

- * - *

- * Elements can be searched for by the getXXXWithId() methods. E.g., - *

- * - *
- * Model model = sedml.getModelWithId("myModel");
- * if (model != null) {
- *     // do something.
- * }
- * 
- *

- * It should be noted that all collections held in this object, and elsewhere in - * SEDML, are mutable from outside the object. So, if you pass in a collection - * of Simulations into this class, then modify the collection outside this - * object, then this collection will be modified. - *

- * - * @author anu/radams - * - */ -public final class SedML extends SEDBase { - - @Override - public String toString() { - return "SedML [level=" + level + "]"; - } - - // added for import - private String pathForURI = ""; - private String fileName = ""; - - private int level = 1; - private int version = 2; - private Namespace xmlns = null; - - private List additionalNamespaces = new ArrayList<>(); - - private List models = new ArrayList(); - private List simulations = new ArrayList(); - private List tasks = new ArrayList(); - private List dataGenerators = new ArrayList(); - private List outputs = new ArrayList(); - - /** - * Sorts a list of Outputs into the correct order specified in the schema. - * - * @author radams - * - */ - static class OutputComparator implements Comparator { - static Map changeKindOrder; - static { - changeKindOrder = new HashMap(); - changeKindOrder.put(SEDMLTags.PLOT2D_KIND, 1); - changeKindOrder.put(SEDMLTags.PLOT3D_KIND, 2); - changeKindOrder.put(SEDMLTags.REPORT_KIND, 3); - - } - - public int compare(Output o1, Output o2) { - return changeKindOrder.get(o1.getKind()).compareTo( - changeKindOrder.get(o2.getKind())); - } - - } - - SedML(int aLevel, int aVersion, Namespace aNameSpace) { - if (aLevel != 1) { - throw new IllegalArgumentException(MessageFormat.format( - "Invalid level {0}, valid level is {1}", aLevel, "1")); - } - if (aVersion < 1 || aVersion > 4) { - throw new IllegalArgumentException(MessageFormat.format( - "Invalid version {0}, valid versions are {1}", aVersion, - "1,2,3,4")); - } - this.level = aLevel; - this.version = aVersion; - this.xmlns = aNameSpace; - } - - SedML(Namespace aNamespace) { - this.xmlns = aNamespace; - this.level = 1; - this.version = 1; - } - - /** - * Returns a read-only list of models in SedDocument - * - * @return list of {@link Model}s - */ - public List getModels() { - return Collections.unmodifiableList(models); - } - - /** - * Returns a read-only list of simulations in SedML - * - * @return list of simulations - */ - public List getSimulations() { - return Collections.unmodifiableList(simulations); - } - - /** - * Returns a read-only list of tasks in SedMl - * - * @return list of tasks - */ - public List getTasks() { - return Collections.unmodifiableList(tasks); - } - - @Override - public String getElementName() { - return SEDMLTags.ROOT_NODE_TAG; - } - - /** - * Returns a read-only list of data generators in SedML - * - * @return list of datagenerators - */ - public List getDataGenerators() { - return Collections.unmodifiableList(dataGenerators); - } - - /** - * Returns a read-only list of outputs in SedDocument. This method does not - * return the list in the order by whic Outputs were added. Instead, it - * orders the outputs by types to agree with the schema. I.e., Plot2D - * ,Plot3d, Reports.
List of Outputs. - */ - public List getOutputs() { - Collections.sort(outputs, new OutputComparator()); - return Collections.unmodifiableList(outputs); - } - - /** - * Sets additional namespaces on SedDocument - */ - public void setAdditionalNamespaces(List additionalNamespaces) { - this.additionalNamespaces = additionalNamespaces; - } - - /** - * Sets list of models on SedDocument - * - * @param models - * list of Model objects - */ - public void setModels(List models) { - this.models = models; - } - - /** - * Adds a {@link Model} to this object's list of Models, if not already - * present. - * - * @param model - * A non-null {@link Model} element - * @return true if model added, false otherwise. - */ - public boolean addModel(Model model) { - if (!models.contains(model)) - return models.add(model); - return false; - } - - /** - * Removes a {@link Model} from this object's list of Models. - * - * @param model - * A non-null {@link Model} element - * @return true if model removed, false - * otherwise. - */ - public boolean removeModel(Model model) { - return models.remove(model); - } - - /** - * Sets list of simulations on SedDocument - * - * @param simulations - * list of simulations - */ - public void setSimulations(List simulations) { - this.simulations = simulations; - } - - /** - * Adds a {@link Simulation} to this object's list of Simulations, if not - * already present. - * - * @param simulation - * A non-null {@link Simulation} element - * @return true if simulation added, false - * otherwise. - */ - public boolean addSimulation(Simulation simulation) { - if (!simulations.contains(simulation)) - return simulations.add(simulation); - return false; - } - - /** - * Removes a {@link Simulation} from this object's list of Simulations. - * - * @param simulation - * A non-null {@link Simulation} element - * @return true if simulation removed, false - * otherwise. - */ - public boolean removeSimulation(Simulation simulation) { - return simulations.remove(simulation); - } - - /** - * Sets list of tasks on SedDocument - * - * @param tasks - * list of Tasks - */ - public void setTasks(List tasks) { - this.tasks = tasks; - } - - /** - * Adds a {@link Task} to this object's list of Tasks, if not already - * present. - * - * @param task - * A non-null {@link Task} element - * @return true if task added, false otherwise. - */ - public boolean addTask(AbstractTask task) { - if (!tasks.contains(task)) - return tasks.add(task); - return false; - } - - /** - * Removes a {@link Task} from this object's list of Tasks. - * - * @param task - * A non-null {@link Task} element - * @return true if task removed, false otherwise. - */ - public boolean removeTask(AbstractTask task) { - return tasks.remove(task); - } - - /** - * Sets list of data generators on SedDocument - * - * @param dataGenerators - * list of DataGenerators - */ - public void setDataGenerators(List dataGenerators) { - this.dataGenerators = dataGenerators; - } - - /** - * Adds a {@link DataGenerator} to this object's list of DataGenerators, if - * not already present. - * - * @param dataGenerator - * A non-null {@link DataGenerator} element - * @return true if dataGenerator added, false - * otherwise. - */ - public boolean addDataGenerator(DataGenerator dataGenerator) { - if (!dataGenerators.contains(dataGenerator)) - return dataGenerators.add(dataGenerator); - return false; - } - - /** - * Removes a {@link DataGenerator} from this object's list of - * DataGenerators. - * - * @param dg - * A non-null {@link DataGenerator} element - * @return true if removed, false otherwise. - */ - public boolean removeDataGenerator(DataGenerator dg) { - return dataGenerators.remove(dg); - } - - /** - * Sets list of outputs on SedDocument - */ - public void setOutputs(List outputs) { - this.outputs = outputs; - } - - /** - * Adds a {@link Output} to this object's list of Outputs, if not already - * present. - * - * @param output - * A non-null {@link Output} element - * @return true if dataGenerator added, false - * otherwise. - */ - public boolean addOutput(Output output) { - if (!outputs.contains(output)) - return outputs.add(output); - return false; - } - - /** - * Removes a {@link Output} from this object's list of Outputs. - * - * @param output - * A non-null {@link Output} element - * @return true if removed, false otherwise. - */ - public boolean removeOutput(Output output) { - return outputs.remove(output); - } - - /** - * Returns the SED-ML {@link Model} with id 'modelRef', if present.
- * This method does not return the content of the model; use implementations - * of {@link IModelResolver} together with the {@link ModelResolver} class - * to get the model content. - * - * @param modelRef - * A non-null model Identifier - * @return The model element with the specified ID, or null if - * not found. - */ - public Model getModelWithId(String modelRef) { - String modelIdCandidate = modelRef; - if(modelRef.startsWith("#")) { - modelIdCandidate = modelIdCandidate.substring(1); - } - for (int i = 0; i < models.size(); i++) { - Model m = models.get(i); - if (m.getId().equals(modelIdCandidate)) { - return m; - } - } - return null; - } - - /** - * Boolean test for whether the modelRef argument corresponds - * to a model ID defined in the list of models. - * - * @param modelRef - * @return true if a model is defined with - * modelRef argument, false otherwise. - */ - public boolean isModel(String modelRef) { - String modelIdCandidate = modelRef; - if(modelRef.startsWith("#")) { - modelIdCandidate = modelIdCandidate.substring(1); - } - for (int i = 0; i < models.size(); i++) { - Model m = models.get(i); - if (m.getId().equals(modelIdCandidate)) { - return true; - } - } - return false; - } - - /** - * Returns simulation with id 'simRef', if present. - * - * @param simRef - * @return A {@link Simulation} object, or null if not found. - */ - public Simulation getSimulation(String simRef) { - for (int i = 0; i < simulations.size(); i++) { - Simulation s = simulations.get(i); - if (s.getId().equals(simRef)) { - return s; - } - } - return null; - } - - /** - * Returns task with id 'taskRef', if present - * - * @param taskRef - * @return A {@link Task} object, or null if not found. - */ - public AbstractTask getTaskWithId(String taskRef) { - for (int i = 0; i < tasks.size(); i++) { - AbstractTask t = tasks.get(i); - if (t.getId().equals(taskRef)) { - return t; - } - } - return null; - } - - /** - * Returns a {@link DataGenerator} with id 'dataGenRef', if present. - * - * @param dataGenRef - * @return A {@link DataGenerator} object, or null if not - * found. - */ - public DataGenerator getDataGeneratorWithId(String dataGenRef) { - for (int i = 0; i < dataGenerators.size(); i++) { - DataGenerator d = dataGenerators.get(i); - if (d.getId().equals(dataGenRef)) { - return d; - } - } - return null; - } - - /** - * Returns the {@link Output} with id 'outputID', if present. - * - * @param outputID - * @return A {@link Output} object, or null if not found. - */ - public Output getOutputWithId(String outputID) { - for (int i = 0; i < outputs.size(); i++) { - Output d = outputs.get(i); - if (d.getId().equals(outputID)) { - return d; - } - } - return null; - } - - /** - * Returns list of additional namespaces in SedDocument - * - * @return list of namespaces - */ - public List getAdditionalNamespaces() { - return additionalNamespaces; - } - - /** - * Returns version of SedDocument - * - * @return the version of this document's SEDML level - */ - public int getVersion() { - return version; - } - - /** - * Returns level of SedDocument - * - * @return the SEDML level of this document - */ - public int getLevel() { - return level; - } - - /** - * Boolean test for whether this object is L1V1 of the specification - * - * @return true if is Level-1 Version-1, false - * otherwise - * @since 2.2.3 - */ - public boolean isL1V1() { - return level == 1 && version == 1; - } - - /** - * Boolean test for whether this object is L1V2 of the specification - * - * @return true if is Level-1 Version-2, false - * otherwise - * @since 2.2.3 - */ - public boolean isL1V2() { - return level == 1 && version == 2; - } - - /** - * Returns default namespace of SedDocument - * - * @return a {@link Namespace} - */ - public Namespace getNamespace() { - return xmlns; - } - - /** - * Convenience method to add a Variable and DataGenerator together.
- * This method should be useful when only the raw form of an output is - * required, and no post-processing of the data is required.
- *

- * For example, for a parameter in an SBML model with id k1: - * - *

-     * String xpathAsString = new SBMLSupport().getXPathForGlobalParameter("k1");
-     * XPathTarget xpath = new XPathTarget(xpathAsString);
-     * DataGenerator dg = sed.addSimpleSpeciesAsOutput(xpath, "k1", "A param name",
-     *         t1, true);
-     * 
- * - * will produce the following SED-ML: - * - *
-     *  <dataGenerator id="k1_dg" name="A param name">
-     *  <listOfVariables>
-     *     <variable id="k1" name=" A param name" target="/path/to/param/"/>
-     *  </listOfVariables>
-     *   <math:math>
-     *       <math:ci>k1</math:ci>
-     *      </math:math>
-     *  </dataGenerator>
-     * 
- * - *

- * - * @param xpath - * The {@link XPathTarget} identifying the element. - * @param id - * A suggested ID for the Variable element. If this ID is not - * currently used in the model, a suffix will be appended to make - * the ID unique.
- * The suffix will be '__x' where x is an integer.
- * The dataGenerator ID will be the variableID + '_dg'. - * @param name - * An optional name. This will be used for the Variable and - * DataGenerator name. - * @param task - * A {@link Task} object. - * @param force - * A boolean. If true, will force a new - * identifier if need be, if false, will not force a - * new identifier if there is already a Variable in this SED-ML - * object with the given id. - * @return the created {@link DataGenerator} if was successfully added, - * null otherwise ( for example if no unique identifier - * can be generated in a fixed number of attempts ). - * @throws IllegalArgumentException - * if any argument except name is null. - * @since 2.1 - */ - public final DataGenerator addSimpleSpeciesAsOutput( - final XPathTarget xpath, final String id, final String name, - final AbstractTask task, boolean force) { - Assert.checkNoNullArgs(xpath, id, task); - ElementSearchVisitor vis1 = new ElementSearchVisitor(id); - String dgID = id + "_dg"; - - ElementSearchVisitor vis2 = new ElementSearchVisitor(dgID); - final int MAX_ATTEMPTS = 10; - int attempts = 0; - accept(vis1); - accept(vis2); - int suffix = 0; - String newID = id; - while (vis1.getFoundElement() != null && vis2.getFoundElement() != null - && attempts < MAX_ATTEMPTS) { - if (!force) { - return null; - } - // try a new id to see if it's unique - attempts++; - newID = id + "__" + ++suffix; - dgID = newID + "_dg"; - vis1 = new ElementSearchVisitor(newID); - vis2 = new ElementSearchVisitor(dgID); - accept(vis1); - accept(vis2); - - } - if (attempts < MAX_ATTEMPTS) { - // create the SEDML objects - DataGenerator dg = new DataGenerator(dgID, name); - ASTNode node = Libsedml.parseFormulaString(newID); - dg.setMathML(node); - Variable var = new Variable(newID, name, task.getId(), - xpath.getTargetAsString()); - dg.addVariable(var); - addDataGenerator(dg); - return dg; - } else { - return null; - } - - } - - /** - * This is a convenience method to add many data generators using just the - * IDs of model elements.
- * There are several pre-conditions for this method to succeed: - *
    - *
  • The identifiers supplied in the var-args list of {@link IdName} - * objects should uniquely identify a model element. If the id is not - * unique, this method will generate an XPath expression to locate the first - * match found. - *
  • The model content should be retrievable and should be a valid, - * well-formed XML document. - *
  • Each {@link DataGenerator} will be created using the conventions of - * {@link #addIdentifiersAsDataGenerators(AbstractTask, String, boolean, IModelResolver, IdName...)}. - *
  • The list of {@link IdName} objects should contain no duplicate - * identifiers. - *
- * Post-conditions are that: - *
    - *
  • The model source will be unaffected. - *
  • Only the list of {@link DataGenerator} objects will be affected by - * this method; the other parts of the SED-ML object remain unaltered. - *
- * - * @param task - * The {@link Task} to which the DataGenerators will refer. This - * object MUST belong to this SEDML object - * @param attributeIdentifierName - * The name of the attribute used to uniquely identify the - * elements to which DataGenerators will refer. For example, in - * an SBML file, this parameter would be "id" or "meta_id" - * @param allOrNothing - * A boolean. If true, either all the - * requested identifiers will be converted into DataGenerators, - * or none if at least one identifier could not be resolved in - * the model. If false, a best effort will be made - * to resolve the identifiers and as many datagenerators added - * for each match. - * @param modelResolver - * An {@link IModelResolver} capable of obtaining the model - * content from the Task's model reference. - * @param idNameList - * A var-args list of id-name pairs. The ID part should uniquely - * identify an element in the model. - * @return true if at least some datagenerators were added, or - * false if none were ( for example, if the model - * content could not be obtained, or the model was not well-formed, - * or the Task did not belong to this SedML object - * @throws IllegalArgumentException - * if any argument is null. - * @since 2.1 - */ - public final boolean addIdentifiersAsDataGenerators(AbstractTask task, - final String attributeIdentifierName, boolean allOrNothing, - IModelResolver modelResolver, IdName... idNameList) { - Assert.checkNoNullArgs(task, attributeIdentifierName, modelResolver, - idNameList); - boolean foundTask = false; - for (AbstractTask belonging : getTasks()) { - if (task == belonging) { - foundTask = true; - } - } - if (!foundTask) { - return false; - } - XpathGeneratorHelper gen = new XpathGeneratorHelper(this); - return gen.addIdentifiersAsDataGenerators(task, - attributeIdentifierName, allOrNothing, modelResolver, - idNameList); - } - - /** - * This method returns a non-redundant List of models which - * refer to external sources and do not refer to other models in this - * document. - * - * @return Returns a possibly empty but non-null read-only list of base - * models in this document. - */ - public List getBaseModels() { - Set set = new HashSet(); - for (Model m : getModels()) { - if (getModelWithId(m.getSourcePathOrURIString()) == null) { - set.add(m); - } - } - List rc = new ArrayList(); - for (Model m : set) { - rc.add(m); - } - return Collections.unmodifiableList(rc); - } - - @Override - public boolean accept(SEDMLVisitor visitor) { - if (!visitor.visit(this)) { - return false; - } - for (Simulation sim : getSimulations()) { - if (!sim.accept(visitor)) { - return false; - } - } - for (AbstractTask t : getTasks()) { - if (!t.accept(visitor)) { - return false; - } - } - for (Model m : getModels()) { - if (!m.accept(visitor)) { - return false; - } - } - for (DataGenerator dg : getDataGenerators()) { - if (!dg.accept(visitor)) { - return false; - } - } - for (Output o : getOutputs()) { - if (!o.accept(visitor)) { - return false; - } - } - return true; - } - - public String getPathForURI() { - return pathForURI; - } - - public void setPathForURI(String pathForURI) { - this.pathForURI = pathForURI; - } - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/SedMLDataContainer.java b/vcell-core/src/main/java/org/jlibsedml/SedMLDataContainer.java new file mode 100644 index 0000000000..daced74bf0 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/SedMLDataContainer.java @@ -0,0 +1,508 @@ +package org.jlibsedml; + +import java.text.MessageFormat; +import java.util.*; + +import org.jdom2.Namespace; +import org.jlibsedml.components.*; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.output.*; +import org.jlibsedml.components.simulation.OneStep; +import org.jlibsedml.components.simulation.Simulation; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.task.RepeatedTask; +import org.jlibsedml.components.task.SubTask; +import org.jlibsedml.components.task.Task; + +/** + * This class serves a bridge between the implementations of SedML classes / components, and all the utility work + * and surrounding XML needed to make a reproducible description. + */ +public final class SedMLDataContainer { + + // added for import + private String pathForURI; + private String fileName; + private final Map xmlPrefixToNamespaceMap; + private final SedML sedml; + + private static List getDefaultNamespaces(int sedmlLevel, int sedmlVersion){ + String sedmlURI = String.format("http://sed-ml.org/sed-ml/level%d/version%d", sedmlLevel, sedmlVersion); + return Arrays.asList( + Namespace.getNamespace(sedmlURI), + Namespace.getNamespace("math", "http://www.w3.org/1998/Math/MathML"), + Namespace.getNamespace("sbml", "http://www.sbml.org/sbml/level3/version2/core") + ); + } + + SedMLDataContainer(SedML sedml) { + if (sedml.getLevel() != 1) { + String message = MessageFormat.format("Invalid level {0}, valid level is {1}", sedml.getLevel(), "1"); + throw new IllegalArgumentException(message); + } + if (sedml.getVersion() < 1 || sedml.getVersion() > 5) { + String message = MessageFormat.format("Invalid version {0}, valid versions are {1}", sedml.getVersion(), "1,2,3,4,5"); + throw new IllegalArgumentException(message); + } + this.sedml = sedml; + this.xmlPrefixToNamespaceMap = new HashMap<>(); + } + + public SedMLDataContainer(SedMLDataContainer containerToCopy, boolean deepCopySedml) throws CloneNotSupportedException { + this.sedml = deepCopySedml ? containerToCopy.sedml.clone() : containerToCopy.sedml; + this.pathForURI = containerToCopy.pathForURI; + this.fileName = containerToCopy.fileName; + this.xmlPrefixToNamespaceMap = new HashMap<>(containerToCopy.xmlPrefixToNamespaceMap); + } + + public SedMLDataContainer(SedMLDataContainer containerToCopy, SedML sedMLToReplaceWith){ + this.sedml = sedMLToReplaceWith; + this.pathForURI = containerToCopy.pathForURI; + this.fileName = containerToCopy.fileName; + this.xmlPrefixToNamespaceMap = new HashMap<>(containerToCopy.xmlPrefixToNamespaceMap); + } + + public SedML getSedML() { + return this.sedml; + } + + /** + * Removes any dangling references from the internal SedML + */ + public void pruneSedML(){ + this.pruneSedMLTasks(); + this.pruneSedMLDataGenerators(); + this.pruneSedMLOutputs(); + } + + private void pruneSedMLTasks(){ + // Step 0; separate into base and repeated tasks + List baseTasks = new ArrayList<>(); + List repeatedTasks = new ArrayList<>(); + for (AbstractTask abTask: this.sedml.getTasks()){ + if (abTask instanceof RepeatedTask repTask) repeatedTasks.add(repTask); + if (abTask instanceof Task task) baseTasks.add(task); + } + // Step 1: prune base tasks + for (Task task: baseTasks){ + Model possibleModel = this.findModelById(task.getModelReference()); + if (null == possibleModel) this.sedml.getListOfTasks().removeContent(task); + Simulation possibleSim = this.findSimulationById(task.getSimulationReference()); + if (null == possibleSim) this.sedml.getListOfTasks().removeContent(task); + } + + // Step 2: prune repeated tasks + int currentNumTask; + do { + currentNumTask = this.sedml.getListOfTasks().size(); + for (RepeatedTask repTask: repeatedTasks){ + if (!this.sedml.getListOfTasks().containsContent(repTask.getId())) continue; + for (SubTask subTask: repTask.getSubTasks()){ + AbstractTask possibleReferredToTask = this.findAbstractTaskById(subTask.getTask()); + if (null == possibleReferredToTask) this.sedml.getListOfTasks().removeContent(repTask); + } + } + } while (currentNumTask != this.sedml.getListOfTasks().size()); // There may be nested repeated tasks to prune + } + + private void pruneSedMLDataGenerators(){ + for (DataGenerator generator: new ArrayList<>(this.sedml.getDataGenerators())) { + for (Variable var: generator.getVariables()) { + Model possibleModel = this.findModelById(var.getModelReference()); + AbstractTask possibleTask = this.findAbstractTaskById(var.getTaskReference()); + if (null == possibleModel && null == possibleTask) this.sedml.getListOfDataGenerators().removeContent(generator); + } + } + } + + private void pruneSedMLOutputs(){ + for (Output output: new ArrayList<>(this.sedml.getOutputs())) { + if (output instanceof Report report) { + for (DataSet dataSet: report.getDataSets()) { + SedBase dataGen = this.sedml.searchInDataGeneratorsFor(dataSet.getDataReference()); + if (dataGen instanceof DataGenerator) continue; + this.sedml.getListOfOutputs().removeContent(output); + break; + } + } else if (output instanceof Plot2D plot) { + for (AbstractCurve abCurve: plot.getCurves()) { + SedBase dataGenX = this.sedml.searchInDataGeneratorsFor(abCurve.getXDataReference()); + if (!(dataGenX instanceof DataGenerator)){ this.sedml.getListOfOutputs().removeContent(output); break; } + if (!(abCurve instanceof Curve curve)) continue; + SedBase dataGenY = this.sedml.searchInDataGeneratorsFor(curve.getYDataReference()); + if (dataGenY instanceof DataGenerator) continue; + this.sedml.getListOfOutputs().removeContent(output); + break; + } + } else if (output instanceof Plot3D plot) { + for (Surface surface: plot.getSurfaces()) { + SedBase dataGenX = this.sedml.searchInDataGeneratorsFor(surface.getXDataReference()); + if (!(dataGenX instanceof DataGenerator)){ this.sedml.getListOfOutputs().removeContent(output); break; } + SedBase dataGenY = this.sedml.searchInDataGeneratorsFor(surface.getYDataReference()); + if (!(dataGenY instanceof DataGenerator)){ this.sedml.getListOfOutputs().removeContent(output); break; } + SedBase dataGenZ = this.sedml.searchInDataGeneratorsFor(surface.getZDataReference()); + if (dataGenZ instanceof DataGenerator) continue; + this.sedml.getListOfOutputs().removeContent(output); + break; + } + } + } + } + + /** + * Sets additional namespaces on SedDocument + */ + public void addAllAdditionalNamespaces(List additionalNamespaces) { + for (Namespace namespace : additionalNamespaces) { + if (namespace.getPrefix() == null || namespace.getPrefix().isEmpty()) continue; + this.xmlPrefixToNamespaceMap.put(namespace.getPrefix(), namespace); + } + } + + /** + * Fetches the namespaces associated with this SedML object + * @return an unmodifiable {@link List} of {@link Namespace} + */ + public List getAllNamespaces() { + return this.xmlPrefixToNamespaceMap.keySet().stream().map(this.xmlPrefixToNamespaceMap::get).toList(); + } + + /** + * Fetches the namespaces associated with this SedML object + * @return an unmodifiable {@link List} of {@link Namespace} + */ + public List getExtraNamespaces() { + Set extraNamespaces = new HashSet<>(this.xmlPrefixToNamespaceMap.keySet()); + extraNamespaces.remove(""); + return extraNamespaces.stream().map(this.xmlPrefixToNamespaceMap::get).toList(); + } + + public void addExtraNamespace(Namespace namespace) { + String prefix = namespace.getPrefix(); + if (prefix == null) prefix = ""; + if (this.xmlPrefixToNamespaceMap.containsKey(prefix)) + throw new IllegalStateException(String.format("Namespace already exists for prefix %s", prefix.isEmpty() ? "": "\"" + prefix + "\"" )); + this.xmlPrefixToNamespaceMap.put(prefix, namespace); + } + + public void removeExtraNamespace(Namespace namespace) { + String prefix = namespace.getPrefix(); + if (prefix == null) prefix = ""; + if (!this.xmlPrefixToNamespaceMap.containsKey(prefix)) return; + this.xmlPrefixToNamespaceMap.remove(prefix); + } + + /** + * Boolean test for whether this object is L1V1 of the specification + * + * @return true if is Level-1 Version-1, false + * otherwise + * @since 2.2.3 + */ + public boolean isL1V1() { + return this.sedml.getLevel() == 1 && this.sedml.getVersion() == 1; + } + + /** + * Boolean test for whether this object is L1V2 of the specification + * + * @return true if is Level-1 Version-2, false + * otherwise + * @since 2.2.3 + */ + public boolean isL1V2() { + return this.sedml.getLevel() == 1 && this.sedml.getVersion() == 2; + } + + /** + * Boolean test for whether this object is L1V2 of the specification + * + * @return true if is Level-1 Version-2, false + * otherwise + * @since 2.2.3 + */ + public boolean isL1V3() { + return this.sedml.getLevel() == 1 && this.sedml.getVersion() == 3; + } + + /** + * Boolean test for whether this object is L1V2 of the specification + * + * @return true if is Level-1 Version-2, false + * otherwise + * @since 2.2.3 + */ + public boolean isL1V4() { + return this.sedml.getLevel() == 1 && this.sedml.getVersion() == 4; + } + + /** + * Boolean test for whether this object is L1V2 of the specification + * + * @return true if is Level-1 Version-2, false + * otherwise + * @since 2.2.3 + */ + public boolean isL1V5() { + return this.sedml.getLevel() == 1 && this.sedml.getVersion() == 5; + } + + public Model getBaseModel(SId startingModelID) { + SedBase foundBase = this.sedml.searchInModelsFor(startingModelID); + if (!(foundBase instanceof Model modelFound)) return null; + String modelSource = modelFound.getSourceAsString().strip(); + if (!modelSource.startsWith("#")) return modelFound; + SId nextLevelDownId; + try { + nextLevelDownId = new SId(modelSource.substring(1)); + } catch (IllegalArgumentException e) { + return null; + } + return this.getBaseModel(nextLevelDownId); + } + + /** + * Attempts to find a base task + * @param startingTaskID + * @return + */ + public Task getBaseTask(SId startingTaskID) { + SedBase foundBase = this.sedml.searchInTasksFor(startingTaskID); + if (foundBase instanceof Task task) return task; + if (foundBase instanceof RepeatedTask rTask){ + if (rTask.getSubTasks().size() != 1) return null; + return this.getBaseTask(rTask.getSubTasks().get(0).getTask()); + } + return null; + } + + public Set getBaseTasks(SId startingTaskID) { + SedBase foundBase = this.sedml.searchInTasksFor(startingTaskID); + if (foundBase instanceof Task task) return new HashSet<>(List.of(task)); + if (foundBase instanceof RepeatedTask rTask){ + Set tasks = new HashSet<>(); + for (SubTask subTask : rTask.getSubTasks()) { + tasks.addAll(this.getBaseTasks(subTask.getTask())); + } + return tasks; + } + throw new IllegalArgumentException(String.format("provided identifier `%s` corresponds to an unknown type of Task (%s)", startingTaskID.string(), foundBase.getClass().getSimpleName())); + } + + /** + * Returns the next "submodel" the model referenced by startingModelID uses. If the model does + * not contain any "sub-models" (i.e. the source is not another sedml model), then null is returned. + * @param startingModelID id of the model to start with + * @return null if there is no sub-model or the ID is invalid, otherwise the model object itself + */ + public Model getSubModel(SId startingModelID) { + SedBase foundBase = this.sedml.searchInModelsFor(startingModelID); + if (!(foundBase instanceof Model startingModel)) return null; + String modelSource = startingModel.getSourceAsString().strip(); + if (!modelSource.startsWith("#")) return null; // no next level to return! + SId nextLevelDownId; + try { + nextLevelDownId = new SId(modelSource.substring(1)); + } catch (IllegalArgumentException e) { + return null; + } + foundBase = this.sedml.searchInModelsFor(nextLevelDownId); + if (!(foundBase instanceof Model subModel)) return null; + return subModel; + } + + /** + * Attempts to find a subtask task + * @param startingTaskID + * @return + */ + public AbstractTask getActualSubTask(SId startingTaskID) { + SedBase foundBase = this.sedml.searchInTasksFor(startingTaskID); + if (!(foundBase instanceof RepeatedTask rTask)) throw new IllegalArgumentException("provided id does not correspond to a repeated Task"); + return this.getActualSubTask(rTask); + } + + public AbstractTask getActualSubTask(RepeatedTask repeatedTask){ + if (repeatedTask.getSubTasks().size() != 1) return null; + SedBase foundSubTask = this.sedml.searchInModelsFor(repeatedTask.getSubTasks().get(0).getTask()); + if (foundSubTask instanceof AbstractTask desiredTask) return desiredTask; + throw new IllegalArgumentException(String.format("provided repeated task `%s` corresponds to an unknown type of Task (%s) as a subtask", repeatedTask.getId().string(), foundSubTask.getClass().getSimpleName())); + } + + public Set getActualSubTasks(SId startingTaskID) { + SedBase foundBase = this.sedml.searchInTasksFor(startingTaskID); + if (!(foundBase instanceof RepeatedTask rTask)) throw new IllegalArgumentException("provided id does not correspond to a repeated Task"); + return this.getActualSubTasks(rTask); + } + + public Set getActualSubTasks(RepeatedTask repeatedTask) { + List subTaskBases = repeatedTask.getSubTasks().stream().map(SubTask::getTask).map(this.sedml::searchInTasksFor).toList(); + List tasks = subTaskBases.stream().filter(AbstractTask.class::isInstance).map(AbstractTask.class::cast).toList(); + if (tasks.size() != subTaskBases.size()) throw new RuntimeException("Non-tasks were filtered out of list of subtasks!"); + return new HashSet<>(tasks); + } + + + /** + * Convenience method to add a Variable and DataGenerator together.
+ * This method should be useful when only the raw form of an output is + * required, and no post-processing of the data is required.
+ *

+ * For example, for a parameter in an SBML model with id k1: + * + *

+     * String xpathAsString = new SBMLSupport().getXPathForGlobalParameter("k1");
+     * XPathTarget xpath = new XPathTarget(xpathAsString);
+     * DataGenerator dg = sed.addSimpleSpeciesAsOutput(xpath, "k1", "A param name",
+     *         t1, true);
+     * 
+ *

+ * will produce the following SED-ML: + * + *

+     *  <dataGenerator id="k1_dg" name="A param name">
+     *  <listOfVariables>
+     *     <variable id="k1" name=" A param name" target="/path/to/param/"/>
+     *  </listOfVariables>
+     *   <math:math>
+     *       <math:ci>k1</math:ci>
+     *      </math:math>
+     *  </dataGenerator>
+     * 
+ * + *

+ * + * @param variableToAdd The {@link Variable} to create a data generator for. + * @return `null` if a sufficient {@link DataGenerator} already exists, else a new {@link DataGenerator} is returned. + * @throws IllegalArgumentException if the variable has a null id, null task reference, or refers to a task not in this SedML + * @since 2.1 + */ + public DataGenerator createIdentityDataGeneratorForSpecies(final Variable variableToAdd) { + SedGeneralClass.checkNoNullArgs(variableToAdd); + // First, check the variable + if (null == variableToAdd.getId()) + throw new IllegalArgumentException("Provided variable has a null id."); + SId taskReference = variableToAdd.getTaskReference(); + if (null == taskReference) + throw new IllegalArgumentException("Variable must have task reference for use with this method."); + if (!(this.sedml.searchInTasksFor(taskReference) instanceof AbstractTask task)) + throw new IllegalArgumentException(String.format("Unable to find task `%s` in this SedML.", taskReference)); + + // Now, check if we need to add an identity Data Generator + if (this.sedml.appropriateIdentityDataGeneratorAlreadyExistsFor(variableToAdd)) + return null; + DataGenerator dg = new DataGenerator(new SId(variableToAdd.getId().string() + "_dg"), + variableToAdd.getName() != null ? String.format("Data Generator for %s", variableToAdd.getName()) : ""); + dg.setMath(Libsedml.parseFormulaString(dg.getId().string())); + dg.addVariable(variableToAdd); + return dg; + } + + public String getPathForURI() { + return this.pathForURI; + } + + public void setPathForURI(String pathForURI) { + this.pathForURI = pathForURI; + } + + public String getFileName() { + return this.fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public Model findModelById(SId id) { + SedBase foundBase = this.sedml.searchInModelsFor(id); + if (!(foundBase instanceof Model model)) return null; + return model; + } + + public org.jlibsedml.components.simulation.Simulation findSimulationById(SId id) { + SedBase foundBase = this.sedml.searchInSimulationsFor(id); + if (!(foundBase instanceof org.jlibsedml.components.simulation.Simulation sim)) return null; + return sim; + } + + public UniformTimeCourse findUniformTimeCourseById(SId id) { + SedBase foundBase = this.sedml.searchInSimulationsFor(id); + if (!(foundBase instanceof UniformTimeCourse uniformTimeCourse)) return null; + return uniformTimeCourse; + } + + public OneStep findOneStepById(SId id) { + SedBase foundBase = this.sedml.searchInSimulationsFor(id); + if (!(foundBase instanceof OneStep oneStep)) return null; + return oneStep; + } + + public AbstractTask findAbstractTaskById(SId id) { + SedBase foundBase = this.sedml.searchInTasksFor(id); + if (!(foundBase instanceof AbstractTask abstractTask)) return null; + return abstractTask; + } + + public Task findTaskById(SId id) { + SedBase foundBase = this.sedml.searchInTasksFor(id); + if (!(foundBase instanceof Task task)) return null; + return task; + } + + public Task findBaseTaskByAbstractTaskId(SId id) { + SedBase foundBase = this.sedml.searchInTasksFor(id); + if (!(foundBase instanceof AbstractTask)) throw new RuntimeException("The element `" + id + "` is not an abstract task."); + return this.getBaseTask(id); + } + + public RepeatedTask findRepeatedTaskById(SId id) { + SedBase foundBase = this.sedml.searchInTasksFor(id); + if (!(foundBase instanceof RepeatedTask repeatedTask)) return null; + return repeatedTask; + } + + public DataGenerator findDataGeneratorById(SId id) { + SedBase foundBase = this.sedml.searchInDataGeneratorsFor(id); + if (!(foundBase instanceof DataGenerator dataGenerator)) return null; + return dataGenerator; + } + + public Output findOutputById(SId id) { + SedBase foundBase = this.sedml.searchInOutputsFor(id); + if (!(foundBase instanceof Output output)) return null; + return output; + } + + public Plot findPlotById(SId id) { + SedBase foundBase = this.sedml.searchInOutputsFor(id); + if (!(foundBase instanceof Plot plot)) return null; + return plot; + } + + public Plot2D findPlot2DById(SId id) { + SedBase foundBase = this.sedml.searchInOutputsFor(id); + if (!(foundBase instanceof Plot2D plot)) return null; + return plot; + } + + public Plot3D findPlot3DById(SId id) { + SedBase foundBase = this.sedml.searchInOutputsFor(id); + if (!(foundBase instanceof Plot3D plot)) return null; + return plot; + } + + public Report findReportById(SId id) { + SedBase foundBase = this.sedml.searchInOutputsFor(id); + if (!(foundBase instanceof Report report)) return null; + return report; + } + + public Axis findAxisById(SId id) { + SedBase foundBase = this.sedml.searchInOutputsFor(id); + if (!(foundBase instanceof Axis axis)) return null; + return axis; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDMLDocument.java b/vcell-core/src/main/java/org/jlibsedml/SedMLDocument.java similarity index 52% rename from vcell-core/src/main/java/org/jlibsedml/SEDMLDocument.java rename to vcell-core/src/main/java/org/jlibsedml/SedMLDocument.java index 468f577a0e..9b198a17ee 100644 --- a/vcell-core/src/main/java/org/jlibsedml/SEDMLDocument.java +++ b/vcell-core/src/main/java/org/jlibsedml/SedMLDocument.java @@ -1,32 +1,37 @@ package org.jlibsedml; -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; - import org.jdom2.Comment; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.Namespace; import org.jdom2.output.XMLOutputter; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.Version; +import org.jlibsedml.components.model.AddXML; +import org.jlibsedml.components.model.Change; +import org.jlibsedml.components.model.ChangeXML; +import org.jlibsedml.components.model.Model; import org.jlibsedml.extensions.XMLUtils; import org.jlibsedml.validation.ValidatorController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** - * Encapsulates a {@link SedML} model and provides additional validation + * Encapsulates a {@link SedMLDataContainer} model and provides additional validation * services.
* E.g., typical usage might be: - * + * *
  * SEDMLDocument doc = new SEDMLDocument();
  * SedML sedml = doc.getSedMLModel();
@@ -40,122 +45,114 @@
  *  doc.writeDocument (myFile);
  *  String doc = doc.writeDocumentToString();
  * 
- * + * * @author Richard Adams - * + * */ -public class SEDMLDocument { - Logger log = LoggerFactory.getLogger(SEDMLDocument.class); - private List errors = new ArrayList(); +public class SedMLDocument { + private static final Logger log = LoggerFactory.getLogger(SedMLDocument.class); + private List errors = new ArrayList<>(); - private SedML sedml; + private final SedMLDataContainer sedMLDataContainer; - private boolean isValidationPerformed; + private boolean validationHasBeenPerformed; - private static final String jlibsedmlVersion = "2.2.3"; - static final String PROVENANCE = "This file was generated by jlibsedml, version " - + jlibsedmlVersion + "."; + private static final String jlibsedmlVersion = "3.0.0"; + static final String PROVENANCE = "This file was generated by jlibsedml (version " + jlibsedmlVersion + ")."; /** * No parameter can be null, errors can be an empty list. - * - * @param model - * A SedML element - * @param errors - * A non-null List of {@link SedMLError} - * @throws IllegalArgumentException - * if any arg is null. + * + * @param model A SedML element + * @param errors A non-null List of {@link SedMLError} + * @throws IllegalArgumentException if any arg is null. */ - public SEDMLDocument(SedML model, List errors) { - Assert.checkNoNullArgs(model, errors); - this.sedml = model; + public SedMLDocument(SedMLDataContainer model, List errors) { + if (null == model) throw new IllegalArgumentException("Cannot create SedMLDocument with a null Model"); + if (null == errors) errors = Collections.emptyList(); + for (SedMLError error : errors) { + if (null == error) throw new IllegalArgumentException("Sed Errors reported, but Error itself is `null`"); + } + this.sedMLDataContainer = model; this.errors = errors; } /** - * Alternative constructor for creating a {@link SEDMLDocument} - * - * @param sedML - * An already created SED-ML model object. - * @throws IllegalArgumentException - * if any arg is null. + * Alternative constructor for creating a {@link SedMLDocument} + * + * @param sedMLModel An already created SED-ML model object. + * @throws IllegalArgumentException if any arg is null. */ - public SEDMLDocument(SedML sedML) { - this(sedML, new ArrayList()); + public SedMLDocument(SedMLDataContainer sedMLModel) { + this(sedMLModel, new ArrayList<>()); } /** * Default constructor creates empty SED-ML element with default version, * currently level 1, version 1. + * * @since 1.1 */ - public SEDMLDocument() { + public SedMLDocument() { this(1, 1); } - - /** - * - * @param level The SED-ML level (1) - * @param version The SED-ML version ( 1 or 2) - * @throws IllegalArgumentException if values are invalid - * @since 2.2.3 - */ - public SEDMLDocument(int level, int version) { - if(version == 1) { - this.sedml = new SedML(level, version, Namespace.getNamespace(SEDMLTags.SEDML_L1V1_NS)); - } else if (version == 2) { - this.sedml = new SedML(level, version, Namespace.getNamespace(SEDMLTags.SEDML_L1V2_NS)); - } else { - throw new IllegalArgumentException("Invalid version must be 1 or 2"); - } - sedml.setAdditionalNamespaces(Arrays.asList(new Namespace[] { Namespace - .getNamespace(SEDMLTags.MATHML_NS_PREFIX, SEDMLTags.MATHML_NS) })); + + /** + * + * @param level The SED-ML level (1) + * @param version The SED-ML version ( 1 or 2) + * @throws IllegalArgumentException if values are invalid + * @since 2.2.3 + */ + public SedMLDocument(int level, int version) { + SedML sedML = new SedML(level, version); + this.sedMLDataContainer = new SedMLDataContainer(sedML); + this.sedMLDataContainer.addExtraNamespace(Namespace.getNamespace(SedMLTags.MATHML_NS_PREFIX, SedMLTags.MATHML_NS)); } /** * Gets a read-only list of this document's errors. - * + * * @return An unmodifiable (read-only), non-null list of this document's - * errors. + * errors. */ public List getErrors() { - return Collections.unmodifiableList(errors); + return Collections.unmodifiableList(this.errors); } /** * Returns a {@link SedMLValidationReport} if validate() has previously been * called on this object, otherwise null. - * + * * @return A SedMLValidationReport or null. */ public SedMLValidationReport getValidationReport() { - if (!isValidationPerformed) { + if (!this.validationHasBeenPerformed) { return null; } else { - return new SedMLValidationReport(errors, - getSedMLDocumentAsString(sedml)); + return new SedMLValidationReport(this.errors, getSedMLDocumentAsString(this.sedMLDataContainer)); } } /** * A boolean test as to whether the SEDML referenced by this document has * errors or not. - * + * * @return true if this document has at least one validation - * error. + * error. */ public boolean hasErrors() { - return errors.size() > 0; + return !this.errors.isEmpty(); } /** * Gets the SED-ML model contained in this document. - * - * @return A non-null {@link SedML} object + * + * @return A non-null {@link SedMLDataContainer} object */ - public SedML getSedMLModel() { - return sedml; + public SedMLDataContainer getSedMLModel() { + return this.sedMLDataContainer; } /** @@ -166,48 +163,44 @@ public SedML getSedMLModel() { * This validate method validates an existing object model of SED-ML, and * not the original input file. To validate the raw input from an external * file, use - * + * *
      * Libsedml.validate(InputStream)
      * 
- * + * * @return An unmodifiable, non-null List of {@link SedMLError} - * . - * @throws XMLException - * if the XML generated from the underlying SED-ML object is - * unable to be parsed, is unavailable or unreadable. + * . + * @throws XMLException if the XML generated from the underlying SED-ML object is + * unable to be parsed, is unavailable or unreadable. */ public List validate() throws XMLException { - Document doc = createDocument(writeDocumentToString()); - List errs = new ValidatorController().validate(sedml, doc); - errs.addAll(errors); - errors = errs; - isValidationPerformed = true; - return getErrors(); + Document doc = this.createDocument(this.writeDocumentToString()); + List errs = new ValidatorController().validate(this.sedMLDataContainer, doc); + errs.addAll(this.errors); + this.errors = errs; + this.validationHasBeenPerformed = true; + return this.getErrors(); } /** * @see Object#toString() */ public String toString() { - return "SEDML Document for " + sedml.getNotes(); + return String.format("SedML Document for SedML L%dV%d", this.sedMLDataContainer.getSedML().getLevel(), this.sedMLDataContainer.getSedML().getVersion()); } /** * Writes out a document to file. This operation will write valid and * invalid documents. To check a document is valid, call validate() and * hasErrors() before writing the document. - * - * @param file - * A {@link File} that can be written to. - * - * @throws IllegalArgumentException - * if file argument is null + * + * @param file A {@link File} that can be written to. + * @throws IllegalArgumentException if file argument is null */ public void writeDocument(File file) { - Assert.checkNoNullArgs(file); + if (null == file) throw new IllegalArgumentException("A valid file must be provided to write a document to!"); - String xmlString = getSedMLDocumentAsString(sedml); + String xmlString = getSedMLDocumentAsString(this.sedMLDataContainer); try { Libsedml.writeXMLStringToFile(xmlString, file.getAbsolutePath(), true); @@ -217,33 +210,33 @@ public void writeDocument(File file) { } } - static String getSedMLDocumentAsString(SedML sedRoot) { - SEDMLWriter producer = new SEDMLWriter(); + static String getSedMLDocumentAsString(SedMLDataContainer sedRoot) { + SedMLWriter producer = new SedMLWriter(); Element root = producer.getXML(sedRoot); root.addContent(0, new Comment(PROVENANCE)); Document sedmlDoc = new Document(); sedmlDoc.setRootElement(root); - String xmlString = SEDMLUtils.xmlToString(sedmlDoc, true); - return xmlString; + return SEDMLUtils.xmlToString(sedmlDoc, true); } /** * Writes the document contents to formatted XML format, and returns it as a * String. - * + * * @return A String of XML */ public String writeDocumentToString() { - return getSedMLDocumentAsString(sedml); + return getSedMLDocumentAsString(this.sedMLDataContainer); } /** * Getter for the SED-ML version of this document + * * @return A Version object. */ public Version getVersion() { - return new Version(sedml.getLevel(), sedml.getVersion()); + return new Version(this.sedMLDataContainer.getSedML().getLevel(), this.sedMLDataContainer.getSedML().getVersion()); } /** @@ -255,7 +248,7 @@ public Version getVersion() { * set of changes. E.g., if model3 is the desired output, then model2 must * be supplied as input. *

- * + *

* In order for an XPath expression to be applied, the namespace of the XML * to which the XPath is evaluated must be known. Since this library is * model-agnostic, this method tries to resolve the XPath prefix with a @@ -263,55 +256,49 @@ public Version getVersion() { * containing the XPath prefix in a case-insensitive match.
* For example, an XPath prefix of 'math' will match the namespace * 'http://www.w3.org/1998/Math/MathML'. The method - * canResolveXPathExpressions (String model_ID, final String originalModel); + * canResolveXPathExpressions (String model_ID, final String originalModel); * in this class provides a test as to whether prefixes can be * resolved. *

- * @param model_ID - * The id of the SEDML Model element containing the description - * whose changes are to be applied. - * @param originalModel - * A String representation of the XML of the model to be altered. - * + * + * @param modelID The id of the SEDML Model element containing the description + * whose changes are to be applied. + * @param originalModel A String representation of the XML of the model to be altered. * @return A String representation of the changed model. The original model - * will be unchanged by this operation. IF there are no changes, or - * the model_ID cannot be resolved, the original model will be - * returned unchanged. - * @throws XMLException - * if XML cannot be parsed or the XPath expression applied. - * @throws IllegalArgumentException - * if modelID is not defined in the ListOfModels for - * this document. + * will be unchanged by this operation. IF there are no changes, or + * the model_ID cannot be resolved, the original model will be + * returned unchanged. + * @throws XMLException if XML cannot be parsed or the XPath expression applied. + * @throws IllegalArgumentException if modelID is not defined in the ListOfModels for + * this document. */ - public String getChangedModel(String modelID, final String originalModel) + public static String getChangedModel(SedMLDataContainer sedml, SId modelID, final String originalModel) throws XPathExpressionException, XMLException { - String xmlString = ""; - Model m = sedml.getModelWithId(modelID); + String xmlString; + Model sedModelFound = sedml.findModelById(modelID); + if (sedModelFound == null) throw new IllegalArgumentException("Provided ID does not exist as a SedML Model!"); + if (!sedModelFound.hasChanges()) return originalModel; - if (m == null || !m.hasChanges()) { - return originalModel; - } - List changes = m.getListOfChanges(); + List changes = sedModelFound.getChanges(); try { - org.w3c.dom.Document doc = ModelTransformationUtils - .getXMLDocumentFromModelString(originalModel); + org.w3c.dom.Document doc = ModelTransformationUtils.getXMLDocumentFromModelString(originalModel); XPathFactory xpf = XPathFactory.newInstance(); XPath xpath = xpf.newXPath(); for (Change change : changes) { - org.jdom2.Document docj = createDocument(originalModel); - NamespaceContextHelper nc = new NamespaceContextHelper(docj); + Document jdomDoc = SedMLDocument.createDocument(originalModel); + NamespaceContextHelper nc = new NamespaceContextHelper(jdomDoc); nc.process(change.getTargetXPath()); xpath.setNamespaceContext(nc); - if (change.getChangeKind().equals(SEDMLTags.CHANGE_ATTRIBUTE_KIND)) { + if (change.getChangeKind().equals(SedMLTags.CHANGE_ATTRIBUTE_KIND)) { ModelTransformationUtils.applyAttributeChange(doc, xpath, change); - } else if (change.getChangeKind().equals(SEDMLTags.REMOVE_XML_KIND)) { + } else if (change.getChangeKind().equals(SedMLTags.REMOVE_XML_KIND)) { ModelTransformationUtils.deleteXMLElement(doc, change.getTargetXPath().getTargetAsString(), xpath); - } else if (change.getChangeKind().equals(SEDMLTags.ADD_XML_KIND)) { + } else if (change.getChangeKind().equals(SedMLTags.ADD_XML_KIND)) { AddXML addXML = (AddXML) change; - for (Element el : addXML.getNewXML().getXml()) { + for (Element el : addXML.getNewXML().xml()) { el.setNamespace(Namespace.NO_NAMESPACE); String elAsString = new XMLOutputter().outputString(el); log.debug(elAsString); @@ -320,7 +307,7 @@ public String getChangedModel(String modelID, final String originalModel) xpath); } } else if (change.getChangeKind().equals( - SEDMLTags.CHANGE_XML_KIND)) { + SedMLTags.CHANGE_XML_KIND)) { ChangeXML changeXML = (ChangeXML) change; ModelTransformationUtils.changeXMLElement(doc, changeXML .getNewXML(), changeXML.getTargetXPath() @@ -330,35 +317,33 @@ public String getChangedModel(String modelID, final String originalModel) } xmlString = ModelTransformationUtils.exportChangedXMLAsString(doc); } catch (Exception e) { - throw new XMLException("Error generating new model" - + e.getMessage(), e); + throw new XMLException("Error generating new model: ", e); } return xmlString; } - private org.jdom2.Document createDocument(String sedml) throws XMLException { + private static org.jdom2.Document createDocument(String sedml) throws XMLException { return new XMLUtils().readDoc(sedml); } /** * Boolean test for whether XPath expressions can be mapped to XML * namespaces in the model XML. - * - * @param modelID - * @param originalModel + * + * @param modelID id of the sedml model to interrogate + * @param originalModelXMLStr the string-representation of the XML (SBML) model to validate against * @return true if can be mapped, false otherwise. * @throws XMLException */ - public boolean canResolveXPathExpressions(String modelID, - final String originalModel) throws XMLException { - org.jdom2.Document doc = createDocument(originalModel); - Model model = sedml.getModelWithId(modelID); - List changes = model.getListOfChanges(); - for (Change change : changes) { + public boolean canResolveXPathExpressions(SId modelID, final String originalModelXMLStr) throws XMLException { + org.jdom2.Document doc = SedMLDocument.createDocument(originalModelXMLStr); + Model sedModelFound = this.sedMLDataContainer.findModelById(modelID); + if (sedModelFound == null) throw new IllegalArgumentException("Provided ID does not exist as a SedML Model!"); + for (Change change : sedModelFound.getChanges()) { XPathTarget target = change.getTargetXPath(); NamespaceContextHelper nc = new NamespaceContextHelper(doc); nc.process(target); - if (!nc.isAllXPathPrefixesMapped(target)) { + if (!nc.areAllXPathPrefixesMapped(target)) { return false; } } diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDMLElementFactory.java b/vcell-core/src/main/java/org/jlibsedml/SedMLElementFactory.java similarity index 60% rename from vcell-core/src/main/java/org/jlibsedml/SEDMLElementFactory.java rename to vcell-core/src/main/java/org/jlibsedml/SedMLElementFactory.java index ce612d7493..72abd6bc68 100644 --- a/vcell-core/src/main/java/org/jlibsedml/SEDMLElementFactory.java +++ b/vcell-core/src/main/java/org/jlibsedml/SedMLElementFactory.java @@ -7,27 +7,25 @@ * into the object model so they can be validated, by suppressing IllegalArgumentExceptions * thrown through public API. */ -class SEDMLElementFactory { +public class SedMLElementFactory { - private static SEDMLElementFactory instance; + private static SedMLElementFactory instance; private boolean isStrictCreation=true; - boolean isStrictCreation() { - return isStrictCreation; + public boolean isStrictCreation() { + return this.isStrictCreation; } - void setStrictCreation(boolean isStrictCreation) { + public void setStrictCreation(boolean isStrictCreation) { this.isStrictCreation = isStrictCreation; } - static SEDMLElementFactory getInstance() { - if(instance == null){ - instance=new SEDMLElementFactory(); - } + public static SedMLElementFactory getInstance() { + if(instance == null) instance = new SedMLElementFactory(); return instance; } - private SEDMLElementFactory(){} + private SedMLElementFactory(){} diff --git a/vcell-core/src/main/java/org/jlibsedml/SedMLReader.java b/vcell-core/src/main/java/org/jlibsedml/SedMLReader.java new file mode 100644 index 0000000000..d7449fcd8a --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/SedMLReader.java @@ -0,0 +1,833 @@ +package org.jlibsedml; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.jdom2.*; +import org.jdom2.input.SAXBuilder; +import org.jlibsedml.components.*; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.simulation.OneStep; +import org.jlibsedml.components.task.UniformRange.UniformType; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.components.algorithm.AlgorithmParameter; +import org.jlibsedml.components.model.*; +import org.jlibsedml.components.output.*; +import org.jlibsedml.components.simulation.Simulation; +import org.jlibsedml.components.simulation.SteadyState; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.task.*; +import org.jlibsedml.mathsymbols.SedMLSymbolFactory; +import org.jmathml.ASTNode; +import org.jmathml.ASTRootNode; +import org.jmathml.MathMLReader; +import org.jmathml.SymbolRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class SedMLReader { + static final Logger lg = LoggerFactory.getLogger(SedMLReader.class); + + public static SedMLDataContainer readFile(File file) throws JDOMException, IOException, XMLException { + SAXBuilder builder = new SAXBuilder(); + Document doc = builder.build(file); + Element sedRoot = doc.getRootElement(); + try { + SedMLElementFactory.getInstance().setStrictCreation(false); + SedMLReader reader = new SedMLReader(); + return reader.getSedDocument(sedRoot, file.getName()); + } finally { + SedMLElementFactory.getInstance().setStrictCreation(true); + } + } + + public static SedMLDataContainer readFile(InputStream iStream) throws JDOMException, IOException, XMLException { + return SedMLReader.readFile(iStream, null); + } + + public static SedMLDataContainer readFile(InputStream iStream, String sourceFileName) throws JDOMException, IOException, XMLException { + SAXBuilder builder = new SAXBuilder(); + Document doc = builder.build(iStream); + Element sedRoot = doc.getRootElement(); + try { + SedMLElementFactory.getInstance().setStrictCreation(false); + SedMLReader reader = new SedMLReader(); + return reader.getSedDocument(sedRoot, sourceFileName); + } finally { + SedMLElementFactory.getInstance().setStrictCreation(true); + } + } + + /* + * returns a SedML model given an Element of xml for a complete model non - + * api method + */ + public SedMLDataContainer getSedDocument(Element sedRoot) throws XMLException{ + return this.getSedDocument(sedRoot, null); + } + + /* + * returns a SedML model given an Element of xml for a complete model non - + * api method + */ + public SedMLDataContainer getSedDocument(Element sedRoot, String filename) throws XMLException { + Namespace sedNS; + SedMLDataContainer sedDoc; + SedML sedML; + SymbolRegistry.getInstance().addSymbolFactory(new SedMLSymbolFactory()); + try { + sedNS = sedRoot.getNamespace(); + Attribute idAttr = sedRoot.getAttribute(SedMLTags.ATTRIBUTE_ID); + SId idStr = SedMLReader.parseId(idAttr == null ? null : idAttr.getValue(), filename); + String nameStr = sedRoot.getAttributeValue(SedMLTags.ATTRIBUTE_NAME); + String lvlStr = sedRoot.getAttributeValue(SedMLTags.LEVEL_TAG); + String verStr = sedRoot.getAttributeValue(SedMLTags.VERSION_TAG); + int level = Integer.parseInt(lvlStr); + int version = Integer.parseInt(verStr); + sedML = new SedML(idStr, nameStr, level, version); + if (!sedML.getSedMLNamespaceURI().equals(sedNS.getURI())) + throw new XMLException(String.format("SedML namespace mismatch;'%s' != '%s' (expected)", sedNS.getURI(), sedML.getSedMLNamespaceURI())); + } catch (Exception e) { + throw new XMLException("Error loading start of SedML document : ", e); + } + sedDoc = new SedMLDataContainer(sedML); + + try { + // Get additional namespaces if mentioned : mathml, sbml, etc. + sedDoc.addAllAdditionalNamespaces(sedRoot.getAdditionalNamespaces()); + // notes and annotations + this.addNotesAndAnnotation(sedML, sedRoot); + } catch (Exception e) { + throw new XMLException("Error loading basics of SedML document : ", e); + } + + try { + // models + Element modelElementsParent; + if (null != (modelElementsParent = sedRoot.getChild(SedMLTags.MODELS, sedNS))) { + for (Element modelElement : modelElementsParent.getChildren()) { + if (!SedMLTags.MODEL_TAG.equals(modelElement.getName())) continue; + sedML.addModel(this.getModel(modelElement)); + } + } + } catch (Exception e) { + throw new XMLException("Error loading models of SedML document : ", e); + } + + try { + // simulations + Element simulationElementsParent; + if (null != (simulationElementsParent = sedRoot.getChild(SedMLTags.SIMS, sedNS))) { + for (Element simElement : simulationElementsParent.getChildren()){ + sedML.addSimulation(this.getSimulation(simElement)); + } + } + } catch (Exception e) { + throw new XMLException("Error loading simulations of SedML document : ", e); + } + + try { + // Tasks + Element taskElementsParent; + if (null != (taskElementsParent = sedRoot.getChild(SedMLTags.TASKS, sedNS))) { + for (Element taskElement : taskElementsParent.getChildren()){ + if (taskElement.getName().equals(SedMLTags.TASK_TAG)) { + sedML.addTask(this.getTask(taskElement)); + } else if (taskElement.getName().equals(SedMLTags.REPEATED_TASK_TAG)) { + sedML.addTask(this.getRepeatedTask(taskElement)); + } + //TODO: Add Parameter Estimation Task parsing here! + } + } + } catch (Exception e) { + throw new XMLException("Error loading tasks of SedML document : ", e); + } + + try { + // Data Generators + Element dataGeneratorElementsParent; + if (null != (dataGeneratorElementsParent = sedRoot.getChild(SedMLTags.DATA_GENERATORS, sedNS))) { + for (Element dataGeneratorElement : dataGeneratorElementsParent.getChildren()){ + if (!SedMLTags.DATA_GENERATOR_TAG.equals(dataGeneratorElement.getName())) continue; + sedML.addDataGenerator(this.getDataGenerator(dataGeneratorElement)); + } + } + } catch (Exception e) { + throw new XMLException("Error loading data generators of SedML document : ", e); + } + + try { + // Outputs + Element outputElementsParent; + if (null != (outputElementsParent = sedRoot.getChild(SedMLTags.OUTPUTS, sedNS))) { + for (Element outputElement : outputElementsParent.getChildren()){ + switch (outputElement.getName()) { + case SedMLTags.OUTPUT_P2D -> sedML.addOutput(this.getPlot2D(outputElement)); + case SedMLTags.OUTPUT_P3D -> sedML.addOutput(this.getPlot3D(outputElement)); + case SedMLTags.OUTPUT_REPORT -> sedML.addOutput(this.getReport(outputElement)); + default -> lg.warn("Unknown output element name: {}", outputElement.getName()); + } + } + } + + } catch (Exception e) { + throw new XMLException("Error loading outputs of sed-ml document : " + e.getMessage(), e); + } + return sedDoc; + } + + Model getModel(Element xmlEncodedModel) throws DataConversionException { + SId modelId = SedMLReader.parseId(xmlEncodedModel.getAttributeValue(SedMLTags.MODEL_ATTR_ID)); + String modelNameStr = xmlEncodedModel.getAttributeValue(SedMLTags.MODEL_ATTR_NAME); + String languageStr = xmlEncodedModel.getAttributeValue(SedMLTags.MODEL_ATTR_LANGUAGE); + String sourceStr = xmlEncodedModel.getAttributeValue(SedMLTags.MODEL_ATTR_SOURCE); + + Model sedmlModel = new Model(modelId, modelNameStr, languageStr, sourceStr); + for (Element eModelChild : xmlEncodedModel.getChildren()) { + if (!eModelChild.getName().equals(SedMLTags.CHANGES)) continue; + for (Element aChange : eModelChild.getChildren()) { + sedmlModel.addChange(this.getChange(aChange)); + } + } + this.addNotesAndAnnotation(sedmlModel, xmlEncodedModel); + return sedmlModel; + } + + Change getChange(Element xmlEncodedChange) throws DataConversionException { + Change rc = null; + switch (xmlEncodedChange.getName()) { + case SedMLTags.CHANGE_ATTRIBUTE -> { + XPathTarget changeTarget = new XPathTarget(xmlEncodedChange.getAttributeValue(SedMLTags.CHANGE_ATTR_TARGET)); + String valueToChangeTo = xmlEncodedChange.getAttributeValue(SedMLTags.CHANGE_ATTR_NEWVALUE); + rc = new ChangeAttribute(changeTarget, valueToChangeTo); + } + case SedMLTags.CHANGE_XML, SedMLTags.ADD_XML -> { + for (Element el : xmlEncodedChange.getChildren()) { + if (!el.getName().equals(SedMLTags.NEW_XML)) continue; + List xml = this.getNewXML(el); + NewXML newxml = new NewXML(xml); + XPathTarget newTarget = new XPathTarget(xmlEncodedChange.getAttributeValue(SedMLTags.CHANGE_ATTR_TARGET)); + boolean isChangeXML = SedMLTags.CHANGE_XML.equals(xmlEncodedChange.getName()); + rc = isChangeXML ? (new ChangeXML(newTarget, newxml)) : (new AddXML(newTarget, newxml)); + } + } + case SedMLTags.REMOVE_XML -> { + String changeAttributeTarget = xmlEncodedChange.getAttributeValue(SedMLTags.CHANGE_ATTR_TARGET); + rc = new RemoveXML(new XPathTarget(changeAttributeTarget)); + } + case SedMLTags.COMPUTE_CHANGE -> { + ASTRootNode math = null; + List vars = new ArrayList<>(); + List params = new ArrayList<>(); + for (Element el : xmlEncodedChange.getChildren()) { + switch (el.getName()) { + case SedMLTags.CHANGE_ATTR_MATH -> + math = (ASTRootNode) new MathMLReader().parseMathML(el); + case SedMLTags.DATAGEN_ATTR_VARS_LIST -> { + for (Element eVariable : el.getChildren()) { + if (!SedMLTags.DATAGEN_ATTR_VARIABLE.equals(eVariable.getName())) continue; + vars.add(this.createVariableWithinComputeChange(eVariable)); + } + } + case SedMLTags.DATAGEN_ATTR_PARAMS_LIST -> { + for (Element eParameter : el.getChildren()) { + if (!SedMLTags.DATAGEN_ATTR_PARAMETER.equals(eParameter.getName())) continue; + params.add(this.createParameter(eParameter)); + } + } + } + // TODO: variable and parameter need to be also loaded here + } + XPathTarget newChangeTarget = new XPathTarget(xmlEncodedChange.getAttributeValue(SedMLTags.CHANGE_ATTR_TARGET)); + ComputeChange cc = new ComputeChange(newChangeTarget, math); + cc.setListOfParameters(params); + cc.setListOfVariables(vars); + rc = cc; + } + } + this.addNotesAndAnnotation(rc, xmlEncodedChange); + return rc; + } + + // The Change within a repeated task (SetChange) + private void addChanges(RepeatedTask t, Element element) throws DataConversionException { + for (Element eChild : element.getChildren()) { + if (!SedMLTags.SET_VALUE.equals(eChild.getName())) { lg.warn("Unexpected non-SetValue encountered:" + eChild); continue; } + XPathTarget target = new XPathTarget(eChild.getAttributeValue(SedMLTags.SET_VALUE_ATTR_TARGET)); + SId rangeId = SedMLReader.parseId(eChild.getAttributeValue(SedMLTags.SET_VALUE_ATTR_RANGE_REF)); + SId modelId = SedMLReader.parseId(eChild.getAttributeValue(SedMLTags.SET_VALUE_ATTR_MODEL_REF)); + SetValue sv = new SetValue(target, rangeId, modelId); + this.setValueContent(sv, eChild); + t.addChange(sv); + } + } + + private void setValueContent(SetValue c, Element element) throws DataConversionException { + for (Element eChild : element.getChildren()) { + switch (eChild.getName()) { + case SedMLTags.CHANGE_ATTR_MATH -> { + ASTNode math = new MathMLReader().parseMathML(eChild); + c.setMath(math); + } + case SedMLTags.DATAGEN_ATTR_VARS_LIST -> { + List lVariables = eChild.getChildren(); + for (Element eVariable : lVariables) { + if (!SedMLTags.DATAGEN_ATTR_VARIABLE.equals(eVariable.getName())) continue; + c.addVariable(this.createVariableWithinSetValue(eVariable)); + } + } + case SedMLTags.DATAGEN_ATTR_PARAMS_LIST -> { + List lParameters = eChild.getChildren(); + for (Element eParameter : lParameters) { + if (!SedMLTags.DATAGEN_ATTR_PARAMETER.equals(eParameter.getName())) continue; + c.addParameter(this.createParameter(eParameter)); + } + } + default -> lg.warn("Unexpected " + eChild); + } + } + } + + + private void addNotesAndAnnotation(SedBase sedbase, Element xmlElement) { + List children = xmlElement.getChildren(); + + for (Element eChild : children) { + if (SedMLTags.NOTES.equals(eChild.getName())) sedbase.setNotes(new Notes(eChild)); + if (SedMLTags.ANNOTATION.equals(eChild.getName())) sedbase.setAnnotation(this.getAnnotation(eChild)); + } + + sedbase.setMetaId(xmlElement.getAttributeValue(SedMLTags.META_ID_ATTR_NAME)); + } + + Simulation getSimulation(Element simElement) { + Simulation s; + List children = simElement.getChildren(); + Algorithm requestedAlgorithm = null; + for (Element el : children) { + if (!SedMLTags.ALGORITHM_TAG.equals(el.getName())) continue; + requestedAlgorithm = this.getAlgorithm(el); + break; + } + String simElementName = simElement.getName(); + if (null == simElementName){ + throw new IllegalArgumentException("Sim 'name' element is null"); + } + switch (simElementName) { + case SedMLTags.SIM_UTC -> { + String attributeValue = simElement.getAttributeValue(SedMLTags.UTCA_STEPS_NUM); + if (null == attributeValue) attributeValue = simElement.getAttributeValue(SedMLTags.UTCA_POINTS_NUM); // UTCA_POINTS_NUM deprecated in version 4 + if (null == attributeValue) throw new IllegalArgumentException("Number of UTC Time points cannot be determined."); + + s = new UniformTimeCourse( + SedMLReader.parseId(simElement.getAttributeValue(SedMLTags.SIM_ATTR_ID)), + simElement.getAttributeValue(SedMLTags.SIM_ATTR_NAME), + Double.parseDouble(simElement.getAttributeValue(SedMLTags.UTCA_INIT_T)), + Double.parseDouble(simElement.getAttributeValue(SedMLTags.UTCA_OUT_START_T)), + Double.parseDouble(simElement.getAttributeValue(SedMLTags.UTCA_OUT_END_T)), + Integer.parseInt(attributeValue), + requestedAlgorithm + ); + } + case SedMLTags.SIM_ONE_STEP -> s = new OneStep( + SedMLReader.parseId(simElement.getAttributeValue(SedMLTags.SIM_ATTR_ID)), + simElement.getAttributeValue(SedMLTags.SIM_ATTR_NAME), + requestedAlgorithm, + Double.parseDouble(simElement.getAttributeValue(SedMLTags.ONE_STEP_STEP)) + ); + case SedMLTags.SIM_STEADY_STATE -> s = new SteadyState( + SedMLReader.parseId(simElement.getAttributeValue(SedMLTags.SIM_ATTR_ID)), + simElement.getAttributeValue(SedMLTags.SIM_ATTR_NAME), + requestedAlgorithm + ); + case SedMLTags.SIM_ANALYSIS -> +// s = new SteadyState(simElement.getAttributeValue(SEDMLTags.SIM_ATTR_ID), +// simElement.getAttributeValue(SEDMLTags.SIM_ATTR_NAME), alg); + throw new UnsupportedOperationException("Analysis simulations not yet supported"); + default -> throw new UnsupportedOperationException("Unknown simulation: " + simElementName); + } + this.addNotesAndAnnotation(s, simElement); + + return s; + } + + Algorithm getAlgorithm(Element algorithmElement) { + Algorithm alg = new Algorithm(algorithmElement.getAttributeValue(SedMLTags.ALGORITHM_ATTR_KISAOID)); + this.addNotesAndAnnotation(alg, algorithmElement); + for (Element eChild : algorithmElement.getChildren()) { + if (!SedMLTags.ALGORITHM_PARAMETER_LIST.equals(eChild.getName())) { + lg.warn("Unexpected " + eChild); + continue; + } + this.addAlgorithmParameters(alg, eChild); + } + return alg; + } + + private void addAlgorithmParameters(Algorithm a, Element element) { + for (Element eChild : element.getChildren()) { + if (!SedMLTags.ALGORITHM_PARAMETER_TAG.equals(eChild.getName())) { + lg.warn("Unexpected " + eChild); + continue; + } + AlgorithmParameter ap = new AlgorithmParameter( + eChild.getAttributeValue(SedMLTags.ALGORITHM_PARAMETER_KISAOID), + eChild.getAttributeValue(SedMLTags.ALGORITHM_PARAMETER_VALUE) + ); + a.addAlgorithmParameter(ap); + } + } + + Task getTask(Element taskElement) { + Task t; + t = new Task( + SedMLReader.parseId(taskElement.getAttributeValue(SedMLTags.TASK_ATTR_ID)), + taskElement.getAttributeValue(SedMLTags.TASK_ATTR_NAME), + SedMLReader.parseId(taskElement.getAttributeValue(SedMLTags.TASK_ATTR_MODELREF)), + SedMLReader.parseId(taskElement.getAttributeValue(SedMLTags.TASK_ATTR_SIMREF)) + ); + // notes and annotations + this.addNotesAndAnnotation(t, taskElement); + + return t; + } + + RepeatedTask getRepeatedTask(Element taskElement) throws DataConversionException, XMLException { + RepeatedTask t; + String resetModel = taskElement.getAttributeValue(SedMLTags.REPEATED_TASK_RESET_MODEL); + boolean bResetModel = resetModel == null || resetModel.equals("true"); + t = new RepeatedTask( + SedMLReader.parseId(taskElement.getAttributeValue(SedMLTags.TASK_ATTR_ID)), + taskElement.getAttributeValue(SedMLTags.TASK_ATTR_NAME), + bResetModel, + SedMLReader.parseId(taskElement.getAttributeValue(SedMLTags.REPEATED_TASK_ATTR_RANGE)) + ); + + this.addNotesAndAnnotation(t, taskElement); // notes and annotations + + for (Element eChild : taskElement.getChildren()) { + String repeatedTaskChildName = eChild.getName(); + if (SedMLTags.REPEATED_TASK_RANGES_LIST.equals(repeatedTaskChildName)) { + this.addRanges(t, eChild); + } else if (SedMLTags.REPEATED_TASK_CHANGES_LIST.equals(repeatedTaskChildName)) { + this.addChanges(t, eChild); + } else if (SedMLTags.REPEATED_TASK_SUBTASKS_LIST.equals(repeatedTaskChildName)) { + this.addSubTasks(t, eChild); + } else { + lg.warn("Unexpected " + eChild); + } + } + return t; + } + + private void addSubTasks(RepeatedTask t, Element element) throws XMLException { + for (Element eChild : element.getChildren()) { + if (!SedMLTags.SUBTASK_TAG.equals(eChild.getName())) { + lg.warn("Unexpected " + eChild); continue; + } + Attribute orderAttr = eChild.getAttribute(SedMLTags.SUBTASK_ATTR_ORDER); + SubTask s; + try { + s = new SubTask( + orderAttr == null ? null : Integer.parseInt(orderAttr.getValue()), + SedMLReader.parseId(eChild.getAttributeValue(SedMLTags.SUBTASK_ATTR_TASK)) + ); + } catch (NumberFormatException e) { + throw new XMLException("Invalid type for SubTask: order cannot be parsed into an integer"); + } + t.addSubtask(s); + } + } + + private void addRanges(RepeatedTask task, Element element) throws DataConversionException { + for (Element eChild : element.getChildren()) { + String childName = eChild.getName(); + if (null == childName) throw new IllegalArgumentException("Child 'name' element is null"); + Range range; + switch (childName) { + case SedMLTags.VECTOR_RANGE_TAG -> { + range = new VectorRange(SedMLReader.parseId(eChild.getAttributeValue(SedMLTags.RANGE_ATTR_ID))); + this.addVectorRangeValues((VectorRange)range, eChild); + } + case SedMLTags.UNIFORM_RANGE_TAG -> { + String numRangePointsAsString = eChild.getAttributeValue(SedMLTags.UNIFORM_RANGE_ATTR_NUMP); + if (numRangePointsAsString == null) numRangePointsAsString = eChild.getAttributeValue(SedMLTags.UNIFORM_RANGE_ATTR_NUMS); + if (numRangePointsAsString == null) throw new IllegalArgumentException("Number of Range points cannot be determined."); + + range = new UniformRange( + SedMLReader.parseId(eChild.getAttributeValue(SedMLTags.RANGE_ATTR_ID)), + Double.parseDouble(eChild.getAttributeValue(SedMLTags.UNIFORM_RANGE_ATTR_START)), + Double.parseDouble(eChild.getAttributeValue(SedMLTags.UNIFORM_RANGE_ATTR_END)), + Integer.parseInt(numRangePointsAsString), + UniformType.fromString(eChild.getAttributeValue(SedMLTags.UNIFORM_RANGE_ATTR_TYPE)) + ); + + } + case SedMLTags.FUNCTIONAL_RANGE_TAG -> { + SId id = SedMLReader.parseId(eChild.getAttributeValue(SedMLTags.RANGE_ATTR_ID)); + SId index = SedMLReader.parseId(eChild.getAttributeValue(SedMLTags.FUNCTIONAL_RANGE_INDEX)); + range = new FunctionalRange(id, index); + this.addFunctionalRangeLists((FunctionalRange)range, eChild); + } + default -> { + lg.warn("Unexpected range type {}", eChild); + continue; + } + } + task.addRange(range); + } + } + + private void addFunctionalRangeLists(FunctionalRange fr, Element element) + throws DataConversionException { + String childName; + List children = element.getChildren(); + for (Element eChild : children) { + if (null == (childName = eChild.getName())) throw new IllegalArgumentException("Child 'name' element is null"); + switch (childName) { + case SedMLTags.FUNCTIONAL_RANGE_VAR_LIST -> this.addFunctionalRangeVariable(fr, eChild); + case SedMLTags.FUNCTIONAL_RANGE_PAR_LIST -> this.addFunctionalRangeParameter(fr, eChild); + case SedMLTags.FUNCTION_MATH_TAG -> { + ASTNode math = new MathMLReader().parseMathML(eChild); + fr.setMath(math); + } + default -> lg.warn("Unexpected " + eChild); + } + } + } + + private void addFunctionalRangeVariable(FunctionalRange fr, Element element) { + for (Element eChild : element.getChildren()) { + if (!SedMLTags.DATAGEN_ATTR_VARIABLE.equals(eChild.getName())) { + lg.warn("Unexpected " + eChild); continue; + } + fr.addVariable(this.createVariableWithinFunctionalRange(eChild)); + } + } + + private void addFunctionalRangeParameter(FunctionalRange fr, Element element) throws DataConversionException { + for (Element eChild : element.getChildren()) { + if (!SedMLTags.DATAGEN_ATTR_PARAMETER.equals(eChild.getName())) { + lg.warn("Unexpected " + eChild); continue; + } + fr.addParameter(this.createParameter(eChild)); + } + } + + private void addVectorRangeValues(VectorRange vr, Element element) { + for (Element eChild : element.getChildren()) { + if (!SedMLTags.VECTOR_RANGE_VALUE_TAG.equals(eChild.getName())) { + lg.warn("Unexpected " + eChild); continue; + } + vr.addValue(Double.parseDouble(eChild.getText())); + } + } + + DataGenerator getDataGenerator(Element dataGenElement) throws DataConversionException { + ASTNode math = null; + DataGenerator d = new DataGenerator( + SedMLReader.parseId(dataGenElement.getAttributeValue(SedMLTags.DATAGEN_ATTR_ID)), + dataGenElement.getAttributeValue(SedMLTags.DATAGEN_ATTR_NAME) + ); + // eDataGenerator.getAttributeValue(MiaseMLTags.DGA_MATH)); + for (Element eDataGeneratorChild : dataGenElement.getChildren()) { + switch (eDataGeneratorChild.getName()) { + case SedMLTags.DATAGEN_ATTR_VARS_LIST -> { + for (Element eVariable : eDataGeneratorChild.getChildren()) { + if (!SedMLTags.DATAGEN_ATTR_VARIABLE.equals(eVariable.getName())) continue; + d.addVariable(this.createVariableWithinDataGenerator(eVariable)); + } + } + case SedMLTags.DATAGEN_ATTR_PARAMS_LIST -> { + for (Element eParameter : eDataGeneratorChild.getChildren()) { + if (!SedMLTags.DATAGEN_ATTR_PARAMETER.equals(eParameter.getName())) continue; + d.addParameter(this.createParameter(eParameter)); + } + } + case SedMLTags.DATAGEN_ATTR_MATH -> math = new MathMLReader().parseMathML(eDataGeneratorChild); + } + } + d.setMath(math); + // notes and annotations + this.addNotesAndAnnotation(d, dataGenElement); + + return d; + } + + Parameter createParameter(Element eParameter) throws DataConversionException { + Parameter p = new Parameter( + SedMLReader.parseId(eParameter.getAttributeValue(SedMLTags.PARAMETER_ID)), + eParameter.getAttributeValue(SedMLTags.PARAMETER_NAME), + eParameter.getAttribute(SedMLTags.PARAMETER_VALUE).getDoubleValue() + ); + this.addNotesAndAnnotation(p, eParameter); + return p; + } + + Variable createVariableWithinDataGenerator(Element eVariable) { + return this.createVariable(eVariable, false, true); + } + + Variable createVariableWithinComputeChange(Element eVariable) { + return this.createVariable(eVariable, true, false); + } + + Variable createVariableWithinFunctionalRange(Element eVariable) { + return this.createVariable(eVariable, false, false); + } + + Variable createVariableWithinSetValue(Element eVariable) { + return this.createVariable(eVariable, false, false); + } + + + private Variable createVariable(Element eVariable, boolean requireModelRef, boolean requireTaskRef) { + SId varId = SedMLReader.parseId(eVariable.getAttributeValue(SedMLTags.VARIABLE_ID)); + String varName = eVariable.getAttributeValue(SedMLTags.VARIABLE_NAME); + Attribute symbolAttr = eVariable.getAttribute(SedMLTags.VARIABLE_SYMBOL); + boolean hasSymbol = symbolAttr != null; + Attribute targetAttr = eVariable.getAttribute(SedMLTags.VARIABLE_TARGET); + SId modelRef = SedMLReader.parseId(eVariable.getAttributeValue(SedMLTags.VARIABLE_MODEL)); + if (requireModelRef && modelRef == null) throw new IllegalArgumentException("maskRef attribute is null; either the document is invalid, or this is the incorrect method to call."); + SId taskRef = SedMLReader.parseId(eVariable.getAttributeValue(SedMLTags.VARIABLE_TASK)); + if (requireTaskRef && taskRef == null) throw new IllegalArgumentException("taskRef attribute is null; either the document is invalid, or this is the incorrect method to call."); + + Variable v; // Can't condense; these use two different signatures + if (hasSymbol) v = new Variable(varId, varName, modelRef, taskRef, VariableSymbol.getVariableSymbolFor(symbolAttr.getValue())); + else v = new Variable(varId, varName, modelRef, taskRef, targetAttr.getValue()); + + this.addNotesAndAnnotation(v, eVariable); + return v; + } + + private Plot2D getPlot2D(Element ePlot2D) { + Plot2D p2d = new Plot2D( + SedMLReader.parseId(ePlot2D.getAttributeValue(SedMLTags.OUTPUT_ID)), + ePlot2D.getAttributeValue(SedMLTags.OUTPUT_NAME) + ); + Attribute showLegendAttr = ePlot2D.getAttribute(SedMLTags.OUTPUT_LEGEND); + if (showLegendAttr != null) p2d.setUseLegend(Boolean.parseBoolean(showLegendAttr.getValue())); + Attribute plotHeightAttr = ePlot2D.getAttribute(SedMLTags.OUTPUT_HEIGHT); + if (plotHeightAttr != null) p2d.setPlotHeight(Double.parseDouble(plotHeightAttr.getValue())); + Attribute plotWidthAttr = ePlot2D.getAttribute(SedMLTags.OUTPUT_WIDTH); + if (plotWidthAttr != null) p2d.setPlotWidth(Double.parseDouble(plotWidthAttr.getValue())); + + for (Element ePlot2DChild : ePlot2D.getChildren()) { + if (SedMLTags.OUTPUT_CURVES_LIST.equals(ePlot2DChild.getName())){ + for (Element aCurve : ePlot2DChild.getChildren()) { + if (!SedMLTags.OUTPUT_CURVE.equals(aCurve.getName())) continue; + p2d.addCurve(this.getCurve(aCurve)); + } + } else if (SedMLTags.AXIS_X.equals(ePlot2DChild.getName())){ + p2d.setXAxis(this.getXAxis(ePlot2DChild)); + } else if (SedMLTags.AXIS_Y.equals(ePlot2DChild.getName())){ + p2d.setYAxis(this.getYAxis(ePlot2DChild)); + } else if (SedMLTags.AXIS_RIGHT_Y.equals(ePlot2DChild.getName())){ + p2d.setRightYAxis(this.getRightYAxis(ePlot2DChild)); + } + } + this.addNotesAndAnnotation(p2d, ePlot2D); + return p2d; + } + + private Plot3D getPlot3D(Element ePlot3D) { + Plot3D p3d = new Plot3D( + SedMLReader.parseId(ePlot3D.getAttributeValue(SedMLTags.OUTPUT_ID)), + ePlot3D.getAttributeValue(SedMLTags.OUTPUT_NAME) + ); + + Attribute showLegendAttr = ePlot3D.getAttribute(SedMLTags.OUTPUT_LEGEND); + if (showLegendAttr != null) p3d.setUseLegend(Boolean.parseBoolean(showLegendAttr.getValue())); + Attribute plotHeightAttr = ePlot3D.getAttribute(SedMLTags.OUTPUT_HEIGHT); + if (plotHeightAttr != null) p3d.setPlotHeight(Double.parseDouble(plotHeightAttr.getValue())); + Attribute plotWidthAttr = ePlot3D.getAttribute(SedMLTags.OUTPUT_WIDTH); + if (plotWidthAttr != null) p3d.setPlotWidth(Double.parseDouble(plotWidthAttr.getValue())); + + for (Element ePlot3DChild : ePlot3D.getChildren()) { + if (SedMLTags.OUTPUT_SURFACES_LIST.equals(ePlot3DChild.getName())) { + for (Element aSurface : ePlot3DChild.getChildren()) { + if (!SedMLTags.OUTPUT_SURFACE.equals(aSurface.getName())) continue; + p3d.addSurface(this.getSurface(aSurface)); + } + } else if (SedMLTags.AXIS_X.equals(ePlot3DChild.getName())){ + p3d.setXAxis(this.getXAxis(ePlot3DChild)); + } else if (SedMLTags.AXIS_Y.equals(ePlot3DChild.getName())){ + p3d.setYAxis(this.getYAxis(ePlot3DChild)); + } else if (SedMLTags.AXIS_RIGHT_Y.equals(ePlot3DChild.getName())){ + p3d.setZAxis(this.getZAxis(ePlot3DChild)); + } + + } + this.addNotesAndAnnotation(p3d, ePlot3D); + return p3d; + } + + private XAxis getXAxis(Element eXAxis) { + XAxis xAxis = new XAxis( + SedMLReader.parseId(eXAxis.getAttributeValue(SedMLTags.AXIS_ID)), + eXAxis.getAttributeValue(SedMLTags.AXIS_NAME), + Axis.Type.fromTag(eXAxis.getAttributeValue(SedMLTags.AXIS_TYPE)) + ); + this.updateGeneralAxis(xAxis, eXAxis); + return xAxis; + } + + private YAxis getYAxis(Element eYAxis) { + YAxis yAxis = new YAxis( + SedMLReader.parseId(eYAxis.getAttributeValue(SedMLTags.AXIS_ID)), + eYAxis.getAttributeValue(SedMLTags.AXIS_NAME), + Axis.Type.fromTag(eYAxis.getAttributeValue(SedMLTags.AXIS_TYPE)) + ); + this.updateGeneralAxis(yAxis, eYAxis); + return yAxis; + } + + private ZAxis getZAxis(Element eZAxis) { + ZAxis zAxis = new ZAxis( + SedMLReader.parseId(eZAxis.getAttributeValue(SedMLTags.AXIS_ID)), + eZAxis.getAttributeValue(SedMLTags.AXIS_NAME), + Axis.Type.fromTag(eZAxis.getAttributeValue(SedMLTags.AXIS_TYPE)) + ); + this.updateGeneralAxis(zAxis, eZAxis); + return zAxis; + } + + private RightYAxis getRightYAxis(Element eRightYAxis) { + RightYAxis rightYAxis = new RightYAxis( + SedMLReader.parseId(eRightYAxis.getAttributeValue(SedMLTags.AXIS_ID)), + eRightYAxis.getAttributeValue(SedMLTags.AXIS_NAME), + Axis.Type.fromTag(eRightYAxis.getAttributeValue(SedMLTags.AXIS_TYPE)) + ); + this.updateGeneralAxis(rightYAxis, eRightYAxis); + return rightYAxis; + } + + private void updateGeneralAxis(Axis axis, Element eAxis){ + Attribute minAttr = eAxis.getAttribute(SedMLTags.AXIS_MIN); + if (minAttr != null) axis.setMin(Double.parseDouble(minAttr.getValue())); + Attribute maxAttr = eAxis.getAttribute(SedMLTags.AXIS_MAX); + if (maxAttr != null) axis.setMax(Double.parseDouble(maxAttr.getValue())); + Attribute gridAttr = eAxis.getAttribute(SedMLTags.AXIS_GRID); + if (gridAttr != null) axis.setGrid(Boolean.parseBoolean(gridAttr.getValue())); + //Attribute styleAttr = eAxis.getAttribute(SedMLTags.AXIS_STYLE); + // Not implemented yet... + Attribute reverseAttr = eAxis.getAttribute(SedMLTags.AXIS_REVERSE); + if (reverseAttr != null) axis.setReverse(Boolean.parseBoolean(reverseAttr.getValue())); + } + + private Report getReport(Element eReport) { + Report r = new Report( + SedMLReader.parseId(eReport.getAttributeValue(SedMLTags.OUTPUT_ID)), + eReport.getAttributeValue(SedMLTags.OUTPUT_NAME) + ); + for (Element eReportDChild : eReport.getChildren()) { + if (!SedMLTags.OUTPUT_DATASETS_LIST.equals(eReportDChild.getName())) continue; + for (Element aDataSet : eReportDChild.getChildren()) { + if (!SedMLTags.OUTPUT_DATASET.equals(aDataSet.getName())) continue; + r.addDataSet(this.getDataset(aDataSet)); + } + } + + this.addNotesAndAnnotation(r, eReport); + return r; + } + + DataSet getDataset(Element aDataSet) { + DataSet ds = new DataSet( + SedMLReader.parseId(aDataSet.getAttributeValue(SedMLTags.OUTPUT_ID)), + aDataSet.getAttributeValue(SedMLTags.OUTPUT_NAME), + aDataSet.getAttributeValue(SedMLTags.OUTPUT_DATASET_LABEL), + SedMLReader.parseId(aDataSet.getAttributeValue(SedMLTags.OUTPUT_DATA_REFERENCE)) + ); + this.addNotesAndAnnotation(ds, aDataSet); + return ds; + } + + Curve getCurve(Element aCurve) { + SId id = SedMLReader.parseId(aCurve.getAttributeValue(SedMLTags.OUTPUT_ID)); + String name = aCurve.getAttributeValue(SedMLTags.OUTPUT_NAME); + SId xDataReference = SedMLReader.parseId(aCurve.getAttributeValue(SedMLTags.OUTPUT_DATA_REFERENCE_X)); + SId yDataReference = SedMLReader.parseId(aCurve.getAttributeValue(SedMLTags.OUTPUT_DATA_REFERENCE_Y)); + + Attribute logXAttr = aCurve.getAttribute(SedMLTags.OUTPUT_LOG_X); + Attribute logYAttr = aCurve.getAttribute(SedMLTags.OUTPUT_LOG_Y); + Attribute typeAttr = aCurve.getAttribute(SedMLTags.OUTPUT_TYPE); + Attribute orderAttr = aCurve.getAttribute(SedMLTags.OUTPUT_ORDER); + //Attribute styleAttr = ...; //Not Yet Implemented + Attribute yAxisAttr = aCurve.getAttribute(SedMLTags.OUTPUT_RIGHT_Y_AXIS); + Attribute xErrUpAttr = aCurve.getAttribute(SedMLTags.OUTPUT_ERROR_X_UPPER); + Attribute xErrLowAttr = aCurve.getAttribute(SedMLTags.OUTPUT_ERROR_X_LOWER); + Attribute yErrUpAttr = aCurve.getAttribute(SedMLTags.OUTPUT_ERROR_Y_UPPER); + Attribute yErrLowAttr = aCurve.getAttribute(SedMLTags.OUTPUT_ERROR_Y_LOWER); + + Boolean logScaleXAxis = null == logXAttr ? null: Boolean.valueOf(logXAttr.getValue()); + Boolean logScaleYAxis = null == logYAttr ? null: Boolean.valueOf(logYAttr.getValue()); + Curve.Type type = null == typeAttr ? Curve.Type.POINTS: Curve.Type.fromTag(typeAttr.getValue()); + Integer order = null == orderAttr ? null: Integer.valueOf(orderAttr.getValue()); + // SId style; Not Yet Implemented + AbstractCurve.YAxisAlignment yAxis = null == yAxisAttr ? AbstractCurve.YAxisAlignment.NOT_APPLICABLE : AbstractCurve.YAxisAlignment.fromTag(yAxisAttr.getValue()) ; + SId xErrorUpper = null == xErrUpAttr ? null: SedMLReader.parseId(xErrUpAttr.getValue()); + SId xErrorLower = null == xErrLowAttr ? null: SedMLReader.parseId(xErrLowAttr.getValue()); + SId yErrorUpper = null == yErrUpAttr ? null: SedMLReader.parseId(yErrUpAttr.getValue()); + SId yErrorLower = null == yErrLowAttr ? null: SedMLReader.parseId(yErrLowAttr.getValue()); + + Curve c = new Curve(id, name, xDataReference, yDataReference, logScaleXAxis, logScaleYAxis, type, order, + null, yAxis, xErrorUpper, xErrorLower, yErrorUpper, yErrorLower); + this.addNotesAndAnnotation(c, aCurve); + return c; + + } + + Surface getSurface(Element aSurface) { + SId id = SedMLReader.parseId(aSurface.getAttributeValue(SedMLTags.OUTPUT_ID)); + String name = aSurface.getAttributeValue(SedMLTags.OUTPUT_NAME); + SId xDataReference = SedMLReader.parseId(aSurface.getAttributeValue(SedMLTags.OUTPUT_DATA_REFERENCE_X)); + SId yDataReference = SedMLReader.parseId(aSurface.getAttributeValue(SedMLTags.OUTPUT_DATA_REFERENCE_Y)); + SId zDataReference = SedMLReader.parseId(aSurface.getAttributeValue(SedMLTags.OUTPUT_DATA_REFERENCE_Z)); + + Attribute logXAttr = aSurface.getAttribute(SedMLTags.OUTPUT_LOG_X); + Attribute logYAttr = aSurface.getAttribute(SedMLTags.OUTPUT_LOG_Y); + Attribute logZAttr = aSurface.getAttribute(SedMLTags.OUTPUT_LOG_Z); + Attribute typeAttr = aSurface.getAttribute(SedMLTags.OUTPUT_TYPE); + Attribute orderAttr = aSurface.getAttribute(SedMLTags.OUTPUT_ORDER); + //Attribute styleAttr = ...; //Not Yet Implemented + + Boolean logScaleXAxis = null == logXAttr ? null: Boolean.valueOf(logXAttr.getValue()); + Boolean logScaleYAxis = null == logYAttr ? null: Boolean.valueOf(logYAttr.getValue()); + Boolean logScaleZAxis = null == logZAttr ? null: Boolean.valueOf(logZAttr.getValue()); + Surface.Type type = null == typeAttr ? null: Surface.Type.fromTag(typeAttr.getValue()); + Integer order = null == orderAttr ? null: Integer.valueOf(orderAttr.getValue()); + // SId style; Not Yet Implemented + Surface s = new Surface(id, name, xDataReference, yDataReference, zDataReference, logScaleXAxis, logScaleYAxis, logScaleZAxis, null, type, order); + this.addNotesAndAnnotation(s, aSurface); + return s; + } + + List getNewXML(Element newXMLElement) { + List rc = new ArrayList<>(); + for (int i = 0; i < newXMLElement.getChildren().size(); i++) { + rc.add(newXMLElement.getChildren().get(0).detach()); + } + return rc; + } + + Annotation getAnnotation(Element annotationElement) { + return new Annotation(annotationElement.getChildren().get(0).detach()); + } + + private static SId parseId(String id) { + return null == id ? null : new SId(id); + } + + // This one attempts to create an id if one doesn't exist, but a file name is provided! + private static SId parseId(String id, String filename){ + if (null != id) return new SId(id); + if (filename == null) return null; + String alternativeFilename = filename.substring(0, filename.lastIndexOf(".")); + if (!alternativeFilename.matches("[a-zA-z0-9_]+")) return null; + return new SId(alternativeFilename); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/SEDMLTags.java b/vcell-core/src/main/java/org/jlibsedml/SedMLTags.java similarity index 54% rename from vcell-core/src/main/java/org/jlibsedml/SEDMLTags.java rename to vcell-core/src/main/java/org/jlibsedml/SedMLTags.java index 2125bfe770..c81762caa4 100644 --- a/vcell-core/src/main/java/org/jlibsedml/SEDMLTags.java +++ b/vcell-core/src/main/java/org/jlibsedml/SedMLTags.java @@ -2,71 +2,81 @@ /** * This class contains all the XML tags and attribute names in a SEDML document. */ -public class SEDMLTags { +public class SedMLTags { - private SEDMLTags(){} + private SedMLTags(){} // SBML, MathML, SEDML namespaces public static final String SEDML_L1V1_NS = "http://sed-ml.org/"; public static final String SEDML_L1V2_NS = "http://sed-ml.org/sed-ml/level1/version2"; public static final String SEDML_L1V3_NS = "http://sed-ml.org/sed-ml/level1/version3"; public static final String SEDML_L1V4_NS = "http://sed-ml.org/sed-ml/level1/version4"; + public static final String SEDML_L1V5_NS = "http://sed-ml.org/sed-ml/level1/version5"; public static final String SBML_NS = "http://www.sbml.org/sbml/level2"; public static final String SBML_NS_L2V4 = "http://www.sbml.org/sbml/level2/version4"; public static final String MATHML_NS = "http://www.w3.org/1998/Math/MathML"; public static final String XHTML_NS = "http://www.w3.org/1999/xhtml"; // namespace prefixes: - public static final String MATHML_NS_PREFIX = "math"; - public static final String SBML_NS_PREFIX = "sbml"; + public static final String MATHML_NS_PREFIX = "math"; + public static final String SBML_NS_PREFIX = "sbml"; - public static final String ROOT_NODE_TAG = "sedML"; - public static final String SED = "Sed"; - public static final String VERSION_TAG = "version"; - public static final String LEVEL_TAG = "level"; + public static final String SED_ML_ROOT = "sedML"; + public static final String SED = "Sed"; + public static final String VERSION_TAG = "version"; + public static final String LEVEL_TAG = "level"; + public static final String ATTRIBUTE_ID = "id"; + public static final String ATTRIBUTE_NAME = "name"; - public static final String NOTES = "notes"; - public static final String ANNOTATION = "annotation"; - public static final String META_ID_ATTR_NAME = "metaid"; - public static final String MODELS = "listOfModels"; - public static final String SIMS = "listOfSimulations"; - public static final String TASKS = "listOfTasks"; - public static final String DATAGENERATORS = "listOfDataGenerators"; - public static final String OUTPUTS = "listOfOutputs"; + public static final String NOTES = "notes"; + public static final String ANNOTATION = "annotation"; + public static final String META_ID_ATTR_NAME = "metaid"; + public static final String DATA_DESCRIPTIONS = "listOfDataDescriptions"; + public static final String MODELS = "listOfModels"; + public static final String SIMS = "listOfSimulations"; + public static final String TASKS = "listOfTasks"; + public static final String DATA_GENERATORS = "listOfDataGenerators"; + public static final String OUTPUTS = "listOfOutputs"; + public static final String STYLES = "listOfStyles"; + public static final String ALGORITHM_PARAMETERS = "listOfAlgorithmParameters"; + public static final String PARAMETERS = "listOfParameters"; + public static final String VARIABLES = "listOfVariables"; + + // model attributes - public static final String MODEL_TAG = "model"; - public static final String MODEL_ATTR_ID = "id"; - public static final String MODEL_ATTR_NAME = "name"; - public static final String MODEL_ATTR_LANGUAGE = "language"; - public static final String MODEL_ATTR_SOURCE = "source"; + public static final String MODEL_TAG = "model"; + public static final String MODEL_ATTR_ID = "id"; + public static final String MODEL_ATTR_NAME = "name"; + public static final String MODEL_ATTR_LANGUAGE = "language"; + public static final String MODEL_ATTR_SOURCE = "source"; // types of model changes - public static final String CHANGES = "listOfChanges"; - public static final String CHANGE_ATTRIBUTE = "changeAttribute"; - public static final String CHANGE_XML = "changeXML"; - public static final String ADD_XML = "addXML"; - public static final String REMOVE_XML = "removeXML"; - public static final String NEW_XML = "newXML"; - public static final String COMPUTE_CHANGE = "computeChange"; - public static final String COMPUTE_CHANGE_VARS = "listOfVariables"; - public static final String COMPUTE_CHANGE_PARAMS = "listOfParameters"; + public static final String CHANGES = "listOfChanges"; + public static final String CHANGE_ATTRIBUTE = "changeAttribute"; + public static final String CHANGE_XML = "changeXML"; + public static final String ADD_XML = "addXML"; + public static final String REMOVE_XML = "removeXML"; + public static final String NEW_XML = "newXML"; + public static final String COMPUTE_CHANGE = "computeChange"; + public static final String COMPUTE_CHANGE_VARS = "listOfVariables"; + public static final String COMPUTE_CHANGE_PARAMS = "listOfParameters"; // change attributes - public static final String CHANGE_ATTR_TARGET = "target"; - public static final String CHANGE_ATTR_NEWVALUE = "newValue"; - public static final String CHANGE_ATTR_NEWXML = "newXML"; - public static final String CHANGE_ATTR_MATH = "math"; + public static final String CHANGE_ATTR_TARGET = "target"; + public static final String CHANGE_ATTR_NEWVALUE = "newValue"; + public static final String CHANGE_ATTR_NEWXML = "newXML"; + public static final String CHANGE_ATTR_MATH = "math"; // simulation attributes - public static final String SIM_ATTR_ID = "id"; - public static final String SIM_ATTR_NAME = "name"; - public static final String SIM_ATTR_ALGORITM = "algorithm"; + public static final String SIM_ATTR_ID = "id"; + public static final String SIM_ATTR_NAME = "name"; + public static final String SIM_ATTR_ALGORITM = "algorithm"; // types of simulations - public static final String SIM_UTC = "uniformTimeCourse"; - public static final String SIM_ANY = "anySimulation"; - public static final String SIM_OS = "oneStep"; - public static final String SIM_SS = "steadyState"; + public static final String SIM_UTC = "uniformTimeCourse"; + public static final String SIM_ANALYSIS = "anySimulation"; + public static final String SIM_ONE_STEP = "oneStep"; + public static final String SIM_STEADY_STATE = "steadyState"; //algorithm element public static final String ALGORITHM_TAG = "algorithm"; @@ -77,23 +87,23 @@ private SEDMLTags(){} public static final String ALGORITHM_PARAMETER_VALUE = "value"; // uniform time course attributes - public static final String UTCA_INIT_T = "initialTime"; - public static final String UTCA_OUT_START_T = "outputStartTime"; - public static final String UTCA_OUT_END_T = "outputEndTime"; - public static final String UTCA_POINTS_NUM = "numberOfPoints"; - public static final String UTCA_STEPS_NUM = "numberOfSteps"; + public static final String UTCA_INIT_T = "initialTime"; + public static final String UTCA_OUT_START_T = "outputStartTime"; + public static final String UTCA_OUT_END_T = "outputEndTime"; + public static final String UTCA_POINTS_NUM = "numberOfPoints"; + public static final String UTCA_STEPS_NUM = "numberOfSteps"; // one step attributes - public static final String OS_STEP = "step"; + public static final String ONE_STEP_STEP = "step"; // task attributes - public static final String TASK_TAG = "task"; - public static final String TASK_ATTR_ID = "id"; - public static final String TASK_ATTR_NAME = "name"; - public static final String TASK_ATTR_MODELREF = "modelReference"; - public static final String TASK_ATTR_SIMREF = "simulationReference"; + public static final String TASK_TAG = "task"; + public static final String TASK_ATTR_ID = "id"; + public static final String TASK_ATTR_NAME = "name"; + public static final String TASK_ATTR_MODELREF = "modelReference"; + public static final String TASK_ATTR_SIMREF = "simulationReference"; // repeated task attributes - public static final String REPEATED_TASK_TAG = "repeatedTask"; + public static final String REPEATED_TASK_TAG = "repeatedTask"; public static final String REPEATED_TASK_RESET_MODEL = "resetModel"; public static final String REPEATED_TASK_ATTR_RANGE = "range"; // should be REPEATED_TASK_ATTR_RANGEREF public static final String REPEATED_TASK_RANGES_LIST = "listOfRanges"; @@ -102,7 +112,7 @@ private SEDMLTags(){} public static final String SUBTASK_TAG = "subTask"; public static final String SUBTASK_ATTR_ORDER = "order"; public static final String SUBTASK_ATTR_TASK = "task"; - public static final String DEPENDENTTASK_TAG = "dependentTask"; + public static final String DEPENDENT_TASK = "dependentTask"; public static final String DEPENDENT_TASK_SUBTASKS_LIST = "listOfDependentTasks"; // set value @@ -130,7 +140,7 @@ private SEDMLTags(){} // data generator attributes and children - public static final String DATAGENERATOR_TAG = "dataGenerator"; + public static final String DATA_GENERATOR_TAG = "dataGenerator"; public static final String DATAGEN_ATTR_ID = "id"; public static final String DATAGEN_ATTR_NAME = "name"; public static final String DATAGEN_ATTR_MATH = "math"; @@ -146,6 +156,9 @@ private SEDMLTags(){} // outputs attributes and children public static final String OUTPUT_ID = "id"; public static final String OUTPUT_NAME = "name"; + public static final String OUTPUT_LEGEND = "legend"; + public static final String OUTPUT_HEIGHT = "height"; + public static final String OUTPUT_WIDTH = "width"; public static final String OUTPUT_CURVES_LIST = "listOfCurves"; public static final String OUTPUT_SURFACES_LIST = "listOfSurfaces"; public static final String OUTPUT_DATASETS_LIST = "listOfDataSets"; @@ -160,7 +173,26 @@ private SEDMLTags(){} public static final String OUTPUT_DATA_REFERENCE_Y = "yDataReference"; public static final String OUTPUT_DATA_REFERENCE_Z = "zDataReference"; public static final String OUTPUT_DATASET_LABEL = "label"; - + public static final String OUTPUT_TYPE = "type"; + public static final String OUTPUT_ORDER = "order"; + public static final String OUTPUT_ERROR_X_UPPER = "xErrorUpper"; + public static final String OUTPUT_ERROR_X_LOWER = "xErrorLower"; + public static final String OUTPUT_ERROR_Y_UPPER = "yErrorUpper"; + public static final String OUTPUT_ERROR_Y_LOWER = "yErrorLower"; + public static final String OUTPUT_RIGHT_Y_AXIS = "yAxis"; + + // axis attributes + public static final String AXIS_ID = "id"; + public static final String AXIS_NAME = "name"; + public static final String AXIS_TYPE = "type"; + public static final String AXIS_X = "xAxis"; + public static final String AXIS_Y = "yAxis"; + public static final String AXIS_Z = "zAxis"; + public static final String AXIS_RIGHT_Y = "rightYAxis"; + public static final String AXIS_MIN = "min"; + public static final String AXIS_MAX = "max"; + public static final String AXIS_GRID = "grid"; + public static final String AXIS_REVERSE = "reverse"; // variable attributes public static final String VARIABLE_ID = "id"; @@ -185,15 +217,10 @@ private SEDMLTags(){} public static final String DATAGEN_VARIABLE_KIND = "DataGenVariable"; // refers to a task public static final String CHANGE_MATH_VARIABLE_KIND = "ChangeMathVariable"; // refers to a model public static final String PLOT2D_KIND = "SedPlot2D"; // refers to a data generator - public static final String PLOT3D_KIND = "SedPlot3D"; + public static final String PLOT3D_KIND = "SedPlot3D"; public static final String REPORT_KIND = "SedReport"; public static final String SIMUL_UTC_KIND = "uniformTimeCourse"; public static final String SIMUL_OS_KIND = "oneStep"; public static final String SIMUL_SS_KIND = "steadyState"; - public static final String SIMUL_ANY_KIND = "anySimulation"; - - - - - + public static final String SIMUL_ANALYSIS = "analysis"; } \ No newline at end of file diff --git a/vcell-core/src/main/java/org/jlibsedml/SedMLWriter.java b/vcell-core/src/main/java/org/jlibsedml/SedMLWriter.java new file mode 100644 index 0000000000..97e15f2086 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/SedMLWriter.java @@ -0,0 +1,519 @@ +package org.jlibsedml; + +import java.util.List; + +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jlibsedml.components.*; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.components.algorithm.AlgorithmParameter; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.listOfConstructs.*; +import org.jlibsedml.components.model.*; +import org.jlibsedml.components.output.*; +import org.jlibsedml.components.simulation.*; +import org.jlibsedml.components.task.*; +import org.jmathml.ASTToXMLElementVisitor; + +class SedMLWriter { + + Element getXML(SedMLDataContainer sedmlDoc){ + SedML sedml = sedmlDoc.getSedML(); + Element root = this.getXML(sedml); + root.setNamespace(sedml.getSedMLNamespace()); + List additionalNSs = sedmlDoc.getExtraNamespaces(); + for (Namespace nSs : additionalNSs) { + root.addNamespaceDeclaration(nSs); + } + return root; + } + + Element getXML(SedML sedmlObject) { + Element sedDocElement = new Element(SedMLTags.SED_ML_ROOT); + sedDocElement.setAttribute(SedMLTags.LEVEL_TAG, String.valueOf(sedmlObject.getLevel())); + sedDocElement.setAttribute(SedMLTags.VERSION_TAG, String.valueOf(sedmlObject.getVersion())); + + sedDocElement.addContent(this.getXML(sedmlObject.getListOfModels())); + sedDocElement.addContent(this.getXML(sedmlObject.getListOfSimulations())); + sedDocElement.addContent(this.getXML(sedmlObject.getListOfTasks())); + sedDocElement.addContent(this.getXML(sedmlObject.getListOfDataGenerators())); + sedDocElement.addContent(this.getXML(sedmlObject.getListOfOutputs())); + + // set sedML namespace for sedMLElement (but this shouldn't trigger anything unless SedML changes) + return this.setDefaultNamespace(sedDocElement, sedmlObject.getSedMLNamespace()); + } + + Element getXML(ListOfModels listOfModels) { + Element listOfModelsElements = this.createElementFromBase(listOfModels); // create + for (Model elem: listOfModels.getContents()) { + listOfModelsElements.addContent(this.getXML(elem)); + } + return listOfModelsElements; + } + + Element getXML(ListOfSimulations listOfSimulations) { + Element listOfSimulationsElements = this.createElementFromBase(listOfSimulations); // create + for (Simulation elem: listOfSimulations.getContents()) { + listOfSimulationsElements.addContent(this.getXML(elem)); + } + return listOfSimulationsElements; + } + + Element getXML(ListOfTasks listOfTasks) { + Element listOfTasksElements = this.createElementFromBase(listOfTasks); // create + for (AbstractTask elem: listOfTasks.getContents()) { + listOfTasksElements.addContent(this.getXML(elem)); + } + return listOfTasksElements; + } + + Element getXML(ListOfDataGenerators listOfDataGenerators) { + Element listOfDataGeneratorsElements = this.createElementFromBase(listOfDataGenerators); // create + for (DataGenerator elem: listOfDataGenerators.getContents()) { + listOfDataGeneratorsElements.addContent(this.getXML(elem)); + } + return listOfDataGeneratorsElements; + } + + Element getXML(ListOfOutputs listOfOutputs) { + Element listOfOutputsElement = this.createElementFromBase(listOfOutputs); // create + for (Output elem: listOfOutputs.getContents()) { + listOfOutputsElement.addContent(this.getXML(elem)); + } + return listOfOutputsElement; + } + + // ================= Models + Element getXML(Model sedmlModel) { + Element node = this.createElementFromBase(sedmlModel); + + String language = sedmlModel.getLanguage(); + if (language != null) node.setAttribute(SedMLTags.MODEL_ATTR_LANGUAGE, language); + String source = sedmlModel.getSourceAsString(); + if (source != null) node.setAttribute(SedMLTags.MODEL_ATTR_SOURCE, source); + + if (sedmlModel.getChanges() != null && !sedmlModel.getChanges().isEmpty()) { + node.addContent(this.getXML(sedmlModel.getListOfChanges())); + } + + return node; + } + + Element getXML(ListOfChanges sedModelChanges) { + Element list = this.createElementFromBase(sedModelChanges); + for (Change change: sedModelChanges.getContents()) { + list.addContent(this.getXML(change)); + } + return list; + } + + Element getXML(Change sedmlChange) { + Element node = this.createElementFromBase(sedmlChange); + + node.setAttribute(SedMLTags.CHANGE_ATTR_TARGET, sedmlChange.getTargetXPath().getTargetAsString()); + + if (sedmlChange instanceof ChangeAttribute changeAttribute) { + String s = changeAttribute.getNewValue(); + if (s != null) node.setAttribute(SedMLTags.CHANGE_ATTR_NEWVALUE, s); + } else if (sedmlChange instanceof ChangeXML changeXML) { + Element newXML = new Element(SedMLTags.NEW_XML); + node.addContent(newXML); + for (Element el : changeXML.getNewXML().xml()) { + newXML.addContent(el.detach()); + } + } else if (sedmlChange instanceof AddXML addXML) { + Element newXML = new Element(SedMLTags.NEW_XML); + node.addContent(newXML); + for (Element el : addXML.getNewXML().xml()) { + newXML.addContent(el.detach()); + } + } else if (sedmlChange instanceof SetValue setValue) { // SetValue + SId rangeId = setValue.getRangeReference(); + if (rangeId != null) node.setAttribute(SedMLTags.SET_VALUE_ATTR_RANGE_REF, rangeId.string()); + SId modelRef = setValue.getModelReference(); + if (modelRef != null) node.setAttribute(SedMLTags.SET_VALUE_ATTR_MODEL_REF, modelRef.string()); + this.addCalculationContent(node, setValue); + } else if (sedmlChange instanceof ComputeChange computeChange) { + this.addCalculationContent(node, computeChange); + } else if (!(sedmlChange instanceof RemoveXML)) { + throw new IllegalArgumentException("Unknown change kind: " + sedmlChange.getChangeKind()); + } + return node; + } + + + void addCalculationContent(Element rootElement, Calculation sedCalc){ + ListOfVariables vars = sedCalc.getListOfVariables(); + if(!vars.isEmpty()) { + Element varList = this.createElementFromBase(vars); + for (Variable var : vars.getContents()) { + varList.addContent(this.getXML(var)); + } + rootElement.addContent(varList); + } + ListOfParameters params = sedCalc.getListOfParameters(); + if(!params.isEmpty()) { + Element paramList = this.createElementFromBase(params); + for (Parameter param : params.getContents()) { + paramList.addContent(this.getXML(param)); + } + rootElement.addContent(paramList); + } + ASTToXMLElementVisitor astElementVisitor = new ASTToXMLElementVisitor(); + sedCalc.getMath().accept(astElementVisitor); + rootElement.addContent(astElementVisitor.getElement()); + } + + + Element getXML(Simulation sedmlSim) { + final Element node = this.createElementFromBase(sedmlSim); + // Add simulations to list of simulations + if (sedmlSim instanceof UniformTimeCourse utcSim) { + node.setAttribute(SedMLTags.UTCA_INIT_T, Double.toString(utcSim.getInitialTime())); + node.setAttribute(SedMLTags.UTCA_OUT_START_T, Double.toString(utcSim.getOutputStartTime())); + node.setAttribute(SedMLTags.UTCA_OUT_END_T, Double.toString(utcSim.getOutputEndTime())); + node.setAttribute(SedMLTags.UTCA_POINTS_NUM, Integer.toString(utcSim.getNumberOfSteps())); + } else if (sedmlSim instanceof OneStep oneStepSim) { + node.setAttribute(SedMLTags.ONE_STEP_STEP, Double.toString(oneStepSim.getStep())); + } else if (!(sedmlSim instanceof SteadyState || sedmlSim instanceof Analysis)){ + throw new RuntimeException("Simulation must be uniformTimeCourse, oneStep or steadyState or analysis'" + sedmlSim.getId().string()); + } + Algorithm algorithm = sedmlSim.getAlgorithm(); + if (algorithm != null) node.addContent(this.getXML(algorithm)); + return node; + } + + Element getXML(Algorithm algorithm) { + Element node = this.createElementFromBase(algorithm); + String kisaoID = algorithm.getKisaoID(); + if (kisaoID != null) node.setAttribute(SedMLTags.ALGORITHM_ATTR_KISAOID, kisaoID); + + // list of algorithm parameters + ListOfAlgorithmParameters aps = algorithm.getListOfAlgorithmParameters(); + if (!aps.isEmpty()) node.addContent(this.getXML(aps)); + + return node; + } + + Element getXML(ListOfAlgorithmParameters aps) { + Element apList = new Element(SedMLTags.ALGORITHM_PARAMETER_LIST); + for (AlgorithmParameter ap : aps.getContents()) apList.addContent(this.getXML(ap)); + return apList; + } + + Element getXML(AlgorithmParameter ap) { + Element node = this.createElementFromBase(ap); + String kisaoID = ap.getKisaoID(); + if (kisaoID != null) node.setAttribute(SedMLTags.ALGORITHM_PARAMETER_KISAOID, kisaoID); + String value = ap.getValue(); + if (value != null) node.setAttribute(SedMLTags.ALGORITHM_PARAMETER_VALUE, value); + return node; + } + + private Element getXML(Range range) { + final Element node = this.createElementFromBase(range); + if (range instanceof VectorRange vecRange) { + for (double val : vecRange.getElements()){ + Element v = new Element(SedMLTags.VECTOR_RANGE_VALUE_TAG); + v.setText(Double.toString(val)); + node.addContent(v); + } + + } else if (range instanceof UniformRange ur) { + node.setAttribute(SedMLTags.UNIFORM_RANGE_ATTR_START, Double.toString(ur.getStart())); + node.setAttribute(SedMLTags.UNIFORM_RANGE_ATTR_END, Double.toString(ur.getEnd())); + node.setAttribute(SedMLTags.UNIFORM_RANGE_ATTR_NUMP, Integer.toString(ur.getNumberOfSteps())); + node.setAttribute(SedMLTags.UNIFORM_RANGE_ATTR_TYPE, ur.getType().getText()); + } else if (range instanceof FunctionalRange fr) { + SId rangeAttr = fr.getRange(); + if (rangeAttr == null) throw new IllegalArgumentException("range is null"); + node.setAttribute(SedMLTags.FUNCTIONAL_RANGE_INDEX, rangeAttr.string()); + this.addCalculationContent(node, fr); + } else { + throw new IllegalArgumentException("Unknown range type requested"); + } + return node; + } + + // ============== SubTasks + private Element getXML(SubTask t) { + Element node = this.createElementFromBase(t); + Integer order = t.getOrder(); + if (order != null) node.setAttribute(SedMLTags.SUBTASK_ATTR_ORDER, order.toString()); + SId task = t.getTask(); + if (task == null) throw new IllegalArgumentException("task is null"); + node.setAttribute(SedMLTags.SUBTASK_ATTR_TASK, task.string()); + return node; + } + + Element getXML(AbstractTask abstractTask) { + final Element node = this.createElementFromBase(abstractTask); + if (abstractTask instanceof RepeatedTask repeatedTask) { + node.setAttribute(SedMLTags.REPEATED_TASK_RESET_MODEL, Boolean.toString(repeatedTask.getResetModel())); + SId range = repeatedTask.getRange(); + if (range == null) throw new IllegalArgumentException("range is null"); + node.setAttribute(SedMLTags.REPEATED_TASK_ATTR_RANGE, range.string()); + // Add list of ranges + ListOfRanges ranges = repeatedTask.getListOfRanges(); + if (!ranges.isEmpty()) node.addContent(this.getXML(ranges)); + + // Add list of changes + ListOfRepeatedTaskChanges lcs = repeatedTask.getListOfChanges(); + if (!lcs.isEmpty()) node.addContent(this.getXML(lcs)); + + // Add list of subtasks + ListOfSubTasks subTasks = repeatedTask.getListOfSubTasks(); + if (!subTasks.isEmpty()) node.addContent(this.getXML(subTasks)); + } else if (abstractTask instanceof Task task) { + // Add Attributes to tasks + SId modelReference = task.getModelReference(); + if (modelReference == null) throw new IllegalArgumentException("Model reference cannot be null!"); + node.setAttribute(SedMLTags.TASK_ATTR_MODELREF, modelReference.string()); + + SId simulationReference = task.getSimulationReference(); + if (simulationReference == null) throw new IllegalArgumentException("Task reference cannot be null!"); + node.setAttribute(SedMLTags.TASK_ATTR_SIMREF, simulationReference.string()); + + } else { + throw new IllegalArgumentException("Unknown task type"); + } + return node; + } + + Element getXML(ListOfRanges ranges){ + Element rangesListElement = this.createElementFromBase(ranges); + for (Range range : ranges.getContents()) { + rangesListElement.addContent(this.getXML(range)); + } + return rangesListElement; + } + + Element getXML(ListOfRepeatedTaskChanges changes){ + Element changesListElement = this.createElementFromBase(changes); + for (SetValue sv : changes.getContents()) { + changesListElement.addContent(this.getXML(sv)); + } + return changesListElement; + } + + Element getXML(ListOfSubTasks subTasks){ + Element subTasksListElement = this.createElementFromBase(subTasks); + for (SubTask st : subTasks.getContents()) { + subTasksListElement.addContent(this.getXML(st)); + } + return subTasksListElement; + } + + private void addNotesAndAnnotation(SedBase sedbase, Element node) { + + // add 'notes' elements from sedml + Notes note = sedbase.getNotes(); + if(note != null) { + Element newElement = new Element(SedMLTags.NOTES); + for (Element noteElement : note.getNotesElements()){ + newElement.addContent(noteElement.detach()); + } + node.addContent(newElement); + } + + // add 'annotation' elements from sedml + Annotation annotation = sedbase.getAnnotations(); + if (annotation != null) { + Element newElement = new Element(SedMLTags.ANNOTATION); + for (Element annElement : annotation.getAnnotationElements()) { + newElement.addContent(annElement.detach()); + } + node.addContent(newElement); + } + + String metaID = sedbase.getMetaId(); + if (metaID != null) { + node.setAttribute(SedMLTags.META_ID_ATTR_NAME, metaID); + } + } + + Element getXML(DataGenerator sedmlDataGen) { + Element node = this.createElementFromBase(sedmlDataGen); + this.addCalculationContent(node, sedmlDataGen); + return node; + } + + Element getXML(Variable variable) { + Element node = this.createElementFromBase(variable); + SId modelReference = variable.getModelReference(); + if (modelReference != null) node.setAttribute(SedMLTags.VARIABLE_MODEL, modelReference.string()); + SId taskReference = variable.getTaskReference(); + if (taskReference != null) node.setAttribute(SedMLTags.VARIABLE_TASK, taskReference.string()); + + if (variable.referencesXPath()) { + node.setAttribute(SedMLTags.VARIABLE_TARGET, variable.getTarget()); + } else if (variable.isSymbol()) { + node.setAttribute(SedMLTags.VARIABLE_SYMBOL, variable.getSymbol().getUrn()); + } + return node; + } + + Element getXML(Parameter parameter) { + Element node = this.createElementFromBase(parameter); + node.setAttribute(SedMLTags.PARAMETER_VALUE, Double.toString(parameter.getValue())); + return node; + } + + Element getXML(Output sedmlOutput) { + final Element node = this.createElementFromBase(sedmlOutput); + + if (sedmlOutput instanceof Plot plot){ + Boolean useLegend = plot.getUseLegend(); + if (useLegend != null) node.setAttribute(SedMLTags.OUTPUT_LEGEND, useLegend.toString()); + Double plotHeight = plot.getPlotHeight(); + if (plotHeight != null) node.setAttribute(SedMLTags.OUTPUT_HEIGHT, plotHeight.toString()); + Double plotWidth = plot.getPlotWidth(); + if (plotWidth != null) node.setAttribute(SedMLTags.OUTPUT_WIDTH, plotWidth.toString()); + + XAxis xAxis = plot.getXAxis(); + boolean hasXAxis = xAxis != null; + if (hasXAxis) node.addContent(this.getXML(xAxis)); + YAxis yAxis = plot.getYAxis(); + boolean hasYAxis = yAxis != null; + if (hasYAxis) node.addContent(this.getXML(yAxis)); + + if (plot instanceof Plot2D plot2D){ + RightYAxis rightYAxis = plot2D.getRightYAxis(); + if (rightYAxis != null) node.addContent(this.getXML(rightYAxis)); + ListOfCurves listOfCurves = plot2D.getListOfCurves(); + if (!listOfCurves.isEmpty()) node.addContent(this.getXML(listOfCurves, hasXAxis, hasYAxis)); + } else if (plot instanceof Plot3D plot3D){ + ZAxis zAxis = plot3D.getZAxis(); + boolean hasZAxis = zAxis != null; + if (hasZAxis) node.addContent(this.getXML(zAxis)); + ListOfSurfaces listOfSurfaces = plot3D.getListOfSurfaces(); + if (!listOfSurfaces.isEmpty()) node.addContent(this.getXML(listOfSurfaces, hasXAxis, hasYAxis, hasZAxis)); + } + } else if (sedmlOutput instanceof Report report){ + ListOfDataSets listOfDataSets = report.getListOfDataSets(); + if (!listOfDataSets.isEmpty()) node.addContent(this.getXML(listOfDataSets)); + } + + return node; + } + + Element getXML(ListOfCurves listOfCurves, boolean xAxisIncluded, boolean yAxisIncluded) { + Element node = this.createElementFromBase(listOfCurves); + for (AbstractCurve abstractCurve : listOfCurves.getContents()) { + if (abstractCurve instanceof Curve curve) node.addContent(this.getXML(curve, xAxisIncluded, yAxisIncluded)); + else throw new IllegalArgumentException("Unsupported curve encountered: " + abstractCurve); + } + return node; + } + + Element getXML(ListOfSurfaces listOfSurfaces, boolean xAxisIncluded, boolean yAxisIncluded, boolean zAxisIncluded) { + Element node = this.createElementFromBase(listOfSurfaces); + for (Surface surface : listOfSurfaces.getContents()) { + node.addContent(this.getXML(surface, xAxisIncluded, yAxisIncluded, zAxisIncluded)); + } + return node; + } + + Element getXML(ListOfDataSets listOfDataSets) { + Element node = this.createElementFromBase(listOfDataSets); + for (DataSet dataSet : listOfDataSets.getContents()) { + node.addContent(this.getXML(dataSet)); + } + return node; + } + + Element getXML(Axis axis) { + Element node = this.createElementFromBase(axis); + node.setAttribute(SedMLTags.AXIS_TYPE, axis.getType().getTag()); + Double min = axis.getMin(); + if (min != null) node.setAttribute(SedMLTags.AXIS_MIN, min.toString()); + Double max = axis.getMax(); + if (max != null) node.setAttribute(SedMLTags.AXIS_MAX, max.toString()); + Boolean grid = axis.getGrid(); + if (grid != null) node.setAttribute(SedMLTags.AXIS_GRID, grid.toString()); + Boolean reverse = axis.getReverse(); + if (reverse != null) node.setAttribute(SedMLTags.AXIS_REVERSE, reverse.toString()); + return node; + } + + Element getXML(Curve sedCurve, boolean xAxisIncluded, boolean yAxisIncluded) { + Element node = this.createElementFromBase(sedCurve); + + node.setAttribute(SedMLTags.OUTPUT_TYPE, sedCurve.getType().getTag()); + Integer order = sedCurve.getOrder(); + if (order != null) node.setAttribute(SedMLTags.OUTPUT_ORDER, order.toString()); + + Boolean logXAxis = sedCurve.getLogScaleXAxis(); + Boolean logYAxis = sedCurve.getLogScaleYAxis(); + if (!xAxisIncluded) node.setAttribute(SedMLTags.OUTPUT_LOG_X, Boolean.toString(logXAxis != null && logXAxis)); + if (!yAxisIncluded) node.setAttribute(SedMLTags.OUTPUT_LOG_Y, Boolean.toString(logYAxis != null && logYAxis)); + + node.setAttribute(SedMLTags.OUTPUT_DATA_REFERENCE_X, sedCurve.getXDataReference().string()); + node.setAttribute(SedMLTags.OUTPUT_DATA_REFERENCE_Y, sedCurve.getYDataReference().string()); + + SId xErrUpper = sedCurve.getXErrorUpper(); + SId xErrLower = sedCurve.getXErrorLower(); + SId yErrUpper = sedCurve.getYErrorUpper(); + SId yErrLower = sedCurve.getYErrorLower(); + if (xErrUpper != null) node.setAttribute(SedMLTags.OUTPUT_ERROR_X_UPPER, xErrUpper.string()); + if (xErrLower != null) node.setAttribute(SedMLTags.OUTPUT_ERROR_X_LOWER, xErrLower.string()); + if (yErrUpper != null) node.setAttribute(SedMLTags.OUTPUT_ERROR_Y_UPPER, yErrUpper.string()); + if (yErrLower != null) node.setAttribute(SedMLTags.OUTPUT_ERROR_Y_LOWER, yErrLower.string()); + + return node; + } + + Element getXML(Surface sedSurface, boolean xAxisIncluded, boolean yAxisIncluded, boolean zAxisIncluded) { + // Surfaces + Element node = this.createElementFromBase(sedSurface); + + node.setAttribute(SedMLTags.OUTPUT_TYPE, sedSurface.getType().getTag()); + Integer order = sedSurface.getOrder(); + if (order != null) node.setAttribute(SedMLTags.OUTPUT_ORDER, order.toString()); + + Boolean logZAxis = sedSurface.getLogScaleZAxis(); + Boolean logYAxis = sedSurface.getLogScaleYAxis(); + Boolean logXAxis = sedSurface.getLogScaleXAxis(); + if (!zAxisIncluded) node.setAttribute(SedMLTags.OUTPUT_LOG_Z, Boolean.toString(logZAxis != null && logZAxis)); + if (!yAxisIncluded) node.setAttribute(SedMLTags.OUTPUT_LOG_Y, Boolean.toString(logYAxis != null && logYAxis)); + if (!xAxisIncluded) node.setAttribute(SedMLTags.OUTPUT_LOG_X, Boolean.toString(logXAxis != null && logXAxis)); + + node.setAttribute(SedMLTags.OUTPUT_DATA_REFERENCE_X, sedSurface.getXDataReference().string()); + node.setAttribute(SedMLTags.OUTPUT_DATA_REFERENCE_Y, sedSurface.getYDataReference().string()); + node.setAttribute(SedMLTags.OUTPUT_DATA_REFERENCE_Z, sedSurface.getZDataReference().string()); + return node; + } + + Element getXML(DataSet sedDataSet) { // DataSets + Element node = this.createElementFromBase(sedDataSet); + SId dataReference = sedDataSet.getDataReference(); + if (dataReference == null) throw new IllegalArgumentException("Null data reference"); + node.setAttribute(SedMLTags.OUTPUT_DATA_REFERENCE, dataReference.string()); + String label = sedDataSet.getLabel(); + if (label == null) throw new IllegalArgumentException("Null label"); + node.setAttribute(SedMLTags.OUTPUT_DATASET_LABEL, label); + + return node; + } + + private Element setDefaultNamespace(Element rootNode, Namespace namespace) { + // only if there is a node and it has no default namespace! + if (rootNode != null && rootNode.getNamespaceURI().isEmpty()) { + // set namespace for this node + rootNode.setNamespace(namespace); + for (Element child : rootNode.getChildren()) { + // check children + this.setDefaultNamespace(child, namespace); + } + } + return rootNode; + } + + private Element createElementFromBase(SedBase baseSedml){ + Element root = new Element(baseSedml.getElementName()); + if (baseSedml.getId() != null) root.setAttribute(SedMLTags.ATTRIBUTE_ID, baseSedml.getIdAsString()); + if (baseSedml.getName() != null) root.setAttribute(SedMLTags.ATTRIBUTE_NAME, baseSedml.getName()); + this.addNotesAndAnnotation(baseSedml, root); + return root; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/SetValue.java b/vcell-core/src/main/java/org/jlibsedml/SetValue.java deleted file mode 100644 index 10a373b890..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/SetValue.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.jlibsedml; - -import org.jmathml.ASTNode; - -public class SetValue extends ComputeChange { - -/* - - - - current - - - - - - - - current - - - -*/ - private String modelReference = null; - // as for functionalRange, variable references always retrieve the current value of the - // model variable or range at the current iteration of the enclosing repeatedTask. For a model not being - // simulated by any subTask, the initial state of the model is used. - private String rangeReference = null; - - // Remember to set the math separately - public SetValue(XPathTarget target, String rangeReference, String modelReference) { - super(target); - this.rangeReference = rangeReference; - this.setModelReference(modelReference); - }; - public SetValue(XPathTarget target, ASTNode math, String rangeReference, String modelReference) { - super(target, math); - this.rangeReference = rangeReference; - this.setModelReference(modelReference); - }; - - public void setRangeReference(String rangeReference) { - this.rangeReference = rangeReference; - } - public String getRangeReference() { - return rangeReference; - } - public void setModelReference(String modelReference) { - this.modelReference = modelReference; - } - public String getModelReference() { - return modelReference; - } - - @Override - public String toString() { - return "SetValue [getTargetXPath()=" + getTargetXPath() - + ", getRangeReference()=" + getRangeReference() - + ", getModelReference()=" + getModelReference() - + ", getListOfVariables().size()=" + getListOfVariables().size() - + ", getListOfParameters().size()=" + getListOfParameters().size() - + "]"; - } - - @Override - public String getChangeKind() { - return SEDMLTags.SET_VALUE_KIND; - } - - @Override - public String getElementName() { - return SEDMLTags.SET_VALUE; - } - - @Override - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Simulation.java b/vcell-core/src/main/java/org/jlibsedml/Simulation.java deleted file mode 100644 index ad9607e968..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Simulation.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.jlibsedml; - -/** - * Encapsulates a description of a simulation. - * @author anu/radams - * - */ -public abstract class Simulation extends AbstractIdentifiableElement { - private Algorithm algorithm; - - @Override - public String toString() { - return "Simulation [algorithm=" + algorithm + ", name=" + getName() - + ", getId()=" + getId() + "]"; - } - - /** - * @param id A required String identifier for this element. - * @param name - optional, can be null. - * @param algorithm - not null. - * @throws IllegalArgumentException if id is null or empty string. - */ - public Simulation(String id, String name,Algorithm algorithm) { - super(id,name); - if(SEDMLElementFactory.getInstance().isStrictCreation()){ - Assert.checkNoNullArgs(algorithm); - } - this.algorithm = algorithm; - } - - /** - * Returns the {@link Algorithm} for this simulation - * @return the {@link Algorithm} - */ - public Algorithm getAlgorithm() { - return algorithm; - } - - /** - * Setter for the {@link Algorithm} element of this simulation - * @param algorithm a non-null {@link Algorithm}. - */ - public void setAlgorithm(Algorithm algorithm) { - Assert.checkNoNullArgs(algorithm); - this.algorithm = algorithm; - } - - /** - * Getter for the type of this simulation. - * @return A String - */ - public abstract String getSimulationKind(); - - public boolean accept(SEDMLVisitor visitor){ - return visitor.visit(this); - } -} \ No newline at end of file diff --git a/vcell-core/src/main/java/org/jlibsedml/SteadyState.java b/vcell-core/src/main/java/org/jlibsedml/SteadyState.java deleted file mode 100644 index 1d681e7555..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/SteadyState.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.jlibsedml; - -public class SteadyState extends Simulation { - - public SteadyState(String id, String name, Algorithm algorithm) { - super(id, name, algorithm); - } - - @Override - public String toString() { - return "SteadyState [" + getAlgorithm() - + ", name=" + getName() - + ", getId()=" + getId() - + "]"; - } - @Override - public String getSimulationKind() { - return SEDMLTags.SIMUL_SS_KIND; - } - - @Override - public String getElementName() { - return SEDMLTags.SIM_SS; - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/SubTask.java b/vcell-core/src/main/java/org/jlibsedml/SubTask.java deleted file mode 100644 index f241a428e4..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/SubTask.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.jlibsedml; - -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SubTask { -/* - - - - - - - - -*/ - - private String order; - private String taskId = new String(); // id of the Task which is a SubTask of the current RepeatedTask - private Map dependentTasks = new HashMap (); - private RepeatedTask ownerTask = null; - private Logger log = LoggerFactory.getLogger(SubTask.class); - - public SubTask(String taskId) { - this.order = null; - this.taskId = taskId; - } - public SubTask(String order, String taskId) { - this(taskId); - if(order == null) { - return; - } - try { - Integer i = Integer.parseInt(order); // we just check whether can be parsed to an int - this.order = order; - } catch (NumberFormatException e) { - log.warn("SubTask: order is not an Integer: " + order); - this.order = null; - } - } - - @Override - public String toString() { - return "SubTask [" - + "getTaskId()=" + getTaskId() - + ", getOrder()=" + getOrder() - + ", dependentTasks.size()=" + dependentTasks.size() - + "]"; - } - - @Override - public int hashCode(){ - return this.taskId.hashCode(); - } - - public String getTaskId() { - return taskId; - } - public String getOrder() { - return order; - } - public void addDependentTask(SubTask dependentTask) { - if(dependentTask == null || dependentTask.getTaskId() == null || dependentTask.getTaskId().equals("")) { - log.warn("dependentTask cant't be null, key can't be null, key can't be empty string"); - log.warn(" ...dependent task not added to list"); - return; // dependentTask cant't be null, key can't be null, key can't be "" - } - if(this.getTaskId().equals(dependentTask.getTaskId())) { - log.warn("'this' subTask cannot be a dependentTask for itself"); - log.warn(" ...dependent task " + dependentTask.getTaskId() + " not added to list"); - return; // "this" subTask cannot be a dependentTask for itself - } - if(ownerTask != null && ownerTask.getId().equals(dependentTask.getTaskId())) { - log.warn("the RepeatedTask which owns this subTask cannot be a dependentTask for itself"); - log.warn(" ...dependent task " + dependentTask.getTaskId() + " not added to list"); - return; // the RepeatedTask which owns this subTask cannot be a dependentTask for itself - } - if(!dependentTasks.containsKey(dependentTask.getTaskId())) { // no duplicates - dependentTasks.put(dependentTask.getTaskId(), dependentTask); - } else { - log.warn("dependent task already in dependent task list"); - log.warn(" ...dependent task " + dependentTask.getTaskId() + " not added to list"); - return; - } - } - public Map getDependentTasks() { - return dependentTasks; - } - public void removeOwnerFromDependentTasksList(RepeatedTask repeatedTask) { - this.ownerTask = repeatedTask; - if(dependentTasks != null && !dependentTasks.isEmpty()) { - for(SubTask dt : dependentTasks.values()) { - if(ownerTask.getId().equals(dt.getTaskId())) { - dependentTasks.remove(dt.getTaskId()); - log.warn("the RepeatedTask which owns this subTask cannot be a dependentTask for itself"); - log.warn(" ...dependent task " + dt.getTaskId() + " removed from list"); - return; - } - } - } - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Surface.java b/vcell-core/src/main/java/org/jlibsedml/Surface.java deleted file mode 100644 index e9c7b418b8..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Surface.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.jlibsedml; - - -/** - * Encapsulates the {@link Surface} element in SED-ML for representing an element of a 3-dimensional plot. - * - */ -public final class Surface extends AbstractIdentifiableElement{ - @Override - public String toString() { - return "Surface [logZ=" + logZ + ", zDataReference=" + zDataReference - + ", getId()=" + getId() + ", getLogX()=" + getLogX() - + ", getLogY()=" + getLogY() + ", getXDataReference()=" - + getXDataReference() + ", getYDataReference()=" - + getYDataReference() + "]"; - } - -/** - * Sets the x and y axes for this surface, using a Curveobject. - * @param curve A non-null Curve. - * @since 1.2.0 - */ - public void setCurve(Curve curve) { - this.curve = curve; - } - - /** - * Setter for whether the z-axis of this object should be on a log scale, or not. - * @param logZ A boolean. - * @since 1.2.0 - */ - public void setLogZ(boolean logZ) { - this.logZ = logZ; - } - - /** - * Sets the z Data Reference for this object. - * @param zDataReference A non-null, non empty String that should - * refer to a {@link DataGenerator} identifier. - * @since 1.2.0 - */ - public void setZDataReference(String zDataReference) { - this.zDataReference = zDataReference; - } - - /** - * Getter for the reference to the {@link DataGenerator} for the y-axis. - * @return A non-null String - */ - public String getYDataReference() { - return curve.getYDataReference(); - } - - /** - * Getter for the reference to the {@link DataGenerator} for the x-axis. - * @return A non-null String - */ - public String getXDataReference() { - return curve.getXDataReference(); - } - - /** - * Boolean test for whether the y-axis is on a log scale. - * @return true if it is in a log-scale, false otherwise. - */ - public boolean getLogY() { - return curve.getLogY(); - } - - /** - * Boolean test for whether the x-axis is on a log scale. - * @return true if it is in a log-scale, false otherwise. - */ - public boolean getLogX() { - return curve.getLogX(); - } - - @Override - public String getElementName() { - return SEDMLTags.OUTPUT_SURFACE; - } - - - private Curve curve =null; - private boolean logZ = false; - - private String zDataReference = null; // DataGenerator.id - - /** - * - * @param argId A String identifier that is unique in this document. - * @param argName An optional String name - * @param logX boolean as to whether x-axis is a log scale. - * @param logY boolean as to whether y-axis is a log scale. - * @param logZ boolean as to whether z-axis is a log scale. - * @param xDataReference A String reference to the {@link DataGenerator} for the x-axis. - * @param yDataReference A String reference to the {@link DataGenerator} for the y-axis. - * @param zDataReference A String reference to the {@link DataGenerator} for the z-axis. - * @throws IllegalArgumentException if any argument except name is null or empty. - */ - public Surface(String argId, String argName, boolean logX, boolean logY, boolean logZ, String xDataReference, String yDataReference, String zDataReference) { - super(argId,argName); - curve = new Curve(argId, argName,logX, logY, xDataReference, yDataReference); - if(SEDMLElementFactory.getInstance().isStrictCreation()){ - Assert.checkNoNullArgs(zDataReference,logZ); - Assert.stringsNotEmpty(zDataReference); - } - this.logZ=logZ; - this.zDataReference=zDataReference; - } - - - /** - * @return the reference to the {@link DataGenerator} for the z-axis - */ - public final String getZDataReference () { - return zDataReference; - } - - /** - * @return true if the z-axis should be displayed on a log scale, false otherwise. - */ - public boolean getLogZ() { - return logZ; - } - - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } -} - diff --git a/vcell-core/src/main/java/org/jlibsedml/Task.java b/vcell-core/src/main/java/org/jlibsedml/Task.java deleted file mode 100644 index c060547543..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Task.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.jlibsedml; - -/** - * Encapsulates the Task element linking a simulation description to a model. - * - */ -public class Task extends AbstractTask { - - - @Override - public String toString() { - return "Task [modelReference=" + modelReference + ", name=" + getName() - + ", simulationReference=" + simulationReference + ", getId()=" - + getId() + "]"; - } - - @Override - public String getElementName() { - return SEDMLTags.TASK_TAG; - } - - - private String modelReference = null; - private String simulationReference = null; - - /** - * - * @param id - * @param name - optional, can be null. - * @param modelReference - * @param simulationReference - * @throws IllegalArgumentException if any argument except name is null or empty string. - */ - public Task(String id, String name, String modelReference, String simulationReference) { - super(id,name); - if(SEDMLElementFactory.getInstance().isStrictCreation()){ - Assert.checkNoNullArgs(modelReference, simulationReference); - Assert.stringsNotEmpty(modelReference, simulationReference); - } - // store and initialize - this.modelReference = modelReference; - this.simulationReference = simulationReference; - } - - - - /** - * Sets the model reference for this task. This should be the value of the 'id' - * attribute of a {@link Model} element. - * @param modelReference A non-null String. - * @since 1.2.0 - */ - public void setModelReference(String modelReference) { - this.modelReference = modelReference; - } - - /** - * Sets the simulation reference for this task. This should be the value of the 'id' - * attribute of a {@link Simulation} element. - * @param simulationReference A non-null String. - * @since 1.2.0 - */ - public void setSimulationReference(String simulationReference) { - this.simulationReference = simulationReference; - } - - /** - * Getter for the model reference. - * @return A String that should correspond to a model's id attribute. - */ - @Override - public String getModelReference() { - return modelReference; - } - - /** - * Getter for the simulation reference. - * @return A String that should correspond to a simulation's id attribute. - */ - @Override - public String getSimulationReference() { - return simulationReference; - } - - public boolean accept(SEDMLVisitor visitor){ - return visitor.visit(this); - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/UniformTimeCourse.java b/vcell-core/src/main/java/org/jlibsedml/UniformTimeCourse.java deleted file mode 100644 index 8612ab30e1..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/UniformTimeCourse.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.jlibsedml; - - -/** - * Encapsulates a basic time course simulation of a model. - * - */ -public final class UniformTimeCourse extends Simulation { - @Override - public String toString() { - return "UniformTimeCourse [initialTime=" + initialTime - + ", numberOfSteps=" + numberOfSteps + ", outputEndTime=" - + outputEndTime + ", outputStartTime=" + outputStartTime - + ", " + getAlgorithm() + ", getId()=" + getId() - + "]"; - } - - /** - * Sets the initial time for this simulation. - * @param initialTime A double. - * @since 1.2.0 - */ - public void setInitialTime(double initialTime) { - this.initialTime = initialTime; - } - - /** - * Sets the output start time for this simulation. - * @param outputStartTime A double. - * @since 1.2.0 - */ - public void setOutputStartTime(double outputStartTime) { - this.outputStartTime = outputStartTime; - } - - /** - * Sets the output end time for this simulation. - * @param outputEndTime A double. - * @since 1.2.0 - */ - public void setOutputEndTime(double outputEndTime) { - this.outputEndTime = outputEndTime; - } - - /** - * Sets the number of output points for this simulation. - * @param numberOfSteps A double. - * @since 1.2.0 - */ - public void setNumberOfSteps(int numberOfSteps) { - this.numberOfSteps = numberOfSteps; - } - - @Override - public String getElementName() { - return SEDMLTags.SIM_UTC; - } - - private double initialTime = 0.0; - private double outputStartTime = 0.0; - private double outputEndTime = 0.0; - private int numberOfSteps = 0; - - /** - * This constructor does not perform validation at this stage of the simulation configuration ( for example, - * that outputStartTime < outputEndTime). This can be checked by validating the the SEDML document by a call to - *

-	    *   doc.validate();
-	    * 
- * @param id a mandatory, unique identifier for this element - * @param name optional, can be null. - * @param initialTime - * @param outputStartTime - * @param outputEndTime - * @param numberOfSteps - */ - public UniformTimeCourse(String id, String name, - double initialTime, double outputStartTime, double outputEndTime, int numberOfSteps, Algorithm algorithm) { - super(id, name,algorithm); - this.initialTime = initialTime; - this.outputStartTime = outputStartTime; - this.outputEndTime = outputEndTime; - this.numberOfSteps = numberOfSteps; - } - - /** - * Getter for the initial time value, i.e., the value of t at the start of the simulation. - * @return a double - */ - public double getInitialTime() { - return initialTime; - } - - /** - * Getter for the time value at which output should be started - * @return a double - */ - public double getOutputStartTime() { - return outputStartTime; - } - - /** - * Getter for the time value at which output should be terminated. - * @return a double - */ - public double getOutputEndTime() { - return outputEndTime; - } - - /** - * Getter for the number of time-points in the simulation. - * @return a double - */ - public int getNumberOfSteps() { - return numberOfSteps; - } - - /** - * @return {@link SEDMLTags#SIMUL_UTC_KIND} - */ - public String getSimulationKind() { - return SEDMLTags.SIMUL_UTC_KIND; - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/Variable.java b/vcell-core/src/main/java/org/jlibsedml/Variable.java deleted file mode 100644 index 7076f4be40..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/Variable.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.jlibsedml; - -/** - * Encapsulates a SED-ML Variable element. A variable can have EITHER a - * taskRef or a modelRef depending on whether it - * belongs to a ComputeChange or DataGenerator element. - *

- * The 'taskRef' and 'modelRef' attributes have different meanings. The - * 'modelRef', used for instance in a computeChange structure, means that the - * variable refers to the value in the model. This is currently a scalar in - * SBML, but may be something else in the future (for instance a list or a - * function). The important concept is that it is the value BEFORE simulation. - *

- *

- * The 'taskRef' means the variable refers to the value**S** as output by the - * task. For instance, in COPASI, a variable with 'modelRef' would correspond to - * the "initial concentration" while a variable with 'taskRef' would correspond - * to the "transient concentration". - *

- * - */ -public final class Variable extends AbstractIdentifiableElement { - - private String targetXPathStr = null; - private String reference = null; // task OR model ID - private VariableSymbol symbol; - - /** - * Standard constructor. - * - * @param id - * A non-null ID - * @param name - * An optional name ( can be null or empty string) - * @param reference - * A non-null, non-emptyString reference, to either - * a taskId or a model Id ( see class documentation ) - * @param targetXPath - * An XPath expression identifying the variable in the model - * referenced by the {@link Task} element. - * @throws IllegalArgumentException - * id id,reference, or - * targetXPath is null. - */ - public Variable(String id, String name, String reference, String targetXPath) { - super(id, name); - if (SEDMLElementFactory.getInstance().isStrictCreation()) { - Assert.checkNoNullArgs(reference, targetXPath); - } - this.targetXPathStr = targetXPath; - this.reference = reference; - } - - /** - * Alternative constructor for use when this object refers to an implicit - * variable such as time. - * - * @param id - * @param name - * @param reference - * @param symbol - * A non-null {@link VariableSymbol} object. - * @throws IllegalArgumentException - * id id,reference, or - * symbol is null. - */ - public Variable(String id, String name, String reference, - VariableSymbol symbol) { - super(id, name); - Assert.checkNoNullArgs(reference, symbol); - this.symbol = symbol; - this.reference = reference; - } - - /** - * Gets the symbol for this variable, if isSymbol() ==true. - * - * @return Gets a {@link VariableSymbol}, or null if - * isVariable()==true. - */ - public VariableSymbol getSymbol() { - return symbol; - } - - /** - * Boolean query for whether or not this object represents a SEDML implicit - * variable, such as time. - * - * @return true if this object represents a SEDML implicit - * variable, false otherwise. - */ - public boolean isSymbol() { - return symbol != null; - } - - /** - * Sets the target XPath for this object. This method will have the - * side-effect of setting any symbol reference to null, since a - * Variable can only refer to a symbol or a model variable, but not both. - * - * @param targetXPathStr - * A non-null representing an XPath statement. - * @since 1.2.0 - */ - public void setTargetXPathStr(String targetXPathStr) { - this.targetXPathStr = targetXPathStr; - this.symbol = null; - } - - /** - * Sets the task reference for this variable. - * - * @param reference - * A non-nullString. - * @since 1.2.0 - */ - public void setReference(String reference) { - this.reference = reference; - - } - - /** - * Sets the symbol reference for this variable. This method will have the - * side-effect of setting any task reference to null, since a - * Variable can only refer to a symbol or a task reference, but not both. - * - * @param symbol - * A non-null {@link VariableSymbol}. - * @since 1.2.0 - */ - public void setSymbol(VariableSymbol symbol) { - this.symbol = symbol; - this.targetXPathStr = null; - } - - @Override - public String toString() { - return "Variable [name=" + getName() + ", reference=" + reference - + ", symbol=" + symbol + ", targetXPathStr=" + targetXPathStr - + ", getId()=" + getId() + "]"; - } - - /** - * Boolean test for whether or not this object represents a SEDML variable. - * @return true if this object represents a SEDML variable, - * false otherwise. - */ - public boolean isVariable() { - return targetXPathStr != null; - } - - /** - * Boolean test for whether or not this object represents the SEDML implicit - * variable for urn:sedml:symbol:time. - * @return true if this object represents the SEDML implicit - * variable urn:sedml:symbol:time, false - * otherwise. - */ - public boolean isTime() { - return isSymbol() && symbol.equals(VariableSymbol.TIME); - } - - /** - * Getter for the XPath string which identifies this variable in the model. - * If isSymbol() == true, this will return null. - * - * @return An XPath string - */ - public final String getTarget() { - return targetXPathStr; - } - - /** - * Gets the cross-reference for this variable. If this variable is defined - * inside a DataGenerator element, this cross-reference will refer to a - * task id. If it occurs in a ComputeChange - * element, it will refer to a Model id. - * - * @return the reference ID. - */ - public final String getReference() { - return reference; - } - - @Override - public String getElementName() { - return SEDMLTags.DATAGEN_ATTR_VARIABLE; - } - - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } -} diff --git a/vcell-core/src/main/java/org/jlibsedml/VectorRange.java b/vcell-core/src/main/java/org/jlibsedml/VectorRange.java deleted file mode 100644 index 53ff651660..0000000000 --- a/vcell-core/src/main/java/org/jlibsedml/VectorRange.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.jlibsedml; - -import java.util.ArrayList; -import java.util.List; - -public class VectorRange extends Range { - - private List values = new ArrayList (); - - public VectorRange(String id, List values) { - super(id); - if(values != null) { - this.values = values; - } - } - public VectorRange(String id) { - super(id); - } - - public void addValue(Double value) { - values.add(value); - } - - @Override - public int getNumElements() { - return values.size(); - } - - @Override - public double getElementAt(int index) { - return values.get(index); - } - - @Override - public String toString() { - return "Vector Range [" - + "getId()=" + getId() - + ", values.size()=" + values.size() - + "]"; - } - - @Override - public String getElementName() { - return SEDMLTags.VECTOR_RANGE_TAG; - } - - @Override - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); - } - -} diff --git a/vcell-core/src/main/java/org/jlibsedml/XPathTarget.java b/vcell-core/src/main/java/org/jlibsedml/XPathTarget.java index 306dbc98e5..59089e5a5e 100644 --- a/vcell-core/src/main/java/org/jlibsedml/XPathTarget.java +++ b/vcell-core/src/main/java/org/jlibsedml/XPathTarget.java @@ -1,5 +1,8 @@ package org.jlibsedml; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.model.Change; + import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; @@ -23,8 +26,7 @@ public final class XPathTarget { */ public XPathTarget(String xPathStr) { super(); - Assert.checkNoNullArgs(xPathStr); - Assert.stringsNotEmpty(xPathStr); + if (xPathStr == null || xPathStr.isEmpty()) throw new IllegalArgumentException("xPathStr is null or empty"); this.xPathStr = xPathStr; } diff --git a/vcell-core/src/main/java/org/jlibsedml/XpathGeneratorHelper.java b/vcell-core/src/main/java/org/jlibsedml/XpathGeneratorHelper.java index 98bc2874fc..d61cad14c6 100644 --- a/vcell-core/src/main/java/org/jlibsedml/XpathGeneratorHelper.java +++ b/vcell-core/src/main/java/org/jlibsedml/XpathGeneratorHelper.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -13,88 +14,100 @@ import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.filter.ElementFilter; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.Variable; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.task.SubTask; +import org.jlibsedml.components.task.Task; import org.jlibsedml.execution.IModelResolver; import org.jlibsedml.extensions.XMLUtils; /* * Helper class to add multiple simple datagenerators / variables to the SEDML file */ - class XpathGeneratorHelper { - private final static Logger lg = LogManager.getLogger(XpathGeneratorHelper.class); - - public XpathGeneratorHelper(SedML sedml) { +public class XpathGeneratorHelper { + private final static Logger lg = LogManager.getLogger(XpathGeneratorHelper.class); + + public XpathGeneratorHelper(SedMLDataContainer sedml) { super(); this.sedml = sedml; } - private final SedML sedml; - + private final SedMLDataContainer sedml; + - boolean addIdentifiersAsDataGenerators (final AbstractTask task, final String attributeIdentifierName, - boolean allOrNothing, final IModelResolver modelResolver, final IdName ... idNameList) { + public boolean addIdentifiersAsDataGenerators(final AbstractTask task, final String attributeIdentifierName, + boolean allOrNothing, final IModelResolver modelResolver, final IdName... idNameList) { XMLUtils utils = new XMLUtils(); - + try { - String model = modelResolver.getModelXMLFor(sedml.getModelWithId(task.getModelReference()).getSourceURI()); - if (model == null){ - return false; - } - Document doc = utils.readDoc(new ByteArrayInputStream(model.getBytes())); - - List configs = new ArrayList(); - for (IdName idn: idNameList){ - String id =idn.getId(); - Element toIdentify= findElement(doc, id); - if(toIdentify == null && !allOrNothing) { - continue; - }else if (toIdentify == null && allOrNothing){ - return false; - } - - String xpath = utils.getXPathForElementIdentifiedByAttribute(toIdentify, - doc, toIdentify.getAttribute(attributeIdentifierName)); - XPathTarget targ = new XPathTarget(xpath); - // build up collection to execute, so as to satisfy all-or-nothing criteria. - configs.add(new AllOrNothingConfig(targ, idn)); - } - for (AllOrNothingConfig cfg: configs){ - sedml.addSimpleSpeciesAsOutput(cfg.targ, cfg.id.getId(), cfg.id.getName(), task, true); - } - - + Set potentialBaseTasks = this.sedml.getBaseTasks(task.getId()); + if (potentialBaseTasks.size() != 1) throw new IllegalArgumentException("Cannot make data generators for repeated task with multiple different base tasks!"); + Task baseTask = potentialBaseTasks.stream().findFirst().orElse(null); + Model modelFound = this.sedml.findModelById(baseTask.getModelReference()); + if (null == modelFound) throw new IllegalArgumentException("provided task has invalid model reference!"); + String modelStrRep = modelResolver.getModelXMLFor(modelFound.getSourceURI()); + if (modelStrRep == null) return false; + + Document doc = utils.readDoc(new ByteArrayInputStream(modelStrRep.getBytes())); + + List configs = new ArrayList<>(); + for (IdName idn : idNameList) { + String id = idn.getId(); + Element toIdentify = this.findElement(doc, id); + if (toIdentify == null && !allOrNothing) { + continue; + } else if (toIdentify == null) { + return false; + } + + String xpath = utils.getXPathForElementIdentifiedByAttribute(toIdentify, + doc, toIdentify.getAttribute(attributeIdentifierName)); + XPathTarget targ = new XPathTarget(xpath); + // build up collection to execute, so as to satisfy all-or-nothing criteria. + configs.add(new AllOrNothingConfig(targ, idn)); + } + for (AllOrNothingConfig cfg : configs) { + this.sedml.createIdentityDataGeneratorForSpecies(new Variable(new SId(cfg.id.getId()), cfg.id.getName(), task.getId(), cfg.targ.toString())); + } + } catch (URISyntaxException | JDOMException | IOException e) { lg.error(e); return false; } - + return true; } - - + + class AllOrNothingConfig { - public AllOrNothingConfig(XPathTarget targ, IdName id) { + public AllOrNothingConfig(XPathTarget targ, IdName id) { super(); this.targ = targ; this.id = id; } - XPathTarget targ; - IdName id; + + XPathTarget targ; + IdName id; + } + + private Element findElement(Document doc, String id) { + Iterator it = doc.getDescendants(new ElementFilter() { + @Override + public Element filter(Object obj) { + Element el = super.filter(obj); + if (el != null && el.getAttribute("id") != null && el.getAttribute("id").getValue().equals(id)) { + return el; + } + return null; + } + }); + if (it.hasNext()) { + return (Element) it.next(); + } else { + return null; + } } - private Element findElement(Document doc, String id) { - Iterator it = doc.getDescendants(new ElementFilter() { - @Override - public Element filter(Object obj) { - Element el = super.filter(obj); - if (el != null && el.getAttribute("id") != null && el.getAttribute("id").getValue().equals(id)) { - return el; - } - return null; - } - }); - if(it.hasNext()){ - return (Element)it.next(); - } else { - return null; - } - } } diff --git a/vcell-core/src/main/java/org/jlibsedml/components/Annotation.java b/vcell-core/src/main/java/org/jlibsedml/components/Annotation.java new file mode 100644 index 0000000000..864ae6739b --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/Annotation.java @@ -0,0 +1,84 @@ +package org.jlibsedml.components; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.SedMLElementFactory; + +/** + * Encapsulates an annotation element which can be used for software tools to + * annotate a SED-ML document. + * The content of an annotation need not be human-readable.
+ * For human-readable content, the 'Notes' elements should be used.
+ * Elements added to this Annotation should be in their own XML namespace. + * + */ +public final class Annotation implements SedGeneralClass, Cloneable { + + private List elements; + + + /** + * Accepts an annotation element and adds it as the first XML element in the List + * of XML elements. + * @param argAnnotElement a non-null {@link Element} + */ + public Annotation(Element argAnnotElement) { + this.elements = new ArrayList<>(); + if(SedMLElementFactory.getInstance().isStrictCreation()) SedGeneralClass.checkNoNullArgs(argAnnotElement); + // We can NOT use a for-each loop, as we can end up causing a "concurrentAccessViolation" exception + while (!argAnnotElement.getChildren().isEmpty()) { + Element elementToAdd = argAnnotElement.getChildren().get(0).detach(); + this.elements.add(0, elementToAdd.setNamespace(Namespace.getNamespace(SedMLTags.XHTML_NS))); + } + } + + public Annotation clone() throws CloneNotSupportedException { + Annotation clone = (Annotation) super.clone(); + clone.elements = new ArrayList<>(this.elements); + return clone; + } + + + + /** + * Get an unmodifiable list of sub element for this Annotation object, will not return null. + * + * @return An {@link List} of {@link Element}s + */ + public List getAnnotationElements() { + return Collections.unmodifiableList(this.elements); + } + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.ANNOTATION; + } + + +// /** +// * Will remove this element based on its namespace. In section 2.3.3.3 of the L1V1 specification, +// * it is recommended that an Annotation only has one top-level element in a particular +// * namespace. +// * @param el The Element to remove. +// * @return true if an element in the same namespace as this was removed. +// */ +// public boolean removeElement (Element el){ +// Namespace ns = el.getNamespace(); +// for (Element child: this.elements) { +// if (child.getNamespace().equals(el.getNamespace())){ +// return this.elements.remove(child); +// } +// } +// return false; +// } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/Calculation.java b/vcell-core/src/main/java/org/jlibsedml/components/Calculation.java new file mode 100644 index 0000000000..b9bcb298ce --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/Calculation.java @@ -0,0 +1,50 @@ +package org.jlibsedml.components; + +import org.jlibsedml.components.listOfConstructs.ListOfParameters; +import org.jlibsedml.components.listOfConstructs.ListOfVariables; +import org.jmathml.ASTNode; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.ArrayList; +import java.util.List; + +// Java does not allow for multiple inheritance, this is the most appropriate work around, since `Calculation` is abstract. +public interface Calculation extends SedGeneralClass { + SId getId(); // This should end up being implemented through `SedBase` + + ListOfParameters getListOfParameters(); + List getParameters(); + void addParameter(Parameter parameter); + void removeParameter(Parameter parameter); + + ListOfVariables getListOfVariables(); + List getVariables(); + void addVariable(Variable variable); + void removeVariable(Variable variable); + + /** + * Convenience function to return the maths expression as a C-style string. + * @return A String representation of the maths of this DataGenerator. + */ + String getMathAsString(); + ASTNode getMath(); + void setMath(ASTNode math); + + /** + * Proxy for shared code used by `parametersToString()` method children should implement + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + default List getMathParamsAndVarsAsStringParams(){ + List params = new ArrayList<>(), paramParams = new ArrayList<>(), varParams = new ArrayList<>(); + if (this.getMath() != null) params.add(String.format("math=%s", this.getMathAsString())); + for (Parameter p : this.getParameters()) paramParams.add(p.getId() != null ? p.getIdAsString() : '{' + p.parametersToString() + '}'); + for (Variable var : this.getVariables()) varParams.add(var.getId() != null ? var.getIdAsString() : '{' + var.parametersToString() + '}'); + params.add(String.format("parameters=[%s]", String.join(",", paramParams))); + params.add(String.format("variables=[%s]", String.join(",", varParams))); + return params; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/Notes.java b/vcell-core/src/main/java/org/jlibsedml/components/Notes.java new file mode 100644 index 0000000000..58ab64d009 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/Notes.java @@ -0,0 +1,76 @@ +package org.jlibsedml.components; + +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jlibsedml.SedMLElementFactory; +import org.jlibsedml.SedMLTags; + +/** + * Contains notes elements to display human readable information to the user. + * The content should be XHTML.
+ * Usage is as follows:
+ * Create a new Notes element + * + *
+ * // create some xhtml. E.g.,
+ * Element para = new Element("p");
+ * para.setText("some comment");
+ * 
+ * // create a notes element
+ * Notes note = new Notes(para);
+ * 
+ * + * Clients do not have to set the namespace of the XHTML contents, this is performed by this class. + * + */ +public final class Notes implements SedGeneralClass, Cloneable { + + private List elements; + + /** + * @param argNotesElement + * A non-null Element contains XHTM. The children will be detached and have its namespace set + * to "http://www.w3.org/1999/xhtml" + */ + public Notes(Element argNotesElement) { + this.elements = new ArrayList<>(); + if (SedMLElementFactory.getInstance().isStrictCreation()) SedGeneralClass.checkNoNullArgs(argNotesElement); + // We can NOT use a for-each loop, as we can end up causing a "concurrentAccessViolation" exception + while (!argNotesElement.getChildren().isEmpty()) { + Element elementToAdd = argNotesElement.getChildren().get(0).detach(); + this.elements.add(elementToAdd.setNamespace(Namespace.getNamespace(SedMLTags.XHTML_NS))); + } + } + + public Notes clone() throws CloneNotSupportedException { + Notes clone = (Notes) super.clone(); + clone.elements = new ArrayList<>(this.elements); + return clone; + } + + public void addNote(Element note) { + this.elements.add(note); + } + + /** + * Get an unmodifiable list of sub element for this Notes object, will not return null. + * + * @return An {@link List} of {@link Element}s + */ + public List getNotesElements() { + return Collections.unmodifiableList(this.elements); + } + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.NOTES; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/Parameter.java b/vcell-core/src/main/java/org/jlibsedml/components/Parameter.java similarity index 56% rename from vcell-core/src/main/java/org/jlibsedml/Parameter.java rename to vcell-core/src/main/java/org/jlibsedml/components/Parameter.java index 9e3bffd8df..4a79d441b8 100644 --- a/vcell-core/src/main/java/org/jlibsedml/Parameter.java +++ b/vcell-core/src/main/java/org/jlibsedml/components/Parameter.java @@ -1,24 +1,16 @@ -package org.jlibsedml; +package org.jlibsedml.components; + +import org.jlibsedml.SedMLElementFactory; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.SEDMLVisitor; /** * Represents the SED-ML Parameter element which defines a constant value in * either ComputeChange or Variable elements. * */ -public final class Parameter extends AbstractIdentifiableElement { - - @Override - public String toString() { - return "Parameter [name=" + getName() + ", value=" + value + ", getId()=" - + getId() + "]"; - } - - public boolean accept(SEDMLVisitor visitor){ - return visitor.visit(this); - } - - - private double value; +public final class Parameter extends SedBase { + private double value; /** * Copy constructor @@ -28,8 +20,6 @@ public Parameter(Parameter parameter) { this(parameter.getId(), parameter.isNameSet() ? parameter.getName() : null, parameter.getValue()); } - - /** * * @param id The id of this element. @@ -37,15 +27,20 @@ public Parameter(Parameter parameter) { * @param value the Parameter value. * @throws IllegalArgumentException if id or value is null or the empty string. */ - public Parameter(String id, String name, double value) { + public Parameter(SId id, String name, double value) { super(id,name); - if(SEDMLElementFactory.getInstance().isStrictCreation()){ - Assert.checkNoNullArgs(value); - Assert.stringsNotEmpty(id); + if(SedMLElementFactory.getInstance().isStrictCreation()){ + SedGeneralClass.checkNoNullArgs(value); } this.value = value; } + public Parameter clone() throws CloneNotSupportedException { + Parameter clone = (Parameter) super.clone(); + clone.value = this.value; + return clone; + } + /** * Boolean test for whether this parameter is set or not. * @return true if set, false otherwise @@ -74,6 +69,16 @@ public final void setValue(double value) { @Override public String getElementName() { - return SEDMLTags.DATAGEN_ATTR_PARAMETER; + return SedMLTags.DATAGEN_ATTR_PARAMETER; } + + @Override + public String parametersToString() { + return super.parametersToString() + ", value=" + this.value; + } + + @Override + public SedBase searchFor(SId idOfElement){ + return super.searchFor(idOfElement); + } } \ No newline at end of file diff --git a/vcell-core/src/main/java/org/jlibsedml/components/SId.java b/vcell-core/src/main/java/org/jlibsedml/components/SId.java new file mode 100644 index 0000000000..1c6f1dd79c --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/SId.java @@ -0,0 +1,49 @@ +package org.jlibsedml.components; + +import javax.annotation.Nonnull; + +public record SId(String string) { + + public SId { + if (!isValidSId(string)) { + throw new IllegalArgumentException("Invalid SId: " + string); + } + } + + public static String unwrap(SId sid) { + return sid == null ? null : sid.string(); + } + + public int length() { + return this.string.length(); + } + + /* + * letter ::= 'a'..'z','A'..'Z' + * digit ::= '0'..'9' + * idChar ::= letter | digit | '_' + * SId ::= ( letter | '_' ) idChar* + */ + public static boolean isValidSId(String string) { + if (string == null) return false; + return string.matches("[a-zA-Z_]\\w*"); + } + + @Override + @Nonnull + public String toString() { + return "SId [" + this.string() + "]"; + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other instanceof SId sid)) return false; + return this.string().equals(sid.string()); + } + + @Override + public int hashCode() { + return this.string().hashCode(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/SedBase.java b/vcell-core/src/main/java/org/jlibsedml/components/SedBase.java new file mode 100644 index 0000000000..2368f7233f --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/SedBase.java @@ -0,0 +1,199 @@ +package org.jlibsedml.components; + +import org.jlibsedml.IIdentifiable; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Base class of SEDML elements that can be annotated or contain notes. + * @author anu/radams + * + */ +public abstract class SedBase implements SedGeneralClass, IIdentifiable, Comparable, Cloneable { + private String metaId = null; + private SId id; + private String name; + + private Notes notes; + private Annotation annotation; + + + public SedBase(SId id, String name) { + super(); + this.id = id; // Since id is optional, we use `null` to indicate "not present" + this.name = name; + } + + public SedBase clone() throws CloneNotSupportedException { + SedBase copy = (SedBase) super.clone(); + copy.id = this.id == null ? null : new SId(this.id.string()); + copy.name = this.name; + copy.notes = this.notes == null ? null : this.notes.clone(); + copy.annotation = this.annotation == null ? null : this.annotation.clone(); + return copy; + } + + /** + * Attempts to search this and relevant children for the object with the matching id + * @param idOfElement ID to look for + * @return null if the element could not be found, otherwise, returns the element itself + * + */ + //@throws IllegalArgumentException if null is provided as an argument + @OverridingMethodsMustInvokeSuper + public SedBase searchFor(SId idOfElement){ + if (idOfElement == null) return null; + //if (idOfElement == null) throw new IllegalArgumentException("`null` is not a valid id to search for."); + if (idOfElement.equals(this.id)) return this; + return null; + } + + /** + * Getter for the metaid attribute of this element. + * @return If present, a non-empty string. Otherwise, null is returned + */ + public String getMetaId() { + return this.metaId; + } + + /** + * Setter for the metaid attribute of this element. + * @param metaId A non-null String of the meta_id attribute. + */ + public void setMetaId(String metaId) { + this.metaId = metaId; + } + + /** + * Getter for the id attribute. + * @return If present, a non-empty string. Otherwise, null is returned + */ + public String getIdAsString() { + if (this.id == null) return null; + return this.id.string(); + } + + /** + * Getter for the id attribute. + * @return If present, a non-empty string. Otherwise, null is returned + */ + public SId getId() { + return this.id; + } + + /** + * @return If present, a non-empty string. Otherwise, null is returned + */ + public String getName() { + return this.name; + } + + /** + * Setter for the name of this object. + * @param name A short human-readable String. Can be null. + * @since 1.2.0 + */ + public void setName(String name) { + this.name = name; + } + + + /** + * Getter for the {@link Notes} object associated with this SedBase object. + * @return The {@link Notes} for this object, or null if no {@link Notes} object exists + */ + public Notes getNotes() { + return this.notes; + } + + /** + * Setter for a {@link Notes}. + * @param notes Can be null, if the {@link Notes} are to be removed from this object. + */ + public void setNotes(Notes notes) { + this.notes = notes; + } + + + /** + * Getter for the {@link Annotation} object associated with this SedBase object. + * @return The {@link Annotation} for this object, or null if no {@link Annotation} object exists + */ + public Annotation getAnnotations() { return this.annotation; } + + + /** + * Sets the {@link Annotation} for this SedBase object. + * @param ann Can be null, if the {@link Annotation} is to be removed from this object. + */ + public void setAnnotation(Annotation ann) { + if (null == ann) throw new IllegalArgumentException("Annotation provided is null"); + this.annotation = ann; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + List params = new ArrayList<>(); + String idStr = this.id != null ? this.id.string() : ""; + params.add(String.format("id=%s", idStr)); + if (this.name != null) params.add(String.format("name=%s", this.name)); + if (this.metaId != null) params.add(String.format("metaId=%s", this.metaId)); + return String.join(", ", params); // We're the top level call! No super call to make here! + } + + @Override + public String toString(){ + return this.generateVerboseDescription(); + } + + public String generateVerboseDescription(){ + // Warning! This method is used in both hashcode and compare to! Know what you're doing if you edit this! + return String.format("%s {%s}", this.getClass().getSimpleName(), this.parametersToString()); + } + + @Override + public boolean equals(Object obj){ + if (obj == this) + return true; + if (!(obj instanceof SedBase otherSedBase)) + return false; + + return //Objects.equals(this.getMetaId(), otherSedBase.getMetaId()) && + Objects.equals(this.getId(), otherSedBase.getId()) && + Objects.equals(this.getName(), otherSedBase.getName()); + + } + + //TODO: Verify this is the hash code we want! + @Override + public int hashCode() { // written by Ion +// final int prime = 31; +// int result = 1; +// result = prime * result + ((id == null) ? 0 : id.hashCode()); +// return result; + // Due to IDs potentially being null, we can't rely on the above alone. + return this.generateVerboseDescription().hashCode(); // Since toString uses `parametersToString()`, every object should add its identifiable parts + } + + /** + * Compares identifiable elements based on String comparison + * of their identifiers. + */ + public int compareTo(SedBase o) { + String thisDesc = this.generateVerboseDescription(); + String otherDesc = o.generateVerboseDescription(); + return thisDesc.compareTo(otherDesc); + } + + +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/SedGeneralClass.java b/vcell-core/src/main/java/org/jlibsedml/components/SedGeneralClass.java new file mode 100644 index 0000000000..f03393c0a7 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/SedGeneralClass.java @@ -0,0 +1,34 @@ +package org.jlibsedml.components; + +import org.jlibsedml.SEDMLVisitor; + +public interface SedGeneralClass { + /* + * Checks any argument list for null and throws an IllegalArgumentException if they are. + */ + static void checkNoNullArgs (Object ... args) { + for (Object o : args) { + if (o == null){ + throw new IllegalArgumentException("SedGeneralClass: null args passed"); + } + } + } + + /* + * Throws IllegalArgumentException if strings are empty. + */ + static void stringsNotEmpty(String ...args) { + for (String o : args) { + if (o.isEmpty()){ + throw new IllegalArgumentException("SedGeneralClass: empty args passed"); + } + } + + } + + /** + * Provides a link between the object model and the XML element names + * @return A non-null String of the XML element name of the object. + */ + String getElementName(); +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/SedML.java b/vcell-core/src/main/java/org/jlibsedml/components/SedML.java new file mode 100644 index 0000000000..69ad9d190c --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/SedML.java @@ -0,0 +1,369 @@ +package org.jlibsedml.components; + +import org.jdom2.Namespace; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.listOfConstructs.*; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.output.Output; +import org.jlibsedml.components.simulation.Simulation; +import org.jlibsedml.components.task.AbstractTask; + +import java.util.*; + + +/** + * The top level object in a SED-ML document. + *

+ * To create a SedML object, create a SED-ML document first. + *

+ * + *

+ * This class is basically a container element for the 5 different parts of the + * SED-ML - the model, simulation, task description, data generator, and output. + *

+ * + * Elements can be added in two ways: + *
    + *
  • Either pass in a previously created list, e.g., + * + *
    + * List<Simulation> simulations = createListOfSimulationsSomewhereElse();
    + * sedml.setSimulations(simulations);
    + * 
    + * + * or + *
  • Add simulations one at a time: + * + *
    + * Simulation sim = createASimulation();
    + * sedml.addSimulation(sim);
    + * 
    + * + *
+ * + * Elements can be added to a list that has previously been set, however setting + * in a list will overwrite any collections generated from either of the two + * approaches above.

+ * + * All getListOfXXX() methods will return read-only lists. Attempting to modify + * these lists will generate runtime exceptions. To manipulate the lists, then, + * use the add/remove/set methods. + *

+ * + *

+ * Elements can be searched for by the getXXXWithId() methods. E.g., + *

+ * + *
+ * Model model = sedml.getModelWithId("myModel");
+ * if (model != null) {
+ *     // do something.
+ * }
+ * 
+ *

+ * It should be noted that all collections held in this object, and elsewhere in + * SEDML, are mutable from outside the object. So, if you pass in a collection + * of Simulations into this class, then modify the collection outside this + * object, then this collection will be modified. + *

+ * + * @author anu/radams + * + */ +public class SedML extends SedBase { + private final static int DEFAULT_LEVEL = 1; + private final static int DEFAULT_VERSION = 5; + + private int level; + private int version; + private ListOfModels models; + private ListOfSimulations simulations; + private ListOfTasks tasks; + private ListOfDataGenerators dataGenerators; + private ListOfOutputs outputs; + + public SedML() { + this(SedML.DEFAULT_LEVEL, SedML.DEFAULT_VERSION); + } + + public SedML(int level, int version) { + this(null, null, level, version); + } + + public SedML(SId id, String name) { + this(id, name, SedML.DEFAULT_LEVEL, SedML.DEFAULT_VERSION); + } + + public SedML(SId id, String name, int level, int version) { + this(id, name, level, version, new ListOfModels(), new ListOfSimulations(), new ListOfTasks(), new ListOfDataGenerators(), new ListOfOutputs()); + } + + public SedML(SId id, String name, int level, int version, ListOfModels models, ListOfSimulations simulations, + ListOfTasks tasks, ListOfDataGenerators dataGenerators, ListOfOutputs outputs) { + super(id, name); + this.level = level; + this.version = version; + this.models = models; + this.simulations = simulations; + this.tasks = tasks; + this.dataGenerators = dataGenerators; + this.outputs = outputs; + } + + public SedML clone() throws CloneNotSupportedException { + SedML clone = (SedML) super.clone(); + clone.level = this.level; + clone.version = this.version; + clone.models = this.models.clone(); + clone.simulations = this.simulations.clone(); + clone.tasks = this.tasks.clone(); + clone.dataGenerators = this.dataGenerators.clone(); + clone.outputs = this.outputs.clone(); + return clone; + } + + public int getLevel() { + return this.level; + } + + public int getVersion() { + return this.version; + } + + public Namespace getSedMLNamespace(){ + return Namespace.getNamespace("", this.getSedMLNamespaceURI()); + } + + public String getSedMLNamespaceURI(){ + return String.format("http://sed-ml.org/sed-ml/level%d/version%d", this.level, this.version); + } + + public ListOfModels getListOfModels() { + return this.models; + + } + + /** + * Returns a read-only list of models in SedML + * + * @return unmodifiable list of {@link Model}s + */ + public List getModels() { + return this.models.getContents(); + } + + /** + * Adds a {@link Model} to this object's {@link ListOfModels}, if not already present. + * + * @param model A non-null {@link Model} element + */ + public void addModel(Model model) { + this.models.addContent(model); + } + + /** + * Removes a {@link Model} from this object's {@link ListOfModels}, if it is present. + * + * @param model A non-null {@link Model} element + */ + public void removeModel(Model model) { + this.models.removeContent(model); + } + + public ListOfSimulations getListOfSimulations() { + return this.simulations; + + } + + /** + * Returns a read-only list of simulations in SedML + * + * @return unmodifiable list of {@link Simulation}s + */ + public List getSimulations() { + return this.simulations.getContents(); + } + + /** + * Adds a {@link Simulation} to this object's {@link ListOfSimulations}, if not already present. + * + * @param sim A non-null {@link Simulation} element + */ + public void addSimulation(Simulation sim) { + this.simulations.addContent(sim); + } + + /** + * Removes a {@link Simulation} from this object's {@link ListOfSimulations}, if it is present. + * + * @param sim A non-null {@link Simulation} element + */ + public void removeSimulation(Simulation sim) { + this.simulations.removeContent(sim); + } + + public ListOfTasks getListOfTasks() { + return this.tasks; + } + + /** + * Returns a read-only list of tasks in SedMl + * + * @return unmodifiable list of {@link AbstractTask}s + */ + public List getTasks() { + return this.tasks.getContents(); + } + + /** + * Adds a {@link AbstractTask} to this object's {@link ListOfTasks}, if not already present. + * + * @param task A non-null {@link AbstractTask} element + */ + public void addTask(AbstractTask task) { + this.tasks.addContent(task); + } + + /** + * Removes an {@link AbstractTask} from this object's {@link ListOfTasks}, if it is present. + * + * @param task A non-null {@link AbstractTask} element + */ + public void removeTask(AbstractTask task) { + this.tasks.removeContent(task); + } + + public ListOfDataGenerators getListOfDataGenerators() { + return this.dataGenerators; + } + + /** + * Returns a read-only list of data generators in SedML + * + * @return unmodifiable list of {@link DataGenerator}s + */ + public List getDataGenerators() { + return this.dataGenerators.getContents(); + } + + /** + * Adds a {@link DataGenerator} to this object's {@link ListOfDataGenerators}, if not already present. + * + * @param dataGenerator A non-null {@link DataGenerator} element + */ + public void addDataGenerator(DataGenerator dataGenerator) { + this.dataGenerators.addContent(dataGenerator); + } + + /** + * Removes a {@link DataGenerator} from this object's {@link ListOfDataGenerators}, if it is present. + * + * @param dataGenerator A non-null {@link DataGenerator} element + */ + public void removeDataGenerator(DataGenerator dataGenerator) { + this.dataGenerators.removeContent(dataGenerator); + } + + /** + * Ease-of-use function to help when auto-creating "identity" DataGenerators + * @param var a variable to potentially be referenced by a new data generator + * @return true, if an identity data-generator already exists, else false + */ + public boolean appropriateIdentityDataGeneratorAlreadyExistsFor(Variable var){ + return this.dataGenerators.appropriateIdentityDataGeneratorAlreadyExistsFor(var); + } + + public ListOfOutputs getListOfOutputs() { + return this.outputs; + } + + /** + * Returns a read-only list of outputs in SedDocument. This method does not + * return the list in the order by which Outputs were added. Instead, it + * orders the outputs by types to agree with the schema. + * I.e.: Plot2D, Plot3D, Reports. + *
+ * + * @return A possibly empty but non-null List of Outputs. + */ + public List getOutputs() { + return this.outputs.getContents(); + } + + /** + * Adds a {@link Output} to this object's {@link ListOfOutputs}, if not already present. + * + * @param output A non-null {@link Output} element + */ + public void addOutput(Output output) { + this.outputs.addContent(output); + } + + /** + * Removes a {@link Output} from this object's {@link ListOfOutputs}, if it is present. + * + * @param output A non-null {@link Output} element + */ + public void removeOutput(Output output) { + this.outputs.removeContent(output); + } + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.SED_ML_ROOT; + } + + @Override + public String parametersToString(){ + List params = new ArrayList<>(); + params.add(String.format("level=%d", this.level)); + params.add(String.format("version=%d", this.version)); + if (this.models != null) params.add(String.format("models=%s", this.models.getId() != null ? this.models.getIdAsString() : '{' + this.models.parametersToString() +'}')); + if (this.simulations != null) params.add(String.format("simulations=%s", this.simulations.getId() != null ? this.simulations.getIdAsString() : '{' + this.simulations.parametersToString() +'}')); + if (this.tasks != null) params.add(String.format("tasks=%s", this.tasks.getId() != null ? this.tasks.getIdAsString() : '{' + this.tasks.parametersToString() +'}')); + if (this.dataGenerators != null) params.add(String.format("dataGenerators=%s", this.dataGenerators.getId() != null ? this.dataGenerators.getIdAsString() : '{' + this.dataGenerators.parametersToString() +'}')); + if (this.outputs != null) params.add(String.format("outputs=%s", this.outputs.getId() != null ? this.outputs.getIdAsString() : '{' + this.outputs.parametersToString() +'}')); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + elementFound = this.models.searchFor(idOfElement); + if (elementFound != null) return elementFound; + elementFound = this.simulations.searchFor(idOfElement); + if (elementFound != null) return elementFound; + elementFound = this.tasks.searchFor(idOfElement); + if (elementFound != null) return elementFound; + elementFound = this.dataGenerators.searchFor(idOfElement); + if (elementFound != null) return elementFound; + return this.outputs.searchFor(idOfElement); + } + + public SedBase searchInModelsFor(SId idOfElement){ + return this.models.searchFor(idOfElement); + } + + public SedBase searchInSimulationsFor(SId idOfElement){ + return this.simulations.searchFor(idOfElement); + } + + public SedBase searchInTasksFor(SId idOfElement){ + return this.tasks.searchFor(idOfElement); + } + + public SedBase searchInDataGeneratorsFor(SId idOfElement){ + return this.dataGenerators.searchFor(idOfElement); + } + + public SedBase searchInOutputsFor(SId idOfElement){ + return this.outputs.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/Variable.java b/vcell-core/src/main/java/org/jlibsedml/components/Variable.java new file mode 100644 index 0000000000..8da5fd7cd9 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/Variable.java @@ -0,0 +1,330 @@ +package org.jlibsedml.components; + +import org.jlibsedml.SedMLElementFactory; +import org.jlibsedml.SedMLTags; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates a SED-ML Variable element. A variable can have EITHER a + * taskRef or a modelRef depending on whether it + * belongs to a ComputeChange or DataGenerator element. + *

+ * The 'taskRef' and 'modelRef' attributes have different meanings. The + * 'modelRef', used for instance in a computeChange structure, means that the + * variable refers to the value in the model. This is currently a scalar in + * SBML, but may be something else in the future (for instance a list or a + * function). The important concept is that it is the value BEFORE simulation. + *

+ *

+ * The 'taskRef' means the variable refers to the value**S** as output by the + * task. For instance, in COPASI, a variable with 'modelRef' would correspond to + * the "initial concentration" while a variable with 'taskRef' would correspond + * to the "transient concentration". + *

+ * + */ +public final class Variable extends SedBase { + + private String targetXPathStr; // maybe make: `private XPathTarget targetXPathStr;` ? + private SId modelReference; + private SId taskReference; + private VariableSymbol symbol; +// private XPathTarget target2; // Not used yet +// private VariableSymbol symbol2; // Not used yet +// private String anyURI; // Not used yet +// private String dimensionTerm; // Not used yet + + /** + * Case 1: Variable references a target-only value (ex: DataSource [not currently implemented]) + * + * @param id + * A non-null ID + * @param name + * An optional name ( can be null or empty string) + * @param targetXPath + * An XPath expression identifying the variable in the data + * @throws IllegalArgumentException + * id or targetXPath is null. + */ + public Variable(SId id, String name, String targetXPath) { + super(id, name); + if (SedMLElementFactory.getInstance().isStrictCreation()) { + SedGeneralClass.checkNoNullArgs(id, targetXPath); + } + this.targetXPathStr = targetXPath; + this.modelReference = null; + this.taskReference = null; + this.symbol = null; + } + + /** + * Case 2a: Variable references a model by target; note that argument order matters!! + * + * @param id + * A non-null ID + * @param name + * An optional name ( can be null or empty string) + * @param targetXPath + * An XPath expression identifying the variable in the model referenced. + * @param modelReference + * A non-null {@link SId} to a model Id ( see class documentation ) + * @throws IllegalArgumentException + * id,modelReference, or + * targetXPath is null. + */ + public Variable(SId id, String name, String targetXPath, SId modelReference) { + this(id, name, modelReference, null, targetXPath); + } + + /** + * Case 2b: Variable references a model by symbol; note that argument order matters!! + * + * @param id + * A non-null ID + * @param name + * An optional name ( can be null or empty string) + * @param symbol + * An {@link VariableSymbol} identifying the variable in the model referenced + * @param modelReference + * A non-null {@link SId} to a model Id ( see class documentation ) + * @throws IllegalArgumentException + * id,modelReference, or + * targetXPath is null. + */ + public Variable(SId id, String name, VariableSymbol symbol, SId modelReference) { + this(id, name, modelReference, null, symbol); + } + + /** + * Case 3a: Variable references a task by target; note that argument order matters!! + * + * @param id + * A non-null ID + * @param name + * An optional name ( can be null or empty string) + * @param targetXPath + * An XPath expression identifying the variable in the task referenced. + * @param taskReference + * A non-null {@link SId} to a taskId + * @throws IllegalArgumentException + * id,taskReference, or + * targetXPath is null. + */ + public Variable(SId id, String name, SId taskReference, String targetXPath) { + this(id, name, null, taskReference, targetXPath); + } + + /** + * Case 3b: Variable references a task by symbol; note that argument order matters!! + * + * @param id + * A non-null ID + * @param name + * An optional name ( can be null or empty string) + * @param symbol + * An {@link VariableSymbol} identifying the variable in the task referenced. + * @param taskReference + * A non-null {@link SId} to a taskId ( see class documentation ) + * @throws IllegalArgumentException + * id,taskReference, or + * targetXPath is null. + */ + public Variable(SId id, String name, SId taskReference, VariableSymbol symbol) { + this(id, name, null, taskReference, symbol); + } + + /** + * Case 4a: Variable references a task AND model by symbol; note that argument order matters!! + * + * @param id + * A non-null ID + * @param name + * An optional name ( can be null or empty string) + * @param targetXPath + * An XPath expression identifying the variable in the task + model referenced. + * @param modelReference + * A non-null {@link SId} to a modelId ( see class documentation ) + * @param taskReference + * A non-null {@link SId} to a taskId ( see class documentation ) + * @throws IllegalArgumentException + * id,taskReference, or + * targetXPath is null. + */ + public Variable(SId id, String name, SId modelReference, SId taskReference, String targetXPath) { + this(id, name, modelReference, taskReference, targetXPath, null); + } + + /** + * Case 4b: Variable references a task AND model by symbol; note that argument order matters!! + * + * @param id + * A non-null ID + * @param name + * An optional name ( can be null or empty string) + * @param symbol + * An {@link VariableSymbol} identifying the variable in the task + model referenced. + * @param modelReference + * A non-null {@link SId} to a modelId ( see class documentation ) + * @param taskReference + * A non-null {@link SId} to a taskId ( see class documentation ) + * @throws IllegalArgumentException + * id,taskReference, or + * targetXPath is null. + */ + public Variable(SId id, String name, SId modelReference, SId taskReference, VariableSymbol symbol) { + this(id, name, modelReference, taskReference, null, symbol); + } + + // Internal constructor + private Variable(SId id, String name, SId modelReference, SId taskReference, String targetXPath, VariableSymbol symbol){ + super(id, name); + SId oneRefNotNull = (modelReference != null) ? modelReference : taskReference; + Object oneTargetNotNull = (targetXPath != null) ? targetXPath : symbol; + SedGeneralClass.checkNoNullArgs(id, oneRefNotNull, oneTargetNotNull); + this.targetXPathStr = targetXPath; + this.modelReference = modelReference; + this.taskReference = taskReference; + this.symbol = symbol; + } + + public Variable clone() throws CloneNotSupportedException { + Variable clone = (Variable) super.clone(); + clone.targetXPathStr = this.targetXPathStr; + clone.modelReference = new SId(this.modelReference.string()); + clone.taskReference = new SId(this.taskReference.string()); + clone.symbol = this.symbol; + return clone; + } + + /** + * Getter for the XPath string which identifies this variable in the model. + * If isSymbol() == true, this will return null. + * + * @return An XPath string + */ + public String getTarget() { + return this.targetXPathStr; + } + + public void setTarget(String target) { + this.targetXPathStr = target; + } + + /** + * Gets the cross-reference for this variable. + * + * @return the reference ID. + */ + public SId getModelReference() { + return this.modelReference; + } + + /** + * Sets the task reference for this variable. + * + * @param reference + * A non-nullString. + * @since 1.2.0 + */ + public void setModelReference(SId reference) { + this.modelReference = reference; + } + + /** + * Gets the cross-reference for this variable. + * + * @return the reference ID. + */ + public SId getTaskReference() { + return this.taskReference; + } + + /** + * Sets the task reference for this variable. + * + * @param reference + * A non-nullString. + * @since 1.2.0 + */ + public void setTaskReference(SId reference) { + this.taskReference = reference; + } + + /** + * Gets the symbol for this variable, if isSymbol() ==true. + * + * @return Gets a {@link VariableSymbol}, or null if + * isVariable()==true. + */ + public VariableSymbol getSymbol() { + return this.symbol; + } + + /** + * Sets the symbol reference for this variable. This method will have the + * side-effect of setting any task reference to null, since a + * Variable can only refer to a symbol or a task reference, but not both. + * + * @param symbol + * A non-null {@link VariableSymbol}. + * @since 1.2.0 + */ + public void setSymbol(VariableSymbol symbol) { + this.symbol = symbol; + this.targetXPathStr = null; + } + + /** + * Boolean query for whether this object represents a SEDML implicit + * variable, such as time. + * + * @return true if this object represents a SEDML implicit + * variable, false otherwise. + */ + public boolean isSymbol() { + return this.symbol != null; + } + + /** + * Boolean test for whether this object represents a SEDML variable. + * @return true if this object represents a SEDML variable, + * false otherwise. + */ + public boolean referencesXPath() { + return this.targetXPathStr != null; + } + + /** + * Boolean test for whether this object represents the SEDML implicit + * variable for urn:sedml:symbol:time. + * @return true if this object represents the SEDML implicit + * variable urn:sedml:symbol:time, false + * otherwise. + */ + public boolean isTime() { + return this.isSymbol() && this.symbol.equals(VariableSymbol.TIME); + } + + + @Override + public String getElementName() { + return SedMLTags.DATAGEN_ATTR_VARIABLE; + } + + @Override + public String parametersToString(){ + List params = new ArrayList<>(); + if (this.modelReference != null) params.add(String.format("modelReference=%s", this.modelReference.string())); + if (this.taskReference != null)params.add(String.format("taskReference=%s", this.taskReference.string())); + if (this.symbol != null) params.add(String.format("symbol=%s", this.symbol)); + if (this.targetXPathStr != null) params.add(String.format("targetXPathStr=%s", this.targetXPathStr)); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/VariableSymbol.java b/vcell-core/src/main/java/org/jlibsedml/components/VariableSymbol.java similarity index 96% rename from vcell-core/src/main/java/org/jlibsedml/VariableSymbol.java rename to vcell-core/src/main/java/org/jlibsedml/components/VariableSymbol.java index 46c8ef1a58..508baf5c0f 100644 --- a/vcell-core/src/main/java/org/jlibsedml/VariableSymbol.java +++ b/vcell-core/src/main/java/org/jlibsedml/components/VariableSymbol.java @@ -1,4 +1,4 @@ -package org.jlibsedml; +package org.jlibsedml.components; /** * Enumeration of special SED-ML symbol URNs. diff --git a/vcell-core/src/main/java/org/jlibsedml/Version.java b/vcell-core/src/main/java/org/jlibsedml/components/Version.java similarity index 97% rename from vcell-core/src/main/java/org/jlibsedml/Version.java rename to vcell-core/src/main/java/org/jlibsedml/components/Version.java index c5647cff30..500d3db30b 100644 --- a/vcell-core/src/main/java/org/jlibsedml/Version.java +++ b/vcell-core/src/main/java/org/jlibsedml/components/Version.java @@ -1,4 +1,4 @@ -package org.jlibsedml; +package org.jlibsedml.components; /** * Encapsulates the versioning information of a SEDML description. diff --git a/vcell-core/src/main/java/org/jlibsedml/components/algorithm/Algorithm.java b/vcell-core/src/main/java/org/jlibsedml/components/algorithm/Algorithm.java new file mode 100644 index 0000000000..c1a7995a80 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/algorithm/Algorithm.java @@ -0,0 +1,126 @@ +package org.jlibsedml.components.algorithm; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.listOfConstructs.ListOfAlgorithmParameters; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates an Algorithm element in SED-ML. Equality of objects is based on the KISAO ID. + * @author radams + * @link http://www.ebi.ac.uk/compneur-srv/kisao/ + * + */ +public final class Algorithm extends SedBase { + private String kisaoID; + private ListOfAlgorithmParameters listOfAlgorithmParameters; + + + /** + * Takes a non-null, non empty KisaoID. + * @param kisaoID A String. + * @throws IllegalArgumentException if kisaoID is null or empty. + */ + public Algorithm(String kisaoID) { + this(null, null, kisaoID); + } + + /** + * Takes a non-null, non empty KisaoID. + * @param kisaoID A String. + * @throws IllegalArgumentException if kisaoID is null or empty. + */ + public Algorithm(SId id, String name, String kisaoID) { + super(id, name); + SedGeneralClass.checkNoNullArgs(kisaoID); + SedGeneralClass.stringsNotEmpty(kisaoID); + this.listOfAlgorithmParameters = new ListOfAlgorithmParameters(); + this.kisaoID = kisaoID; + } + + public Algorithm clone() throws CloneNotSupportedException { + Algorithm clone = (Algorithm) super.clone(); + clone.kisaoID = this.kisaoID; + clone.listOfAlgorithmParameters = this.listOfAlgorithmParameters.clone(); + return clone; + } + + /** + * Getter for the KisaoID of the algorithm. + * @return the Kisao ID + */ + public String getKisaoID() { + return this.kisaoID; + } + + public ListOfAlgorithmParameters getListOfAlgorithmParameters() { + return this.listOfAlgorithmParameters; + } + + public List getAlgorithmParameters() { + return this.listOfAlgorithmParameters.getContents(); + } + + public void addAlgorithmParameter(AlgorithmParameter algorithmParameter) { + this.listOfAlgorithmParameters.addContent(algorithmParameter); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((kisaoID == null) ? 0 : kisaoID.hashCode()); + return result; + } + + // We'll assume that Algorithms with the same kisaoID are equal regardless of the list of parameters + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Algorithm otherAlg)) return false; + if (this.kisaoID == null) return otherAlg.kisaoID == null; + else return this.kisaoID.equals(otherAlg.kisaoID); + } + + @Override + public String getElementName() { + return SedMLTags.ALGORITHM_TAG; + } + + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + List params = new ArrayList<>(), paramParams = new ArrayList<>(); + if (this.kisaoID != null) + params.add(String.format("kisaoID=%s", this.kisaoID)); + for (AlgorithmParameter ap : this.getAlgorithmParameters()) + paramParams.add(ap.getId() != null ? ap.getIdAsString() : '{' + ap.parametersToString() + '}'); + if (!this.listOfAlgorithmParameters.isEmpty()) + params.add(String.format("algParams=[%s]", String.join(", ", paramParams))); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + for (AlgorithmParameter ap : this.getAlgorithmParameters()) { + elementFound = ap.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return elementFound; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/algorithm/AlgorithmParameter.java b/vcell-core/src/main/java/org/jlibsedml/components/algorithm/AlgorithmParameter.java new file mode 100644 index 0000000000..174887e598 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/algorithm/AlgorithmParameter.java @@ -0,0 +1,109 @@ +package org.jlibsedml.components.algorithm; + +import org.jlibsedml.SEDMLVisitor; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.ArrayList; +import java.util.List; + +/** + * An Algorithm Parameter for a specific Algorithm. + *
+ * This class does not currently verify the validity of a parameter for the + * given algorithm. + */ +public class AlgorithmParameter extends SedBase { + private String kisaoID; + private String value; + + public AlgorithmParameter(String kisaoID, String value) { + this(null, null, kisaoID, value); + } + + public AlgorithmParameter(SId id, String name, String kisaoID, String value) { + super(id, name); + SedGeneralClass.checkNoNullArgs(kisaoID); + SedGeneralClass.stringsNotEmpty(kisaoID); + this.kisaoID = kisaoID; + this.setValue(value); + } + + public AlgorithmParameter clone() throws CloneNotSupportedException { + AlgorithmParameter clone = (AlgorithmParameter) super.clone(); + clone.kisaoID = this.kisaoID; + clone.value = this.value; + return clone; + } + + public void setKisaoID(String kisaoID) { + this.kisaoID = kisaoID; + } + public String getKisaoID() { + return kisaoID; + } + public void setValue(String value) { + this.value = value; + } + public String getValue() { + return value; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((kisaoID == null) ? 0 : kisaoID.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AlgorithmParameter other = (AlgorithmParameter) obj; + if (kisaoID == null) { + if (other.kisaoID != null) + return false; + } else if (!kisaoID.equals(other.kisaoID)) + return false; + return true; + } + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.ALGORITHM_PARAMETER_TAG; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + List params = new ArrayList<>(); + if (this.kisaoID != null) params.add(String.format("kisaoID=%s", this.kisaoID)); + if (this.value != null) params.add(String.format("value=%s", this.value)); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/dataGenerator/DataGenerator.java b/vcell-core/src/main/java/org/jlibsedml/components/dataGenerator/DataGenerator.java new file mode 100644 index 0000000000..a683e1525d --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/dataGenerator/DataGenerator.java @@ -0,0 +1,194 @@ +package org.jlibsedml.components.dataGenerator; + +import java.util.List; + +import org.jlibsedml.*; +import org.jlibsedml.components.*; +import org.jlibsedml.components.listOfConstructs.ListOfParameters; +import org.jlibsedml.components.listOfConstructs.ListOfVariables; +import org.jmathml.ASTNode; +import org.jmathml.FormulaFormatter; + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +/** + * Encapsulates a DataGenerator element in SED-ML. + * + * @author anu/radams + * + */ +public final class DataGenerator extends SedBase implements Calculation { + private final static FormulaFormatter formulaFormatter = new FormulaFormatter(); + + private ASTNode math = null; + private ListOfVariables listOfVariables = new ListOfVariables(); + private ListOfParameters listOfParameters = new ListOfParameters(); + + /** + * + * @param id + * A unique identifier for this element. + * @param name + * An optional name, can be null. + * @param math An ASTNode + * @throws IllegalArgumentException + * if id is null or empty string, or math is null. + */ + public DataGenerator(SId id, String name, ASTNode math) { + super(id, name); + if (SedMLElementFactory.getInstance().isStrictCreation()) { + SedGeneralClass.checkNoNullArgs(math); + } + this.math = math; + } + + /* + * Package scoped constructor for reading from XML + */ + public DataGenerator(SId id, String name) { + super(id, name); + } + + public DataGenerator clone() throws CloneNotSupportedException { + DataGenerator clone = (DataGenerator) super.clone(); + clone.math = this.math; + clone.listOfVariables = this.listOfVariables; + clone.listOfParameters = this.listOfParameters; + return clone; + } + + public ListOfVariables getListOfVariables() { + return this.listOfVariables; + } + + /** + * Returns a read-only list of this element's List of + * variables. + * + * @return A possibly empty but non-null List of {@link Variable} + * objects. + */ + public List getVariables() { + return this.listOfVariables.getContents(); + } + + public boolean containsVariable(Variable variable) { + return this.listOfVariables.containsContent(variable.getId()); + } + + /** + * Adds a {@link Variable} to this object's list of Variables, if not + * already present. + * + * @param variable + * A non-null {@link Variable} element + */ + public void addVariable(Variable variable) { + this.listOfVariables.addContent(variable); + } + + /** + * Removes a {@link Variable} from this object's list of Variables. + * + * @param variable + * A non-null {@link Variable} element + */ + public void removeVariable(Variable variable) { + this.listOfVariables.removeContent(variable); + } + + public ListOfParameters getListOfParameters() { + return this.listOfParameters; + } + + /** + * Getter for a read-only list of parameters. + * + * @return A possibly empty but non-null List of {@link Parameter} + * objects. + */ + public List getParameters() { + return this.listOfParameters.getContents(); + } + + + public boolean containsParameter(Variable variable) { + return this.listOfParameters.containsContent(variable.getId()); + } + + /** + * Adds a {@link Parameter} to this object's list of Parameters, if not + * already present. + * + * @param parameter + * A non-null {@link Parameter} element + */ + public void addParameter(Parameter parameter) { + this.listOfParameters.addContent(parameter); + } + + /** + * Removes a {@link Parameter} from this object's list of Parameters. + * + * @param parameter + * A non-null {@link Parameter} element. + */ + public void removeParameter(Parameter parameter) { + this.listOfParameters.removeContent(parameter); + + } + + /** + * Gets the {@link ASTNode} maths for this object + * @return an {@link ASTNode}. + */ + public ASTNode getMath() { + return this.math; + } + + @Override + public void setMath(ASTNode math) { + this.math = math; + } + + /** + * Convenience function to return the maths expression as a C-style string. + * @return A String representation of the maths of this DataGenerator. + */ + public String getMathAsString(){ + return DataGenerator.formulaFormatter.formulaToString(this.math); + } + + @Override + public String getElementName() { + return SedMLTags.DATA_GENERATOR_TAG; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + return super.parametersToString() + ", " + String.join(", ", this.getMathParamsAndVarsAsStringParams()); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + for (Variable var : this.getVariables()) { + elementFound = var.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + for (Parameter p : this.getParameters()) { + elementFound = p.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return elementFound; + } + +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOf.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOf.java new file mode 100644 index 0000000000..f58b32d8c5 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOf.java @@ -0,0 +1,152 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; + +import java.util.*; + +public abstract class ListOf extends SedBase { + // Since IDs should be unique...ironically we need a Set; + // also, since we sometimes want ID -> Object mappings, we'll use Map + // BUT sometimes there won't be IDs, so...we need both. + protected final Map contentIdMapping; + protected final Set contents; + + + /** + * Constructor to create empty list + */ + public ListOf() { + this(null, null); + } + + /** + * Constructor to create empty list (with id and name as options) + */ + public ListOf(SId id, String name) { + this(id, name, Collections.emptyList()); + } + + /** + * Constructor allowing elements to be passed in. + * + * @param elements the elements to add + */ + public ListOf(List elements) { + this(null, null, elements); + } + + /** + * Constructor allowing elements to be passed in. + * + * @param elements the elements to add + */ + public ListOf(SId id, String name, List elements) { + this(id, name, elements, null); + } + + protected ListOf(SId id, String name, List elements, Comparator comparatorToUseForContents) { + super(id, name); + // LinkedHashSet -> preserves insertion order, treeSet-> allows for sort-as-you-go (and we want different ordering). + this.contents = comparatorToUseForContents == null ? new LinkedHashSet<>() : new TreeSet<>(comparatorToUseForContents); + this.contentIdMapping = new HashMap<>(); + for (T element : elements) { + this.addContent(element); // We want to invoke the add method, for overrides. + SId sid = element.getId(); + if (sid == null) continue; + this.contentIdMapping.put(sid, element); + } + } + + @SuppressWarnings("unchecked") + public ListOf clone() throws CloneNotSupportedException { + ListOf clone = (ListOf) super.clone(); + for (T element : this.contents) { + T cloneElement = (T) element.clone(); + clone.addContent(cloneElement); + } + return clone; + } + + public boolean isEmpty() { + return this.contents.isEmpty(); + } + + public int size() { + return this.contents.size(); + } + + public boolean containsContent(SId content) { + return this.contentIdMapping.containsKey(content); + } + + /** + * Gets the contents as an unmodifiable list + * + * @return an unmodifiable {@link List} of generic type T + */ + public List getContents() { + return this.contents.stream().toList(); + } + + public T getContentById(SId contentId) { + if (contentId == null) throw new IllegalArgumentException("`null` is not a valid id."); + return this.contentIdMapping.get(contentId); + } + + public void addContent(T content) { + if (null == content) return; + SId contentId = content.getId(); + if (null != contentId) { + if (this.contentIdMapping.containsKey(contentId)) return; // Do not override what we have + this.contentIdMapping.put(contentId, content); + } + this.contents.add(content); + } + + public void removeContent(T content) { + if (null == content) return; + this.contents.remove(content); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + // shortcut check to save time + if (this.contentIdMapping.containsKey(idOfElement)) + return this.contentIdMapping.get(idOfElement); + // Now we have to check children + for (SId key : this.contentIdMapping.keySet()) { + elementFound = this.contentIdMapping.get(key).searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return null; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * + * @return the parameters and their values, listed in string form + */ + @Override + public String parametersToString() { + // SEE ORIGINAL PARENT!! + List params = new ArrayList<>(); + for (T content : this.getContents()) { + if (content.getId() != null) params.add(content.getIdAsString()); + else params.add(content.toString()); + } + return super.parametersToString() + ", values=[" + String.join(", ", params) + ']'; + } + + protected static class GeneralListOfComparator implements Comparator { + public int compare(N o1, N o2) { + if (o1 == o2) return 0; + return o1.compareTo(o2); + } + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfAlgorithmParameters.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfAlgorithmParameters.java new file mode 100644 index 0000000000..e213219b16 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfAlgorithmParameters.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.algorithm.AlgorithmParameter; + +public class ListOfAlgorithmParameters extends ListOf { + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.ALGORITHM_PARAMETERS; + } + + public ListOfAlgorithmParameters clone() throws CloneNotSupportedException { + return (ListOfAlgorithmParameters) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfChanges.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfChanges.java new file mode 100644 index 0000000000..a6f9dd5c2f --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfChanges.java @@ -0,0 +1,90 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.model.Change; +import org.jlibsedml.components.output.Output; + +import java.util.*; + +public class ListOfChanges extends ListOf { + + /** + * Constructor to create empty list + */ + public ListOfChanges() { + this(null, null); + } + + /** + * Constructor to create empty list (with id and name as options) + */ + public ListOfChanges(SId id, String name) { + this(id, name, Collections.emptyList()); + } + + /** + * Constructor allowing elements to be passed in. + * @param elements the elements to add + */ + public ListOfChanges(List elements) { + this(null, null, elements); + } + + /** + * Constructor allowing elements to be passed in. + * @param elements the elements to add + */ + public ListOfChanges(SId id, String name, List elements) { + super(id, name, elements, new ListOfChanges.ChangeComparator()); + } + + public ListOfChanges clone() throws CloneNotSupportedException { + return (ListOfChanges) super.clone(); + } + + /** + * Gets the contents as an unmodifiable list + * @return an unmodifiable {@link List} of type {@link Output} + */ + @Override // Declaring this because technically, List != List ... even though it is in this class. + public List getContents() { + return this.contents.stream().toList(); + } + + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.CHANGES; + } + + /** + * Sorts a list of Changes into the correct order specified in the schema. + * @author radams + * + */ + static class ChangeComparator extends GeneralListOfComparator { + static Map changeKindOrder; + static { + ChangeComparator.changeKindOrder = new HashMap<>(); + ChangeComparator.changeKindOrder.put(SedMLTags.CHANGE_ATTRIBUTE_KIND, 1); + ChangeComparator.changeKindOrder.put(SedMLTags.CHANGE_XML_KIND, 2); + ChangeComparator.changeKindOrder.put(SedMLTags.ADD_XML_KIND, 3); + ChangeComparator.changeKindOrder.put(SedMLTags.REMOVE_XML_KIND, 4); + ChangeComparator.changeKindOrder.put(SedMLTags.COMPUTE_CHANGE_KIND, 5); + ChangeComparator.changeKindOrder.put(SedMLTags.SET_VALUE_KIND, 6); + } + @Override + public int compare(Change o1, Change o2) { + if (o1 == o2) return 0; + int firstCompare = ChangeComparator.changeKindOrder.get(o1.getChangeKind()).compareTo(ChangeComparator.changeKindOrder.get(o2.getChangeKind())); + if (firstCompare != 0) return firstCompare; + return super.compare(o1, o2); + } + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfCurves.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfCurves.java new file mode 100644 index 0000000000..2847adf5fa --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfCurves.java @@ -0,0 +1,28 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.output.AbstractCurve; + +public class ListOfCurves extends ListOf { + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.OUTPUT_CURVES_LIST; + } + + public ListOfCurves clone() throws CloneNotSupportedException { + return (ListOfCurves) super.clone(); + } + +// @Override +// public void addContent(AbstractCurve content) { +// if (null == content) return; +// +// super.addContent(content); +// } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfDataGenerators.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfDataGenerators.java new file mode 100644 index 0000000000..cb6fce8fb3 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfDataGenerators.java @@ -0,0 +1,85 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.Variable; +import org.jlibsedml.components.dataGenerator.DataGenerator; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class ListOfDataGenerators extends ListOf { + private Map> variableIdToDataGenerators = new HashMap<>(); + + public ListOfDataGenerators clone() throws CloneNotSupportedException { + ListOfDataGenerators clone = (ListOfDataGenerators) super.clone(); + Map> clonedVariableIdToDataGenerators = new HashMap<>(); + for (SId id : this.variableIdToDataGenerators.keySet()) { + Set cloneSet = new HashSet<>(); + for (SId dataGenId : this.variableIdToDataGenerators.get(id)) { + cloneSet.add(new SId(dataGenId.string())); + } + clonedVariableIdToDataGenerators.put(new SId(id.string()), cloneSet); + } + clone.variableIdToDataGenerators = clonedVariableIdToDataGenerators; + return clone; + } + + /** + * Ease-of-use function to help when auto-creating "identity" DataGenerators + * @param var a variable to potentially be referenced by a new data generator + * @return true, if an identity data-generator already exists, else false + */ + public boolean appropriateIdentityDataGeneratorAlreadyExistsFor(Variable var){ + if (!this.variableIdToDataGenerators.containsKey(var.getId())) return false; + for (SId dataGenId : this.variableIdToDataGenerators.get(var.getId())) { + DataGenerator targetDataGenerator = this.contentIdMapping.get(dataGenId); + if (!targetDataGenerator.containsVariable(var)) continue; + // Identity DataGenerator => data generator that does no modification to a variable, just "renames" it as a DataGenerator + if (targetDataGenerator.getVariables().size() > 1) continue; + if (!targetDataGenerator.getParameters().isEmpty()) continue; + return true; + } + return false; + } + + @Override + public void addContent(DataGenerator dataGenerator) { + if (null == dataGenerator) return; + SId contentId = dataGenerator.getId(); + if (null != contentId) { + if (this.contentIdMapping.containsKey(contentId)) return; // Do not override what we have + this.contentIdMapping.put(contentId, dataGenerator); + } + for (Variable var: dataGenerator.getVariables()){ + SId varId = var.getId(); + if (varId == null) continue; + if (!this.variableIdToDataGenerators.containsKey(varId)) + this.variableIdToDataGenerators.put(varId, new HashSet<>()); + this.variableIdToDataGenerators.get(varId).add(contentId); + } + this.contents.add(dataGenerator); + } + + @Override + public void removeContent(DataGenerator content) { + if (null == content) return; + if (content.getId() != null) for (Variable var: content.getVariables()){ + if (!this.variableIdToDataGenerators.containsKey(var.getId())) continue; + this.variableIdToDataGenerators.get(var.getId()).remove(content.getId()); + } + this.contents.remove(content); + } + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.DATA_GENERATORS; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfDataSets.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfDataSets.java new file mode 100644 index 0000000000..f83a5550bd --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfDataSets.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.output.DataSet; + +public class ListOfDataSets extends ListOf{ + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.OUTPUT_DATASETS_LIST; + } + + public ListOfDataSets clone() throws CloneNotSupportedException { + return (ListOfDataSets) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfModels.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfModels.java new file mode 100644 index 0000000000..57eab00a8f --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfModels.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.model.Model; + +public class ListOfModels extends ListOf{ + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.MODELS; + } + + public ListOfModels clone() throws CloneNotSupportedException { + return (ListOfModels) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfOutputs.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfOutputs.java new file mode 100644 index 0000000000..4d3202bd3c --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfOutputs.java @@ -0,0 +1,97 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.output.Output; +import org.jlibsedml.components.output.Plot2D; +import org.jlibsedml.components.output.Plot3D; +import org.jlibsedml.components.output.Report; + +import java.util.*; + + +/** + * This class is a special case for overrides, because we want to group outputs by type. + * A Tree-set with a custom comparator is used to keep efficient ordering of outputs. + */ +public class ListOfOutputs extends ListOf { + + /** + * Constructor to create empty list + */ + public ListOfOutputs() { + this(null, null); + } + + /** + * Constructor to create empty list (with id and name as options) + */ + public ListOfOutputs(SId id, String name) { + this(id, name, Collections.emptyList()); + } + + /** + * Constructor allowing elements to be passed in. + * @param elements the elements to add + */ + public ListOfOutputs(List elements) { + this(null, null, elements); + } + + /** + * Constructor allowing elements to be passed in. + * @param elements the elements to add + */ + public ListOfOutputs(SId id, String name, List elements) { + super(id, name, elements, new OutputComparator()); + } + + public ListOfOutputs clone() throws CloneNotSupportedException { + return (ListOfOutputs) super.clone(); + } + + /** + * Gets the contents as an unmodifiable list + * @return an unmodifiable {@link List} of type {@link Output} + */ + @Override // Declaring this because technically, List != List ... even though it is in this class. + public List getContents() { + return this.contents.stream().toList(); + } + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.OUTPUTS; + } + + /** + * Sorts a list of Outputs into the correct order specified in the schema. + * + * @author radams + * + */ + private static class OutputComparator extends GeneralListOfComparator { + private static final Map, Integer> subclassPriorityMapping; + static { + subclassPriorityMapping = new HashMap<>(); + subclassPriorityMapping.put(Plot2D.class, 0); + subclassPriorityMapping.put(Plot3D.class, 1); + subclassPriorityMapping.put(Report.class, 2); + } + + public int compare(Output o1, Output o2) { + if (o1 == o2) return 0; + Class c1 = o1.getClass(), c2 = o2.getClass(); + Integer priority1 = subclassPriorityMapping.get(c1), priority2 = subclassPriorityMapping.get(c2); + int firstCompare = Integer.compare(priority1, priority2); + if (firstCompare != 0) return firstCompare; + return super.compare(o1, o2); + } + + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfParameters.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfParameters.java new file mode 100644 index 0000000000..47d7fe4f94 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfParameters.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.Parameter; + +public class ListOfParameters extends ListOf { + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.PARAMETERS; + } + + public ListOfParameters clone () throws CloneNotSupportedException { + return (ListOfParameters) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfRanges.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfRanges.java new file mode 100644 index 0000000000..a1cca74009 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfRanges.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.task.Range; + +public class ListOfRanges extends ListOf { + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.REPEATED_TASK_RANGES_LIST; + } + + public ListOfRanges clone() throws CloneNotSupportedException { + return (ListOfRanges) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfRepeatedTaskChanges.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfRepeatedTaskChanges.java new file mode 100644 index 0000000000..c96f7ec8a6 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfRepeatedTaskChanges.java @@ -0,0 +1,21 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.task.SetValue; + +public class ListOfRepeatedTaskChanges extends ListOf { + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.CHANGES; + } + + public ListOfRepeatedTaskChanges clone() throws CloneNotSupportedException { + return (ListOfRepeatedTaskChanges) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSimulations.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSimulations.java new file mode 100644 index 0000000000..eff5194628 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSimulations.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.simulation.Simulation; + +public class ListOfSimulations extends ListOf { + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.SIMS; + } + + public ListOfSimulations clone() throws CloneNotSupportedException { + return (ListOfSimulations) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSubTasks.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSubTasks.java new file mode 100644 index 0000000000..757c90afee --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSubTasks.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.task.SubTask; + +public class ListOfSubTasks extends ListOf { + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.REPEATED_TASK_SUBTASKS_LIST; + } + + public ListOfSubTasks clone() throws CloneNotSupportedException { + return (ListOfSubTasks) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSurfaces.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSurfaces.java new file mode 100644 index 0000000000..efc0fd2afe --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfSurfaces.java @@ -0,0 +1,21 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.output.Surface; + +public class ListOfSurfaces extends ListOf { + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.OUTPUT_SURFACES_LIST; + } + + public ListOfSurfaces clone() throws CloneNotSupportedException { + return (ListOfSurfaces) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfTasks.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfTasks.java new file mode 100644 index 0000000000..bf853b0864 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfTasks.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.task.AbstractTask; + +public class ListOfTasks extends ListOf { + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.TASKS; + } + + public ListOfTasks clone() throws CloneNotSupportedException { + return (ListOfTasks) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfVariables.java b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfVariables.java new file mode 100644 index 0000000000..f6b3914ccf --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/listOfConstructs/ListOfVariables.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.listOfConstructs; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.Variable; + +public class ListOfVariables extends ListOf { + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.VARIABLES; + } + + public ListOfVariables clone() throws CloneNotSupportedException { + return (ListOfVariables) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/model/AddXML.java b/vcell-core/src/main/java/org/jlibsedml/components/model/AddXML.java new file mode 100644 index 0000000000..7dc83ec3e3 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/model/AddXML.java @@ -0,0 +1,103 @@ +package org.jlibsedml.components.model; + +import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates an AddXML element in SED-ML. This requires the value of the + * 'target' attribute to refer to a parent or container element into which the XML will be added. + * * @author anu/radams + * + */ +public final class AddXML extends Change { + private NewXML newXML; + + + /** + * + * @param target A non-null XPathTarget of the XPath target + * @param newXML A non-null NewXML of new XML + * @throws IllegalArgumentException if either argument is null. + */ + public AddXML(XPathTarget target, NewXML newXML) { + this(null, null, target, newXML); + } + + /** + * + * @param target A non-null XPathTarget of the XPath target + * @param newXML A non-null NewXML of new XML + * @throws IllegalArgumentException if either argument is null. + */ + public AddXML(SId id, String name, XPathTarget target, NewXML newXML) { + super(id, name, target); + if (SedMLElementFactory.getInstance().isStrictCreation()) SedGeneralClass.checkNoNullArgs(newXML); + this.newXML = newXML; + } + + public AddXML clone() throws CloneNotSupportedException { + AddXML copy = (AddXML) super.clone(); + copy.newXML = this.newXML; + return copy; + } + + /** + * Getter for the new XML to be added to the target. + * + * @return the {@link NewXML} to be added. + */ + public NewXML getNewXML() { + return this.newXML; + } + + + /** + * Sets the NewXML for this element. + * + * @param newXML A non-null {@link NewXML} object. + * @since 1.2.0 + */ + public void setNewXML(NewXML newXML) { + this.newXML = newXML; + } + + /** + * Getter for the change kind. + * + * @return SEDMLTags.ADD_XML_KIND + */ + @Override + public String getChangeKind() { + return SedMLTags.ADD_XML_KIND; + } + + @Override + public String getElementName() { + return SedMLTags.ADD_XML; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } + + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + if (this.newXML == null) return super.parametersToString(); + else return super.parametersToString() + ", AddXML=[" + this.newXML.toString() + ']'; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/model/Change.java b/vcell-core/src/main/java/org/jlibsedml/components/model/Change.java new file mode 100644 index 0000000000..911981c5ce --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/model/Change.java @@ -0,0 +1,148 @@ +package org.jlibsedml.components.model; + +import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.*; + +/** + * Abstract class for ChangeXXX classes that manipulate a {@link Model}. + * This class is not intended to be sub-classed by clients. + * + * @author anu/radams + * + */ +public abstract class Change extends SedBase { + private XPathTarget target; + + /** + * + * @param target A non-null, non-empty String + * that identifies an XPath expression to change. + */ + public Change(SId id, String name, XPathTarget target) { + super(id, name); + if (SedMLElementFactory.getInstance().isStrictCreation()) + SedGeneralClass.checkNoNullArgs(target); + + this.target = target; + } + + public Change clone() throws CloneNotSupportedException { + Change clone = (Change) super.clone(); + clone.target = this.target; + return clone; + } + + /** + * Setter for the target XPath expression that identifies where the change should be + * applied. + * + * @param target A non-null {@link XPathTarget} + * @since 1.2.0 + */ + public void setTarget(XPathTarget target) { + if (SedMLElementFactory.getInstance().isStrictCreation()) + SedGeneralClass.checkNoNullArgs(target); + this.target = target; + } + + /** + * Gets the XPath expression that identifies the target XML to which the change will be applied. + * + * @return An XPath String. + */ + public final XPathTarget getTargetXPath() { + return this.target; + } + + /** + * Type definition of the type of change to be applied.
+ * Returns one of: + *
    + *
  • SEDMLTags.CHANGE_ATTRIBUTE_KIND + *
  • SEDMLTags.CHANGE_XML_KIND + *
  • SEDMLTags.ADD_XML_KIND + *
  • SEDMLTags.REMOVE_XML_KIND + *
  • SEDMLTags.COMPUTE_CHANGE_KIND + *
  • SET_VALUE_KIND + *
+ * + * @return a String for the type of change element. + */ + public abstract String getChangeKind(); + + /** + * Boolean test for whether this object is of type ChangeAttribute. + * + * @return a boolean + */ + public boolean isChangeAttribute() { + return SedMLTags.CHANGE_ATTRIBUTE_KIND.equals(this.getChangeKind()); + } + + /** + * Boolean test for whether this object is of type ChangeXML. + * + * @return a boolean + */ + public boolean isChangeXML() { + return SedMLTags.CHANGE_XML_KIND.equals(this.getChangeKind()); + } + + /** + * Boolean test for whether this object is of type AddXML. + * + * @return a boolean + */ + public boolean isAddXML() { + return SedMLTags.ADD_XML_KIND.equals(this.getChangeKind()); + } + + /** + * Boolean test for whether this object is of type RemoveXML. + * + * @return a boolean + */ + public boolean isRemoveXML() { + return SedMLTags.REMOVE_XML_KIND.equals(this.getChangeKind()); + } + + /** + * Boolean test for whether this object is of type ComputeChange. + * + * @return a boolean + */ + public boolean isComputeChange() { + return SedMLTags.COMPUTE_CHANGE_KIND.equals(this.getChangeKind()); + } + + public boolean isSetValue() { + return SedMLTags.SET_VALUE_KIND.equals(this.getChangeKind()); + } + + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString() { + if (this.target == null) return super.parametersToString(); + else return super.parametersToString() + ", target={" + this.target.toString() + '}'; + } + + +} \ No newline at end of file diff --git a/vcell-core/src/main/java/org/jlibsedml/components/model/ChangeAttribute.java b/vcell-core/src/main/java/org/jlibsedml/components/model/ChangeAttribute.java new file mode 100644 index 0000000000..9bf15d287e --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/model/ChangeAttribute.java @@ -0,0 +1,108 @@ +package org.jlibsedml.components.model; + +import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +/** + * Class for manipulating the value of an attribute via XPath. + * + * @author anu/radams + * + */ +public final class ChangeAttribute extends Change { + private String newValue; + + /** + * @param target An {@link XPathTarget} to an attribute whose value is to be changed. + * @param newValue The new value of target attribute. + * @throws IllegalArgumentException if either argument is null or empty. + */ + public ChangeAttribute(XPathTarget target, String newValue) { + this(null, null, target, newValue); + } + + /** + * + * @param target An {@link XPathTarget} to an attribute whose value is to be changed. + * @param newValue The new value of target attribute. + * @throws IllegalArgumentException if either argument is null or empty. + */ + public ChangeAttribute(SId id, String name, XPathTarget target, String newValue) { + super(id, name, target); + if (SedMLElementFactory.getInstance().isStrictCreation()) { + SedGeneralClass.checkNoNullArgs(newValue); + SedGeneralClass.stringsNotEmpty(newValue); + } + this.newValue = newValue; + } + + public ChangeAttribute clone() throws CloneNotSupportedException { + ChangeAttribute clone = (ChangeAttribute) super.clone(); + clone.newValue = this.newValue; + return clone; + } + + /** + * Getter for the change kind. + * + * @return SEDMLTags.CHANGE_ATTRIBUTE_KIND; + */ + @Override + public String getChangeKind() { + return SedMLTags.CHANGE_ATTRIBUTE_KIND; + } + + /** + * Getter for the new attribute value to apply to the target expression. + * + * @return A non-null, non-empty String. + */ + public String getNewValue() { + return this.newValue; + } + + + + /** + * Setter for the new value of this object. + * + * @param newValue A non-null, non-empty String. + * @throws IllegalArgumentException if newValue is null or empty. + * @since 1.2.0 + */ + public void setNewValue(String newValue) { + if (SedMLElementFactory.getInstance().isStrictCreation()) { + SedGeneralClass.checkNoNullArgs(newValue); + SedGeneralClass.stringsNotEmpty(newValue); + } + this.newValue = newValue; + } + + @Override + public String getElementName() { + // TODO Auto-generated method stub + return SedMLTags.CHANGE_ATTRIBUTE; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + if (this.newValue == null) return super.parametersToString(); + else return super.parametersToString() + ", newValue=[" + this.newValue + ']'; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/model/ChangeXML.java b/vcell-core/src/main/java/org/jlibsedml/components/model/ChangeXML.java new file mode 100644 index 0000000000..ccd54aa157 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/model/ChangeXML.java @@ -0,0 +1,97 @@ +package org.jlibsedml.components.model; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.SEDMLVisitor; +import org.jlibsedml.XPathTarget; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +/** + * Encapsulates a changeXML element in SED-ML. Currently this is achieved by replacing + * the 'target' content element with the 'newXML' content. + * * @author anu/radams + * + */ +public final class ChangeXML extends Change { + private NewXML newXML; + + /** + * Setter for the {@link NewXML} for this object. + * + * @param newXML A non-null {@link NewXML} object. + * @throws IllegalArgumentException if newXML is null. + * @since 1.2.0 + */ + public void setNewXML(NewXML newXML) { + this.newXML = newXML; + } + + /** + * + * @param target A XPathTargetobject + * @param newXML A String of new XML + */ + public ChangeXML(XPathTarget target, NewXML newXML) { + this(null, null, target, newXML); + } + + /** + * + * @param target A XPathTargetobject + * @param newXML A String of new XML + */ + public ChangeXML(SId id, String name, XPathTarget target, NewXML newXML) { + super(id, name, target); + this.newXML = newXML; + } + + public ChangeXML clone() throws CloneNotSupportedException { + ChangeXML clone = (ChangeXML) super.clone(); + clone.newXML = this.newXML; + return clone; + } + + /** + * Getter for the change kind. + * + * @return SEDMLTags.CHANGE_XML_KIND; + */ + @Override + public String getChangeKind() { + return SedMLTags.CHANGE_XML_KIND; + } + + /** + * Getter for the new XML that replaces the old XML. + * + * @return a NewXML object + */ + public NewXML getNewXML() { + return this.newXML; + } + + @Override + public String getElementName() { + return SedMLTags.CHANGE_XML; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + if (this.newXML == null) return super.parametersToString(); + else return super.parametersToString() + ", newXML=[" + this.newXML.toString() + ']'; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/model/ComputeChange.java b/vcell-core/src/main/java/org/jlibsedml/components/model/ComputeChange.java new file mode 100644 index 0000000000..bc90823837 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/model/ComputeChange.java @@ -0,0 +1,203 @@ +package org.jlibsedml.components.model; + +import java.util.List; + + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.XPathTarget; +import org.jlibsedml.components.*; +import org.jlibsedml.components.listOfConstructs.ListOfParameters; +import org.jlibsedml.components.listOfConstructs.ListOfVariables; +import org.jmathml.ASTNode; +import org.jmathml.ASTRootNode; +import org.jmathml.FormulaFormatter; + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +/** + * Encapsulates the ComputeChange element of SEDML. + *

+ * * @author anu/radams + * + */ +public class ComputeChange extends Change implements Calculation { + private final static FormulaFormatter formulaFormatter = new FormulaFormatter(); + + private ASTNode math = null; + private ListOfVariables listOfVariables; + private ListOfParameters listOfParameters; + + /** + * + * @param target A non-null XPathTarget to which the change should be + * applied. + */ + public ComputeChange(XPathTarget target) { + this(null, null, target); + } + + /** + * + * @param target A non-null XPathTarget to which the change should be + * applied. + * @param math An {@link ASTRootNode} used to compute the new value of the target element. + */ + public ComputeChange(XPathTarget target, ASTNode math) { + this(null, null, target, math); + } + + /** + * + * @param id id of the element + * @param name name of the element + * @param target A non-null XPathTarget to which the change should be applied. + */ + public ComputeChange(SId id, String name, XPathTarget target) { + this(id, name, target, null); + } + + /** + * + * @param target A non-null XPathTarget to which the change should be + * applied. + * @param math An {@link ASTRootNode} used to compute the new value of the target element. + */ + public ComputeChange(SId id, String name, XPathTarget target, ASTNode math) { + super(id, name, target); + this.setMath(math); + this.listOfVariables = new ListOfVariables(); + this.listOfParameters = new ListOfParameters(); + } + + public ComputeChange clone() throws CloneNotSupportedException { + ComputeChange clone = (ComputeChange) super.clone(); + clone.math = this.math; + clone.listOfVariables = this.listOfVariables; + clone.listOfParameters = this.listOfParameters; + return clone; + } + + public ASTNode getMath() { + return this.math; + } + public void setMath(ASTNode math) { + this.math = math; + } + + + + /** + * Getter for the change kind. + * + * @return SEDMLTags.COMPUTE_CHANGE_KIND; + */ + @Override + public String getChangeKind() { + return SedMLTags.COMPUTE_CHANGE_KIND; + } + + + public void setListOfVariables(List listOfVariables) { + for (Variable variable : listOfVariables) this.listOfVariables.addContent(variable); + } + + public void setListOfParameters(List listOfParameters) { + for (Parameter parameter : listOfParameters) this.listOfParameters.addContent(parameter); + } + + @Override + public ListOfParameters getListOfParameters() { + return this.listOfParameters; + } + + /** + * Returns a possible empty but non-null list of {@link Parameter} objects + * + * @return a list of {@link Parameter} + */ + public List getParameters() { + return this.listOfParameters.getContents(); + } + + /** + * Adds a parameter to this element + * + * @param param parameter to add + */ + public void addParameter(Parameter param) { + this.listOfParameters.addContent(param); + } + + @Override + public void removeParameter(Parameter parameter) { + + } + + @Override + public ListOfVariables getListOfVariables() { + return this.listOfVariables; + } + + /** + * Returns a possible empty but non-null list of {@link Variable} objects + * + * @return a list of {@link Variable} + */ + public List getVariables() { + return this.listOfVariables.getContents(); + } + + /** + * Adds a variable to this element + * + * @param var a {@link Variable} + */ + public void addVariable(Variable var) { + this.listOfVariables.addContent(var); + } + + @Override + public void removeVariable(Variable variable) { + + } + + @Override + public String getElementName() { + return SedMLTags.COMPUTE_CHANGE; + } + + /** + * Convenience function to return the maths expression as a C-style string. + * @return A String representation of the maths of this DataGenerator. + */ + public String getMathAsString(){ + return ComputeChange.formulaFormatter.formulaToString(this.math); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + for (Variable var : this.getVariables()) { + elementFound = var.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + for (Parameter p : this.getParameters()) { + elementFound = p.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return elementFound; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + return super.parametersToString() + ", " + String.join(", ", this.getMathParamsAndVarsAsStringParams()); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/Model.java b/vcell-core/src/main/java/org/jlibsedml/components/model/Model.java similarity index 55% rename from vcell-core/src/main/java/org/jlibsedml/Model.java rename to vcell-core/src/main/java/org/jlibsedml/components/model/Model.java index b5d8392e2f..0895ef07db 100644 --- a/vcell-core/src/main/java/org/jlibsedml/Model.java +++ b/vcell-core/src/main/java/org/jlibsedml/components/model/Model.java @@ -1,13 +1,17 @@ -package org.jlibsedml; +package org.jlibsedml.components.model; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import org.jlibsedml.*; +import org.jlibsedml.components.*; +import org.jlibsedml.components.listOfConstructs.ListOfChanges; import org.jlibsedml.modelsupport.SUPPORTED_LANGUAGE; +import javax.annotation.OverridingMethodsMustInvokeSuper; + import static org.jlibsedml.execution.ArchiveModelResolver.SPACE_URI_ESCAPE_SEQUENCE; /** @@ -18,26 +22,14 @@ * @author anu/radams * */ -public final class Model extends AbstractIdentifiableElement { - - private String language = null; - private final String source_path_or_URI_string; - - /** - * Sets the model language that this element refers to. This should ideally be a URN, - * which may already be defined in {@link SUPPORTED_LANGUAGE}. If no language describes this - * model, the argument may be null. - * @param language A modelling language name. - * @since 1.2.0 - */ - public void setLanguage(String language) { - this.language = language; - } - - private List listOfChanges = new ArrayList(); +public final class Model extends SedBase { + private String language; + private String source_path_or_URI_string; + private ListOfChanges listOfChanges = new ListOfChanges(); /** - * + * Standard Constructor for Models + * * @param id * - a unique identifier for the model within this SED-ML * description. @@ -53,13 +45,13 @@ public void setLanguage(String language) { * if any argument except name is null or an empty * string. */ - public Model(String id, String name, String language, String source_path_or_URI_string) { + public Model(SId id, String name, String language, String source_path_or_URI_string) { super(id, name); - if (SEDMLElementFactory.getInstance().isStrictCreation()) { - Assert.checkNoNullArgs(source_path_or_URI_string); - Assert.stringsNotEmpty(source_path_or_URI_string); + if (this.getId() == null) throw new IllegalArgumentException("id is required for SedML class `" + this.getClass().getSimpleName() + "`"); + if (SedMLElementFactory.getInstance().isStrictCreation()) { + SedGeneralClass.checkNoNullArgs(language, source_path_or_URI_string); + SedGeneralClass.stringsNotEmpty(language, source_path_or_URI_string); } - this.language = language; this.source_path_or_URI_string = source_path_or_URI_string; } @@ -77,19 +69,51 @@ public Model(String id, String name, String language, String source_path_or_URI_ * @throws IllegalArgumentException * if any argument is null */ - public Model(Model toCopy, String id) { - this(id, toCopy.getName(), toCopy.getLanguage(), toCopy.getSourcePathOrURIString()); + public Model(Model toCopy, SId id) { + this(id, toCopy.getName(), toCopy.getLanguage(), toCopy.getSourceAsString()); } + public Model clone() throws CloneNotSupportedException { + Model clone = (Model) super.clone(); + clone.language = this.language; + clone.listOfChanges = this.listOfChanges.clone(); + clone.source_path_or_URI_string = this.source_path_or_URI_string; + return clone; + } + + /** + * Returns the model's language. + * + * @return A String + */ + public String getLanguage() { + return this.language; + } + + /** + * Sets the model language that this element refers to. This should ideally be a URN, + * which may already be defined in {@link SUPPORTED_LANGUAGE}. If no language describes this + * model, the argument may be null. + * @param language A modelling language name. + * @since 1.2.0 + */ + public void setLanguage(String language) { + this.language = language; + } + + + public ListOfChanges getListOfChanges() { + return this.listOfChanges; + } + /** * Gets a possibly empty but non-null unmodifiable List of * {@link Change} objects. * * @return List */ - public List getListOfChanges() { - Collections.sort(listOfChanges, new Change.ChangeComparator()); - return Collections.unmodifiableList(listOfChanges); + public List getChanges() { + return this.listOfChanges.getContents(); } /** @@ -99,49 +123,36 @@ public List getListOfChanges() { * false otherwise. */ public boolean hasChanges() { - return listOfChanges.size() > 0; + return !this.listOfChanges.isEmpty(); } /** - * Adds a Change to this object's list of Changes, if not already - * present. - * @param change - * A non-null {@link Change} element - * @return true if change added, false otherwise. - */ - public boolean addChange(Change change) { - if (!listOfChanges.contains(change)) - return listOfChanges.add(change); - return false; - } + * Adds a Change to this object's list of Changes, if not already + * present. + * @param change + * A non-null {@link Change} element to add + */ + public void addChange(Change change) { + this.listOfChanges.addContent(change); + } /** * Removes a Change from this object's list of Change objects. * @param change - * A non-null {@link Change} element - * @return true if change removed, false - * otherwise. - */ - public boolean removeChange(Change change) { - return listOfChanges.add(change); - } - - /** - * Returns the model's language. - * - * @return A String + * A non-null {@link Change} element that to remove. + * If the change is not found in the list of changes, the function returns early. */ - public String getLanguage() { - return language; + public void removeChange(Change change) { + this.listOfChanges.removeContent(change); } /** - * Returns the model's source, as a URI from where it can retrieved. This - * can be be a file location or a stable database identifier, for example. + * Returns the model's source, as a URI from where it can be retrieved. This + * can be a file location or a stable database identifier, for example. * * @return A String */ - public String getSourcePathOrURIString() { + public String getSourceAsString() { return this.source_path_or_URI_string; } @@ -222,13 +233,11 @@ public String getSourcePathOrURIString() { * /products/abc.html * type=all&colour=brown * - * - * * * * @return A {@link URI} object * @throws URISyntaxException - * if the value of the the 'source' attribute of a model element + * if the value of the 'source' attribute of a model element * cannot be converted to a URI. */ public URI getSourceURI() throws URISyntaxException { @@ -244,20 +253,20 @@ public URI getSourceURI() throws URISyntaxException { * @link {@link Model#getSourceURI()}; */ public boolean isSourceURIRelative() { - URI srcURI = null; + URI srcURI; try { - srcURI = getSourceURI(); + srcURI = this.getSourceURI(); } catch (URISyntaxException e) { return false; } - if (srcURI.getAuthority() == null && srcURI.getFragment() == null - && srcURI.getHost() == null && srcURI.getQuery() == null - && srcURI.getScheme() == null && srcURI.getPath() != null) { - return true; - } - return false; + return srcURI.getAuthority() == null + && srcURI.getFragment() == null + && srcURI.getHost() == null + && srcURI.getQuery() == null + && srcURI.getScheme() == null + && srcURI.getPath() != null; - } + } /** * Boolean test for whether the source attribute for this model is a valid @@ -268,37 +277,41 @@ public boolean isSourceURIRelative() { */ public boolean isSourceValidURI() { try { - getSourceURI(); + this.getSourceURI(); } catch (URISyntaxException e) { return false; } return true; } - /** - * @see Object#toString() - */ - public String toString() { - return new StringBuffer().append("Model [").append("id=").append( - getId()).append(", ").append("name=").append(getName()) - .append(", ").append("language=").append(language).append( - ", ").append("src=").append(this.source_path_or_URI_string).append("]").toString(); - } + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + List params = new ArrayList<>(); + if (this.language != null) params.add(String.format("language=%s", this.language)); + if (this.source_path_or_URI_string != null) params.add(String.format("src=%s", this.source_path_or_URI_string)); + return super.parametersToString() + ", " + String.join(", ", params); + } @Override public String getElementName() { - return SEDMLTags.MODEL_TAG; + return SedMLTags.MODEL_TAG; } - public boolean accept(SEDMLVisitor visitor){ - if (!visitor.visit(this)){ - return false; + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + for (Change var : this.getChanges()) { + elementFound = var.searchFor(idOfElement); + if (elementFound != null) return elementFound; } - for (Change c: getListOfChanges()){ - if(!c.accept(visitor)){ - return false; - } - } - return true; + return elementFound; } } diff --git a/vcell-core/src/main/java/org/jlibsedml/components/model/NewXML.java b/vcell-core/src/main/java/org/jlibsedml/components/model/NewXML.java new file mode 100644 index 0000000000..4a5c039058 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/model/NewXML.java @@ -0,0 +1,46 @@ +package org.jlibsedml.components.model; + +import java.util.Collections; +import java.util.List; + +import org.jdom2.Element; + +/** + * Encapsulates a NewXML element in the SED-ML specification. This object can contain one or more XML elements. + * For example, + * *

+ * <addXML target="/sbml:sbml/sbml:model/sbml:listOfParameters">
+ *  <newXML>
+ *    <parameter metaid="metaid_0000010"id="V_mT"value="0.7"/>
+ *    <parameter metaid="metaid_0000011"id="V_mT2"value="0.71"/>
+ *    </newXML>
+ *  </addXML>
+ * 
+ * will produce a NewXML object with a List of two {@link Element}s, all of which will be added as children of the + * target element. + * + * @author radams + * + */ +public record NewXML(List xml) { + + /** + * Getter for the model XML fragments to be inserted into the model XML. + * + * @return An unmodifiable + */ + @Override + public List xml() { + return Collections.unmodifiableList(this.xml); + } + + /** + * Gets the number of top-level XML elements contained in this object. + * + * @return an integer, >= 0 + */ + public int numElements() { + return this.xml.size(); + } + +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/model/RemoveXML.java b/vcell-core/src/main/java/org/jlibsedml/components/model/RemoveXML.java new file mode 100644 index 0000000000..f7a1a0c2af --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/model/RemoveXML.java @@ -0,0 +1,65 @@ +package org.jlibsedml.components.model; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.SEDMLVisitor; +import org.jlibsedml.XPathTarget; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +/** + * Class encapsulating a RemoveXML element to be applied to a model. + * + * @author radams + * + */ +public class RemoveXML extends Change { + + /** + * + * @param target A non-null {@link XPathTarget} object + */ + public RemoveXML(XPathTarget target) { + this(null, null, target); + } + + /** + * + * @param target A non-null {@link XPathTarget} object + */ + public RemoveXML(SId id, String name, XPathTarget target) { + super(id, name, target); + } + + public RemoveXML clone() throws CloneNotSupportedException { + return (RemoveXML) super.clone(); + } + + @Override + public String getElementName() { + return SedMLTags.REMOVE_XML; + } + + @Override + public final String getChangeKind() { + return SedMLTags.REMOVE_XML_KIND; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + return super.parametersToString(); + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/AbstractCurve.java b/vcell-core/src/main/java/org/jlibsedml/components/output/AbstractCurve.java new file mode 100644 index 0000000000..a44b222f07 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/AbstractCurve.java @@ -0,0 +1,113 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.SedMLElementFactory; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; + +public abstract class AbstractCurve extends SedBase { + + public enum YAxisAlignment { + NOT_APPLICABLE(null), + LEFT("left"), + RIGHT("right"); + + private final String tag; + YAxisAlignment(String tag){ + this.tag = tag; + } + + public String getTag() { return this.tag; } + + public static YAxisAlignment fromTag(String tag) { + if (tag == null) return NOT_APPLICABLE; + return switch (tag) { + case "left" -> LEFT; + case "right" -> RIGHT; + default -> throw new IllegalArgumentException("Unknown tag " + tag); + }; + } + } + + + + protected SId xDataReference; + @Deprecated protected Boolean logScaleXAxis; + protected Integer order; + protected SId style; + protected YAxisAlignment yAxis; + + + public AbstractCurve(SId id, String name, SId xDataReference) { + this(id, name, xDataReference, null, null, null, YAxisAlignment.NOT_APPLICABLE); + } + + public AbstractCurve(SId id, String name, SId xDataReference, Boolean logScaleXAxis, Integer order, SId style, YAxisAlignment yAxis) { + super(id, name); + if (order != null && order < 0) throw new IllegalArgumentException("order must be >= 0"); + this.xDataReference = xDataReference; + this.logScaleXAxis = logScaleXAxis; + this.order = order; + this.style = style; + this.yAxis = yAxis; + } + + public AbstractCurve clone() throws CloneNotSupportedException { + AbstractCurve copy = (AbstractCurve) super.clone(); + copy.xDataReference = this.xDataReference; + copy.logScaleXAxis = this.logScaleXAxis; + copy.order = this.order; + copy.style = this.style; + copy.yAxis = this.yAxis; + return copy; + } + + public SId getXDataReference() { + return this.xDataReference; + } + + public void setXDataReference(SId xDataReference) { + if(SedMLElementFactory.getInstance().isStrictCreation()){ + SedGeneralClass.checkNoNullArgs( xDataReference); + } + this.xDataReference = xDataReference; + } + + public Boolean getLogScaleXAxis() { + return this.logScaleXAxis; + } + + public void setLogScaleXAxis(Boolean logScaleXAxis) { + this.logScaleXAxis = logScaleXAxis; + } + + public Integer getOrder() { + return this.order; + } + + public void setOrder(Integer order) { + if (order != null && order < 0) throw new IllegalArgumentException("order must be >= 0"); + this.order = order; + } + + public SId getStyle() { + return this.style; + } + + public void setStyle(SId style) { + this.style = style; + } + + public YAxisAlignment getYAxis() { + return this.yAxis; + } + + public void setYAxis(YAxisAlignment yAxis) { + this.yAxis = yAxis; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/Axis.java b/vcell-core/src/main/java/org/jlibsedml/components/output/Axis.java new file mode 100644 index 0000000000..4f459b2688 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/Axis.java @@ -0,0 +1,139 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; + +import java.util.ArrayList; +import java.util.List; + +public abstract class Axis extends SedBase { + + public enum Type { + LINEAR("linear"), + LOG10("log10"); + + private final String tagStr; + Type(String tagStr){ + this.tagStr = tagStr; + } + + public String getTag() { + return this.tagStr; + } + + public static Type fromTag(String tagStr) { + if ("linear".equals(tagStr)) return LINEAR; + if ("log10".equals(tagStr)) return LOG10; + throw new IllegalArgumentException("invalid tag: " + (tagStr == null ? "null" : tagStr)); + } + } + + private Type type; + private Double min, max; + private Boolean grid; + private SId styleId; + private Boolean reverse; + + + public Axis(SId id, String name, Type type) { + this(id, name, type, null, null, null, null, null); + } + + public Axis(SId id, String name, Type type, Double min, Double max, Boolean grid, SId styleId, Boolean reverse) { + super(id, name); + this.type = type; + this.min = min; + this.max = max; + this.grid = grid; + this.styleId = styleId; + this.reverse = reverse; + } + + public Axis clone() throws CloneNotSupportedException { + Axis newAxis = (Axis) super.clone(); + newAxis.type = this.type; + newAxis.min = this.min; + newAxis.max = this.max; + newAxis.grid = this.grid; + newAxis.styleId = this.styleId; + newAxis.reverse = this.reverse; + return newAxis; + } + + public Type getType() { + return this.type; + } + + public void setType(Type type) { + this.type = type; + } + + public Double getMin() { + return this.min; + } + + public void setMin(Double min) { + this.min = min; + } + + public Double getMax() { + return this.max; + } + + public void setMax(Double max) { + this.max = max; + } + + public Boolean getGrid() { + return this.grid; + } + + public void setGrid(Boolean grid) { + this.grid = grid; + } + + public SId getStyleId() { + return this.styleId; + } + + public void setStyleId(SId styleId) { + this.styleId = styleId; + } + + public Boolean getReverse() { + return this.reverse; + } + + public void setReverse(Boolean reverse) { + this.reverse = reverse; + } + + @Override + public String parametersToString() { + List params = new ArrayList<>(); + params.add(String.format("type=%s", this.type)); + if (this.min != null) params.add(String.format("min=%s", this.min)); + if (this.max != null) params.add(String.format("max=%s", this.max)); + if (this.grid != null) params.add(String.format("grid=%s", this.grid)); + if (this.styleId != null) params.add(String.format("styleId=%s", this.styleId.string())); + if (this.reverse != null) params.add(String.format("reverse=%s", this.reverse)); + return super.parametersToString() + ", " + String.join(", ", params); + } + + public abstract String getAxisTagName(); + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return this.getAxisTagName(); + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/Curve.java b/vcell-core/src/main/java/org/jlibsedml/components/output/Curve.java new file mode 100644 index 0000000000..cb5b280378 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/Curve.java @@ -0,0 +1,203 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.dataGenerator.DataGenerator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Supports the SED-ML 'Curve' element representing a trace on a 2D Plot. + */ +public class Curve extends AbstractCurve { + public enum Type { + POINTS("points"), + BAR("bar"), + BAR_STACKED("barStacked"), + HORIZONTAL_BAR("horizontalBar"), + HORIZONTAL_BAR_STACKED("horizontalBarStacked"), + ; + + private final String tag; + + Type(String tag) { + this.tag = tag; + } + + public String getTag() { + return this.tag; + } + + public static Type fromTag(String tag) { + return switch (tag) { + case "points" -> POINTS; + case "bar" -> BAR; + case "barStacked" -> BAR_STACKED; + case "horizontalBar" -> HORIZONTAL_BAR; + case "horizontalBarStacked" -> HORIZONTAL_BAR_STACKED; + default -> throw new IllegalArgumentException("Unknown tag " + tag); + }; + } + } + + @Deprecated + protected Boolean logScaleYAxis; // now a capital-B Boolean because deprecated + protected SId yDataReference; // DataGenerator.id + protected SId xErrorUpper, xErrorLower, yErrorUpper, yErrorLower; + protected Type type; + + + /** + * @param id An identifier that is unique in this document. + * @param name An optional name + * @param xDataReference An {@link SId} reference to the {@link DataGenerator} for the x-axis. + * @param yDataReference An {@link SId} reference to the {@link DataGenerator} for the y-axis. + * @throws IllegalArgumentException if any required argument is `null` + */ + public Curve(SId id, String name, SId xDataReference, SId yDataReference) { + this(id, name, xDataReference, yDataReference, null, null, Type.POINTS, null, null, YAxisAlignment.NOT_APPLICABLE, null, null, null, null); + } + + /** + * @param id An identifier that is unique in this document. + * @param name An optional name + * @param logScaleXAxis boolean as to whether x-axis is a log scale. + * @param logScaleYAxis boolean as to whether y-axis is a log scale. + * @param xDataReference An {@link SId} reference to the {@link DataGenerator} for the x-axis. + * @param yDataReference An {@link SId} reference to the {@link DataGenerator} for the y-axis. + * @param xErrorUpper An {@link SId} reference to the {@link DataGenerator} to be used as an upper-bounds line for the x-axis. + * @param xErrorLower An {@link SId} reference to the {@link DataGenerator} to be used as a lower-bounds line for the x-axis. + * @param yErrorUpper An {@link SId} reference to the {@link DataGenerator} to be used as an upper-bounds line for the y-axis. + * @param yErrorLower An {@link SId} reference to the {@link DataGenerator} to be used as a lower-bounds line for the y-axis. + * @throws IllegalArgumentException if any required argument is `null`, or order is a negative integer + */ + public Curve(SId id, String name, SId xDataReference, SId yDataReference, Boolean logScaleXAxis, Boolean logScaleYAxis, Type type, Integer order, SId style, YAxisAlignment yAxis, SId xErrorUpper, SId xErrorLower, SId yErrorUpper, SId yErrorLower) { + super(id, name, xDataReference, logScaleXAxis, order, style, yAxis); + if (SedMLElementFactory.getInstance().isStrictCreation()) + SedGeneralClass.checkNoNullArgs(xDataReference, yDataReference, type); + this.logScaleYAxis = logScaleYAxis; + this.yDataReference = yDataReference; + this.type = type; + this.xErrorUpper = xErrorUpper; + this.xErrorLower = xErrorLower; + this.yErrorUpper = yErrorUpper; + this.yErrorLower = yErrorLower; + } + + public Curve clone() throws CloneNotSupportedException { + Curve clone = (Curve) super.clone(); + clone.logScaleYAxis = this.logScaleYAxis; + clone.yDataReference = this.yDataReference; + clone.xErrorUpper = this.xErrorUpper; + clone.xErrorLower = this.xErrorLower; + clone.yErrorUpper = this.yErrorUpper; + clone.yErrorLower = this.yErrorLower; + clone.type = this.type; + return clone; + } + + /** + * @return true if the y-axis is a log scale, false otherwise. + */ + public Boolean getLogScaleYAxis() { + return this.logScaleYAxis; + } + + /** + * Setter for whether the y-axis of this curve is on a log scale. + * + * @param logScaleYAxis A boolean. + * @since 1.2.0 + */ + public void setLogScaleYAxis(Boolean logScaleYAxis) { + this.logScaleYAxis = logScaleYAxis; + } + + /** + * @return the reference to the {@link DataGenerator} for the y-axis + */ + public SId getYDataReference() { + return this.yDataReference; + } + + /** + * Setter for the y-axis data generator. + * + * @param yDataReference A non-null String that is an identifier of a {@link DataGenerator} + * element. + * @since 1.2.0 + */ + public void setyDataReference(SId yDataReference) { + this.yDataReference = yDataReference; + } + + public Type getType() { + return this.type; + } + + public void setType(Type type) { + this.type = type; + } + + public SId getXErrorUpper() { + return this.xErrorUpper; + } + + public void setXErrorUpper(SId xErrorUpper) { + this.xErrorUpper = xErrorUpper; + } + + public SId getXErrorLower() { + return this.xErrorLower; + } + + public void setXErrorLower(SId xErrorLower) { + this.xErrorLower = xErrorLower; + } + + public SId getYErrorUpper() { + return this.yErrorUpper; + } + + public void setYErrorUpper(SId yErrorUpper) { + this.yErrorUpper = yErrorUpper; + } + + public SId getYErrorLower() { + return this.yErrorLower; + } + + public void setYErrorLower(SId yErrorLower) { + this.yErrorLower = yErrorLower; + } + + @Override + public String getElementName() { + return SedMLTags.OUTPUT_CURVE; + } + + @Override + public String parametersToString() { + List params = new ArrayList<>(); + params.add(String.format("xDataReference=%s", this.xDataReference.string())); + params.add(String.format("yDataReference=%s", this.yDataReference.string())); + if (null != this.logScaleXAxis) params.add(String.format("logX=%s", this.logScaleXAxis)); + if (null != this.logScaleYAxis) params.add(String.format("logY=%s", this.logScaleYAxis)); + if (null != this.order) params.add(String.format("order=%s", this.order)); + if (null != this.style) params.add(String.format("style=%s", this.style.string())); + if (null != this.yAxis) params.add(String.format("yAxis=%s", this.yAxis)); + if (null != this.xErrorUpper) params.add(String.format("xErrorUpper=%s", this.xErrorUpper.string())); + if (null != this.xErrorLower) params.add(String.format("xErrorLower=%s", this.xErrorLower.string())); + if (null != this.yErrorUpper) params.add(String.format("yErrorUpper=%s", this.yErrorUpper.string())); + if (null != this.yErrorLower) params.add(String.format("yErrorLower=%s", this.yErrorLower.string())); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/DataSet.java b/vcell-core/src/main/java/org/jlibsedml/components/output/DataSet.java new file mode 100644 index 0000000000..4807b8ca11 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/DataSet.java @@ -0,0 +1,105 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.model.Change; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates the representation of a data set output from a task. * @author + * anu/radams + * + */ +public final class DataSet extends SedBase { + + private String label; + private SId dataReference; // DataGenerator.id + + /** + * + * @param id + * An identifier that is unique in this document. + * @param name + * An optional name. + * @param label + * to identify the data set in a report. + * @param dataRef + * A String reference to the {@link DataGenerator} + * for this data set. + * @throws IllegalArgumentException + * if any argument except name is null or empty. + */ + public DataSet(SId id, String name, String label, SId dataRef) { + super(id, name); + if (SedMLElementFactory.getInstance().isStrictCreation()) { + SedGeneralClass.checkNoNullArgs(id, label, dataRef); + SedGeneralClass.stringsNotEmpty(label); + } + this.dataReference = dataRef; + this.label = label == null ? "" : label; + + } + + public DataSet clone() throws CloneNotSupportedException { + DataSet clone = (DataSet) super.clone(); + clone.dataReference = this.dataReference; + clone.label = this.label; + return clone; + } + + @Override + public String parametersToString() { + List params = new ArrayList<>(); + if (this.label != null) params.add(String.format("label=%s", this.label)); + params.add(String.format("dataReference=%s", this.dataReference.string())); + return super.parametersToString() + ", " + String.join(", ", params); + } + + /** + * @return the label for this element. + */ + public String getLabel() { + return this.label; + } + + /** + * Sets the label used to identify this DataSet. + * @param label A String. + * @since 1.2.0 + */ + public void setLabel(String label) { + this.label = label; + } + + /** + * @return the reference to the {@link DataGenerator} used to create this + * data set. + */ + public SId getDataReference() { + return this.dataReference; + } + + /** + * Sets the dataReference. This should be a DataGenerator reference. + * @param dataReference A non-nullString. + * @since 1.2.0 + */ + public void setDataReference(SId dataReference) { + this.dataReference = dataReference; + } + + @Override + public String getElementName() { + return SedMLTags.OUTPUT_DATASET; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/Output.java b/vcell-core/src/main/java/org/jlibsedml/components/output/Output.java new file mode 100644 index 0000000000..3e495c8f18 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/Output.java @@ -0,0 +1,80 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.dataGenerator.DataGenerator; + +import java.util.List; +import java.util.Set; + +/** + * Base class for any kind of SED-ML output - e.g., a plot or report. + * + * @author anu/radams + * + */ +public abstract class Output extends SedBase { + + /** + * + * @param id - non null or non-empty String. + * @param name - optional, can be null + * @throws IllegalArgumentException if id is null. + */ + public Output(SId id, String name) { + super(id, name); + if (SedMLElementFactory.getInstance().isStrictCreation()) { + SedGeneralClass.checkNoNullArgs(id); + } + } + + public Output clone() throws CloneNotSupportedException { + return (Output) super.clone(); + } + + /** + * Gets the type of this output (Plot2D, Plot3D, Report) + * + * @return A non-null String. + */ + public abstract String getKind(); + + /** + * Boolean test for whether this output is a Plot2d description. + * + * @return true if this is a Plot2d description, false otherwise. + */ + public boolean isPlot2d() { + return this.getKind().equals(SedMLTags.PLOT2D_KIND); + } + + /** + * Boolean test for whether this output is a Plot3d description. + * + * @return true if this is a Plot3d description, false otherwise. + */ + public boolean isPlot3d() { + return this.getKind().equals(SedMLTags.PLOT3D_KIND); + } + + + /** + * Boolean test for whether this output is a report description. + * + * @return true if this is a report description, false otherwise. + */ + public boolean isReport() { + return this.getKind().equals(SedMLTags.REPORT_KIND); + } + + /** + * Gets a {@link List} of all {@link DataGenerator} identifiers used in this output.
+ * This list will contain only unique entries; the same {@link DataGenerator} id will not appear + * twice in this output. + * + * @return A possibly empty but non-null {@link List} of {@link DataGenerator} id values. + */ + public abstract Set getAllDataGeneratorReferences(); +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/Plot.java b/vcell-core/src/main/java/org/jlibsedml/components/output/Plot.java new file mode 100644 index 0000000000..c699d35f55 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/Plot.java @@ -0,0 +1,98 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.components.SId; + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +public abstract class Plot extends Output { + protected Boolean useLegend; + protected Double plotHeight; + protected Double plotWidth; + + protected XAxis xAxis; + protected YAxis yAxis; + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot(SId id, String name) { + this(id, name, null, null, null); + } + + public Plot(SId id, String name, Boolean useLegend, Double plotHeight, Double plotWidth) { + this(id, name, useLegend, plotHeight, plotWidth, null, null); + } + + public Plot(SId id, String name, Boolean useLegend, Double plotHeight, Double plotWidth, XAxis xAxis, YAxis yAxis) { + super(id, name); + this.useLegend = useLegend; + this.plotHeight = plotHeight; + this.plotWidth = plotWidth; + this.xAxis = xAxis; + this.yAxis = yAxis; + } + + public Plot clone() throws CloneNotSupportedException { + Plot clone = (Plot) super.clone(); + clone.useLegend = this.useLegend; + clone.plotHeight = this.plotHeight; + clone.plotWidth = this.plotWidth; + clone.xAxis = this.xAxis; + clone.yAxis = this.yAxis; + return clone; + } + + public Boolean getUseLegend() { + return this.useLegend; + } + + public void setUseLegend(Boolean useLegend) { + this.useLegend = useLegend; + } + + public Double getPlotHeight() { + return this.plotHeight; + } + + public void setPlotHeight(Double plotHeight) { + this.plotHeight = plotHeight; + } + + public Double getPlotWidth() { + return this.plotWidth; + } + + public void setPlotWidth(Double plotWidth) { + this.plotWidth = plotWidth; + } + + public XAxis getXAxis() { + return this.xAxis; + } + + public void setXAxis(XAxis xAxis) { + this.xAxis = xAxis; + } + + public YAxis getYAxis() { + return this.yAxis; + } + + public void setYAxis(YAxis yAxis) { + this.yAxis = yAxis; + } + + @OverridingMethodsMustInvokeSuper + public Boolean xAxisShouldBeLogarithmic() { + if (this.xAxis != null) return this.xAxis.getType() == Axis.Type.LOG10; + return null; // Note that the subclasses should handle the deprecated way to check for this...but should still call this!!! + } + + @OverridingMethodsMustInvokeSuper + public Boolean yAxisShouldBeLogarithmic() { + if (this.yAxis != null) return this.yAxis.getType() == Axis.Type.LOG10; + return null; // Note that the subclasses should handle the deprecated way to check for this...but should still call this!!! + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/Plot2D.java b/vcell-core/src/main/java/org/jlibsedml/components/output/Plot2D.java new file mode 100644 index 0000000000..cabbaeb606 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/Plot2D.java @@ -0,0 +1,229 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.listOfConstructs.ListOfCurves; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Encapsulates the Plot2d Sed-ML element. + * + * @author anu/radams + * + */ +public class Plot2D extends Plot { + private RightYAxis rightYAxis; + private ListOfCurves listOfCurves; + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot2D(SId id, String name) { + this(id, name, null, new ListOfCurves()); + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot2D(SId id, String name, Boolean useLegend, Double plotHeight, Double plotWidth) { + this(id, name, null, new ListOfCurves(), useLegend, plotHeight, plotWidth); + + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot2D(SId id, String name, Boolean useLegend, Double plotHeight, Double plotWidth, XAxis xAxis, YAxis yAxis){ + this(id, name, null, new ListOfCurves(), useLegend, plotHeight, plotWidth, xAxis, yAxis); + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot2D(SId id, String name, RightYAxis rightYAxis, ListOfCurves listOfCurves) { + super(id, name); + this.rightYAxis = rightYAxis; + this.listOfCurves = listOfCurves; + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot2D(SId id, String name, RightYAxis rightYAxis, ListOfCurves listOfCurves, Boolean useLegend, Double plotHeight, Double plotWidth) { + super(id, name, useLegend, plotHeight, plotWidth); + this.rightYAxis = rightYAxis; + this.listOfCurves = listOfCurves; + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot2D(SId id, String name, RightYAxis rightYAxis, ListOfCurves listOfCurves, Boolean useLegend, Double plotHeight, Double plotWidth, XAxis xAxis, YAxis yAxis) { + super(id, name, useLegend, plotHeight, plotWidth, xAxis, yAxis); + this.rightYAxis = rightYAxis; + this.listOfCurves = listOfCurves; + } + + public Plot2D clone() throws CloneNotSupportedException { + Plot2D clone = (Plot2D) super.clone(); + clone.rightYAxis = this.rightYAxis == null ? null : this.rightYAxis.clone(); + clone.listOfCurves = this.listOfCurves.clone(); + return clone; + } + + public RightYAxis getRightYAxis() { + return this.rightYAxis; + } + + public void setRightYAxis(RightYAxis rightYAxis) { + this.rightYAxis = rightYAxis; + } + + /** + * Gets a read-only list of Curves contained in this element. + * + * @return A possibly empty but non-null {@link List} of {@link Curve} elements. + */ + public ListOfCurves getListOfCurves() { + return this.listOfCurves; + } + + /** + * Gets a read-only list of Curves contained in this element. + * + * @return A possibly empty but non-null {@link List} of {@link Curve} elements. + */ + public List getCurves() { + return this.listOfCurves.getContents(); + } + + /** + * Gets the type of this output. + * + * @return SEDMLTags.PLOT2D_KIND + */ + public String getKind() { + return SedMLTags.PLOT2D_KIND; + } + + /** + * Gets a {@link List} of all {@link DataGenerator} identifiers used in this output.
+ * This list will contain only unique entries; the same {@link DataGenerator} id will not appear + * twice in this output. + * + * @return A possibly empty but non-null {@link List} of {@link DataGenerator} id values. + */ + @Override + public Set getAllDataGeneratorReferences() { + Set refs = new LinkedHashSet<>(); + for (AbstractCurve absCurve : this.getCurves()) { + refs.add(absCurve.getXDataReference()); + if (absCurve instanceof Curve curve) { + refs.add(curve.getYDataReference()); + refs.add(curve.getXErrorUpper()); + refs.add(curve.getXErrorLower()); + refs.add(curve.getYErrorUpper()); + refs.add(curve.getYErrorLower()); + } + // TODO: Add code here if other type of Curve + } + return refs; + } + + /** + * Adds a {@link AbstractCurve} to this object's list of Curves, if not already present. + * + * @param curve A non-null {@link AbstractCurve} element + */ + public void addCurve(AbstractCurve curve) { + this.listOfCurves.addContent(curve); + } + + /** + * Removes a {@link AbstractCurve} from this object's list of Curves, if not already present. + * + * @param curve A non-null {@link AbstractCurve} element + */ + public void removeCurve(AbstractCurve curve) { + this.listOfCurves.removeContent(curve); + } + + @OverridingMethodsMustInvokeSuper + public Boolean xAxisShouldBeLogarithmic(){ + Boolean superResult = super.xAxisShouldBeLogarithmic(); + if (superResult != null) return superResult; + Set logRequestInCurves = this.listOfCurves.getContents().stream().map(AbstractCurve::getLogScaleXAxis).collect(Collectors.toSet()); + if (logRequestInCurves.size() != 1) throw new IllegalArgumentException("Inconsistent curve requests"); + Boolean request = logRequestInCurves.stream().toList().get(0); + return request != null && request; + } + + @OverridingMethodsMustInvokeSuper + public Boolean yAxisShouldBeLogarithmic(){ + Boolean superResult = super.yAxisShouldBeLogarithmic(); + if (superResult != null) return superResult; + Set logRequestInCurves = this.listOfCurves.getContents().stream().filter(Curve.class::isInstance).map(Curve.class::cast).map(Curve::getLogScaleYAxis).collect(Collectors.toSet()); + if (logRequestInCurves.size() != 1) throw new IllegalArgumentException("Inconsistent curve requests"); + Boolean request = logRequestInCurves.stream().toList().get(0); + return request != null && request; + } + + @Override + public String getElementName() { + return SedMLTags.OUTPUT_P2D; + } + + @Override + public String parametersToString() { + List params = new ArrayList<>(), curveParams = new ArrayList<>(); + if (this.rightYAxis != null) params.add(String.format("rightYAxis=%s", this.rightYAxis.getId() != null ? this.rightYAxis.getId().string() : '{' + this.rightYAxis.parametersToString() + '}')); + for (AbstractCurve curve: this.getCurves()) + curveParams.add(String.format("%s", curve.getId() != null ? curve.getId().string() : '{' + curve.parametersToString() + '}')); + params.add(String.format("curves=[%s]", String.join(", ", curveParams))); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + if (this.getXAxis() != null) { + elementFound = this.getXAxis().searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + if (this.getYAxis() != null) { + elementFound = this.getYAxis().searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + if (this.getRightYAxis() != null) { + elementFound = this.getRightYAxis().searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + for (AbstractCurve var : this.getCurves()) { + elementFound = var.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return elementFound; + } + +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/Plot3D.java b/vcell-core/src/main/java/org/jlibsedml/components/output/Plot3D.java new file mode 100644 index 0000000000..81f08639cc --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/Plot3D.java @@ -0,0 +1,219 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.listOfConstructs.ListOfSurfaces; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Encapsulates the information required for a 3d plot in SED-ML. + * + * @author anu/radams + * + */ +public class Plot3D extends Plot { + + private ListOfSurfaces listOfSurfaces; + private ZAxis zAxis; + + /** + * + * @param id + * A unique String identifier for this element. + * @param name + * An optional name. + */ + public Plot3D(SId id, String name) { + super(id, name); + this.listOfSurfaces = new ListOfSurfaces(); + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot3D(SId id, String name, Boolean useLegend, Double plotHeight, Double plotWidth) { + this(id, name, new ListOfSurfaces(), useLegend, plotHeight, plotWidth); + + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot3D(SId id, String name, Boolean useLegend, Double plotHeight, Double plotWidth, XAxis xAxis, YAxis yAxis, ZAxis zAxis){ + this(id, name, new ListOfSurfaces(), useLegend, plotHeight, plotWidth, xAxis, yAxis, zAxis); + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot3D(SId id, String name, ListOfSurfaces listOfSurfaces) { + this(id, name, listOfSurfaces, null, null, null); + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot3D(SId id, String name, ListOfSurfaces listOfSurfaces, Boolean useLegend, Double plotHeight, Double plotWidth) { + this(id, name, listOfSurfaces, useLegend, plotHeight, plotWidth, null, null, null); + } + + /** + * + * @param id A unique id for this element in the document. + * @param name An optional name for this element. + */ + public Plot3D(SId id, String name, ListOfSurfaces listOfSurfaces, Boolean useLegend, Double plotHeight, Double plotWidth, XAxis xAxis, YAxis yAxis, ZAxis zAxis) { + super(id, name, useLegend, plotHeight, plotWidth, xAxis, yAxis); + this.zAxis = zAxis; + this.listOfSurfaces = listOfSurfaces; + } + + public Plot3D clone() throws CloneNotSupportedException { + Plot3D clone = (Plot3D) super.clone(); + clone.listOfSurfaces = this.listOfSurfaces; + clone.zAxis = this.zAxis.clone(); + return clone; + } + + public ZAxis getZAxis() { + return this.zAxis; + } + + public void setZAxis(ZAxis zAxis) { + this.zAxis = zAxis; + } + + /** + * Getter for a read-only list of Surfaces of this object. + * @return list of {@link Surface} + */ + public ListOfSurfaces getListOfSurfaces() { + return this.listOfSurfaces; + } + + /** + * Getter for a read-only list of Surfaces of this object. + * @return list of {@link Surface} + */ + public List getSurfaces() { + return this.listOfSurfaces.getContents(); + } + + /** + * Adds a {@link Surface} to this object's list of Surfaces, if not already + * present. + * + * @param surface + * A non-null {@link Surface} element + */ + public void addSurface(Surface surface) { + this.listOfSurfaces.addContent(surface); + } + + /** + * REmoves a {@link Surface} from this object's list of Surfaces, if not + * already present. + * + * @param surface + * A non-null {@link Surface} element + */ + public void removeSurface(Surface surface) { + this.listOfSurfaces.removeContent(surface); + } + + /** + * Gets the type of this output. + * + * @return SEDMLTags.PLOT3D_KIND + */ + public String getKind() { + return SedMLTags.PLOT3D_KIND; + } + + @Override + public Set getAllDataGeneratorReferences() { + Set rc = new LinkedHashSet<>(); + for (Surface c : this.listOfSurfaces.getContents()) { + rc.add(c.getXDataReference()); + rc.add(c.getYDataReference()); + rc.add(c.getZDataReference()); + } + return rc; + } + + @OverridingMethodsMustInvokeSuper + public Boolean xAxisShouldBeLogarithmic(){ + Boolean superResult = super.xAxisShouldBeLogarithmic(); + if (superResult != null) return superResult; + return this.checkIfSurfacesRequestLogarithmic(Surface::getLogScaleXAxis); + } + + @OverridingMethodsMustInvokeSuper + public Boolean yAxisShouldBeLogarithmic(){ + Boolean superResult = super.yAxisShouldBeLogarithmic(); + if (superResult != null) return superResult; + return this.checkIfSurfacesRequestLogarithmic(Surface::getLogScaleYAxis); + } + + public Boolean zAxisShouldBeLogarithmic(){ + if (this.yAxis != null) return this.yAxis.getType() == Axis.Type.LOG10; + return this.checkIfSurfacesRequestLogarithmic(Surface::getLogScaleZAxis); + } + + private Boolean checkIfSurfacesRequestLogarithmic(java.util.function.Function mappingFunc){ + Set logRequestInCurves = this.listOfSurfaces.getContents().stream().map(mappingFunc).collect(Collectors.toSet()); + if (logRequestInCurves.size() != 1) throw new IllegalArgumentException("Inconsistent surface requests"); + Boolean request = logRequestInCurves.stream().toList().get(0); + return request != null && request; + } + + @Override + public String parametersToString() { + List params = new ArrayList<>(), surfaceParams = new ArrayList<>(); + for (Surface surface : this.getSurfaces()) + surfaceParams.add(String.format("%s", surface.getId() != null ? surface.getId().string() : '{' + surface.parametersToString() + '}')); + params.add(String.format("surfaces=[%s]", String.join(", ", surfaceParams))); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + if (this.getXAxis() != null) { + elementFound = this.getXAxis().searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + if (this.getYAxis() != null) { + elementFound = this.getYAxis().searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + if (this.getZAxis() != null) { + elementFound = this.getZAxis().searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + for (Surface var : this.getSurfaces()) { + elementFound = var.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return elementFound; + } + + @Override + public String getElementName() { + return SedMLTags.OUTPUT_P3D; + } + +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/Report.java b/vcell-core/src/main/java/org/jlibsedml/components/output/Report.java new file mode 100644 index 0000000000..c7776592f1 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/Report.java @@ -0,0 +1,123 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.listOfConstructs.ListOfDataSets; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Represents the SED-ML 'Report' element for describing textual output of + * a simulation. + * + * @author radams + * + */ +public final class Report extends Output { + + private ListOfDataSets listOfDataSets; + + /** + * + * @param id A unique String identifier for this object. + * @param name An optional String name for this object. + */ + public Report(SId id, String name) { + super(id, name); + this.listOfDataSets = new ListOfDataSets(); + } + + public Report clone() throws CloneNotSupportedException { + Report clone = (Report) super.clone(); + clone.listOfDataSets = this.listOfDataSets.clone(); + return clone; + } + + @Override + public String getElementName() { + return SedMLTags.OUTPUT_REPORT; + } + + /** + * Getter for a read-only list of {@link DataSet} objects contained in this report. + * + * @return non-null but possibly empty List . + */ + public ListOfDataSets getListOfDataSets() { + return this.listOfDataSets; + } + + /** + * Getter for a read-only list of {@link DataSet} objects contained in this report. + * + * @return non-null but possibly empty List . + */ + public List getDataSets() { + return this.listOfDataSets.getContents(); + } + + /** + * Adds a {@link DataSet} to this object's list of DataSets, if not already present. + * + * @param dataSet A non-null {@link DataSet} element + */ + public void addDataSet(DataSet dataSet) { + this.listOfDataSets.addContent(dataSet); + } + + /** + * Removes a {@link DataSet} from this object's list of DataSets. + * + * @param dataSet A non-null {@link DataSet} element + */ + public void removeDataSet(DataSet dataSet) { + this.listOfDataSets.removeContent(dataSet); + } + + /** + * Gets the type of this output. + * + * @return SEDMLTags.REPORT_KIND + */ + public String getKind() { + return SedMLTags.REPORT_KIND; + } + + @Override + public Set getAllDataGeneratorReferences() { + return this.listOfDataSets.getContents().stream().map(DataSet::getDataReference).collect(Collectors.toSet()); + } + + @Override + public SedBase searchFor(SId idOfElement){ + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + for (DataSet var : this.getDataSets()) { + elementFound = var.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return elementFound; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * + * @return the parameters and their values, listed in string form + */ + @Override + public String parametersToString() { + // SEE ORIGINAL PARENT!! + List params = new ArrayList<>(), dataSetParams = new ArrayList<>(); + for (DataSet dataSet : this.getDataSets()) + dataSetParams.add(dataSet.getId() != null ? dataSet.getIdAsString() : '{' + dataSet.parametersToString() + '}'); + params.add(String.format("dataSets=[%s]", String.join(",", dataSetParams))); + return super.parametersToString() + ", " + String.join(", ", params); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/RightYAxis.java b/vcell-core/src/main/java/org/jlibsedml/components/output/RightYAxis.java new file mode 100644 index 0000000000..ebb215506d --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/RightYAxis.java @@ -0,0 +1,34 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; + +public class RightYAxis extends Axis { + public RightYAxis(SId id, String name, Axis.Type type) { + super(id, name, type); + } + + public RightYAxis(SId id, String name, Axis.Type type, Double min, Double max, Boolean grid, SId styleId, Boolean reverse) { + super(id, name, type, min, max, grid, styleId, reverse); + } + + public RightYAxis clone() throws CloneNotSupportedException { + return (RightYAxis) super.clone(); + } + + @Override + public String getAxisTagName() { + return SedMLTags.AXIS_RIGHT_Y; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } + + @Override + public String parametersToString() { + return super.parametersToString(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/Surface.java b/vcell-core/src/main/java/org/jlibsedml/components/output/Surface.java new file mode 100644 index 0000000000..d12b4c7b05 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/Surface.java @@ -0,0 +1,245 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.SedMLElementFactory; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.dataGenerator.DataGenerator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates the {@link Surface} element in SED-ML for representing an element of a 3-dimensional plot. + * + */ +public final class Surface extends SedBase { + public enum Type { + PARAMETRIC_CURVE("parametricCurve"), + SURFACE_MESH("surfaceMesh"), + SURFACE_CONTOUR("surfaceContour"), + CONTOUR("contour"), + HEATMAP("heatMap"), + BAR("bar"); + + private final String tag; + Type(String tag){ + this.tag = tag; + } + + public String getTag() { return this.tag; } + + public static Type fromTag(String tag) { + return switch (tag) { + case "parametricCurve" -> PARAMETRIC_CURVE; + case "surfaceMesh" -> SURFACE_MESH; + case "surfaceContour" -> SURFACE_CONTOUR; + case "contour" -> CONTOUR; + case "heatMap" -> HEATMAP; + case "bar" -> BAR; + default -> throw new IllegalArgumentException("Unknown tag " + tag); + }; + } + } + + private SId xDataReference; + @Deprecated private Boolean logScaleXAxis; + private SId yDataReference; + @Deprecated private Boolean logScaleYAxis; + private SId zDataReference; + @Deprecated private Boolean logScaleZAxis; + private SId style; + private Type type; + private Integer order; + + + + /** + * + * @param id A String identifier that is unique in this document. + * @param name An optional String name + * @param xDataReference A {@link SId} reference to the {@link DataGenerator} for the x-axis. + * @param yDataReference A {@link SId} reference to the {@link DataGenerator} for the y-axis. + * @param zDataReference A {@link SId} reference to the {@link DataGenerator} for the z-axis. + * @param logScaleXAxis boolean as to whether x-axis is a log scale. + * @param logScaleYAxis boolean as to whether y-axis is a log scale. + * @param logScaleZAxis boolean as to whether z-axis is a log scale. + * + * @throws IllegalArgumentException if any argument except name is null or empty. + */ + public Surface(SId id, String name, SId xDataReference, SId yDataReference, SId zDataReference, + Boolean logScaleXAxis, Boolean logScaleYAxis, Boolean logScaleZAxis, + SId style, Surface.Type type, Integer order) { + super(id, name); + if(SedMLElementFactory.getInstance().isStrictCreation()){ + SedGeneralClass.checkNoNullArgs(xDataReference, yDataReference, zDataReference); + } + this.xDataReference = xDataReference; + this.logScaleXAxis = logScaleXAxis; + this.yDataReference = yDataReference; + this.logScaleYAxis = logScaleYAxis; + this.zDataReference = zDataReference; + this.logScaleZAxis = logScaleZAxis; + this.style = style; + this.type = type; + this.order = order; + } + + public Surface clone() throws CloneNotSupportedException { + Surface copy = (Surface) super.clone(); + copy.xDataReference = new SId(this.xDataReference.string()); + copy.yDataReference = new SId(this.yDataReference.string()); + copy.zDataReference = new SId(this.zDataReference.string()); + copy.logScaleXAxis = this.logScaleXAxis; + copy.logScaleYAxis = this.logScaleYAxis; + copy.logScaleZAxis = this.logScaleZAxis; + copy.style = new SId(this.style.string()); + copy.type = this.type; + copy.order = Integer.valueOf(this.order); + return copy; + } + + public SId getXDataReference() { + return this.xDataReference; + } + + public void setXDataReference(SId xDataReference) { + if(SedMLElementFactory.getInstance().isStrictCreation()){ + SedGeneralClass.checkNoNullArgs( xDataReference); + } + this.xDataReference = xDataReference; + } + + /** + * @return the reference to the {@link DataGenerator} for the y-axis + */ + public SId getYDataReference() { + return this.yDataReference; + } + + /** + * Setter for the y-axis data generator. + * @param yDataReference A non-null String that is an identifier of a {@link DataGenerator} + * element. + * @since 1.2.0 + */ + public void setyDataReference(SId yDataReference) { + this.yDataReference = yDataReference; + } + + public SId getZDataReference() { + return this.zDataReference; + } + + /** + * Sets the z Data Reference for this object. + * @param zDataReference A non-null, non empty String that should + * refer to a {@link DataGenerator} identifier. + * @since 1.2.0 + */ + public void setzDataReference(SId zDataReference) { + this.zDataReference = zDataReference; + } + + public Boolean getLogScaleXAxis() { + return this.logScaleXAxis; + } + + public void setLogScaleXAxis(Boolean logScaleXAxis) { + this.logScaleXAxis = logScaleXAxis; + } + + /** + * @return true if the y-axis is a log scale, false otherwise. + */ + public Boolean getLogScaleYAxis() { + return this.logScaleYAxis; + } + + /** + * Setter for whether the y-axis of this curve is on a log scale. + * @param logScaleYAxis A boolean. + * @since 1.2.0 + */ + public void setLogScaleYAxis(Boolean logScaleYAxis) { + this.logScaleYAxis = logScaleYAxis; + } + + /** + * @return true if the y-axis is a log scale, false otherwise. + */ + public Boolean getLogScaleZAxis() { + return this.logScaleYAxis; + } + + /** + * Setter for whether the y-axis of this curve is on a log scale. + * @param logScaleYAxis A boolean. + * @since 1.2.0 + */ + public void setLogScaleZAxis(Boolean logScaleYAxis) { + this.logScaleYAxis = logScaleYAxis; + } + + public Integer getOrder() { + return this.order; + } + + public void setOrder(Integer order) { + if (order != null && order < 0) throw new IllegalArgumentException("order must be >= 0"); + this.order = order; + } + + public SId getStyle() { + return this.style; + } + + public void setStyle(SId style) { + this.style = style; + } + + public Type getType() { + return this.type; + } + + public void setType(Type type) { + this.type = type; + } + + + @Override + public String getElementName() { + return SedMLTags.OUTPUT_SURFACE; + } + + @Override + public SedBase searchFor(SId idOfElement){ + return super.searchFor(idOfElement); + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * + * @return the parameters and their values, listed in string form + */ + @Override + public String parametersToString() { + List params = new ArrayList<>(); + params.add(String.format("xDataReference=%s", this.xDataReference.string())); + params.add(String.format("yDataReference=%s", this.yDataReference.string())); + params.add(String.format("zDataReference=%s", this.zDataReference.string())); + if (null != this.logScaleXAxis) params.add(String.format("logX=%s", this.logScaleXAxis)); + if (null != this.logScaleYAxis) params.add(String.format("logY=%s", this.logScaleYAxis)); + if (null != this.logScaleYAxis) params.add(String.format("logZ=%s", this.logScaleZAxis)); + if (null != this.order) params.add(String.format("order=%s", this.order)); + if (null != this.style) params.add(String.format("style=%s", this.style.string())); + return super.parametersToString() + ", " + String.join(", ", params); + } + + +} + diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/XAxis.java b/vcell-core/src/main/java/org/jlibsedml/components/output/XAxis.java new file mode 100644 index 0000000000..30f2e5249a --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/XAxis.java @@ -0,0 +1,34 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; + +public class XAxis extends Axis{ + public XAxis(SId id, String name, Type type) { + super(id, name, type); + } + + public XAxis(SId id, String name, Type type, Double min, Double max, Boolean grid, SId styleId, Boolean reverse) { + super(id, name, type, min, max, grid, styleId, reverse); + } + + public XAxis clone() throws CloneNotSupportedException { + return (XAxis) super.clone(); + } + + @Override + public String getAxisTagName() { + return SedMLTags.AXIS_X; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } + + @Override + public String parametersToString() { + return super.parametersToString(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/YAxis.java b/vcell-core/src/main/java/org/jlibsedml/components/output/YAxis.java new file mode 100644 index 0000000000..8730a506d7 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/YAxis.java @@ -0,0 +1,34 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; + +public class YAxis extends Axis { + public YAxis(SId id, String name, Axis.Type type) { + super(id, name, type); + } + + public YAxis(SId id, String name, Axis.Type type, Double min, Double max, Boolean grid, SId styleId, Boolean reverse) { + super(id, name, type, min, max, grid, styleId, reverse); + } + + public YAxis clone() throws CloneNotSupportedException { + return (YAxis) super.clone(); + } + + @Override + public String getAxisTagName() { + return SedMLTags.AXIS_Y; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } + + @Override + public String parametersToString() { + return super.parametersToString(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/output/ZAxis.java b/vcell-core/src/main/java/org/jlibsedml/components/output/ZAxis.java new file mode 100644 index 0000000000..0ac7ff49e7 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/output/ZAxis.java @@ -0,0 +1,34 @@ +package org.jlibsedml.components.output; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; + +public class ZAxis extends Axis { + public ZAxis(SId id, String name, Axis.Type type) { + super(id, name, type); + } + + public ZAxis(SId id, String name, Axis.Type type, Double min, Double max, Boolean grid, SId styleId, Boolean reverse) { + super(id, name, type, min, max, grid, styleId, reverse); + } + + public ZAxis clone() throws CloneNotSupportedException { + return (ZAxis) super.clone(); + } + + @Override + public String getAxisTagName() { + return SedMLTags.AXIS_Z; + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } + + @Override + public String parametersToString() { + return super.parametersToString(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/simulation/Analysis.java b/vcell-core/src/main/java/org/jlibsedml/components/simulation/Analysis.java new file mode 100644 index 0000000000..f40366dc75 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/simulation/Analysis.java @@ -0,0 +1,62 @@ +package org.jlibsedml.components.simulation; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.algorithm.Algorithm; + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +public class Analysis extends Simulation { + //TODO THIS CLASS IS NOT PRODUCTION READY!! + /** + * @param id A required String identifier for this element. + * @param name - optional, can be null. + * @param algorithm - not null. + * @throws IllegalArgumentException if id is null or empty string. + */ + public Analysis(SId id, String name, Algorithm algorithm) { + super(id, name, algorithm); + } + + public Analysis clone() throws CloneNotSupportedException { + return (Analysis) super.clone(); + } + + /** + * Getter for the type of this simulation. + * + * @return A String + */ + @Override + public String getSimulationKind() { + return SedMLTags.SIMUL_ANALYSIS; + } + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.SIMUL_ANALYSIS; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + return super.parametersToString(); + } + + @Override + public SedBase searchFor(SId idOfElement){ + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/simulation/OneStep.java b/vcell-core/src/main/java/org/jlibsedml/components/simulation/OneStep.java new file mode 100644 index 0000000000..dfdc7bed81 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/simulation/OneStep.java @@ -0,0 +1,76 @@ +package org.jlibsedml.components.simulation; + +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.output.DataSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * Represents the OneStep class in SED-ML. This defines the next output point + * that should be reached by the simulation, as an increment from the current point. + * @since 2.1.0 + */ +public class OneStep extends Simulation { + private double step; + + public OneStep(SId id, String name, Algorithm algorithm, double step) { + super(id, name, algorithm); + this.setStep(step); + } + + public OneStep clone() throws CloneNotSupportedException { + OneStep clone = (OneStep) super.clone(); + clone.step = this.step; + return clone; + } + + @Override + public String getSimulationKind() { + return SedMLTags.SIMUL_OS_KIND; + } + + @Override + public String getElementName() { + return SedMLTags.SIM_ONE_STEP; + } + /** + * Sets the step. + * @param step + */ + public void setStep(double step) { + this.step = step; + } + + /** + * Gets the step + * @return a double. + */ + public double getStep() { + return this.step; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @Override + public String parametersToString(){ + // SEE ORIGINAL PARENT!! + List params = new ArrayList<>(); + params.add(String.format("stepLength=%f", this.getStep())); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement){ + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/simulation/Simulation.java b/vcell-core/src/main/java/org/jlibsedml/components/simulation/Simulation.java new file mode 100644 index 0000000000..c1d8af7c41 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/simulation/Simulation.java @@ -0,0 +1,86 @@ +package org.jlibsedml.components.simulation; + +import org.jlibsedml.components.SId; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.SedMLElementFactory; +import org.jlibsedml.SEDMLVisitor; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.output.DataSet; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates a description of a simulation. + * @author anu/radams + * + */ +public abstract class Simulation extends SedBase { + protected Algorithm algorithm; + + /** + * @param id A required String identifier for this element. + * @param name - optional, can be null. + * @param algorithm - not null. + * @throws IllegalArgumentException if id is null or empty string. + */ + public Simulation(SId id, String name, Algorithm algorithm) { + super(id,name); + if(SedMLElementFactory.getInstance().isStrictCreation()){ + SedGeneralClass.checkNoNullArgs(id, algorithm); + } + this.algorithm = algorithm; + } + + public Simulation clone() throws CloneNotSupportedException { + Simulation clone = (Simulation) super.clone(); + clone.algorithm = this.algorithm.clone(); + return clone; + } + + /** + * Returns the {@link Algorithm} for this simulation + * @return the {@link Algorithm} + */ + public Algorithm getAlgorithm() { + return this.algorithm; + } + + /** + * Setter for the {@link Algorithm} element of this simulation + * @param algorithm a non-null {@link Algorithm}. + */ + public void setAlgorithm(Algorithm algorithm) { + SedGeneralClass.checkNoNullArgs(algorithm); + this.algorithm = algorithm; + } + + /** + * Getter for the type of this simulation. + * @return A String + */ + public abstract String getSimulationKind(); + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + String algoString = String.format("algorithm=%s", this.algorithm.getId() != null ? this.algorithm.getId() : '{' + this.algorithm.parametersToString() + '}') ; + return super.parametersToString() + ", " + algoString; + } + + @Override + public SedBase searchFor(SId idOfElement){ + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + elementFound = this.algorithm.searchFor(idOfElement); + return elementFound; + } +} \ No newline at end of file diff --git a/vcell-core/src/main/java/org/jlibsedml/components/simulation/SteadyState.java b/vcell-core/src/main/java/org/jlibsedml/components/simulation/SteadyState.java new file mode 100644 index 0000000000..f68a24b77d --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/simulation/SteadyState.java @@ -0,0 +1,48 @@ +package org.jlibsedml.components.simulation; + +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.SedMLTags; + +import java.util.ArrayList; +import java.util.List; + +public class SteadyState extends Simulation { + + public SteadyState(SId id, String name, Algorithm algorithm) { + super(id, name, algorithm); + } + + public SteadyState clone() throws CloneNotSupportedException { + return (SteadyState) super.clone(); + } + + @Override + public String getSimulationKind() { + return SedMLTags.SIMUL_SS_KIND; + } + + @Override + public String getElementName() { + return SedMLTags.SIM_STEADY_STATE; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @Override + public String parametersToString(){ + return super.parametersToString(); + } + + @Override + public SedBase searchFor(SId idOfElement){ + return super.searchFor(idOfElement); + } + +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/simulation/UniformTimeCourse.java b/vcell-core/src/main/java/org/jlibsedml/components/simulation/UniformTimeCourse.java new file mode 100644 index 0000000000..7ae6bdf6ec --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/simulation/UniformTimeCourse.java @@ -0,0 +1,160 @@ +package org.jlibsedml.components.simulation; + +import org.jlibsedml.SedMLElementFactory; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.SedMLTags; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates a basic time course simulation of a model. + * + */ +public final class UniformTimeCourse extends Simulation { + private double initialTime; + private double outputStartTime; + private double outputEndTime; + private int numberOfSteps; + + /** + * This constructor does not perform validation at this stage of the simulation configuration ( for example, + * that outputStartTime < outputEndTime). This can be checked by validating the SEDML document by a call to + *
+     *   doc.validate();
+     * 
+ * + * @param id a mandatory, unique identifier for this element + * @param name optional, can be null. + * @param initialTime The value of time at which the simulation is said to start from + * @param outputStartTime The value of time at which the output of the simulation should start to be collected + * @param outputEndTime The value of time after which the output of the simulation should cease being collected + * @param numberOfSteps The number of iterations the algorithm should perform to get from outputStartTime to outputEndTime + */ + public UniformTimeCourse(SId id, String name, double initialTime, double outputStartTime, double outputEndTime, int numberOfSteps, Algorithm algorithm) { + super(id, name, algorithm); + if (SedMLElementFactory.getInstance().isStrictCreation()){ + SedGeneralClass.checkNoNullArgs(id, algorithm); + } + this.initialTime = initialTime; + this.outputStartTime = outputStartTime; + this.outputEndTime = outputEndTime; + this.numberOfSteps = numberOfSteps; + } + + public UniformTimeCourse clone() throws CloneNotSupportedException { + UniformTimeCourse clone = (UniformTimeCourse) super.clone(); + this.initialTime = clone.initialTime; + this.outputStartTime = clone.outputStartTime; + this.outputEndTime = clone.outputEndTime; + this.numberOfSteps = clone.numberOfSteps; + return clone; + } + + /** + * Getter for the initial time value, i.e., the value of t at the start of the simulation. + * @return a double + */ + public double getInitialTime() { + return this.initialTime; + } + + /** + * Sets the initial time for this simulation. + * @param initialTime A double. + * @since 1.2.0 + */ + public void setInitialTime(double initialTime) { + this.initialTime = initialTime; + } + + /** + * Getter for the time value at which output should be started + * @return a double + */ + public double getOutputStartTime() { + return this.outputStartTime; + } + + /** + * Sets the output start time for this simulation. + * @param outputStartTime A double. + * @since 1.2.0 + */ + public void setOutputStartTime(double outputStartTime) { + this.outputStartTime = outputStartTime; + } + + /** + * Getter for the time value at which output should be terminated. + * @return a double + */ + public double getOutputEndTime() { + return this.outputEndTime; + } + + /** + * Sets the output end time for this simulation. + * @param outputEndTime A double. + * @since 1.2.0 + */ + public void setOutputEndTime(double outputEndTime) { + this.outputEndTime = outputEndTime; + } + + /** + * Getter for the number of time-points in the simulation. + * @return a double + */ + public int getNumberOfSteps() { + return this.numberOfSteps; + } + + /** + * Sets the number of output points for this simulation. + * @param numberOfSteps A double. + * @since 1.2.0 + */ + public void setNumberOfSteps(int numberOfSteps) { + this.numberOfSteps = numberOfSteps; + } + + /** + * @return {@link SedMLTags#SIMUL_UTC_KIND} + */ + public String getSimulationKind() { + return SedMLTags.SIMUL_UTC_KIND; + } + + @Override + public String getElementName() { + return SedMLTags.SIM_UTC; + } + + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @Override + public String parametersToString(){ + // SEE ORIGINAL PARENT!! + List params = new ArrayList<>(); + params.add(String.format("initialTime=%f", this.initialTime)); + params.add(String.format("outputStartTime=%f", this.outputStartTime)); + params.add(String.format("outputEndTime=%f", this.outputEndTime)); + params.add(String.format("numberOfSteps=%d", this.numberOfSteps)); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement){ + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/task/AbstractTask.java b/vcell-core/src/main/java/org/jlibsedml/components/task/AbstractTask.java new file mode 100644 index 0000000000..ada69d0459 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/AbstractTask.java @@ -0,0 +1,20 @@ +package org.jlibsedml.components.task; + +import org.jlibsedml.SedMLElementFactory; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; + +public abstract class AbstractTask extends SedBase { + + public AbstractTask(SId id, String name) { + super(id, name); + if(SedMLElementFactory.getInstance().isStrictCreation()){ + SedGeneralClass.checkNoNullArgs(id); + } + } + + public AbstractTask clone() throws CloneNotSupportedException { + return (AbstractTask) super.clone(); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/task/FunctionalRange.java b/vcell-core/src/main/java/org/jlibsedml/components/task/FunctionalRange.java new file mode 100644 index 0000000000..12a1d0ad2c --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/FunctionalRange.java @@ -0,0 +1,180 @@ +package org.jlibsedml.components.task; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.*; +import org.jlibsedml.components.listOfConstructs.ListOfParameters; +import org.jlibsedml.components.listOfConstructs.ListOfVariables; +import org.jmathml.ASTNode; +import org.jmathml.FormulaFormatter; + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +/* + + + + + + + + + w + index + + + + +*/ + + +public class FunctionalRange extends Range implements Calculation { + private final static FormulaFormatter formulaFormatter = new FormulaFormatter(); + + private SId range; + private ListOfVariables variables; + private ListOfParameters parameters; + private ASTNode math; + + + public FunctionalRange(SId id, SId range) { + super(id); + this.range = range; + this.variables = new ListOfVariables(); + this.parameters = new ListOfParameters(); + this.math = null; + } + public FunctionalRange(SId id, SId range, Map variables, Map parameters, ASTNode mathAsNode) { + this(id, range); + if(variables != null) for (SId varKey : variables.keySet()) this.variables.addContent(variables.get(varKey)); + if(parameters != null) for (SId paramKey : parameters.keySet()) this.parameters.addContent(parameters.get(paramKey)); + this.math = mathAsNode; + } + + public FunctionalRange clone() throws CloneNotSupportedException { + FunctionalRange clone = (FunctionalRange) super.clone(); + clone.range = this.range; + clone.variables = this.variables; + clone.parameters = this.parameters; + clone.math = this.math; + return clone; + } + + public SId getRange() { + return this.range; + } + + public void setMath(ASTNode math) { + this.math = math; + } + + @Override + public ListOfParameters getListOfParameters() { + return this.parameters; + } + + @Override + public List getParameters() { + return this.parameters.getContents(); + } + + @Override + public void addParameter(Parameter parameter) { + this.parameters.addContent(parameter); + } + + @Override + public void removeParameter(Parameter parameter) { + this.parameters.removeContent(parameter); + } + + @Override + public ListOfVariables getListOfVariables() { + return this.variables; + } + + @Override + public List getVariables() { + return this.variables.getContents(); + } + + @Override + public void addVariable(Variable variable) { + this.variables.addContent(variable); + } + + @Override + public void removeVariable(Variable variable) { + this.variables.removeContent(variable); + } + + /** + * Convenience function to return the maths expression as a C-style string. + * + * @return A String representation of the maths of this DataGenerator. + */ + @Override + public String getMathAsString() { + return FunctionalRange.formulaFormatter.formulaToString(this.math); + } + + public ASTNode getMath() { + return this.math; + } + + /** + * This method is not supported yet. + * @throws UnsupportedOperationException + */ + @Override + public int getNumElements() { + throw new UnsupportedOperationException("Unsupported method getNumElements() for " + this.getElementName()); + } + + /** + * This method is not supported yet. + * @throws UnsupportedOperationException + */ + @Override + public double getElementAt(int index) { + throw new UnsupportedOperationException("Unsupported method getElementAt() for " + getElementName()); + } + + @Override + public String getElementName() { + return SedMLTags.FUNCTIONAL_RANGE_TAG; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + List params = new ArrayList<>(); + params.add(String.format("range=%s", this.getRange().string())); + params.addAll(this.getMathParamsAndVarsAsStringParams()); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + for (Variable var : this.getVariables()) { + elementFound = var.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + for (Parameter p : this.getParameters()) { + elementFound = p.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return elementFound; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/Range.java b/vcell-core/src/main/java/org/jlibsedml/components/task/Range.java similarity index 56% rename from vcell-core/src/main/java/org/jlibsedml/Range.java rename to vcell-core/src/main/java/org/jlibsedml/components/task/Range.java index e05c907955..5ca511a771 100644 --- a/vcell-core/src/main/java/org/jlibsedml/Range.java +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/Range.java @@ -1,9 +1,20 @@ -package org.jlibsedml; +package org.jlibsedml.components.task; -public abstract class Range extends AbstractIdentifiableElement{ +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; - public Range(String id) { - super(id, ""); +public abstract class Range extends SedBase { + + public Range(SId id) { + this(id, null); + } + + public Range(SId id, String name) { + super(id, name); + } + + public Range clone() throws CloneNotSupportedException { + return (Range) super.clone(); } /** * Gets the number of elements in this range diff --git a/vcell-core/src/main/java/org/jlibsedml/components/task/RepeatedTask.java b/vcell-core/src/main/java/org/jlibsedml/components/task/RepeatedTask.java new file mode 100644 index 0000000000..cd459e00b8 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/RepeatedTask.java @@ -0,0 +1,152 @@ +package org.jlibsedml.components.task; + +import java.util.ArrayList; +import java.util.List; + +import org.jlibsedml.*; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.listOfConstructs.ListOfRanges; +import org.jlibsedml.components.listOfConstructs.ListOfRepeatedTaskChanges; +import org.jlibsedml.components.listOfConstructs.ListOfSubTasks; +import org.jlibsedml.components.model.Change; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RepeatedTask extends AbstractTask { + private static final Logger logger = LoggerFactory.getLogger(RepeatedTask.class); + + private boolean resetModel; + private SId range; + private ListOfRanges ranges = new ListOfRanges(); + private ListOfRepeatedTaskChanges changes = new ListOfRepeatedTaskChanges(); + private ListOfSubTasks subTasks = new ListOfSubTasks(); + + public RepeatedTask(SId id, String name, boolean resetModel, SId range) { + super(id, name); + this.resetModel = resetModel; + this.range = range; + } + + public RepeatedTask clone() throws CloneNotSupportedException { + RepeatedTask clone = (RepeatedTask) super.clone(); + clone.resetModel = this.resetModel; + clone.range = new SId(this.range.string()); + clone.ranges = this.ranges.clone(); + clone.changes = this.changes.clone(); + clone.subTasks = this.subTasks.clone(); + return clone; + } + + public boolean getResetModel() { + return this.resetModel; + } + + public void setResetModel(boolean resetModel) { + this.resetModel = resetModel; + } + + public SId getRange() { + return this.range; + } + + public void setRange(SId range) { + this.range = range; + } + + public Range getRange(SId rangeId) { + return this.ranges.getContentById(rangeId); + } + + public ListOfRanges getListOfRanges() { + return this.ranges; + } + + public List getRanges() { + return this.ranges.getContents(); + } + + public void addRange(Range range) { + this.ranges.addContent(range); + } + + public void removeRange(Range range) { + this.ranges.removeContent(range); + } + + public ListOfRepeatedTaskChanges getListOfChanges() { + return this.changes; + } + + public List getChanges() { + return this.changes.getContents(); + } + + public void addChange(SetValue change) { + this.changes.addContent(change); + } + + public ListOfSubTasks getListOfSubTasks() { + return this.subTasks; + } + + public List getSubTasks() { + return this.subTasks.getContents(); + } + + public void addSubtask(SubTask subTask) { + if (subTask == null) throw new IllegalArgumentException("subTask cannot be null"); + if (subTask.getTask() == null || subTask.getTask().string().isEmpty()) { + logger.warn("subtask cant't be null, key can't be null, key can't be empty string"); + logger.warn(" ...subtask " + subTask.getTask().string() + " not added to list"); + return; // subtask can't be null, key can't be null, key can't be "" + } + if (this.getId().equals(subTask.getTask())) { + logger.warn("'this' repeated task cannot be a subtask for itself"); + logger.warn(" ...subtask " + subTask.getTask() + " not added to list"); + return; // "this" repeated task cannot be a subtask for itself + } + this.subTasks.addContent(subTask); + } + + @Override + public String getElementName() { + return SedMLTags.REPEATED_TASK_TAG; + } + + @Override + public String parametersToString() { + List params = new ArrayList<>(), rangeParams = new ArrayList<>(), + changesParams = new ArrayList<>(), subTasksParams = new ArrayList<>(); + params.add(String.format("resetModel=%b", this.getResetModel())); + for (Range r : this.ranges.getContents()) + rangeParams.add(r.getId() != null ? r.getId().string() : '{' + r.parametersToString() + '}'); + for (SetValue setVal : this.changes.getContents()) + changesParams.add(setVal.getId() != null ? setVal.getId().string() : '{' + setVal.parametersToString() + '}'); + for (SubTask subTask : this.subTasks.getContents()) + subTasksParams.add(subTask.getId() != null ? subTask.getId().string() : '{' + subTask.parametersToString() + '}'); + params.add(String.format("ranges=[%s]", String.join(", ", rangeParams))); + params.add(String.format("changes=[%s]", String.join(", ", changesParams))); + params.add(String.format("subTasks=[%s]", String.join(", ", subTasksParams))); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + SedBase elementFound = super.searchFor(idOfElement); + if (elementFound != null) return elementFound; + for (Range range : this.getRanges()) { + elementFound = range.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + for (Change c : this.getChanges()) { + elementFound = c.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + for (SubTask st : this.getSubTasks()) { + elementFound = st.searchFor(idOfElement); + if (elementFound != null) return elementFound; + } + return elementFound; + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/task/SetValue.java b/vcell-core/src/main/java/org/jlibsedml/components/task/SetValue.java new file mode 100644 index 0000000000..85f1fb4e26 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/SetValue.java @@ -0,0 +1,105 @@ +package org.jlibsedml.components.task; + +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.model.ComputeChange; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.SEDMLVisitor; +import org.jlibsedml.XPathTarget; +import org.jmathml.ASTNode; + +import java.util.ArrayList; +import java.util.List; + +/* + + + + current + + + + + + + + current + + + +*/ + +public class SetValue extends ComputeChange { + + private SId modelReference; + // as for functionalRange, variable references always retrieve the current value of the + // model variable or range at the current iteration of the enclosing repeatedTask. For a model not being + // simulated by any subTask, the initial state of the model is used. + private SId rangeReference; + + // Remember to set the math separately + public SetValue(XPathTarget target, SId rangeReference, SId modelReference) { + this(null, null, target, rangeReference, modelReference); + + } + + public SetValue(XPathTarget target, ASTNode math, SId rangeReference, SId modelReference) { + this(null, null, target, math, rangeReference, modelReference); + } + + // Remember to set the math separately + public SetValue(SId id, String name, XPathTarget target, SId rangeReference, SId modelReference) { + super(id, name, target); + this.modelReference = modelReference; + this.rangeReference = rangeReference; + } + + public SetValue(SId id, String name, XPathTarget target, ASTNode math, SId rangeReference, SId modelReference) { + super(id, name, target, math); + this.rangeReference = rangeReference; + this.modelReference = modelReference; + } + + public SetValue clone() throws CloneNotSupportedException { + SetValue clone = (SetValue) super.clone(); + clone.modelReference = new SId(this.modelReference.string()); + clone.rangeReference = new SId(this.rangeReference.string()); + return clone; + } + + public void setRangeReference(SId rangeReference) { + this.rangeReference = rangeReference; + } + public SId getRangeReference() { + return this.rangeReference; + } + public void setModelReference(SId modelReference) { + this.modelReference = modelReference; + } + public SId getModelReference() { + return this.modelReference; + } + + @Override + public String getChangeKind() { + return SedMLTags.SET_VALUE_KIND; + } + + @Override + public String getElementName() { + return SedMLTags.SET_VALUE; + } + + @Override + public String parametersToString(){ + List params = new ArrayList<>(); + params.add(String.format("rangeId=%s", this.rangeReference.string())); + params.add(String.format("modelId=%s", this.modelReference.string())); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement){ + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/task/SubTask.java b/vcell-core/src/main/java/org/jlibsedml/components/task/SubTask.java new file mode 100644 index 0000000000..c6a2ea5a7b --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/SubTask.java @@ -0,0 +1,97 @@ +package org.jlibsedml.components.task; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedGeneralClass; +import org.jlibsedml.components.model.Change; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/* + + + + + + + + +*/ + +public class SubTask extends SedBase { + private static final Logger log = LoggerFactory.getLogger(SubTask.class); + + private final SId task; // SubTask is basically a pointer to another task to run repeatedly; `this.task` is the id of that task. + private final Integer order; + + public SubTask(SId task) { + this(null, null, null, task); + } + + public SubTask(SId id, String name, SId task) { + this(id, name, null, task); + } + + public SubTask(Integer order, SId task) { + this(null, null, order, task); + } + + public SubTask(SId id, String name, Integer order, SId task) { + super(id, name); + this.order = order; + SedGeneralClass.checkNoNullArgs(task); + this.task = task; + if(order == null) return; + + } + + public SId getTask() { + return this.task; + } + public Integer getOrder() { + return this.order; + } + + @Override + public boolean equals(Object obj){ + if (null == obj) return false; + if (!(obj instanceof SubTask subTask)) return false; + return Objects.equals(this.getId(), subTask.getId()) + && Objects.equals(this.getName(), subTask.getName()) + && this.getTask().equals(subTask.getTask()) + && this.getOrder().equals(subTask.getOrder()); + } + + public int hashCode(){ + return (this.getClass().getSimpleName() + "::" + (this.getId() == null ? "" : this.getId().string()) + "::" + this.getName() + "::" + + this.getTask().string()+ "::" + this.getOrder()).hashCode(); + } + + /** + * Provides a link between the object model and the XML element names + * + * @return A non-null String of the XML element name of the object. + */ + @Override + public String getElementName() { + return SedMLTags.SUBTASK_TAG; + } + + @Override + public String parametersToString() { + List params = new ArrayList<>(); + if (this.order != null) params.add(String.format("order={%s}", this.order)); + params.add(String.format("task={%s}", this.task.string())); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/components/task/Task.java b/vcell-core/src/main/java/org/jlibsedml/components/task/Task.java new file mode 100644 index 0000000000..5a5c539306 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/Task.java @@ -0,0 +1,105 @@ +package org.jlibsedml.components.task; + +import org.jlibsedml.*; +import org.jlibsedml.components.*; +import org.jlibsedml.components.model.Change; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.simulation.Simulation; + +import javax.annotation.OverridingMethodsMustInvokeSuper; +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates the Task element linking a simulation description to a model. + * + */ +public class Task extends AbstractTask { + private SId modelReference; + private SId simulationReference; + + @Override + public String getElementName() { + return SedMLTags.TASK_TAG; + } + + /** + * @param id + * @param name - optional, can be null. + * @param modelReference + * @param simulationReference + * @throws IllegalArgumentException if any argument except name is null or empty string. + */ + public Task(SId id, String name, SId modelReference, SId simulationReference) { + super(id,name); + if(SedMLElementFactory.getInstance().isStrictCreation()){ + SedGeneralClass.checkNoNullArgs(id, modelReference, simulationReference); + } + // store and initialize + this.modelReference = modelReference; + this.simulationReference = simulationReference; + } + + public Task clone() throws CloneNotSupportedException { + Task copy = (Task) super.clone(); + copy.modelReference = new SId(this.modelReference.string()); + copy.simulationReference = new SId(this.simulationReference.string()); + return copy; + } + + /** + * Getter for the model reference. + * @return A String that should correspond to a model's id attribute. + */ + public SId getModelReference() { + return this.modelReference; + } + + /** + * Sets the model reference for this task. This should be the value of the 'id' + * attribute of a {@link Model} element. + * @param modelReference A non-null String. + * @since 1.2.0 + */ + public void setModelReference(SId modelReference) { + this.modelReference = modelReference; + } + + /** + * Getter for the simulation reference. + * @return A String that should correspond to a simulation's id attribute. + */ + public SId getSimulationReference() { + return this.simulationReference; + } + + /** + * Sets the simulation reference for this task. This should be the value of the 'id' + * attribute of a {@link Simulation} element. + * @param simulationReference A non-null String. + * @since 1.2.0 + */ + public void setSimulationReference(SId simulationReference) { + this.simulationReference = simulationReference; + } + + /** + * Returns the parameters that are used in this.equals() to evaluate equality. + * Needs to be returned as `member_name=value.toString(), ` segments, and it should be appended to a `super` call to this function. + * + * e.g.: `super.parametersToString() + ", " + String.format(...)` + * @return the parameters and their values, listed in string form + */ + @OverridingMethodsMustInvokeSuper + public String parametersToString(){ + List params = new ArrayList<>(); + if (this.modelReference != null) params.add(String.format("modelReference=%s", this.modelReference)); + if (this.simulationReference != null) params.add(String.format("simulationReference=%s", this.simulationReference)); + return super.parametersToString() + ", " + String.join(", ", params); + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/UniformRange.java b/vcell-core/src/main/java/org/jlibsedml/components/task/UniformRange.java similarity index 60% rename from vcell-core/src/main/java/org/jlibsedml/UniformRange.java rename to vcell-core/src/main/java/org/jlibsedml/components/task/UniformRange.java index 7a9093a295..457b4896af 100644 --- a/vcell-core/src/main/java/org/jlibsedml/UniformRange.java +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/UniformRange.java @@ -1,6 +1,12 @@ -package org.jlibsedml; +package org.jlibsedml.components.task; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; public class UniformRange extends Range { @@ -17,12 +23,10 @@ public String getText() { } public static UniformType fromString(String text) { - if (text != null) { - for (UniformType b : UniformType.values()) { - if (text.equalsIgnoreCase(b.text)) { - return b; - } - } + if (text == null) return null; + for (UniformType b : UniformType.values()) { + if (!text.equalsIgnoreCase(b.text)) continue; + return b; } return null; } @@ -30,23 +34,23 @@ public static UniformType fromString(String text) { private double start; private double end; - private int numberOfPoints; + private int numberOfSteps; private UniformType type; - public UniformRange(String id, double start, double end, int numberOfPoints) { + public UniformRange(SId id, double start, double end, int numberOfPoints) { super(id); this.start = start; this.end = end; - this.numberOfPoints = numberOfPoints; + this.numberOfSteps = numberOfPoints; this.type = UniformType.LINEAR; } - public UniformRange(String id, double start, double end, + public UniformRange(SId id, double start, double end, int numberOfPoints, UniformType type) { super(id); this.start = start; this.end = end; - this.numberOfPoints = numberOfPoints; + this.numberOfSteps = numberOfPoints; if (type == null) { this.type = UniformType.LINEAR; } else if (type == UniformType.LINEAR) { @@ -61,6 +65,15 @@ public UniformRange(String id, double start, double end, } } + public UniformRange clone() throws CloneNotSupportedException { + UniformRange clone = (UniformRange) super.clone(); + clone.start = this.start; + clone.end = this.end; + clone.numberOfSteps = this.numberOfSteps; + clone.type = this.type; + return clone; + } + @Override public int hashCode() { final int prime = 31; @@ -68,7 +81,7 @@ public int hashCode() { long temp; temp = Double.doubleToLongBits(end); result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + numberOfPoints; + result = prime * result + numberOfSteps; temp = Double.doubleToLongBits(start); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + ((type == null) ? 0 : type.hashCode()); @@ -86,7 +99,7 @@ public boolean equals(Object obj) { UniformRange other = (UniformRange) obj; if (Double.doubleToLongBits(end) != Double.doubleToLongBits(other.end)) return false; - if (numberOfPoints != other.numberOfPoints) + if (numberOfSteps != other.numberOfSteps) return false; if (Double.doubleToLongBits(start) != Double .doubleToLongBits(other.start)) @@ -104,52 +117,59 @@ public double getEnd() { return end; } + @Deprecated public int getNumberOfPoints() { - return numberOfPoints; + return this.numberOfSteps; } - public UniformType getType() { - return type; + public int getNumberOfSteps(){ + return this.numberOfSteps; } - @Override - public String toString() { - return "Uniform Range [" + "getId()=" + getId() + ", getStart()=" - + getStart() + ", getEnd()=" + getEnd() - + ", getNumberOfPoints()=" + getNumberOfPoints() - + ", getType()=" + getType() + "]"; + public UniformType getType() { + return type; } @Override public int getNumElements() { - return numberOfPoints; + return numberOfSteps; } @Override public double getElementAt(int index) { - if (index < 0 || index > numberOfPoints - 1) { + if (index < 0 || index > numberOfSteps - 1) { throw new IllegalArgumentException(MessageFormat.format( "{0} is an invalid index. It must be between 0 and {1}.", - index, numberOfPoints - 1)); + index, numberOfSteps - 1)); } if (type == UniformType.LINEAR) { - return start + ((end - start) / (numberOfPoints - 1)) + return start + ((end - start) / (numberOfSteps - 1)) * ((double) index); } else { return start * Math.pow(end / start, ((double) index) - / (numberOfPoints - 1)); + / (numberOfSteps - 1)); } } @Override public String getElementName() { - return SEDMLTags.UNIFORM_RANGE_TAG; + return SedMLTags.UNIFORM_RANGE_TAG; + } + + @Override + public String parametersToString() { + List params = new ArrayList<>(); + params.add(String.format("start=%f", this.start)); + params.add(String.format("end=%f", this.end)); + params.add(String.format("numberOfPoints=%d", this.numberOfSteps)); + params.add(String.format("type=%s", this.type)); + return super.parametersToString() + ", " + String.join(", ", params); } @Override - public boolean accept(SEDMLVisitor visitor) { - return visitor.visit(this); + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); } } diff --git a/vcell-core/src/main/java/org/jlibsedml/components/task/VectorRange.java b/vcell-core/src/main/java/org/jlibsedml/components/task/VectorRange.java new file mode 100644 index 0000000000..e5b1f17054 --- /dev/null +++ b/vcell-core/src/main/java/org/jlibsedml/components/task/VectorRange.java @@ -0,0 +1,71 @@ +package org.jlibsedml.components.task; + +import org.jlibsedml.SedMLTags; +import org.jlibsedml.SEDMLVisitor; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.model.Change; + +import java.util.ArrayList; +import java.util.List; + +public class VectorRange extends Range { + private List values; + + public VectorRange(SId id) { + this(id, List.of()); + } + + public VectorRange(SId id, String name) { + this(id, name, List.of()); + } + + public VectorRange(SId id, List values) { + this(id, null, values); + } + + public VectorRange(SId id, String name, List values) { + super(id, name); + this.values = new ArrayList<>(values); + } + + public VectorRange clone() throws CloneNotSupportedException { + VectorRange clone = (VectorRange) super.clone(); + clone.values = new ArrayList<>(this.values); + return clone; + } + + public void addValue(Double value) { + this.values.add(value); + } + + @Override + public int getNumElements() { + return this.values.size(); + } + + @Override + public double getElementAt(int index) { + return this.values.get(index); + } + + public List getElements(){ + return List.copyOf(this.values); + } + + @Override + public String getElementName() { + return SedMLTags.VECTOR_RANGE_TAG; + } + + @Override + public String parametersToString(){ + List valueStrings = this.values.stream().map(Object::toString).toList(); + return super.parametersToString() + ", " + String.format("values=[%s]", String.join(", ", valueStrings)); + } + + @Override + public SedBase searchFor(SId idOfElement) { + return super.searchFor(idOfElement); + } +} diff --git a/vcell-core/src/main/java/org/jlibsedml/execution/AbstractSedmlExecutor.java b/vcell-core/src/main/java/org/jlibsedml/execution/AbstractSedmlExecutor.java index 8f2a30a8d7..4fd1d88518 100644 --- a/vcell-core/src/main/java/org/jlibsedml/execution/AbstractSedmlExecutor.java +++ b/vcell-core/src/main/java/org/jlibsedml/execution/AbstractSedmlExecutor.java @@ -9,15 +9,18 @@ import java.util.Set; import java.util.TreeSet; -import org.jlibsedml.AbstractTask; -import org.jlibsedml.DataGenerator; -import org.jlibsedml.Model; -import org.jlibsedml.Output; -import org.jlibsedml.SedML; -import org.jlibsedml.Simulation; -import org.jlibsedml.Task; -import org.jlibsedml.UniformTimeCourse; -import org.jlibsedml.Variable; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.output.Output; +import org.jlibsedml.components.simulation.Simulation; +import org.jlibsedml.components.task.Task; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.Variable; import org.jlibsedml.execution.ExecutionStatusElement.ExecutionStatusType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +49,7 @@ * */ public abstract class AbstractSedmlExecutor { - protected final SedML sedml; + protected final SedMLDataContainer sedml; private final Output output; private List failureMessages = new ArrayList(); @@ -65,13 +68,13 @@ public abstract class AbstractSedmlExecutor { /** * * @param model - * A non-null {@link SedML} model + * A non-null {@link SedMLDataContainer} model * @param output * An {@link Output} which we want to reproduce. * @throws IllegalArgumentException * if model == null or output == null. */ - public AbstractSedmlExecutor(SedML model, Output output) { + public AbstractSedmlExecutor(SedMLDataContainer model, Output output) { if (model == null || output == null) { throw new IllegalArgumentException(); } @@ -86,13 +89,13 @@ public AbstractSedmlExecutor(SedML model, Output output) { * a single time-course and return all variables. * * @param model - * A non-null {@link SedML} model + * A non-null {@link SedMLDataContainer} model * @param task * An {@link Task} which we want to run. * @throws IllegalArgumentException * if model == null or output == null. */ - public AbstractSedmlExecutor(SedML model, AbstractTask task) { + public AbstractSedmlExecutor(SedMLDataContainer model, AbstractTask task) { if (model == null || task == null) { throw new IllegalArgumentException(); } @@ -159,31 +162,33 @@ public final Map runSimulations() { } for (AbstractTask task : tasksToExecute) { - Model m = sedml.getModelWithId(task.getModelReference()); - if (!supportsLanguage(m.getLanguage())) { - addStatus(new ExecutionStatusElement(null, + if (!(task instanceof Task basicTask)) continue; + Model m = this.sedml.findModelById(basicTask.getModelReference()); + if (null == m) throw new RuntimeException("Unexpected non-model found"); + if (!this.supportsLanguage(m.getLanguage())) { + this.addStatus(new ExecutionStatusElement(null, LANGUAGE_NOT_SUPPORTED_ERROR + m.getLanguage(), ExecutionStatusType.ERROR)); return res; } log.debug("language {} is OK", m.getLanguage()); - String changedModel = modelResolver.getModelString(m); + String changedModel = this.modelResolver.getXMLStringRepresentationOfModel(m); log.debug("Changed modell is {}", changedModel); if (changedModel == null) { - addStatus(new ExecutionStatusElement(null, - modelResolver.getMessage(), ExecutionStatusType.ERROR)); + this.addStatus(new ExecutionStatusElement(null, + this.modelResolver.getMessage(), ExecutionStatusType.ERROR)); } log.debug("Ready to execute"); - IRawSedmlSimulationResults results = executeSimulation( - changedModel, (UniformTimeCourse) sedml.getSimulation(task - .getSimulationReference())); + UniformTimeCourse utcSim = this.sedml.findUniformTimeCourseById(basicTask.getSimulationReference()); + if (null == utcSim) throw new RuntimeException("Unexpected non-utc simulation found."); + IRawSedmlSimulationResults results = this.executeSimulation(changedModel, utcSim); if (results == null) { - addStatus(new ExecutionStatusElement(null, + this.addStatus(new ExecutionStatusElement(null, "Simulation failed during execution: " - + task.getSimulationReference() + " with model: " - + task.getModelReference(), + + basicTask.getSimulationReference().string() + " with model: " + + basicTask.getModelReference().string(), ExecutionStatusType.ERROR)); // return res; } @@ -268,26 +273,33 @@ protected abstract IRawSedmlSimulationResults executeSimulation( * IDs. */ public List getSimulatableTasks() { - List rc = new ArrayList(); - for (AbstractTask task : sedml.getTasks()) { - Simulation s = sedml.getSimulation(task.getSimulationReference()); - if (s != null && canExecuteSimulation(s)) { - rc.add(task); - } + SedML sedML = this.sedml.getSedML(); + List rc = new ArrayList<>(); + for (AbstractTask task : this.sedml.getSedML().getTasks()) { + if (!(task instanceof Task basicTask)) continue; + UniformTimeCourse utcSim = this.sedml.findUniformTimeCourseById(basicTask.getSimulationReference()); + if (null == utcSim) continue; + if (!this.canExecuteSimulation(utcSim)) continue; + rc.add(task); } return rc; } private Set findTasks(Output output) { - Set tasksToExecute = new TreeSet(); - Set dgs = new TreeSet(); - for (String dgid : output.getAllDataGeneratorReferences()) { - dgs.add(sedml.getDataGeneratorWithId(dgid)); + Set tasksToExecute = new TreeSet<>(); + Set dgs = new TreeSet<>(); + for (SId dgId : output.getAllDataGeneratorReferences()) { + DataGenerator dg = this.sedml.findDataGeneratorById(dgId); + if (null == dg) continue; + dgs.add(dg); } for (DataGenerator dg : dgs) { - for (Variable v : dg.getListOfVariables()) { - tasksToExecute.add(sedml.getTaskWithId(v.getReference())); + for (Variable v : dg.getVariables()) { + SId taskRef = v.getTaskReference(); + AbstractTask taskToDo = this.sedml.findAbstractTaskById(taskRef); + if (null == taskToDo) continue; + tasksToExecute.add(taskToDo); } } return tasksToExecute; diff --git a/vcell-core/src/main/java/org/jlibsedml/execution/ModelResolver.java b/vcell-core/src/main/java/org/jlibsedml/execution/ModelResolver.java index 35ac685e47..4a0541f6f9 100644 --- a/vcell-core/src/main/java/org/jlibsedml/execution/ModelResolver.java +++ b/vcell-core/src/main/java/org/jlibsedml/execution/ModelResolver.java @@ -3,16 +3,16 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jlibsedml.Model; -import org.jlibsedml.SEDMLDocument; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.SedMLDocument; /** @@ -24,17 +24,20 @@ public class ModelResolver { private final static Logger logger = LogManager.getLogger(ModelResolver.class); - public ModelResolver(SedML sedml) { + public ModelResolver(SedMLDataContainer sedml) { super(); this.sedml = sedml; } + static final String BASE_MODEL_CANNOT_BE_FOUND = "Provided model `%s` could not resolve to a \"base\" model."; + static final String SUB_MODEL_CANNOT_BE_FOUND = "Could not resolve provided model `%s`to its \"sub\" model."; static final String MODEL_CANNOT_BE_RESOLVED_MSG = " The model could not be resolved from its source reference. "; static final String MODEL_SRC_NOT_VALID_URI = "The model 'source' attribute is not a valid URI."; - private SedML sedml; + private final SedMLDataContainer sedml; private String message = ""; - private List resolvers = new ArrayList(); + private final List resolvers = new ArrayList<>(); + private final Map modelCache = new HashMap<>(); /** * Adds an {@link IModelResolver} to the list of those to be used to obtain @@ -43,12 +46,12 @@ public ModelResolver(SedML sedml) { * @param modelResolver */ public void add(IModelResolver modelResolver) { - resolvers.add(modelResolver); + this.resolvers.add(modelResolver); } String getMessage() { - return message; + return this.message; } /** @@ -64,114 +67,73 @@ String getMessage() { * @return A String of the model or null if no * model could be resolved */ - public String getReferenceModelString(Model m) { - List modelRefs = new ArrayList(); - - String baseModelRef = getBaseModelRef(m, modelRefs); - if (baseModelRef == null) { - return null; - } - // resolve base model if we've not already done so - URI srcURI = null; + public String getReferenceModelAsXMLString(Model m) { + this.sedml.getBaseModel(m.getId()); + String baseModelAsStr; try { - srcURI = sedml.getModelWithId(baseModelRef).getSourceURI(); - } catch (URISyntaxException e) { - - message = MODEL_SRC_NOT_VALID_URI; - return null; - } - - String baseModelAsStr = getBaseModel(srcURI); - if (baseModelAsStr == null) { - message = MODEL_CANNOT_BE_RESOLVED_MSG + "(Using uri: " + srcURI - + ")"; + baseModelAsStr = this.resolveURIToModelXMLAsString(m.getSourceURI()); + if (baseModelAsStr == null) throw new URISyntaxException(m.getSourceAsString(), "Unable to determine base model"); + } catch (URISyntaxException e){ + this.message = MODEL_CANNOT_BE_RESOLVED_MSG + "(Model queried: " + m + ")"; return null; } return baseModelAsStr; } - String getBaseModelRef(Model m, List modelRefs) { - getModelModificationTree(m, modelRefs); - if (modelRefs.isEmpty()) { - message = MODEL_CANNOT_BE_RESOLVED_MSG; - return null; - } - String baseModelRef = modelRefs.get(0); - return baseModelRef; - } - /** * The main public method of this class. * - * @param m + * @param startingModel * A SED-ML model element whose model we want to resolve * @return The model, with changes applied, or null if the * model could not be found. */ - public String getModelString(Model m) { - List modelRefs = new ArrayList(); - Map modelID2ModelStr = new HashMap(); - - String baseModelRef = getBaseModelRef(m, modelRefs); - // resolve base model if we've not already done so - URI srcURI = null; - try { - String modelRef = baseModelRef; - do { - org.jlibsedml.Model mod = sedml.getModelWithId(modelRef); - URI testURI = new URI("test%20model.xml"); - String roundTrip = testURI.toASCIIString(); - roundTrip = testURI.toString(); - srcURI = mod.getSourceURI(); - // If we need to recurse to the next level: - modelRef = sedml.getModelWithId(modelRef.startsWith("#") ? - modelRef.substring(1): modelRef).getSourcePathOrURIString(); - } while (srcURI != null && srcURI.toString().contains("#")); - } catch (URISyntaxException e) { - logger.error(MODEL_SRC_NOT_VALID_URI+": "+e.getMessage(), e); - message = MODEL_SRC_NOT_VALID_URI; - return null; - } - if (!modelID2ModelStr.containsKey(baseModelRef)) { - - String baseModelAsStr = getBaseModel(srcURI); - if (baseModelAsStr == null) { - // try again with relative path to sedml - try { - srcURI = new URI(sedml.getPathForURI() + srcURI.toString()); - } catch (URISyntaxException e) { - message = MODEL_SRC_NOT_VALID_URI; - return null; - } - baseModelAsStr = getBaseModel(srcURI); - if (baseModelAsStr == null) { - message = MODEL_CANNOT_BE_RESOLVED_MSG + "(Using uri: " - + srcURI + ")"; - return null; - } - } - modelID2ModelStr.put(srcURI.toString(), baseModelAsStr); + public String getXMLStringRepresentationOfModel(Model startingModel) { + // We need to do this level by level + SId modelID = startingModel.getId(); + // First, did we already do this? + if (this.modelCache.containsKey(modelID)) return this.modelCache.get(modelID); + + String resolvedModelStr; + // Okay, is this a base model? If so, we can just process it right away + if (!startingModel.getSourceAsString().startsWith("#")){ + resolvedModelStr = this.attemptToResolveModel(startingModel); + } else { + // Not a base model? Then we need to go one deeper! + Model subModel = this.sedml.getSubModel(modelID); + if (subModel == null) { + this.message = String.format(SUB_MODEL_CANNOT_BE_FOUND, modelID.string()); + logger.error(this.message); + return null; + } + resolvedModelStr = this.getXMLStringRepresentationOfModel(subModel); } - - // now apply model changes sequentially - String changedModel = applyModelChanges(modelRefs, - modelID2ModelStr.get(srcURI.toString())); + String changedModel = this.applyModelChanges(modelID, resolvedModelStr); + this.modelCache.put(modelID, changedModel); return changedModel; + } + /** + * Order of model refs must be from base to top! + * @param modelRefs + * @param unChangedModelAsXMLStr + * @return + */ + String applyModelChanges(List modelRefs, String unChangedModelAsXMLStr) { + for (SId modelRef : modelRefs) { + unChangedModelAsXMLStr = this.applyModelChanges(modelRef, unChangedModelAsXMLStr); + } + return unChangedModelAsXMLStr; } - String applyModelChanges(List modelRefs, String baseModelAsStr) { - for (int i = 0; i < modelRefs.size(); i++) { - try { - baseModelAsStr = new SEDMLDocument(sedml).getChangedModel( - modelRefs.get(i), baseModelAsStr); - } catch (Exception e) { - message = "Could not apply XPath changes for model id[" + modelRefs.get(i) + "]"; - logger.error(message, e); - throw new RuntimeException(message, e); - } + String applyModelChanges(SId modelRef, String unChangedModelAsXMLStr) { + try { + return SedMLDocument.getChangedModel(this.sedml, modelRef, unChangedModelAsXMLStr); + } catch (Exception e) { + String message = "Could not apply XPath changes for model id[" + modelRef + "]"; + logger.error( message, e); + throw new RuntimeException(message, e); } - return baseModelAsStr; } /** @@ -180,25 +142,36 @@ String applyModelChanges(List modelRefs, String baseModelAsStr) { * @return A String of the model XML, or null if * the model could not be found. */ - final String getBaseModel(URI modelSrc) { - for (IModelResolver resolver : resolvers) { + final String resolveURIToModelXMLAsString(URI modelSrc) { + for (IModelResolver resolver : this.resolvers) { String modelAsXML = resolver.getModelXMLFor(modelSrc); - if (modelAsXML != null) { - return modelAsXML; - } + if (modelAsXML == null) continue; + return modelAsXML; } return null; } - void getModelModificationTree(Model m, List modelRefs2) { - String modelSrcRef = m.getSourcePathOrURIString(); + private String attemptToResolveModel(Model m) { + URI source, relativeSource; + try { + source = m.getSourceURI(); + } catch (URISyntaxException ignored) { + this.message = MODEL_SRC_NOT_VALID_URI; + return null; + } - modelRefs2.add(m.getId()); - if (sedml.getModelWithId(modelSrcRef) != null) { - getModelModificationTree(sedml.getModelWithId(modelSrcRef), - modelRefs2); - } else { - Collections.reverse(modelRefs2); + String baseModelAsStr = this.resolveURIToModelXMLAsString(source); + if (baseModelAsStr != null) return baseModelAsStr; + // try again with relative path to sedml + try { + relativeSource = new URI(this.sedml.getPathForURI() + source); + } catch (URISyntaxException e) { + this.message = MODEL_SRC_NOT_VALID_URI; + return null; } + baseModelAsStr = this.resolveURIToModelXMLAsString(relativeSource); + if (baseModelAsStr != null) return baseModelAsStr; + this.message = MODEL_CANNOT_BE_RESOLVED_MSG + "(Using uri: " + source + ")"; + return null; } } diff --git a/vcell-core/src/main/java/org/jlibsedml/execution/SedMLResultsProcesser2.java b/vcell-core/src/main/java/org/jlibsedml/execution/SedMLResultsProcesser2.java index 2d02c40d5e..cc3c096015 100644 --- a/vcell-core/src/main/java/org/jlibsedml/execution/SedMLResultsProcesser2.java +++ b/vcell-core/src/main/java/org/jlibsedml/execution/SedMLResultsProcesser2.java @@ -9,13 +9,13 @@ import java.util.Map; import java.util.Set; -import org.jlibsedml.AbstractTask; -import org.jlibsedml.DataGenerator; -import org.jlibsedml.Output; -import org.jlibsedml.Parameter; -import org.jlibsedml.SedML; -import org.jlibsedml.Variable; -import org.jlibsedml.VariableSymbol; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.components.*; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.output.Output; import org.jlibsedml.execution.ExecutionStatusElement.ExecutionStatusType; import org.jlibsedml.modelsupport.SBMLSupport; import org.jmathml.ASTCi; @@ -60,16 +60,18 @@ * */ public class SedMLResultsProcesser2 { + private static final Logger logger = LogManager.getLogger(SedMLResultsProcesser2.class); - static final String MISSING_DG_MESSAGE = "Could not identify a data generator for dataset - missing out this data set: "; - static final String NO_DATACOLuMN_FORID_MSG = "Could not identify a data column for variable "; + static final String MISSING_DG_MESSAGE = "Could not find a matching data generator for dataset when searching for data generator: "; + static final String MISSING_TASK_MESSAGE = "Could not find a matching task for data generator when searching for task: "; + static final String NO_DATA_COLUMN_FOR_ID_MSG = "Could not identify a data column for variable "; static final String COULD_NOT_RESOLVE_MATHML_MSG = "Could not resolve the variables in the Mathml required for generating "; - static final String NO_DG_INOUTPUT_MSG = "No data generators listed in output"; + static final String NO_DG_IN_OUTPUT_MSG = "No data generators listed in output"; static final String COULD_NOT_EXECUTE_MATHML_FOR = "Math could not be executed for data generator "; - private SedML sedml; - private Output output; + private final SedMLDataContainer sedml; + private final Output output; private IProcessedSedMLSimulationResults toReturn; - private ProcessReport report = new ProcessReport(); + private final ProcessReport report = new ProcessReport(); private IXPathToVariableIDResolver variable2IDResolver = new SBMLSupport(); /** @@ -78,8 +80,8 @@ public class SedMLResultsProcesser2 { * @author radams * */ - public class ProcessReport { - List messages = new ArrayList(); + public static class ProcessReport { + List messages = new ArrayList<>(); /** * Returns an unmodifiable List of messages. This list may @@ -88,7 +90,7 @@ public class ProcessReport { * @return A ListString of messages. */ public List getMessages() { - return Collections.unmodifiableList(messages); + return Collections.unmodifiableList(this.messages); } } @@ -104,26 +106,18 @@ public List getMessages() { * sedml argument. * @throws IllegalArgumentException * if any argument is null or if the output does - * not belong to the {@link SedML} object ( based on the output + * not belong to the {@link SedMLDataContainer} object ( based on the output * id ). */ - public SedMLResultsProcesser2(final SedML sedml, final Output output) { + public SedMLResultsProcesser2(final SedMLDataContainer sedml, final Output output) { super(); if (sedml == null || output == null) { throw new IllegalArgumentException(); } this.sedml = sedml; this.output = output; - boolean found = false; - for (Output o : sedml.getOutputs()) { - if (o.getId().equals(output.getId())) { - found = true; - } - } - if (!found) { - throw new IllegalArgumentException("Output [" + output.getId() - + "] does not belong the SED-ML object. "); - } + if (null != this.sedml.findOutputById(output.getId())) return; + throw new IllegalArgumentException("Output [" + output.getId() + "] does not belong the SED-ML object. "); } /** @@ -146,67 +140,65 @@ public SedMLResultsProcesser2(final SedML sedml, final Output output) { * if results is null. */ public void process(Map results) { - if (results == null) { - throw new IllegalArgumentException(); - } - Map rawTask2Results = new HashMap(); - int numRows = 0; - numRows = makeDefensiveCopyOfData(results, rawTask2Results, numRows); - - List processed = new ArrayList(); - if (output.getAllDataGeneratorReferences().isEmpty()) { - report.messages.add(new ExecutionStatusElement(null, - NO_DG_INOUTPUT_MSG, ExecutionStatusType.ERROR)); + if (results == null) throw new IllegalArgumentException(); + + Map rawTask2Results = new HashMap<>(); + int numRows = this.makeDefensiveCopyOfData(results, rawTask2Results, 0); + + List processed = new ArrayList<>(); + if (this.output.getAllDataGeneratorReferences().isEmpty()) { + this.report.messages.add(new ExecutionStatusElement(null, + NO_DG_IN_OUTPUT_MSG, ExecutionStatusType.ERROR)); return; } - for (String dgId : output.getAllDataGeneratorReferences()) { + for (SId dgId : this.output.getAllDataGeneratorReferences()) { double[] mutated = new double[numRows]; processed.add(mutated); - DataGenerator dg = sedml.getDataGeneratorWithId(dgId); - if (dg == null) { - report.messages.add(new ExecutionStatusElement(null, - MISSING_DG_MESSAGE + dgId, ExecutionStatusType.ERROR)); + DataGenerator dg = this.sedml.findDataGeneratorById(dgId); + if (null == dg) { + this.report.messages.add(new ExecutionStatusElement(null, MISSING_DG_MESSAGE + dgId.string(), ExecutionStatusType.ERROR)); return; } - List vars = dg.getListOfVariables(); - List params = dg.getListOfParameters(); - Map Var2Model = new HashMap(); - Map var2Result = new HashMap(); - Map var2Data = new HashMap(); - String timeID = ""; + List vars = dg.getVariables(); + List params = dg.getParameters(); + Map variableToTargetSource = new HashMap<>(); + Map variableToResults = new HashMap<>(); + Map variableToData = new HashMap<>(); + SId timeID = null; // map varIds to result, based upon task reference for (Variable variable : vars) { String modelID; - if (variable.isVariable()) { + if (variable.referencesXPath()) { // get the task from which this result variable was // generated. - modelID = variable2IDResolver.getIdFromXPathIdentifer(variable - .getTarget()); - String taskRef = variable.getReference(); - AbstractTask t = sedml.getTaskWithId(taskRef); + modelID = this.variable2IDResolver.getIdFromXPathIdentifer(variable.getTarget()); + SId taskRef = variable.getTaskReference(); + AbstractTask t = this.sedml.findAbstractTaskById(taskRef); + if (null == t){ + this.report.messages.add(new ExecutionStatusElement(null, MISSING_TASK_MESSAGE + taskRef.string(), ExecutionStatusType.ERROR)); + return; + } + // get results for this task IRawSedmlSimulationResults res = results.get(t); // set up lookups to results, raw data and model ID - var2Result.put(variable.getId(), res); - var2Data.put(variable.getId(), rawTask2Results.get(t)); - Var2Model.put(variable.getId(), modelID); + variableToResults.put(variable.getId(), res); + variableToData.put(variable.getId(), rawTask2Results.get(t)); + variableToTargetSource.put(variable.getId(), modelID); // it's a symbol - } else if (variable.isSymbol() - && variable.getSymbol().equals(VariableSymbol.TIME)) { + } else if (variable.isSymbol() && variable.getSymbol().equals(VariableSymbol.TIME)) { timeID = variable.getId(); - var2Data.put(variable.getId(), rawTask2Results.values().iterator() - .next()); - Var2Model.put(variable.getId(), variable.getId()); - + variableToData.put(variable.getId(), rawTask2Results.values().iterator().next()); + variableToTargetSource.put(variable.getId(), variable.getId().toString()); } } // get Parameter values - Map Param2Value = new HashMap(); + Map parameterToValue = new HashMap<>(); for (Parameter p : params) { - Param2Value.put(p.getId(), p.getValue()); + parameterToValue.put(p.getId(), p.getValue()); } // now parse maths, and replace raw simulation results with // processed results. @@ -214,28 +206,22 @@ public void process(Map results) { Set identifiers = node.getIdentifiers(); for (ASTCi var : identifiers) { if (var.isVector()) { - String varName = var.getName(); - IModel2DataMappings coll = var2Result.get(varName) - .getMappings(); - int otherVarInx = coll.getColumnIndexFor(Var2Model - .get(varName)); - if (otherVarInx < 0 - || otherVarInx >= var2Result.get(varName) - .getNumColumns()) { - report.messages.add(new ExecutionStatusElement(null, - NO_DATACOLuMN_FORID_MSG + var, - ExecutionStatusType.ERROR)); + SId varId = new SId(var.getName()); + IModel2DataMappings coll = variableToResults.get(varId).getMappings(); + int otherVarInx = coll.getColumnIndexFor(variableToTargetSource.get(varId)); + if (otherVarInx < 0 || otherVarInx >= variableToResults.get(varId).getNumColumns()) { + this.report.messages.add(new ExecutionStatusElement(null, NO_DATA_COLUMN_FOR_ID_MSG + var, ExecutionStatusType.ERROR)); return; } EvaluationContext con = new EvaluationContext(); - Double[] data = var2Result.get(varName) + Double[] data = variableToResults.get(varId) .getDataByColumnIndex(otherVarInx); - con.setValueFor(varName, Arrays.asList(data)); + con.setValueFor(varId.string(), Arrays.asList(data)); if (var.getParentNode() == null || var.getParentNode().getParentNode() == null) { - report.messages + this.report.messages .add(new ExecutionStatusElement( null, "Could not evaluate [" @@ -245,7 +231,7 @@ public void process(Map results) { return; } if (!var.getParentNode().canEvaluate(con)) { - report.messages.add(new ExecutionStatusElement(null, + this.report.messages.add(new ExecutionStatusElement(null, "Could not evaluate [" + var + "] ", ExecutionStatusType.ERROR)); return; @@ -257,38 +243,38 @@ public void process(Map results) { } } // identifiers.add(var.getSpId()); - if (identifiersMapToData(identifiers, Var2Model, Param2Value, - var2Result, timeID)) { + if (this.identifiersMapToData(identifiers, variableToTargetSource, parameterToValue, + variableToResults, timeID == null ? "" : timeID.string())) { for (int i = 0; i < numRows; i++) { EvaluationContext con = new EvaluationContext(); - for (String id : Param2Value.keySet()) { - con.setValueFor(id, Param2Value.get(id)); + for (SId id : parameterToValue.keySet()) { + con.setValueFor(id.string(), parameterToValue.get(id)); } for (ASTCi var : identifiers) { // we've already resolved parameters - if (Param2Value.get(var.getName()) != null) { + if (parameterToValue.get(var.getName()) != null) { continue; } int otherVarInx = 0; if (!var.getName().equals(timeID)) { - IModel2DataMappings coll = var2Result.get( + IModel2DataMappings coll = variableToResults.get( var.getName()).getMappings(); - otherVarInx = coll.getColumnIndexFor(Var2Model + otherVarInx = coll.getColumnIndexFor(variableToTargetSource .get(var.getName())); if (otherVarInx < 0 - || otherVarInx >= var2Result.get( + || otherVarInx >= variableToResults.get( var.getName()).getNumColumns()) { report.messages.add(new ExecutionStatusElement( - null, NO_DATACOLuMN_FORID_MSG + var, + null, NO_DATA_COLUMN_FOR_ID_MSG + var, ExecutionStatusType.ERROR)); return; } } con.setValueFor(var.getName(), - var2Data.get(var.getName())[i][otherVarInx]); + variableToData.get(var.getName())[i][otherVarInx]); } if (node.canEvaluate(con)) { @@ -350,9 +336,8 @@ private IProcessedSedMLSimulationResults createData( String[] hdrs = new String[processed.size()]; int colInd = 0; - for (Iterator it = output.getAllDataGeneratorReferences() - .iterator(); it.hasNext();) { - hdrs[colInd++] = it.next(); + for (SId dgRef : this.output.getAllDataGeneratorReferences()){ + hdrs[colInd++] = dgRef.string(); } double[][] data = new double[NumRows][hdrs.length]; @@ -367,8 +352,8 @@ private IProcessedSedMLSimulationResults createData( } private boolean identifiersMapToData(Set identifiers, - Map Var2Model, Map Param2Value, - Map var2Result, String timeID) { + Map Var2Model, Map Param2Value, + Map var2Result, String timeID) { for (ASTCi var : identifiers) { boolean seen = false; diff --git a/vcell-core/src/main/java/org/jlibsedml/extensions/ElementSearchVisitor.java b/vcell-core/src/main/java/org/jlibsedml/extensions/ElementSearchVisitor.java index ed66a30feb..26b14c93b8 100644 --- a/vcell-core/src/main/java/org/jlibsedml/extensions/ElementSearchVisitor.java +++ b/vcell-core/src/main/java/org/jlibsedml/extensions/ElementSearchVisitor.java @@ -1,30 +1,8 @@ package org.jlibsedml.extensions; -import org.jlibsedml.AbstractIdentifiableElement; -import org.jlibsedml.AddXML; -import org.jlibsedml.Algorithm; -import org.jlibsedml.ChangeAttribute; -import org.jlibsedml.ChangeXML; -import org.jlibsedml.ComputeChange; -import org.jlibsedml.Curve; -import org.jlibsedml.DataGenerator; -import org.jlibsedml.DataSet; -import org.jlibsedml.FunctionalRange; -import org.jlibsedml.Model; -import org.jlibsedml.Output; -import org.jlibsedml.Parameter; -import org.jlibsedml.RemoveXML; -import org.jlibsedml.RepeatedTask; -import org.jlibsedml.SEDBase; +import org.jlibsedml.components.SedBase; import org.jlibsedml.SEDMLVisitor; -import org.jlibsedml.SedML; -import org.jlibsedml.SetValue; -import org.jlibsedml.Simulation; -import org.jlibsedml.Surface; -import org.jlibsedml.Task; -import org.jlibsedml.UniformRange; -import org.jlibsedml.Variable; -import org.jlibsedml.VectorRange; + /** * This class searches the object structure for an element with its value of an id attribute * equal to that passed into this object's constructor. @@ -33,6 +11,8 @@ */ public class ElementSearchVisitor extends SEDMLVisitor { + String searchTerm; + SedBase foundElement; /** * @param searchTerm A non-null String of an element identifier with which to search the object structure @@ -42,156 +22,38 @@ public ElementSearchVisitor(String searchTerm) { this.searchTerm = searchTerm; } - String searchTerm; - SEDBase foundElement; - @Override - public boolean visit(SedML sedml) { - return true; - } - - @Override - public boolean visit(Simulation sim) { - return checkID(sim); - } - - @Override - public boolean visit(Model model) { - return checkID(model); - } - - @Override - public boolean visit(Task task) { - return checkID(task); - } - - - @Override - public boolean visit(DataGenerator dg) { - return checkID(dg); - - - } - - @Override - public boolean visit(Variable var) { - return checkID(var); - } - - @Override - public boolean visit(Parameter param) { - return checkID(param); - } - - @Override - public boolean visit(Output output) { - return checkID(output); - } - - // returns boolean to visit() methods - 'true' means 'keep searching' - // false means ' found element with that id, stop search' - boolean checkID(AbstractIdentifiableElement aie) { - if(searchTerm.equals(aie.getId())){ - foundElement=aie; - return false; - } - return true; - } - - @Override - public boolean visit(Algorithm algorithm) { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean visit(Curve curve) { - return checkID(curve); - } - - @Override - public boolean visit(DataSet dataSet) { - return checkID(dataSet); - } - - @Override - public boolean visit(Surface surface) { - return checkID(surface); - } - /** * Gets either the found element, or null if no element was found * with the ID specified in the constructor.
* Before calling accept(), this method will always return null. - * @return A {@link SEDBase} or null. + * @return A {@link SedBase} or null. */ - public SEDBase getFoundElement(){ - return foundElement; + public SedBase getFoundElement(){ + return this.foundElement; + } + + // returns boolean to visit() methods + boolean checkID(SedBase aie) { + // `true` => 'keep searching' + // `false` => 'found element with that id, stop search' + if(!this.searchTerm.equals(aie.getId())) return true; + this.foundElement = aie; + return false; } - + /** - * Resets the search. After calling this method, + * Resets the search. After calling this method, *
      * getFoundElement()
      * 
* will return null. */ public void clear(){ - foundElement=null; - } - - @Override - public boolean visit(AddXML change) { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean visit(RemoveXML change) { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean visit(ChangeXML change) { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean visit(ChangeAttribute change) { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean visit(ComputeChange change) { - // TODO Auto-generated method stub - return true; - } - - @Override - public boolean visit(SetValue setValue) { - // TODO Auto-generated method stub - return true; - } - @Override - public boolean visit(RepeatedTask repeatedTask) { - return checkID(repeatedTask); - } - - @Override - public boolean visit(UniformRange uniformRange) { - return checkID(uniformRange); - } - - @Override - public boolean visit(VectorRange vectorRange) { - return checkID(vectorRange); + this.foundElement = null; } - @Override - public boolean visit(FunctionalRange functionalRange) { - return checkID(functionalRange); + public boolean visit(SedBase sedBase){ + return this.checkID(sedBase); } } diff --git a/vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java b/vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java index 406def2e40..287db974c2 100644 --- a/vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java +++ b/vcell-core/src/main/java/org/jlibsedml/modelsupport/SBMLSupport.java @@ -3,7 +3,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.jlibsedml.SEDMLDocument; +import org.jlibsedml.SedMLDocument; import org.jlibsedml.execution.IXPathToVariableIDResolver; /** @@ -16,7 +16,7 @@ *

*

* This class does not apply XPath changes, that is achieved through - * {@link SEDMLDocument}:
+ * {@link SedMLDocument}:
* String getChangedModel(String model_ID, final String originalModel); * * @author radams diff --git a/vcell-core/src/main/java/org/jlibsedml/modelsupport/ToolSupport.java b/vcell-core/src/main/java/org/jlibsedml/modelsupport/ToolSupport.java index f7dfa88106..7f4ee8f605 100644 --- a/vcell-core/src/main/java/org/jlibsedml/modelsupport/ToolSupport.java +++ b/vcell-core/src/main/java/org/jlibsedml/modelsupport/ToolSupport.java @@ -2,7 +2,7 @@ import java.util.HashMap; -import org.jlibsedml.SEDMLTags; +import org.jlibsedml.SedMLTags; /** List of constants useful in this package (model type, simulator type, etc.) * @@ -22,9 +22,9 @@ private ToolSupport(){} public static HashMap nameSpaces_PrefixesHashMap = new HashMap(); static { - nameSpaces_PrefixesHashMap.put(SEDMLTags.SBML_NS_PREFIX, SEDMLTags.SBML_NS); - nameSpaces_PrefixesHashMap.put(SEDMLTags.SBML_NS_PREFIX, SEDMLTags.SBML_NS_L2V4); - nameSpaces_PrefixesHashMap.put(SEDMLTags.MATHML_NS_PREFIX, SEDMLTags.MATHML_NS); + nameSpaces_PrefixesHashMap.put(SedMLTags.SBML_NS_PREFIX, SedMLTags.SBML_NS); + nameSpaces_PrefixesHashMap.put(SedMLTags.SBML_NS_PREFIX, SedMLTags.SBML_NS_L2V4); + nameSpaces_PrefixesHashMap.put(SedMLTags.MATHML_NS_PREFIX, SedMLTags.MATHML_NS); } } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/AbstractDocumentValidator.java b/vcell-core/src/main/java/org/jlibsedml/validation/AbstractDocumentValidator.java index 6a4262490b..1e611034c7 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/AbstractDocumentValidator.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/AbstractDocumentValidator.java @@ -11,21 +11,19 @@ * */ abstract class AbstractDocumentValidator implements ISedMLValidator { + private final Document doc; AbstractDocumentValidator(Document doc) { super(); this.doc = doc; } - private Document doc; - Document getDoc() { - return doc; + return this.doc; } int getLineNumberOfError(String elementKind, IIdentifiable identifiable) { - int line = new LineFinderUtil().getLineForElement(elementKind, - identifiable.getId(), getDoc()); - return line; + return new LineFinderUtil().getLineForElement(elementKind, + identifiable.getId(), this.getDoc()); } } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/KisaoIDValidator.java b/vcell-core/src/main/java/org/jlibsedml/validation/KisaoIDValidator.java index b87966647d..a031ea3a88 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/KisaoIDValidator.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/KisaoIDValidator.java @@ -4,9 +4,9 @@ import java.util.List; import org.jdom2.Document; -import org.jlibsedml.SEDMLTags; +import org.jlibsedml.SedMLTags; import org.jlibsedml.SedMLError; -import org.jlibsedml.Simulation; +import org.jlibsedml.components.simulation.Simulation; import org.jlibsedml.modelsupport.KisaoOntology; /** @@ -31,7 +31,7 @@ public List validate() { for (Simulation sim: sims){ String kisaoID = sim.getAlgorithm().getKisaoID(); if(KisaoOntology.getInstance().getTermById(kisaoID) == null){ - int line = getLineNumberOfError(SEDMLTags.SIMUL_UTC_KIND,sim); + int line = getLineNumberOfError(SedMLTags.SIMUL_UTC_KIND, sim); errs.add(new SedMLError(line, " The supplied KisaoID [" + kisaoID +"] for simulation [" + sim.getId() +"] is not a recognized KISAO identifier.\n" + " Identifiers should be the format 'KISAO:0000001' ", SedMLError.ERROR_SEVERITY.WARNING)); } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/LineFinderUtil.java b/vcell-core/src/main/java/org/jlibsedml/validation/LineFinderUtil.java index 3f73f05a20..874f09e63d 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/LineFinderUtil.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/LineFinderUtil.java @@ -3,49 +3,45 @@ import java.util.Iterator; import org.jdom2.Document; +import org.jdom2.Element; import org.jdom2.filter.ElementFilter; import org.jdom2.located.LocatedElement; -import org.jlibsedml.SEDMLTags; +import org.jlibsedml.SedMLTags; +import org.jlibsedml.components.SId; class LineFinderUtil { - private Document doc; - - Document getDoc() { - return doc; - } - - void setDoc(Document doc) { - this.doc = doc; - } - - int getLineForElement( String elName, String elID, Document doc) - { - for (Iterator iter = doc.getDescendants(new ElementFilter()); iter - .hasNext();) { - LocatedElement e = (LocatedElement) iter.next(); - if (e.getName().equals(elName) - && e.getAttribute(SEDMLTags.MODEL_ATTR_ID) != null - && e.getAttribute(SEDMLTags.MODEL_ATTR_ID).getValue().equals(elID)) { - return e.getLine(); - } - - } - return 0; - } - - int getLineForMathElement( String elName, String elID, Document doc) - { -for (Iterator iter = doc.getDescendants(new ElementFilter()); iter - .hasNext();) { - LocatedElement e = (LocatedElement) iter.next(); - if (e.getName().equals(elName) - && e.getAttribute(SEDMLTags.MODEL_ATTR_ID) != null - && e.getAttribute(SEDMLTags.MODEL_ATTR_ID).getValue().equals(elID)) { - return e.getLine(); - } - -} -return 0; -} + private Document doc; + + Document getDoc() { + return this.doc; + } + + void setDoc(Document doc) { + this.doc = doc; + } + + int getLineForElement(String elName, SId elID, Document doc) { + for (Element element : doc.getDescendants(new ElementFilter())){ + if (!(element instanceof LocatedElement locatedElement)) continue; + if (locatedElement.getName().equals(elName) + && locatedElement.getAttribute(SedMLTags.MODEL_ATTR_ID) != null + && locatedElement.getAttributeValue(SedMLTags.MODEL_ATTR_ID).equals(elID.string())) { + return locatedElement.getLine(); + } + } + return 0; + } + + int getLineForMathElement(String elName, String elID, Document doc) { + for (Element element : doc.getDescendants(new ElementFilter())){ + if (!(element instanceof LocatedElement locatedElement)) continue; + if (locatedElement.getName().equals(elName) + && locatedElement.getAttribute(SedMLTags.MODEL_ATTR_ID) != null + && locatedElement.getAttribute(SedMLTags.MODEL_ATTR_ID).getValue().equals(elID)) { + return locatedElement.getLine(); + } + } + return 0; + } } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/MathMLValidator.java b/vcell-core/src/main/java/org/jlibsedml/validation/MathMLValidator.java index 768e83bc56..8edc1332b0 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/MathMLValidator.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/MathMLValidator.java @@ -6,11 +6,11 @@ import org.jdom2.Document; import org.jlibsedml.IIdentifiable; -import org.jlibsedml.IMathContainer; -import org.jlibsedml.Parameter; +import org.jlibsedml.components.Calculation; +import org.jlibsedml.components.Parameter; import org.jlibsedml.SedMLError; import org.jlibsedml.SedMLError.ERROR_SEVERITY; -import org.jlibsedml.Variable; +import org.jlibsedml.components.Variable; import org.jmathml.ASTCi; import org.jmathml.ASTNode; import org.jmathml.EvaluationContext; @@ -20,87 +20,89 @@ *

  • An MathML element has its variables defined in ListOfVariables and ListOfParameters *
  • A node can be evaluated to a numerical value. * + * * @author radams * */ - public class MathMLValidator extends AbstractDocumentValidator implements ISedMLValidator{ - - private IMathContainer mathContainer; - - /** - * - * @param document - * @param mathElement A non-null {@link IMathContainer} - */ - MathMLValidator(Document document, IMathContainer mathElement) { +public class MathMLValidator extends AbstractDocumentValidator implements ISedMLValidator { + + private final Calculation sedCalculation; + + /** + * + * @param document + * @param mathElement A non-null {@link Calculation} + */ + MathMLValidator(Document document, Calculation mathElement) { super(document); - this.mathContainer=mathElement; + this.sedCalculation = mathElement; + } + + /** + * Alternative constructor taking a single {@link Calculation} object + * + * @param mathElement An {@link Calculation} object. + */ + public MathMLValidator(Calculation mathElement) { + super(null); + this.sedCalculation = mathElement; } - /** - * Alternative constructor taking a single {@link IMathContainer} object - * @param mathElement An {@link IMathContainer} object. - */ - public MathMLValidator( IMathContainer mathElement) { - super(null); - this.mathContainer=mathElement; - } /** * Checks that values of <ci> elements in a MathML block are defined in - * {@link Variable} or {@link Parameter} declarations in this object. + * {@link Variable} or {@link Parameter} declarations in this object. + * * @see ISedMLValidator */ - public List validate() { - List errors= checkMathIds(); - errors.addAll(checkFunctions()); + public List validate() { + List errors = this.checkMathIds(); + errors.addAll(this.checkFunctions()); return errors; } - - private List checkFunctions() { + + private List checkFunctions() { List errors = new ArrayList(); - ASTNode node = mathContainer.getMath(); + ASTNode node = this.sedCalculation.getMath(); EvaluationContext cont = new EvaluationContext(); - for (IIdentifiable id: mathContainer.getListOfParameters()){ - cont.setValueFor(id.getId(), 0.0); + for (IIdentifiable id : this.sedCalculation.getParameters()) { + cont.setValueFor(id.getId().string(), 0.0); } - for (IIdentifiable id: mathContainer.getListOfVariables()){ - cont.setValueFor(id.getId(), 0.0); + for (IIdentifiable id : this.sedCalculation.getVariables()) { + cont.setValueFor(id.getId().string(), 0.0); } - - if(!node.canEvaluate(cont)){ - errors.add(new SedMLError(0, "This node ["+ node.getName() + "] cannot be evaluated",ERROR_SEVERITY.WARNING)); + + if (!node.canEvaluate(cont)) { + errors.add(new SedMLError(0, "This node [" + node.getName() + "] cannot be evaluated", ERROR_SEVERITY.WARNING)); } return errors; - + } private List checkMathIds() { List errors = new ArrayList(); - - List vars = mathContainer.getListOfVariables(); - List params = mathContainer.getListOfParameters(); - Set identifiers = mathContainer.getMath().getIdentifiers(); - for (ASTCi var : identifiers) { - if (!(check(var, vars) || check( - var, params))) { - errors - .add(new SedMLError( - 0, - "Math ml in [" + mathContainer.getId()+"] refers to a variable not defined in the model", - ERROR_SEVERITY.WARNING)); - } - } - - - + + List vars = this.sedCalculation.getVariables(); + List params = this.sedCalculation.getParameters(); + Set identifiers = this.sedCalculation.getMath().getIdentifiers(); + for (ASTCi var : identifiers) { + if (!(this.check(var, vars) || this.check( + var, params))) { + errors + .add(new SedMLError( + 0, + "Math ml in [" + this.sedCalculation.getId() + "] refers to a variable not defined in the model", + ERROR_SEVERITY.WARNING)); + } + } + return errors; } - private boolean check(ASTCi var2, List vars) { + private boolean check(ASTCi var2, List vars) { for (IIdentifiable var : vars) { - if (var.getId().equals(var2.getName())) { + if (var.getId().string().equals(var2.getName())) { return true; } } @@ -108,6 +110,5 @@ private boolean check(ASTCi var2, List vars) { } - } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/ModelCyclesDetector.java b/vcell-core/src/main/java/org/jlibsedml/validation/ModelCyclesDetector.java index 64c2af3e0f..f662fd28c4 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/ModelCyclesDetector.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/ModelCyclesDetector.java @@ -6,9 +6,12 @@ import java.util.Set; import org.jdom2.Document; -import org.jlibsedml.Model; -import org.jlibsedml.SEDMLTags; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.SedMLTags; import org.jlibsedml.SedMLError; import org.jlibsedml.SedMLError.ERROR_SEVERITY; import org.jlibsedml.XMLException; @@ -25,9 +28,9 @@ * is invalid as both models use each other as source references. */ public class ModelCyclesDetector extends AbstractDocumentValidator { - private SedML sedml; + private final SedMLDataContainer sedml; - public ModelCyclesDetector(SedML sedml, Document doc) { + public ModelCyclesDetector(SedMLDataContainer sedml, Document doc) { super(doc); this.sedml = sedml; } @@ -36,29 +39,29 @@ public ModelCyclesDetector(SedML sedml, Document doc) { * @see ISedMLValidator */ public List validate() throws XMLException { - List errs = new ArrayList(); - List models = sedml.getModels(); + SedML sedML = this.sedml.getSedML(); + List errs = new ArrayList<>(); + List models = sedML.getModels(); for (Model model : models) { - String src = model.getSourcePathOrURIString(); - String id = model.getId(); - Set ids = new HashSet(); - ids.add(id); - while (sedml.getModelWithId(src) != null) { - String newID = sedml.getModelWithId(src).getId(); - if (ids.contains(newID)) { - int line = getLineNumberOfError(SEDMLTags.MODEL_TAG, model); - errs.add(new SedMLError(line, - "Cycles detected in source references for model " - + newID + " and " - + sedml.getModelWithId(newID).getSourcePathOrURIString(), - ERROR_SEVERITY.ERROR)); - return errs; - } else { - ids.add(newID); - src = sedml.getModelWithId(src).getSourcePathOrURIString(); - } - } + SedMLError err = this.detectCycles(model); + if (err == null) continue; + errs.add(err); } return errs; } + + private SedMLError detectCycles(Model model) { + Set ids = new HashSet<>(); + Model currentModel = model; + while (currentModel != null) { + if (ids.contains(currentModel.getId())){ + int line = this.getLineNumberOfError(SedMLTags.MODEL_TAG, model); + String message = "Cycle detected with model: " + currentModel.getIdAsString(); + return new SedMLError(line, message, ERROR_SEVERITY.ERROR); + } + ids.add(currentModel.getId()); + currentModel = this.sedml.getSubModel(model.getId()); + } + return null; + } } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/SEDMLSchemaValidator.java b/vcell-core/src/main/java/org/jlibsedml/validation/SEDMLSchemaValidator.java index 48d187aea5..836e785d23 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/SEDMLSchemaValidator.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/SEDMLSchemaValidator.java @@ -3,8 +3,8 @@ import java.util.ArrayList; import java.util.List; -import org.jlibsedml.SEDMLDocument; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.SedMLDocument; import org.jlibsedml.SedMLError; import org.jlibsedml.XMLException; /** @@ -14,13 +14,13 @@ */ public class SEDMLSchemaValidator implements ISedMLValidator { - private final SedML sedml; + private final SedMLDataContainer sedml; /** - * @param sedml A non-null {@link SedML} object. + * @param sedml A non-null {@link SedMLDataContainer} object. * @throws IllegalArgumentException if sedml is null. */ - public SEDMLSchemaValidator(SedML sedml) { + public SEDMLSchemaValidator(SedMLDataContainer sedml) { super(); if( sedml ==null){ throw new IllegalArgumentException(); @@ -34,7 +34,7 @@ public SEDMLSchemaValidator(SedML sedml) { */ public List validate() throws XMLException { final List errors = new ArrayList(); - String xmlAsString = new SEDMLDocument(sedml).writeDocumentToString(); + String xmlAsString = new SedMLDocument(sedml).writeDocumentToString(); new SchemaValidatorImpl().validateSedMLString(errors, xmlAsString); return errors; } 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 ac3f8035e2..f24b5bb069 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/SchematronValidator.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/SchematronValidator.java @@ -29,11 +29,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jdom2.Document; -import org.jlibsedml.SEDMLDocument; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.SedMLDocument; import org.jlibsedml.SedMLError; import org.jlibsedml.SedMLError.ERROR_SEVERITY; import org.jlibsedml.XMLException; +import org.jlibsedml.components.SId; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -50,10 +51,10 @@ public class SchematronValidator extends AbstractDocumentValidator { private static final String SVRL_NS_PREFIX = "svrl"; private static final String SCHEMATRON_NS_URI = "http://purl.oclc.org/dsdl/svrl"; - private SedML sedml; + private SedMLDataContainer sedml; XPathFactory xpf = XPathFactory.newInstance(); - public SchematronValidator(Document doc, SedML sedml) { + public SchematronValidator(Document doc, SedMLDataContainer sedml) { super(doc); this.sedml = sedml; } @@ -61,7 +62,7 @@ public SchematronValidator(Document doc, SedML sedml) { public List validate() throws XMLException { // first of all, get Validation report from Schematron stylesheets. - SEDMLDocument seddoc = new SEDMLDocument(sedml); + SedMLDocument seddoc = new SedMLDocument(sedml); List rc = new ArrayList(); String docAsString = seddoc.writeDocumentToString(); String schematron = getSchematronXSL(); @@ -111,12 +112,12 @@ public List validate() throws XMLException { } private String getSchematronXSL() { - if (sedml.isL1V1()) { + if (this.sedml.isL1V1()) { return "validatorl1v1.xsl"; - } else if (sedml.isL1V2()) { + } else if (this.sedml.isL1V2()) { return "validatorl1v2.xsl"; } else { - lg.warn("Unsupported sedml version `L{}V{}` detected, validating as L1V2", sedml.getLevel(), sedml.getVersion()); + lg.warn("Unsupported sedml version `L{}V{}` detected, validating as L1V2", this.sedml.getSedML().getLevel(), this.sedml.getSedML().getVersion()); return "validatorl1v2.xsl"; // throw new UnsupportedOperationException(MessageFormat.format( // "Invalid level and version - {0}-{1}", sedml.getLevel(), @@ -138,9 +139,7 @@ String getMessageFromAssertFailedNode(NodeList nodes, int i) { int getLineNumber(Node sedmlNode) { LineFinderUtil util = new LineFinderUtil(); - int num = util.getLineForElement(sedmlNode.getLocalName(), sedmlNode - .getAttributes().getNamedItem("id").getNodeValue(), getDoc()); - return num; + return util.getLineForElement(sedmlNode.getLocalName(), new SId(sedmlNode.getAttributes().getNamedItem("id").getNodeValue()), this.getDoc()); } private NodeList getSedmlNodes(String locationInSEDMLXPath, String st) { diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/SemanticValidationManager.java b/vcell-core/src/main/java/org/jlibsedml/validation/SemanticValidationManager.java index 175dd39da2..4e988a1c4e 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/SemanticValidationManager.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/SemanticValidationManager.java @@ -15,32 +15,35 @@ limitations under the License. */ + import java.util.ArrayList; import java.util.List; import org.jdom2.Document; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; import org.jlibsedml.SedMLError; /** * Main point of access for validating the model for semantic consistency. + * * @author radams * */ - class SemanticValidationManager implements ISedMLValidator{ - private SedML sedml; - private Document doc; - public SemanticValidationManager(SedML sedml, Document doc) { - this.sedml=sedml; - this.doc=doc; - } - - public List validate () { - List errs = new ArrayList(); - errs.addAll(new KisaoIDValidator(sedml.getSimulations(),doc).validate()); - errs.addAll(new URIValidator(sedml.getModels()).validate()); - return errs; - - } +class SemanticValidationManager implements ISedMLValidator { + private final SedMLDataContainer sedml; + private final Document doc; + + public SemanticValidationManager(SedMLDataContainer sedml, Document doc) { + this.sedml = sedml; + this.doc = doc; + } + + public List validate() { + List errs = new ArrayList(); + errs.addAll(new KisaoIDValidator(this.sedml.getSedML().getSimulations(), this.doc).validate()); + errs.addAll(new URIValidator(this.sedml.getSedML().getModels()).validate()); + return errs; + + } } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/URIValidator.java b/vcell-core/src/main/java/org/jlibsedml/validation/URIValidator.java index 2c5d186d68..1c76af0bd1 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/URIValidator.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/URIValidator.java @@ -5,7 +5,7 @@ import java.util.ArrayList; import java.util.List; -import org.jlibsedml.Model; +import org.jlibsedml.components.model.Model; import org.jlibsedml.SedMLError; import org.jlibsedml.SedMLError.ERROR_SEVERITY; /** @@ -32,7 +32,7 @@ public List validate() { try { URI uri = model.getSourceURI(); } catch (URISyntaxException e) { - errs.add(new SedMLError(0,"ErrMessageRoot[" + model.getSourcePathOrURIString() +"]", ERROR_SEVERITY.WARNING )); + errs.add(new SedMLError(0,"ErrMessageRoot[" + model.getSourceAsString() +"]", ERROR_SEVERITY.WARNING )); } diff --git a/vcell-core/src/main/java/org/jlibsedml/validation/ValidatorController.java b/vcell-core/src/main/java/org/jlibsedml/validation/ValidatorController.java index 91a36fccde..2e517c98d7 100644 --- a/vcell-core/src/main/java/org/jlibsedml/validation/ValidatorController.java +++ b/vcell-core/src/main/java/org/jlibsedml/validation/ValidatorController.java @@ -4,14 +4,14 @@ import java.util.List; import org.jdom2.Document; -import org.jlibsedml.SEDMLDocument; -import org.jlibsedml.SedML; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.SedMLDocument; import org.jlibsedml.SedMLError; import org.jlibsedml.XMLException; /** * Access point to validators. Clients should access validation through - * {@link SEDMLDocument#validate()}. + * {@link SedMLDocument#validate()}. * * @author radams * @@ -21,13 +21,13 @@ public class ValidatorController { /** * * @param sedml - * A non-null {@link SedML} object + * A non-null {@link SedMLDataContainer} object * @param doc * An org.doc * @return A possibly empty but non-null List of SedML errors. * @throws XMLException */ - public List validate(SedML sedml, Document doc) + public List validate(SedMLDataContainer sedml, Document doc) throws XMLException { List errors = new ArrayList(); // should be first validation diff --git a/vcell-core/src/main/java/org/vcell/sbml/vcell/MathModel_SBMLExporter.java b/vcell-core/src/main/java/org/vcell/sbml/vcell/MathModel_SBMLExporter.java index 24fca6c964..e9191a6bad 100644 --- a/vcell-core/src/main/java/org/vcell/sbml/vcell/MathModel_SBMLExporter.java +++ b/vcell-core/src/main/java/org/vcell/sbml/vcell/MathModel_SBMLExporter.java @@ -20,9 +20,12 @@ import javax.xml.stream.XMLStreamException; -import org.apache.log4j.Level; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerConfig; import org.sbml.jsbml.ASTNode; import org.sbml.jsbml.AssignmentRule; import org.sbml.jsbml.Compartment; @@ -38,6 +41,7 @@ import org.sbml.jsbml.Trigger; import org.sbml.jsbml.ext.spatial.*; import org.sbml.jsbml.validator.SBMLValidator.CHECK_CATEGORY; +import org.slf4j.LoggerFactory; import org.vcell.sbml.SBMLUtils; import org.vcell.util.Extent; import org.vcell.util.ISize; @@ -320,8 +324,23 @@ public static ASTNode getFormulaFromExpression(Expression expression, MathType m throw new RuntimeException("Error converting expression to MathML string :" + e1.getMessage(), e1); } - // Use libSBMl routines to convert MathML string to MathML document and a libSBML-readable formula string - org.apache.log4j.Logger.getLogger(ASTNode.class).setLevel(Level.WARN); // NEEDED TO SUPPRESS JSBML DEBUG LOGGER ERROR + // Use libSBML routines to convert MathML string to MathML document and a libSBML-readable formula string + // NEEDED TO SUPPRESS JSBML DEBUG LOGGER ERROR + Logger logger = LogManager.getLogger(ASTNode.class); + if (logger.getLevel() != Level.WARN){ + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + + LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName()); + // If this logger doesn't have its own config, create one + if (!loggerConfig.getName().equals(logger.getName())) { + loggerConfig = new LoggerConfig(logger.getName(), Level.WARN, true); + config.addLogger(logger.getName(), loggerConfig); + } else { + loggerConfig.setLevel(Level.WARN); + } + context.updateLoggers(); + } ASTNode mathNode = ASTNode.readMathMLFromString(expMathMLStr); return mathNode; } @@ -555,7 +574,22 @@ private static void addGeometry(Model sbmlModel, MathModel vcMathModel) { Expression expr = ((AnalyticSubVolume)vcGeomClasses[i]).getExpression(); try { String mathMLStr = ExpressionMathMLPrinter.getMathML(expr, true); - org.apache.log4j.Logger.getLogger(ASTNode.class).setLevel(Level.WARN); // NEEDED TO SUPPRESS JSBML DEBUG LOGGER ERROR + // NEEDED TO SUPPRESS JSBML DEBUG LOGGER ERROR + Logger logger = LogManager.getLogger(ASTNode.class); + if (logger.getLevel() != Level.WARN){ + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + + LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName()); + // If this logger doesn't have its own config, create one + if (!loggerConfig.getName().equals(logger.getName())) { + loggerConfig = new LoggerConfig(logger.getName(), Level.WARN, true); + config.addLogger(logger.getName(), loggerConfig); + } else { + loggerConfig.setLevel(Level.WARN); + } + context.updateLoggers(); + } ASTNode mathMLNode = ASTNode.readMathMLFromString(mathMLStr); analyticVol.setMath(mathMLNode); } catch (Exception e) { diff --git a/vcell-core/src/main/java/org/vcell/sbml/vcell/NonSpatialSBMLSimResults.java b/vcell-core/src/main/java/org/vcell/sbml/vcell/NonSpatialSBMLSimResults.java index 0b73eabdc7..fdec67344b 100644 --- a/vcell-core/src/main/java/org/vcell/sbml/vcell/NonSpatialSBMLSimResults.java +++ b/vcell-core/src/main/java/org/vcell/sbml/vcell/NonSpatialSBMLSimResults.java @@ -8,10 +8,9 @@ import cbit.vcell.parser.ExpressionException; import cbit.vcell.parser.SymbolTableEntry; import cbit.vcell.solver.ode.ODESolverResultSet; -import org.jlibsedml.UniformTimeCourse; +import org.jlibsedml.components.simulation.UniformTimeCourse; import org.sbml.jsbml.SBase; import org.vcell.sbml.vcell.lazy.LazySBMLNonSpatialDataAccessor; -import org.vcell.util.Pair; import java.util.Arrays; import java.util.HashMap; diff --git a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java index d15eb7ebb3..c08b1e8157 100644 --- a/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java +++ b/vcell-core/src/main/java/org/vcell/sbml/vcell/SBMLExporter.java @@ -61,9 +61,12 @@ import cbit.vcell.xml.XmlHelper; import cbit.vcell.xml.XmlParseException; import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Level; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerConfig; import org.jdom2.Element; import org.jdom2.Namespace; import org.sbml.jsbml.AssignmentRule; @@ -97,7 +100,18 @@ public class SBMLExporter { static { // set the log level to warn to avoid debug logging which causes runtime errors in JSBML - org.apache.log4j.Logger.getLogger(ASTNode.class).setLevel(Level.WARN); + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + Logger logger = LogManager.getLogger(ASTNode.class); + LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName()); + // If this logger doesn't have its own config, create one + if (!loggerConfig.getName().equals(logger.getName())) { + loggerConfig = new LoggerConfig(logger.getName(), Level.WARN, true); + config.addLogger(logger.getName(), loggerConfig); + } else { + loggerConfig.setLevel(Level.WARN); + } + context.updateLoggers(); } public static boolean bWriteDebugFiles = false; @@ -1697,7 +1711,23 @@ private static ASTNode getFormulaFromExpression(Expression expression, MathType } // Use libSBMl routines to convert MathML string to MathML document and a libSBML-readable formula string - org.apache.log4j.Logger.getLogger(ASTNode.class).setLevel(Level.WARN); // NEEDED TO SUPPRESS JSBML DEBUG LOGGER ERROR + // NEEDED TO SUPPRESS JSBML DEBUG LOGGER ERROR + Logger logger = LogManager.getLogger(ASTNode.class); + if (logger.getLevel() != Level.WARN){ + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + + LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName()); + // If this logger doesn't have its own config, create one + if (!loggerConfig.getName().equals(logger.getName())) { + loggerConfig = new LoggerConfig(logger.getName(), Level.WARN, true); + config.addLogger(logger.getName(), loggerConfig); + } else { + loggerConfig.setLevel(Level.WARN); + } + context.updateLoggers(); + } + ASTNode mathNode = ASTNode.readMathMLFromString(expMathMLStr); return mathNode; } @@ -2259,7 +2289,22 @@ private void addGeometry() throws SbmlException { Expression expr = ((AnalyticSubVolume)vcGeomClasses[i]).getExpression(); try { String mathMLStr = ExpressionMathMLPrinter.getMathML(expr, true, MathType.BOOLEAN, ExpressionMathMLPrinter.Dialect.SBML_SUBSET); - org.apache.log4j.Logger.getLogger(ASTNode.class).setLevel(Level.WARN); // NEEDED TO SUPPRESS JSBML DEBUG LOGGER ERROR + // NEEDED TO SUPPRESS JSBML DEBUG LOGGER ERROR + Logger logger = LogManager.getLogger(ASTNode.class); + if (logger.getLevel() != Level.WARN){ + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + + LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName()); + // If this logger doesn't have its own config, create one + if (!loggerConfig.getName().equals(logger.getName())) { + loggerConfig = new LoggerConfig(logger.getName(), Level.WARN, true); + config.addLogger(logger.getName(), loggerConfig); + } else { + loggerConfig.setLevel(Level.WARN); + } + context.updateLoggers(); + } ASTNode mathMLNode = ASTNode.readMathMLFromString(mathMLStr); analyticVol.setMath(mathMLNode); } catch (Exception e) { diff --git a/vcell-core/src/main/java/org/vcell/sbml/vcell/SpatialSBMLSimResults.java b/vcell-core/src/main/java/org/vcell/sbml/vcell/SpatialSBMLSimResults.java index 9c2daa9113..39cac20053 100644 --- a/vcell-core/src/main/java/org/vcell/sbml/vcell/SpatialSBMLSimResults.java +++ b/vcell-core/src/main/java/org/vcell/sbml/vcell/SpatialSBMLSimResults.java @@ -14,7 +14,7 @@ import cbit.vcell.solver.VCSimulationDataIdentifier; import cbit.vcell.solver.VCSimulationIdentifier; import cbit.vcell.solvers.CartesianMesh; -import org.jlibsedml.UniformTimeCourse; +import org.jlibsedml.components.simulation.UniformTimeCourse; import org.sbml.jsbml.SBase; import org.vcell.sbml.vcell.lazy.LazySBMLSpatialDataAccessor; import org.vcell.util.DataAccessException; diff --git a/vcell-core/src/main/java/org/vcell/sedml/GsonSEDMLRecorderSerializer.java b/vcell-core/src/main/java/org/vcell/sedml/GsonSEDMLRecorderSerializer.java index e43f879ae5..76ee1f2e13 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/GsonSEDMLRecorderSerializer.java +++ b/vcell-core/src/main/java/org/vcell/sedml/GsonSEDMLRecorderSerializer.java @@ -9,9 +9,9 @@ import com.google.gson.JsonSerializer; -public class GsonSEDMLRecorderSerializer implements JsonSerializer { +public class GsonSEDMLRecorderSerializer implements JsonSerializer { @Override - public JsonElement serialize(SEDMLRecorder src, Type typeOfSrc, JsonSerializationContext context){ + public JsonElement serialize(SedMLRecorder src, Type typeOfSrc, JsonSerializationContext context){ JsonObject jsonObj = new JsonObject(); jsonObj.add("identifier", new JsonPrimitive(src.getIdentifier())); diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLExporter.java b/vcell-core/src/main/java/org/vcell/sedml/SEDMLExporter.java deleted file mode 100644 index 5645fd1b93..0000000000 --- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLExporter.java +++ /dev/null @@ -1,1582 +0,0 @@ -package org.vcell.sedml; - -import cbit.util.xml.XmlRdfUtil; -import cbit.util.xml.XmlUtil; -import cbit.vcell.biomodel.BioModel; -import cbit.vcell.biomodel.ModelUnitConverter; -import cbit.vcell.geometry.GeometryClass; -import cbit.vcell.mapping.*; -import cbit.vcell.mapping.SimulationContext.Application; -import cbit.vcell.mapping.SpeciesContextSpec.SpeciesContextSpecParameter; -import cbit.vcell.mapping.StructureMapping.StructureMappingParameter; -import cbit.vcell.math.Constant; -import cbit.vcell.math.MathUtilities; -import cbit.vcell.model.*; -import cbit.vcell.model.Kinetics.KineticsParameter; -import cbit.vcell.model.Model.ModelParameter; -import cbit.vcell.model.Model.ReservedSymbol; -import cbit.vcell.model.Structure.StructureSize; -import cbit.vcell.parser.*; -import cbit.vcell.resource.OperatingSystemInfo; -import cbit.vcell.solver.*; -import cbit.vcell.solver.Simulation; -import cbit.vcell.solver.MathOverridesResolver.SymbolReplacement; -import cbit.vcell.xml.*; -import de.unirostock.sems.cbarchive.CombineArchive; -import de.unirostock.sems.cbarchive.meta.OmexMetaDataObject; -import de.unirostock.sems.cbarchive.meta.omex.OmexDescription; -import org.apache.commons.io.FilenameUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jdom2.Element; -import org.jdom2.Namespace; -import org.jlibsedml.Model; -import org.jlibsedml.*; -import org.jlibsedml.UniformRange.UniformType; -import org.jlibsedml.modelsupport.SBMLSupport; -import org.jlibsedml.modelsupport.SBMLSupport.CompartmentAttribute; -import org.jlibsedml.modelsupport.SBMLSupport.ParameterAttribute; -import org.jlibsedml.modelsupport.SBMLSupport.SpeciesAttribute; -import org.jlibsedml.modelsupport.SUPPORTED_LANGUAGE; -import org.jmathml.ASTNode; -import org.sbml.jsbml.Annotation; -import org.sbml.jsbml.Parameter; -import org.sbml.jsbml.SBMLDocument; -import org.sbml.jsbml.SBMLReader; -import org.sbml.jsbml.xml.XMLNode; -import org.vcell.sbml.OmexPythonUtils; -import org.vcell.sbml.SbmlException; -import org.vcell.sbml.SimSpec; -import org.vcell.sbml.UnsupportedSbmlExportException; -import org.vcell.sbml.vcell.SBMLExporter; -import org.vcell.util.FileUtils; -import org.vcell.util.ISize; -import org.vcell.util.Pair; -import org.vcell.util.TokenMangler; -import org.vcell.util.document.BioModelInfo; -import org.vcell.util.document.Version; - -import javax.xml.stream.XMLStreamException; -import java.beans.PropertyVetoException; -import java.io.*; -import java.net.URI; -import java.nio.file.*; -import java.util.*; -import java.util.function.Predicate; -import java.util.stream.Collectors; - - -public class SEDMLExporter { - private final static Logger logger = LogManager.getLogger(SEDMLExporter.class); - - private int sedmlLevel = 1; - private int sedmlVersion = 2; - private SedML sedmlModel = null; - private cbit.vcell.biomodel.BioModel vcBioModel = null; - private String jobId = null; - private ArrayList modelFilePathStrAbsoluteList = new ArrayList(); - private ArrayList sedmlFilePathStrAbsoluteList = new ArrayList(); - private List simsToExport = new ArrayList(); - - private static String DATAGENERATOR_TIME_NAME = "time"; - private static String DATAGENERATOR_TIME_SYMBOL = "t"; - - private String sbmlLanguageURN = SUPPORTED_LANGUAGE.SBML_GENERIC.getURN(); - private String vcmlLanguageURN = SUPPORTED_LANGUAGE.VCELL_GENERIC.getURN(); - - private SEDMLRecorder sedmlRecorder = null; - private int simCount; - private int overrideCount; - - private SBMLSupport sbmlSupport = new SBMLSupport(); - - - public SEDMLExporter(String argJobId, BioModel argBiomodel, int argLevel, int argVersion, List argSimsToExport) { - this(argJobId, argBiomodel, argLevel, argVersion, argSimsToExport, null); - } - - public SEDMLExporter(String argJobId, BioModel argBiomodel, int argLevel, int argVersion, List argSimsToExport, String jsonFilePath) { - - super(); - - this.jobId = argJobId; - this.vcBioModel = argBiomodel; - this.sedmlLevel = argLevel; - this.sedmlVersion = argVersion; - - this.sedmlRecorder = new SEDMLRecorder(argJobId, SEDMLConversion.EXPORT, jsonFilePath); - // we need to collect simulation names to be able to match sims in BioModel clone - if (argSimsToExport != null && argSimsToExport.size() > 0) { - for (Simulation sim : argSimsToExport) { - simsToExport.add(sim.getName()); - } - } else { - simsToExport = null; - } - } - - public SEDMLDocument getSEDMLDocument(String sPath, String sBaseFileName, ModelFormat modelFormat, - boolean bRoundTripSBMLValidation, Predicate simContextExportFilter) { - - double start = System.currentTimeMillis(); - - // Create an SEDMLDocument and create the SEDMLModel from the document, so that other details can be added to it in translateBioModel() - SEDMLDocument sedmlDocument = new SEDMLDocument(this.sedmlLevel, this.sedmlVersion); - - final String VCML_NS = "http://sourceforge.net/projects/vcell/vcml"; - final String VCML_NS_PREFIX = "vcml"; - - List nsList = new ArrayList<>(); - Namespace ns = Namespace.getNamespace(SEDMLTags.MATHML_NS_PREFIX, SEDMLTags.MATHML_NS); - nsList.add(ns); - ns = Namespace.getNamespace(VCML_NS_PREFIX, VCML_NS); - nsList.add(ns); - - if (modelFormat.equals(ModelFormat.SBML)) { - final String SBML_NS = "http://www.sbml.org/sbml/level3/version2/core"; - final String SBML_NS_PREFIX = "sbml"; - final String SPATIAL_NS = "https://sbml.org/documents/specifications/level-3/version-1/spatial"; - final String SPATIAL_NS_PREFIX = "spatial"; - ns = Namespace.getNamespace(SBML_NS_PREFIX, SBML_NS); - nsList.add(ns); - SimulationContext[] simContexts = vcBioModel.getSimulationContexts(); - for (SimulationContext sc : simContexts) { - if (sc.getGeometry() != null && sc.getGeometry().getDimension() > 0) { - ns = Namespace.getNamespace(SPATIAL_NS_PREFIX, SPATIAL_NS); - nsList.add(ns); - break; - } - } - } - sedmlModel = sedmlDocument.getSedMLModel(); - sedmlModel.setAdditionalNamespaces(nsList); - - this.translateBioModelToSedML(sPath, sBaseFileName, modelFormat, bRoundTripSBMLValidation, simContextExportFilter); - - double stop = System.currentTimeMillis(); - Exception timer = new Exception(Double.toString((stop-start)/1000)+" seconds"); - // update overall status - if (sedmlRecorder.hasErrors()) { - sedmlRecorder.addTaskRecord(vcBioModel.getName(), TaskType.BIOMODEL, TaskResult.FAILED, timer); - } else { - sedmlRecorder.addTaskRecord(vcBioModel.getName(), TaskType.BIOMODEL, TaskResult.SUCCEEDED, timer); - } - // should never bomb out just because we fail to export to json... - try { - this.sedmlRecorder.exportToJSON(); - } catch (Exception e) { - logger.error("Failed to export to JSON", e); - } - return sedmlDocument; - } - - private void translateBioModelToSedML(String savePath, String sBaseFileName, ModelFormat modelFormat, - boolean bRoundTripSBMLValidation, Predicate simContextExportFilter) { - modelFilePathStrAbsoluteList.clear(); - try { - - if (modelFormat == ModelFormat.VCML) { - BioModel prunedBM = XmlHelper.cloneBioModel(vcBioModel); - for (Simulation sim : prunedBM.getSimulations()) { - prunedBM.removeSimulation(sim); - } - String vcmlString = XmlHelper.bioModelToXML(prunedBM); - String modelFileNameRel = sBaseFileName+"_sedml.vcml"; - String modelFileNameAbs = Paths.get(savePath,modelFileNameRel).toString(); - XmlUtil.writeXMLStringToFile(vcmlString, modelFileNameAbs, false); - modelFilePathStrAbsoluteList.add(modelFileNameRel); - for (int i = 0; i < vcBioModel.getSimulationContexts().length; i++) { - writeModelVCML(modelFileNameRel, vcBioModel.getSimulationContext(i)); - sedmlRecorder.addTaskRecord(vcBioModel.getSimulationContext(i).getName(), TaskType.SIMCONTEXT, TaskResult.SUCCEEDED, null); - exportSimulations(i, vcBioModel.getSimulationContext(i), null, null, vcmlLanguageURN); - } - } - if (modelFormat == ModelFormat.SBML) { - try { -// // TODO: uncomment the for loop below to only export non-spatial -// for(Simulation sim : vcBioModel.getSimulations()) { -// if(sim.isSpatial()) { -// sedmlRecorder.addTaskLog(vcBioModel.getName(), TaskType.MODEL, TaskResult.FAILED, new RuntimeException("spatial")); -// return; -// } -// } - - // convert to SBML units; this also ensures we will use a clone - vcBioModel = ModelUnitConverter.createBioModelWithSBMLUnitSystem(vcBioModel); - sedmlRecorder.addTaskRecord(vcBioModel.getName(), TaskType.UNITS, TaskResult.SUCCEEDED, null); - } catch (Exception e1) { - String msg = "unit conversion failed for BioModel '"+vcBioModel.getName()+"': " + e1.getMessage(); - logger.error(msg, e1); - sedmlRecorder.addTaskRecord(vcBioModel.getName(), TaskType.UNITS, TaskResult.FAILED, e1); - throw e1; - } - SimulationContext[] simContexts = Arrays.stream(vcBioModel.getSimulationContexts()) - .filter(simContextExportFilter).toArray(SimulationContext[]::new); - - if (simContexts.length == 0) { - sedmlRecorder.addTaskRecord(vcBioModel.getName(), TaskType.MODEL, TaskResult.FAILED, new Exception("Model has no Applications")); - } else { - int simContextCnt = 0; // for model count, task subcount - for (SimulationContext simContext : simContexts) { - // Export the application itself to SBML, with default values (overrides will become model changes or repeated tasks) - String sbmlString = null; - Map, String> l2gMap = null; // local to global translation map - boolean sbmlExportFailed = false; - Exception simContextException = null; - try { - SBMLExporter.validateSimulationContextSupport(simContext); - boolean isSpatial = simContext.getGeometry().getDimension() > 0 ? true : false; - Pair , String>> pair = XmlHelper.exportSBMLwithMap(vcBioModel, 3, 2, 0, isSpatial, simContext, bRoundTripSBMLValidation); - sbmlString = pair.one; - l2gMap = pair.two; - writeModelSBML(savePath, sBaseFileName, sbmlString, simContext); - sedmlRecorder.addTaskRecord(simContext.getName(), TaskType.SIMCONTEXT, TaskResult.SUCCEEDED, null); - } catch (Exception e) { - String msg = "SBML export failed for simContext '"+simContext.getName()+"': " + e.getMessage(); - logger.error(msg, e); - sbmlExportFailed = true; - simContextException = e; - sedmlRecorder.addTaskRecord(simContext.getName(), TaskType.SIMCONTEXT, TaskResult.FAILED, e); - } - - if (!sbmlExportFailed) { - // simContext was exported succesfully, now we try to export its simulations - exportSimulations(simContextCnt, simContext, sbmlString, l2gMap, sbmlLanguageURN); - } else { - System.err.println(sedmlRecorder.getRecordsAsCSV()); - throw new Exception ("SimContext '"+simContext.getName()+"' could not be exported to SBML :" +simContextException.getMessage(), simContextException); - } - simContextCnt++; - } - } - } - if(sedmlModel.getModels() != null && sedmlModel.getModels().size() > 0) { - logger.trace("Number of models in the sedml is " + sedmlModel.getModels().size()); - } - if (sedmlRecorder.hasErrors()) { - System.err.println(sedmlRecorder.getRecordsAsCSV()); - } else { - System.out.println(sedmlRecorder.getRecordsAsCSV()); - } - } catch (Exception e) { - // this only happens if not from CLI, we need to pass this down the calling thread - throw new RuntimeException("Error adding model to SEDML document : " + e.getMessage(), e); - } - } - - private void writeModelVCML(String filePathStrRelative, SimulationContext simContext) { - String simContextName = simContext.getName(); - String simContextId = TokenMangler.mangleToSName(simContextName); - sedmlModel.addModel(new Model(simContextId, simContextName, vcmlLanguageURN, filePathStrRelative+"#"+VCMLSupport.getXPathForSimContext(simContextName))); - } - - private void exportSimulations(int simContextCnt, SimulationContext simContext, - String sbmlString, Map, String> l2gMap, String languageURN) throws Exception { - // ------- - // create sedml objects (simulation, task, datagenerators, report, plot) for each simulation in simcontext - // ------- - String simContextName = simContext.getName(); - String simContextId = TokenMangler.mangleToSName(simContextName); - simCount = 0; - overrideCount = 0; - if (simContext.getSimulations().length == 0) return; - for (Simulation vcSimulation : simContext.getSimulations()) { - try { - // if we have a hash containing a subset of simulations to export - // skip simulations not present in hash - if (simsToExport != null && !simsToExport.contains(vcSimulation.getName())) continue; - - // 1 -------> check compatibility - // if simContext is non-spatial stochastic, check if sim is histogram; if so, skip it, it can't be encoded in sedml 1.x - SolverTaskDescription simTaskDesc = vcSimulation.getSolverTaskDescription(); - if (simContext.getGeometry().getDimension() == 0 && simContext.isStoch()) { - long numOfTrials = simTaskDesc.getStochOpt().getNumOfTrials(); - if (numOfTrials > 1) { - String msg = simContextName + " ( " + vcSimulation.getName() + " ) : export of non-spatial stochastic simulation with histogram option to SEDML not supported at this time."; - throw new Exception(msg); - } - } - - // 2 -------> - // create sedmlSimulation (UniformTimeCourse) with specs and algorithm parameters - UniformTimeCourse utcSim = createSEDMLsim(simTaskDesc); - - // 3 -------> - // create Tasks - Set dataGeneratorTasksSet = new LinkedHashSet<>(); // tasks not referenced as subtasks by any other (repeated) task; only these will have data generators - MathOverrides mathOverrides = vcSimulation.getMathOverrides(); // need to clone so we can manipulate expressions - createSEDMLtasks(simContextCnt, l2gMap, simContextName, simContextId, - vcSimulation, utcSim, dataGeneratorTasksSet, mathOverrides, languageURN); - - // 4 -------> - // Create DataGenerators - - List dataGeneratorsOfSim = createSEDMLdatagens(sbmlString, simContext, dataGeneratorTasksSet); - - // 5 -------> - // create Report and Plot - - for(String taskRef : dataGeneratorTasksSet) { - createSEDMLoutputs(simContext, vcSimulation, dataGeneratorsOfSim, taskRef); - } - sedmlRecorder.addTaskRecord(vcSimulation.getName(), TaskType.SIMULATION, TaskResult.SUCCEEDED, null); - } catch (Exception e) { - String msg = "SEDML export failed for simulation '"+ vcSimulation.getName() + "': " + e.getMessage(); - logger.error(msg, e); - sedmlRecorder.addTaskRecord(vcSimulation.getName(), TaskType.SIMULATION, TaskResult.FAILED, e); - System.err.println(sedmlRecorder.getRecordsAsCSV()); - throw e; - } - simCount++; - } - } - - private void createSEDMLoutputs(SimulationContext simContext, Simulation vcSimulation, - List dataGeneratorsOfSim, String taskRef) { - // add output to sedml Model : 1 plot2d for each non-spatial simulation with all vars (species/output functions) vs time (1 curve per var) - // ignoring output for spatial deterministic (spatial stochastic is not exported to SEDML) and non-spatial stochastic applications with histogram - if (!(simContext.getGeometry().getDimension() > 0)) { - String plot2dId = "plot2d_" + TokenMangler.mangleToSName(vcSimulation.getName()); - String reportId = "report_" + TokenMangler.mangleToSName(vcSimulation.getName()); - // String reportId = "__plot__" + plot2dId; - String plotName = simContext.getName() + "_" + vcSimulation.getName() + "_plot"; - Plot2D sedmlPlot2d = new Plot2D(plot2dId, plotName); - Report sedmlReport = new Report(reportId, plotName); - - sedmlPlot2d.setNote(createNotesElement("Plot of all variables and output functions from application '" + simContext.getName() + "' ; simulation '" + vcSimulation.getName() + "' in VCell model")); - sedmlReport.setNote(createNotesElement("Report of all variables and output functions from application '" + simContext.getName() + "' ; simulation '" + vcSimulation.getName() + "' in VCell model")); - DataGenerator dgtime = sedmlModel.getDataGeneratorWithId(DATAGENERATOR_TIME_NAME + "_" + taskRef); - String xDataRef = dgtime.getId(); - String xDatasetXId = "__data_set__" + plot2dId + dgtime.getId(); - DataSet dataSet = new DataSet(xDatasetXId, DATAGENERATOR_TIME_NAME, xDataRef, xDataRef); // id, name, label, data generator reference - sedmlReport.addDataSet(dataSet); - - // add a curve for each dataGenerator in SEDML model - int curveCnt = 0; - // String id, String name, ASTNode math - for (DataGenerator dg : dataGeneratorsOfSim) { - // no curve for time, since time is xDateReference - if (dg.getId().equals(xDataRef)) { - continue; - } - String curveId = "curve_" + plot2dId + "_" + dg.getId(); - String datasetYId = "__data_set__" + plot2dId + dg.getId(); - Curve curve = new Curve(curveId, dg.getName(), false, false, xDataRef, dg.getId()); - sedmlPlot2d.addCurve(curve); - // // id, name, label, dataRef - // // dataset id <- unique id - // // dataset name <- data generator name - // // dataset label <- dataset id - DataSet yDataSet = new DataSet(datasetYId, dg.getName(), dg.getId(), dg.getId()); - sedmlReport.addDataSet(yDataSet); - curveCnt++; - } - sedmlModel.addOutput(sedmlPlot2d); - sedmlModel.addOutput(sedmlReport); - } else { // spatial deterministic - if(simContext.getApplicationType().equals(Application.NETWORK_DETERMINISTIC)) { // we ignore spatial stochastic (Smoldyn) - // TODO: add curves/surfaces to the plots - String plot3dId = "plot3d_" + TokenMangler.mangleToSName(vcSimulation.getName()); - String reportId = "report_" + TokenMangler.mangleToSName(vcSimulation.getName()); - String plotName = simContext.getName() + "plots"; - Plot3D sedmlPlot3d = new Plot3D(plot3dId, plotName); - Report sedmlReport = new Report(reportId, plotName); - - sedmlPlot3d.setNote(createNotesElement("Plot of all variables and output functions from application '" + simContext.getName() + "' ; simulation '" + vcSimulation.getName() + "' in VCell model")); - sedmlReport.setNote(createNotesElement("Report of all variables and output functions from application '" + simContext.getName() + "' ; simulation '" + vcSimulation.getName() + "' in VCell model")); - DataGenerator dgtime = sedmlModel.getDataGeneratorWithId(DATAGENERATOR_TIME_NAME + "_" + taskRef); - String xDataRef = dgtime.getId(); - String xDatasetXId = "__data_set__" + plot3dId + dgtime.getId(); - DataSet dataSet = new DataSet(xDatasetXId, DATAGENERATOR_TIME_NAME, xDataRef, xDataRef); // id, name, label, data generator reference - sedmlReport.addDataSet(dataSet); - - // add a curve for each dataGenerator in SEDML model - int curveCnt = 0; - // String id, String name, ASTNode math - for (DataGenerator dg : dataGeneratorsOfSim) { - // no curve for time, since time is xDateReference - if (dg.getId().equals(xDataRef)) { - continue; - } - String curveId = "curve_" + plot3dId + "_" + dg.getId(); - String datasetYId = "__data_set__" + plot3dId + dg.getId(); - - DataSet yDataSet = new DataSet(datasetYId, dg.getName(), dg.getId(), dg.getId()); - sedmlReport.addDataSet(yDataSet); - curveCnt++; - } - sedmlModel.addOutput(sedmlReport); - } - } - } - - private List createSEDMLdatagens(String sbmlString, SimulationContext simContext, Set dataGeneratorTasksSet) - throws IOException, SbmlException, XMLStreamException { - List dataGeneratorsOfSim = new ArrayList<> (); - for(String taskRef : dataGeneratorTasksSet) { - // add one DataGenerator for 'time' - String timeDataGenPrefix = DATAGENERATOR_TIME_NAME + "_" + taskRef; - DataGenerator timeDataGen = sedmlModel.getDataGeneratorWithId(timeDataGenPrefix); - org.jlibsedml.Variable timeVar = new org.jlibsedml.Variable(DATAGENERATOR_TIME_SYMBOL + "_" + taskRef, DATAGENERATOR_TIME_SYMBOL, taskRef, VariableSymbol.TIME); - ASTNode math = Libsedml.parseFormulaString(DATAGENERATOR_TIME_SYMBOL + "_" + taskRef); - timeDataGen = new DataGenerator(timeDataGenPrefix, timeDataGenPrefix, math); - timeDataGen.addVariable(timeVar); - sedmlModel.addDataGenerator(timeDataGen); - dataGeneratorsOfSim.add(timeDataGen); - - String dataGenIdPrefix = "dataGen_" + taskRef; - - // add dataGenerators for species - // get species list from SBML model. - ArrayList varNamesList = new ArrayList(); - if (sbmlString != null) { - String[] sbmlVars = SimSpec.fromSBML(sbmlString).getVarsList(); - for (String sbmlVar : sbmlVars) { - varNamesList.add(sbmlVar); - } - } else { - SpeciesContextSpec[] scSpecs = simContext.getReactionContext().getSpeciesContextSpecs(); - for (SpeciesContextSpec scs : scSpecs) { - varNamesList.add(scs.getSpeciesContext().getName()); - } - } - for (String varName : varNamesList) { - String varId = TokenMangler.mangleToSName(varName) + "_" + taskRef; - org.jlibsedml.Variable sedmlVar = null; - if (sbmlString != null) { - sedmlVar = new org.jlibsedml.Variable(varId, varName, taskRef, sbmlSupport.getXPathForSpecies(varName)); - } else { - sedmlVar = new org.jlibsedml.Variable(varId, varName, taskRef, VCMLSupport.getXPathForSpeciesContextSpec(simContext.getName(), varName)); - } - ASTNode varMath = Libsedml.parseFormulaString(varId); - String dataGenId = dataGenIdPrefix + "_" + TokenMangler.mangleToSName(varName); //"dataGen_" + varCount; - old code - DataGenerator dataGen = new DataGenerator(dataGenId, varName, varMath); - dataGen.addVariable(sedmlVar); - sedmlModel.addDataGenerator(dataGen); - dataGeneratorsOfSim.add(dataGen); - } - // add dataGenerators for output functions - // get output function list from SBML model - varNamesList = new ArrayList(); - if (sbmlString != null) { - SBMLDocument sbmlDoc = new SBMLReader().readSBMLFromString(sbmlString); - List listofGlobalParams = sbmlDoc.getModel().getListOfParameters(); - for (int i = 0; i < listofGlobalParams.size(); i++) { - Parameter sbmlGlobalParam = listofGlobalParams.get(i); - // check whether it is a vcell-exported output function - Annotation paramAnnotation = sbmlGlobalParam.getAnnotation(); - if (paramAnnotation != null && paramAnnotation.getNonRDFannotation() != null) { - XMLNode paramElement = paramAnnotation.getNonRDFannotation() - .getChildElement(XMLTags.SBML_VCELL_OutputFunctionTag, "*"); - if (paramElement != null) { - String varName = sbmlGlobalParam.getId(); - varNamesList.add(varName); - } - } - } - } else { - List ofs = simContext.getOutputFunctionContext().getOutputFunctionsList(); - for (AnnotatedFunction of : ofs) { - varNamesList.add(of.getName()); - } - } - for (String varName : varNamesList) { - String varId = TokenMangler.mangleToSName(varName) + "_" + taskRef; - org.jlibsedml.Variable sedmlVar; - if (sbmlString != null) { - sedmlVar = new org.jlibsedml.Variable(varId, varName, taskRef, sbmlSupport.getXPathForGlobalParameter(varName)); - } else { - sedmlVar = new org.jlibsedml.Variable(varId, varName, taskRef, VCMLSupport.getXPathForOutputFunction(simContext.getName(),varName)); - } - ASTNode varMath = Libsedml.parseFormulaString(varId); - String dataGenId = dataGenIdPrefix + "_" + TokenMangler.mangleToSName(varName); //"dataGen_" + varCount; - old code - DataGenerator dataGen = new DataGenerator(dataGenId, varName, varMath); - dataGen.addVariable(sedmlVar); - sedmlModel.addDataGenerator(dataGen); - dataGeneratorsOfSim.add(dataGen); - } - } - return dataGeneratorsOfSim; - } - - private UniformTimeCourse createSEDMLsim(SolverTaskDescription simTaskDesc) { - // list of kisao terms in vcell-core/src/main/resources/kisao_algs.obo - SolverDescription vcSolverDesc = simTaskDesc.getSolverDescription(); - String kiSAOIdStr = vcSolverDesc.getKisao(); - Algorithm sedmlAlgorithm = new Algorithm(kiSAOIdStr); - Notes an = createNotesElement(""); // we show the description of kisao terms for AlgorithmParameters as notes - // for L1V4 and up, AlgorithmParameters has a "name" field we can use instead - sedmlAlgorithm.setNote(an); - TimeBounds vcSimTimeBounds = simTaskDesc.getTimeBounds(); - double startingTime = vcSimTimeBounds.getStartingTime(); - String simName = simTaskDesc.getSimulation().getName(); - UniformTimeCourse utcSim = new UniformTimeCourse(TokenMangler.mangleToSName(simName), simName, startingTime, startingTime, - vcSimTimeBounds.getEndingTime(), (int) simTaskDesc.getExpectedNumTimePoints(), sedmlAlgorithm); - - boolean enableAbsoluteErrorTolerance; // --------- deal with error tolerance - boolean enableRelativeErrorTolerance; - if (vcSolverDesc.isSemiImplicitPdeSolver() || vcSolverDesc.isChomboSolver()) { - enableAbsoluteErrorTolerance = false; - enableRelativeErrorTolerance = true; - } else if (vcSolverDesc.hasErrorTolerance()) { - enableAbsoluteErrorTolerance = true; - enableRelativeErrorTolerance = true; - } else { - enableAbsoluteErrorTolerance = false; - enableRelativeErrorTolerance = false; - } - if(enableAbsoluteErrorTolerance) { - ErrorTolerance et = simTaskDesc.getErrorTolerance(); - String kisaoStr = ErrorTolerance.ErrorToleranceDescription.Absolute.getKisao(); - String kisaoDesc = ErrorTolerance.ErrorToleranceDescription.Absolute.getDescription(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, et.getAbsoluteErrorTolerance()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - if(enableRelativeErrorTolerance) { - ErrorTolerance et = simTaskDesc.getErrorTolerance(); - String kisaoStr = ErrorTolerance.ErrorToleranceDescription.Relative.getKisao(); - String kisaoDesc = ErrorTolerance.ErrorToleranceDescription.Relative.getDescription(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, et.getRelativeErrorTolerance()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - - boolean enableDefaultTimeStep; // ---------- deal with time step (code adapted from TimeSpecPanel.refresh() - boolean enableMinTimeStep; - boolean enableMaxTimeStep; - if (vcSolverDesc.compareEqual(SolverDescription.StochGibson)) { // stochastic time - enableDefaultTimeStep = false; - enableMinTimeStep = false; - enableMaxTimeStep = false; - } else if(vcSolverDesc.compareEqual(SolverDescription.NFSim)) { - enableDefaultTimeStep = false; - enableMinTimeStep = false; - enableMaxTimeStep = false; - } else { - // fixed time step solvers and non spatial stochastic solvers only show default time step. - if (!vcSolverDesc.hasVariableTimestep() || vcSolverDesc.isNonSpatialStochasticSolver()) { - enableDefaultTimeStep = true; - enableMinTimeStep = false; - enableMaxTimeStep = false; - } else { - // variable time step solvers shows min and max, but sundials solvers don't show min - enableDefaultTimeStep = false; - enableMinTimeStep = true; - enableMaxTimeStep = true; - if (vcSolverDesc.hasSundialsTimeStepping()) { - enableMinTimeStep = false; - } - } - } - if (vcSolverDesc == SolverDescription.SundialsPDE) { - String kisaoStr = SolverDescription.AlgorithmParameterDescription.PDEMeshSize.getKisao(); - String kisaoDesc = SolverDescription.AlgorithmParameterDescription.PDEMeshSize.getDescription(); - ISize meshSize = simTaskDesc.getSimulation().getMeshSpecification().getSamplingSize(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, meshSize.toTemporaryKISAOvalue()); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - TimeStep ts = simTaskDesc.getTimeStep(); - if(enableDefaultTimeStep) { - String kisaoStr = TimeStep.TimeStepDescription.Default.getKisao(); - String kisaoDesc = TimeStep.TimeStepDescription.Default.getDescription(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, ts.getDefaultTimeStep()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - if(enableMinTimeStep) { - String kisaoStr = TimeStep.TimeStepDescription.Minimum.getKisao(); - String kisaoDesc = TimeStep.TimeStepDescription.Minimum.getDescription(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, ts.getMinimumTimeStep()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - if(enableMaxTimeStep) { - String kisaoStr = TimeStep.TimeStepDescription.Maximum.getKisao(); - String kisaoDesc = TimeStep.TimeStepDescription.Maximum.getDescription(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, ts.getMaximumTimeStep()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - - if(simTaskDesc.getSimulation().getMathDescription().isNonSpatialStoch()) { // ------- deal with seed - NonspatialStochSimOptions nssso = simTaskDesc.getStochOpt(); - if(nssso.isUseCustomSeed()) { - String kisaoStr = SolverDescription.AlgorithmParameterDescription.Seed.getKisao(); // 488 - String kisaoDesc = SolverDescription.AlgorithmParameterDescription.Seed.getDescription(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssso.getCustomSeed()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - } else { - ; // (... isRuleBased(), isSpatial(), isMovingMembrane(), isSpatialHybrid() ... - } - - if(vcSolverDesc == SolverDescription.HybridEuler || // -------- deal with hybrid solvers (non-spatial) - vcSolverDesc == SolverDescription.HybridMilAdaptive || - vcSolverDesc == SolverDescription.HybridMilstein) { - NonspatialStochHybridOptions nssho = simTaskDesc.getStochHybridOpt(); - - String kisaoStr = SolverDescription.AlgorithmParameterDescription.Epsilon.getKisao(); - String kisaoDesc = SolverDescription.AlgorithmParameterDescription.Epsilon.getDescription(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssho.getEpsilon()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - - kisaoStr = SolverDescription.AlgorithmParameterDescription.Lambda.getKisao(); - kisaoDesc = SolverDescription.AlgorithmParameterDescription.Lambda.getDescription(); - sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssho.getLambda()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - - kisaoStr = SolverDescription.AlgorithmParameterDescription.MSRTolerance.getKisao(); - kisaoDesc = SolverDescription.AlgorithmParameterDescription.MSRTolerance.getDescription(); - sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssho.getMSRTolerance()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - if(vcSolverDesc == SolverDescription.HybridMilAdaptive) { // --------- one more param for hybrid-adaptive - NonspatialStochHybridOptions nssho = simTaskDesc.getStochHybridOpt(); - - String kisaoStr = SolverDescription.AlgorithmParameterDescription.SDETolerance.getKisao(); - String kisaoDesc = SolverDescription.AlgorithmParameterDescription.SDETolerance.getDescription(); - AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssho.getSDETolerance()+""); - sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); - addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); - } - - // add a note to utcSim to indicate actual solver name - String simNotesStr = "Actual Solver Name : '" + vcSolverDesc.getDisplayLabel() + "'."; - utcSim.setNote(createNotesElement(simNotesStr)); - sedmlModel.addSimulation(utcSim); - return utcSim; - } - - private void createSEDMLtasks(int simContextCnt, Map, String> l2gMap, String simContextName, - String simContextId, Simulation vcSimulation, UniformTimeCourse utcSim, - Set dataGeneratorTasksSet, MathOverrides mathOverrides, String languageURN) - throws ExpressionBindingException, ExpressionException, DivideByZeroException, MappingException { - if(mathOverrides != null && mathOverrides.hasOverrides()) { - String[] overridenConstantNames = mathOverrides.getOverridenConstantNames(); - String[] scannedConstantsNames = mathOverrides.getScannedConstantNames(); - HashMap scannedParamHash = new HashMap(); - HashMap unscannedParamHash = new HashMap(); - - VariableSymbolTable varST = new VariableSymbolTable(); - String[] constantNames = mathOverrides.getAllConstantNames(); - final HashMap substitutedConstants = new HashMap<>(); - { - final ArrayList overrides = new ArrayList<>(); - for (String constantName : constantNames) { - overrides.add(new Constant(constantName, new Expression(mathOverrides.getActualExpression(constantName, MathOverrides.ScanIndex.ZERO)))); - } - for (Constant override : overrides) { - varST.addVar(override); - } - for (Constant override : overrides) { - override.bind(varST); - } - for (Constant override : overrides) { - Expression flattened = MathUtilities.substituteFunctions(override.getExpression(), varST, true); - substitutedConstants.put(override.getName(), new Expression(flattened)); - } - } - - // need to check for "leftover" overrides from parameter renaming or other model editing - HashMap missingParamHash = new HashMap(); - for (String name : scannedConstantsNames) { - if (!mathOverrides.isUnusedParameter(name)) { - scannedParamHash.put(name, name); - } else { - missingParamHash.put(name, name); - } - } - for (String name : overridenConstantNames) { - if (!scannedParamHash.containsKey(name)) { - if (!mathOverrides.isUnusedParameter(name)) { - unscannedParamHash.put(name, name); - } else { - missingParamHash.put(name, name); - } - } - } - if (!missingParamHash.isEmpty()) { - for (String missingParamName : missingParamHash.values()) { - logger.error("ERROR: there is an override entry for non-existent parameter "+missingParamName); - throw new MappingException("MathOverrides has entry for non-existent parameter "+missingParamName); - } - } - - SimulationContext simContext = (SimulationContext)vcSimulation.getSimulationOwner(); - MathSymbolMapping mathSymbolMapping = (MathSymbolMapping) simContext.getMathDescription().getSourceSymbolMapping(); - if (!unscannedParamHash.isEmpty() && scannedParamHash.isEmpty()) { - // only parameters with simple overrides (numeric/expression) no scans - // create new model with change for each parameter that has override; add simple task - String overriddenSimContextId = simContextId + "_" + overrideCount; - String overriddenSimContextName = simContextName + " modified"; - Model sedModel = new Model(overriddenSimContextId, overriddenSimContextName, languageURN, "#"+simContextId); - overrideCount++; - - int variableCount = 0; - for (String unscannedParamName : unscannedParamHash.values()) { - SymbolTableEntry ste = getSymbolTableEntryForModelEntity(mathSymbolMapping, unscannedParamName); - Expression unscannedParamExpr = mathOverrides.getActualExpression(unscannedParamName, MathOverrides.ScanIndex.ZERO); - unscannedParamExpr = adjustIfRateParam(vcSimulation, ste, unscannedParamExpr); - if(unscannedParamExpr.isNumeric()) { - // if expression is numeric, add ChangeAttribute to model created above - XPathTarget targetXpath = getTargetAttributeXPath(ste, l2gMap, simContext); - ChangeAttribute changeAttribute = new ChangeAttribute(targetXpath, unscannedParamExpr.infix()); - sedModel.addChange(changeAttribute); - } else { - - Map symbolToTargetMap = new LinkedHashMap<>(); - // create setValue for unscannedParamName (which contains a scanned param in its expression) - String[] symbols = unscannedParamExpr.getSymbols(); - for(String symbol : symbols) { - SymbolTableEntry entry = getSymbolTableEntryForModelEntity(mathSymbolMapping, symbol); - XPathTarget target = getTargetAttributeXPath(entry, l2gMap, simContext); - symbolToTargetMap.put(symbol, target); - } - - // non-numeric expression : add 'computeChange' to modified model - XPathTarget targetXpath = getTargetAttributeXPath(ste, l2gMap, simContext); - ComputeChange computeChange = new ComputeChange(targetXpath); - - Expression expr = new Expression(unscannedParamExpr); - String[] exprSymbols = expr.getSymbols(); - for (String symbol : exprSymbols) { - String varName = overriddenSimContextId + "_" + symbol + "_" + variableCount; - Variable sedmlVar = new Variable(varName, varName, overriddenSimContextId, symbolToTargetMap.get(symbol).toString()); - expr.substituteInPlace(new Expression(symbol), new Expression(varName)); - computeChange.addVariable(sedmlVar); - variableCount++; - } - ASTNode math = Libsedml.parseFormulaString(expr.infix()); - computeChange.setMath(math); - sedModel.addChange(computeChange); - } - } - sedmlModel.addModel(sedModel); - - String taskId = "tsk_" + simContextCnt + "_" + simCount; - Task sedmlTask = new Task(taskId, vcSimulation.getName(), sedModel.getId(), utcSim.getId()); - dataGeneratorTasksSet.add(sedmlTask.getId()); - sedmlModel.addTask(sedmlTask); - - } else if (!scannedParamHash.isEmpty() && unscannedParamHash.isEmpty()) { - // only parameters with scans - String taskId = "tsk_" + simContextCnt + "_" + simCount; - String ownerTaskId = taskId; - Task sedmlTask = new Task(taskId, vcSimulation.getName(), simContextId, utcSim.getId()); - dataGeneratorTasksSet.add(sedmlTask.getId()); - sedmlModel.addTask(sedmlTask); - - int repeatedTaskIndex = 0; - for (String scannedConstName : scannedConstantsNames) { - String repeatedTaskId = "repTsk_" + simContextCnt + "_" + simCount + "_" + repeatedTaskIndex; - String rangeId = "range_" + simContextCnt + "_" + simCount + "_" + scannedConstName; - RepeatedTask rt = createSEDMLreptask(rangeId, l2gMap, simContext, - dataGeneratorTasksSet, mathOverrides, ownerTaskId, scannedConstName, repeatedTaskId, simContextId); - ownerTaskId = repeatedTaskId; - repeatedTaskIndex++; - sedmlModel.addTask(rt); - } - - } else { - // both scanned and simple parameters : create new model with change for each simple override; add RepeatedTask - Map rangeToRepeatedTaskHash = new LinkedHashMap<> (); - List repeatedTasksList = new ArrayList<> (); - - // create new model with change for each unscanned parameter that has override - String overriddenSimContextId = simContextId + "_" + overrideCount; - String overriddenSimContextName = simContextName + " modified"; - Model sedModel = new Model(overriddenSimContextId, overriddenSimContextName, languageURN, "#"+simContextId); - overrideCount++; - - String taskId = "tsk_" + simContextCnt + "_" + simCount; - String ownerTaskId = taskId; - Task sedmlTask = new Task(taskId, vcSimulation.getName(), overriddenSimContextId, utcSim.getId()); - dataGeneratorTasksSet.add(sedmlTask.getId()); - sedmlModel.addTask(sedmlTask); - - // scanned parameters - int repeatedTaskIndex = 0; - int variableCount = 0; - for (String scannedConstName : scannedConstantsNames) { - String repeatedTaskId = "repTsk_" + simContextCnt + "_" + simCount + "_" + repeatedTaskIndex; - String rangeId = "range_" + simContextCnt + "_" + simCount + "_" + scannedConstName; - RepeatedTask rt = createSEDMLreptask(rangeId, l2gMap, simContext, - dataGeneratorTasksSet, mathOverrides, ownerTaskId, scannedConstName, repeatedTaskId, overriddenSimContextId); - ownerTaskId = repeatedTaskId; - repeatedTaskIndex++; - - // use scannedParamHash to store rangeId for that param, since it might be needed if unscanned param has a scanned param in expr. - if (scannedParamHash.get(scannedConstName).equals(scannedConstName)) { - // the hash was originally populated as . Replace 'value' with rangeId for scannedParam - scannedParamHash.put(scannedConstName, rangeId); - rangeToRepeatedTaskHash.put(rangeId, rt); // we'll need the right repeated task for this range later on, in the unscanned loop - } - // add to local list; will be added to sedml doc later - repeatedTasksList.add(rt); - } - - // for unscanned parameter overrides - for (String unscannedParamName : unscannedParamHash.values()) { - SymbolTableEntry ste = getSymbolTableEntryForModelEntity(mathSymbolMapping, unscannedParamName); - Expression unscannedParamExpr = mathOverrides.getActualExpression(unscannedParamName, MathOverrides.ScanIndex.ZERO); - unscannedParamExpr = adjustIfRateParam(vcSimulation, ste, unscannedParamExpr); - if(unscannedParamExpr.isNumeric()) { - // if expression is numeric, add ChangeAttribute to model created above - XPathTarget targetXpath = getTargetAttributeXPath(ste, l2gMap, simContext); - ChangeAttribute changeAttribute = new ChangeAttribute(targetXpath, unscannedParamExpr.infix()); - sedModel.addChange(changeAttribute); - } else { - // check for any scanned parameter in unscanned parameter expression - String[] exprSymbols = unscannedParamExpr.getSymbols(); - boolean bHasScannedParameter = false; - List scannedParamNameInUnscannedParamExpList = new ArrayList<> (); - for (String symbol : exprSymbols) { - if (scannedParamHash.get(symbol) != null) { - bHasScannedParameter = true; - scannedParamNameInUnscannedParamExpList.add(new String(symbol)); - } - } - // (scanned parameter in expr) ? (add setValue for unscanned param in repeatedTask) : (add computeChange to modifiedModel) - Map symbolToTargetMap = new LinkedHashMap<>(); - String[] symbols = unscannedParamExpr.getSymbols(); - for(String symbol : symbols) { - SymbolTableEntry entry = getSymbolTableEntryForModelEntity(mathSymbolMapping, symbol); - XPathTarget target = getTargetAttributeXPath(entry, l2gMap, simContext); - symbolToTargetMap.put(symbol, target); - } - if (bHasScannedParameter) { - // create setValue for unscannedParamName (which contains a scanned param in its expression) - SymbolTableEntry entry = getSymbolTableEntryForModelEntity(mathSymbolMapping, unscannedParamName); - XPathTarget target = getTargetAttributeXPath(entry, l2gMap, simContext); - Set rangeIdSet = new HashSet<>(); - for(String scannedParamNameInUnscannedParamExp : scannedParamNameInUnscannedParamExpList) { - String rangeId = scannedParamHash.get(scannedParamNameInUnscannedParamExp); - rangeIdSet.add(rangeId); // all the ranges referred in the scannedParamNameInUnscannedParamExpList - } - for(String rangeId : rangeIdSet) { - SetValue setValue = new SetValue(target, rangeId, overriddenSimContextId); // @TODO: we have no range?? - Expression expr = new Expression(unscannedParamExpr); - for(String symbol : symbols) { - String symbolName = rangeId + "_" + symbol + "_" + variableCount; - Variable sedmlVar = new Variable(symbolName, symbolName, overriddenSimContextId, symbolToTargetMap.get(symbol).toString()); // sbmlSupport.getXPathForSpecies(symbol)); - setValue.addVariable(sedmlVar); - expr.substituteInPlace(new Expression(symbol), new Expression(symbolName)); - variableCount++; - } - ASTNode math = Libsedml.parseFormulaString(expr.infix()); - setValue.setMath(math); - RepeatedTask rtRecovered = rangeToRepeatedTaskHash.get(rangeId); - rtRecovered.addChange(setValue); - } - } else { - // non-numeric expression : add 'computeChange' to modified model - XPathTarget targetXpath = getTargetAttributeXPath(ste, l2gMap, simContext); - ComputeChange computeChange = new ComputeChange(targetXpath); - Expression expr = new Expression(unscannedParamExpr); - for (String symbol : exprSymbols) { - String varName = overriddenSimContextId + "_" + symbol + "_" + variableCount; - Variable sedmlVar = new Variable(varName, varName, overriddenSimContextId, symbolToTargetMap.get(symbol).toString()); - expr.substituteInPlace(new Expression(symbol), new Expression(varName)); - computeChange.addVariable(sedmlVar); - variableCount++; - } - ASTNode math = Libsedml.parseFormulaString(expr.infix()); - computeChange.setMath(math); - sedModel.addChange(computeChange); - } - } - } - sedmlModel.addModel(sedModel); - for(RepeatedTask rt : repeatedTasksList) { - sedmlModel.addTask(rt); - } - } - } else { // no math overrides, add basic task. - String taskId = "tsk_" + simContextCnt + "_" + simCount; - Task sedmlTask = new Task(taskId, vcSimulation.getName(), simContextId, utcSim.getId()); - dataGeneratorTasksSet.add(sedmlTask.getId()); - sedmlModel.addTask(sedmlTask); -// taskRef = taskId; // to be used later to add dataGenerators : one set of DGs per model (simContext). - } - } - - private Expression adjustIfRateParam(Simulation vcSimulation, SymbolTableEntry ste, Expression unscannedParamExpr) - throws ExpressionException { - if (ste instanceof KineticsParameter) { - KineticsParameter kp = (KineticsParameter)ste; - if (kp.getKinetics().getAuthoritativeParameter() == kp) { - SimulationContext simulationContext = (SimulationContext) vcSimulation.getSimulationOwner(); - boolean bSpatial = simulationContext.getGeometry().getDimension() > 0; - boolean bLumped = kp.getKinetics() instanceof LumpedKinetics; - if (!bLumped && !bSpatial) { - MathSymbolMapping msm = (MathSymbolMapping) simulationContext.getMathDescription().getSourceSymbolMapping(); - cbit.vcell.math.Variable structSize = msm.getVariable(simulationContext.getGeometryContext().getStructureMapping(kp.getKinetics().getReactionStep().getStructure()).getSizeParameter()); - unscannedParamExpr = Expression.mult(unscannedParamExpr, new Expression(structSize.getName())); - } - } - } - return unscannedParamExpr; - } - - private RepeatedTask createSEDMLreptask(String rangeId, Map, String> l2gMap, - SimulationContext simContext, Set dataGeneratorTasksSet, - MathOverrides mathOverrides, String ownerTaskId, String scannedConstName, String repeatedTaskId, String modelReferenceId) - throws ExpressionException, DivideByZeroException, MappingException { - RepeatedTask rt = new RepeatedTask(repeatedTaskId, mathOverrides.getSimulation().getName(), true, rangeId); - dataGeneratorTasksSet.add(rt.getId()); - SubTask subTask = new SubTask("0", ownerTaskId); - dataGeneratorTasksSet.remove(ownerTaskId); - rt.addSubtask(subTask); - ConstantArraySpec constantArraySpec = mathOverrides.getConstantArraySpec(scannedConstName); - MathSymbolMapping mathSymbolMapping = (MathSymbolMapping)simContext.getMathDescription().getSourceSymbolMapping(); - // list of Ranges, if sim is parameter scan. - Range r = createSEDMLrange(rangeId, rt, constantArraySpec, scannedConstName, simContext, l2gMap, modelReferenceId, mathOverrides.getSimulation()); - // list of Changes - SymbolTableEntry ste = getSymbolTableEntryForModelEntity(mathSymbolMapping, scannedConstName); - XPathTarget target = getTargetAttributeXPath(ste, l2gMap, simContext); - //ASTNode math1 = new ASTCi(r.getId()); // was scannedConstName - ASTNode math1 = Libsedml.parseFormulaString(r.getId()); // here the math is always the range id expression - SetValue setValue = new SetValue(target, r.getId(), modelReferenceId); - setValue.setMath(math1); - rt.addChange(setValue); - return rt; - } - - private Range createSEDMLrange(String rangeId, RepeatedTask rt, ConstantArraySpec constantArraySpec, String scannedConstantName, SimulationContext simContext, Map, String> l2gMap, String modelReferenceId, Simulation vcSim) - throws ExpressionException, DivideByZeroException, MappingException { - Range r = null; - SimulationContext sc = (SimulationContext)vcSim.getSimulationOwner(); - SymbolReplacement sr = sc.getMathOverridesResolver().getSymbolReplacement(scannedConstantName, true); - String cName = sr != null ? sr.newName : scannedConstantName; - MathSymbolMapping msm = (MathSymbolMapping)simContext.getMathDescription().getSourceSymbolMapping(); - SymbolTableEntry ste = msm.getBiologicalSymbol(vcSim.getMathOverrides().getConstant(cName))[0]; - if(constantArraySpec.getType() == ConstantArraySpec.TYPE_INTERVAL) { - // ------ Uniform Range - UniformType type = constantArraySpec.isLogInterval() ? UniformType.LOG : UniformType.LINEAR; - if (constantArraySpec.getMinValue().isNumeric() && constantArraySpec.getMaxValue().isNumeric()) { - r = new UniformRange(rangeId, constantArraySpec.getMinValue().evaluateConstant(), - constantArraySpec.getMaxValue().evaluateConstant(), constantArraySpec.getNumValues(), type); - rt.addRange(r); - return r; - } else { - r = new UniformRange(rangeId, 1, 2, constantArraySpec.getNumValues(), type); - rt.addRange(r); - // now make a FunctionalRange with expressions - FunctionalRange fr = new FunctionalRange("fr_"+rangeId, rangeId); - Expression expMin = constantArraySpec.getMinValue(); - expMin = adjustIfRateParam(vcSim, ste, expMin); - Expression expMax = constantArraySpec.getMaxValue(); - expMax = adjustIfRateParam(vcSim, ste, expMax); - Expression trans = Expression.add(new Expression(rangeId), new Expression("-1")); - Expression func = Expression.add(expMax, Expression.negate(expMin)); - func = Expression.mult(func, trans); - func = Expression.add(expMin, func); - createFunctionalRangeElements(fr, func, simContext, l2gMap, modelReferenceId); - rt.addRange(fr); - return fr; - } - } else { - // ----- Vector Range - // we try to preserve symbolic values coming from unit transforms... - cbit.vcell.math.Constant[] cs = constantArraySpec.getConstants(); - ArrayList values = new ArrayList(); - Expression expFact = null; - for (int i = 0; i < cs.length; i++){ - if (!(cs[i].getExpression().evaluateConstant() == 0)) { - expFact = cs[i].getExpression(); - break; - } - } - // compute list of numeric multipliers - for (int i = 0; i < cs.length; i++){ - Expression exp = cs[i].getExpression(); - exp = Expression.div(exp, expFact).simplifyJSCL(); - values.add(exp.evaluateConstant()); - } - r = new VectorRange(rangeId, values); - rt.addRange(r); - // now make a FunctionalRange with expressions - FunctionalRange fr = new FunctionalRange("fr_"+rangeId, rangeId); - expFact = Expression.mult(new Expression(rangeId), expFact); - expFact = adjustIfRateParam(vcSim, ste, expFact); - createFunctionalRangeElements(fr, expFact, simContext, l2gMap, modelReferenceId); - rt.addRange(fr); - return fr; - } - } - - private void createFunctionalRangeElements(FunctionalRange fr, Expression func, SimulationContext simContext, - Map, String> l2gMap, String modelReferenceId) throws ExpressionException, MappingException { - String[] symbols = func.getSymbols(); - MathSymbolMapping msm = (MathSymbolMapping)simContext.getMathDescription().getSourceSymbolMapping(); - for(String symbol : symbols) { - if (symbol.equals(fr.getRange())) continue; - SymbolTableEntry entry = getSymbolTableEntryForModelEntity(msm, symbol); - XPathTarget target = getTargetAttributeXPath(entry, l2gMap, simContext); - String symbolName = fr.getRange() + "_" + symbol; - Variable sedmlVar = new Variable(symbolName, symbolName, modelReferenceId, target.toString()); // sbmlSupport.getXPathForSpecies(symbol)); - fr.addVariable(sedmlVar); - func.substituteInPlace(new Expression(symbol), new Expression(symbolName)); - } - ASTNode math = Libsedml.parseFormulaString(func.infix()); - fr.setMath(math); - } - - private void writeModelSBML(String savePath, String sBaseFileName, String sbmlString, SimulationContext simContext) throws IOException { - String simContextName = simContext.getName(); - String simContextId = TokenMangler.mangleToSName(simContextName); - String filePathStrAbsolute = Paths.get(savePath, sBaseFileName + "_" + simContextId + ".xml").toString(); - String filePathStrRelative = sBaseFileName + "_" + simContextId + ".xml"; - XmlUtil.writeXMLStringToFile(sbmlString, filePathStrAbsolute, true); - modelFilePathStrAbsoluteList.add(filePathStrRelative); - sedmlModel.addModel(new Model(simContextId, simContextName, sbmlLanguageURN, filePathStrRelative)); - } - - private Notes createNotesElement(String notesStr) { - // create some xhtml. E.g., - org.jdom2.Element para = new org.jdom2.Element("p"); - para.setText(notesStr); - // create a notes element - Notes note = new Notes(para); - return note; - } - private void addNotesChild(Notes note, String kisao, String desc) { - Element sub = new Element("AlgoritmParameter", "http://www.biomodels.net/kisao/KISAO_FULL#"); - sub.setAttribute(TokenMangler.mangleToSName(kisao), desc); - note.getNotesElement().addContent(sub); - } - - public static SymbolTableEntry getSymbolTableEntryForModelEntity(MathSymbolMapping mathSymbolMapping, String paramName) throws MappingException { - cbit.vcell.math.Variable mathVar = mathSymbolMapping.findVariableByName(paramName); - if (mathVar == null) { - throw new MappingException("No variable found for parameter: " + paramName); - } - SymbolTableEntry[] stEntries = mathSymbolMapping.getBiologicalSymbol(mathVar); - if (stEntries == null) { - throw new MappingException("No matching biological symbol for variable: " + mathVar); - } - - // if the extra stes in the array are KineticsProxyParameters/ModelQuantities, remove them from array. Should be left with only one entry for overriddenConstantName - if (stEntries.length > 1) { - // - // If there are more than one stEntries, usually, it is a regular ste (species, global parameter, structure, etc) together with - // kineticsProxyParameters (that have the regular ste as target) or Model quantities (structure size, membrane voltage). - // So filtering out the kinticProxyParametes should leave only the regular parameter, - // which is what we want. If there are more, then there is a problem. - // - ArrayList steList = new ArrayList(Arrays.asList(stEntries)); - for (int i = 0; i < stEntries.length; i++) { - if (stEntries[i] instanceof ProxyParameter) { - SymbolTableEntry ppTargetSte = ((ProxyParameter)stEntries[i]).getTarget(); - if (steList.contains(ppTargetSte) || ppTargetSte instanceof ModelQuantity) { - steList.remove(stEntries[i]); - } - } - if (stEntries[i] instanceof ModelQuantity) { - if (steList.contains(stEntries[i])) { - steList.remove(stEntries[i]); - } - } - } - // after removing proxy parameters, cannot have more than one ste in list - if (steList.size() == 0) { - throw new MappingException("No mapping entry for constant : '" + paramName + "'."); - } - if (steList.size() > 1) { - throw new MappingException("Cannot have more than one mapping entry for constant : '" + paramName + "'."); - } - SymbolTableEntry[] stes = (SymbolTableEntry[])steList.toArray(new SymbolTableEntry[0]); - return stes[0]; - } else { - return stEntries[0]; - } - } - - private XPathTarget getTargetAttributeXPath(SymbolTableEntry ste, Map, String> l2gMap, SimulationContext simContext) { - // VCML model format - if (l2gMap == null) return getVCMLTargetXPath(ste, simContext); - // SBML model format - SBMLSupport sbmlSupport = new SBMLSupport(); // to get Xpath string for variables. - XPathTarget targetXpath = null; - if (ste instanceof SpeciesContext || ste instanceof SpeciesContextSpecParameter) { - String speciesId = TokenMangler.mangleToSName(ste.getName()); - // can change species initial concentration or amount - String speciesAttr = ""; - if (ste instanceof SpeciesContextSpecParameter) { - SpeciesContextSpecParameter scsp = (SpeciesContextSpecParameter)ste; - speciesId = TokenMangler.mangleToSName((scsp).getSpeciesContext().getName()); - int role = scsp.getRole(); - if (role == SpeciesContextSpec.ROLE_InitialConcentration) { - speciesAttr = scsp.getName(); - } - if (role == SpeciesContextSpec.ROLE_InitialCount) { - speciesAttr = scsp.getName(); - } - if(role == SpeciesContextSpec.ROLE_DiffusionRate) { - speciesAttr = scsp.getName(); - } - } - if (speciesAttr.length() < 1) { - targetXpath = new XPathTarget(sbmlSupport.getXPathForCompartment(speciesId)); - } else if (speciesAttr.equalsIgnoreCase("initialConcentration") || speciesAttr.equalsIgnoreCase("initConc")) { - targetXpath = new XPathTarget(sbmlSupport.getXPathForSpecies(speciesId, SpeciesAttribute.initialConcentration)); - } else if (speciesAttr.equalsIgnoreCase("initialCount") || speciesAttr.equalsIgnoreCase("initCount")) { - targetXpath = new XPathTarget(sbmlSupport.getXPathForSpecies(speciesId, SpeciesAttribute.initialAmount)); - } else if (speciesAttr.equalsIgnoreCase("diff")) { - targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(speciesId + "_" + speciesAttr, ParameterAttribute.value)); - } else { - throw new RuntimeException("Unknown species attribute '" + speciesAttr + "'; cannot get xpath target for species '" + speciesId + "'."); - } - - } else if (ste instanceof ModelParameter || ste instanceof ReservedSymbol) { - // can only change parameter value. - targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(ste.getName(), ParameterAttribute.value)); - // use Ion's sample 3, with spatial app - } else if (ste instanceof Structure || ste instanceof Structure.StructureSize || ste instanceof StructureMappingParameter) { - String compartmentId = TokenMangler.mangleToSName(ste.getName()); - // can change compartment size or spatial dimension, but in vcell, we cannot change compartment dimension. - String compartmentAttr = ""; - String mappingId = ""; - if (ste instanceof Structure.StructureSize) { - compartmentId = TokenMangler.mangleToSName(((StructureSize)ste).getStructure().getName()); - compartmentAttr = ((StructureSize)ste).getName(); - } - if (ste instanceof StructureMappingParameter) { - StructureMappingParameter smp = (StructureMappingParameter)ste; - compartmentId = TokenMangler.mangleToSName(smp.getStructure().getName()); - int role = ((StructureMappingParameter)ste).getRole(); - if (role == StructureMapping.ROLE_Size) { - compartmentAttr = smp.getName(); - } else if(role == StructureMapping.ROLE_AreaPerUnitArea || role == StructureMapping.ROLE_VolumePerUnitVolume) { - compartmentAttr = smp.getName(); - Structure structure = smp.getStructure(); - GeometryClass gc = smp.getSimulationContext().getGeometryContext().getStructureMapping(structure).getGeometryClass(); - mappingId = TokenMangler.mangleToSName(gc.getName() + structure.getName()); - } - } - if (compartmentAttr.length() < 1) { - targetXpath = new XPathTarget(sbmlSupport.getXPathForCompartment(compartmentId)); - } else if (compartmentAttr.equalsIgnoreCase("size")) { - targetXpath = new XPathTarget(sbmlSupport.getXPathForCompartment(compartmentId, CompartmentAttribute.size)); - } else if(compartmentAttr.equalsIgnoreCase("AreaPerUnitArea") || compartmentAttr.equalsIgnoreCase("VolPerUnitVol")) { - targetXpath = new XPathTarget(sbmlSupport.getXPathForCompartmentMapping(compartmentId, mappingId, CompartmentAttribute.unitSize)); - } else { - throw new RuntimeException("Unknown compartment attribute '" + compartmentAttr + "'; cannot get xpath target for compartment '" + compartmentId + "'."); - } - } else if (ste instanceof KineticsParameter) { - KineticsParameter kp = (KineticsParameter)ste; - String reactionID = kp.getKinetics().getReactionStep().getName(); - String parameterID = kp.getName(); - Pair key = new Pair(reactionID, parameterID); - String value = l2gMap.get(key); - if(value == null) { - // stays as local parameter - targetXpath = new XPathTarget(sbmlSupport.getXPathForKineticLawParameter(reactionID, parameterID, ParameterAttribute.value)); - } else { - // became a global in SBML, we need to refer to that global - targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(value, ParameterAttribute.value)); - } - } else if (ste instanceof Membrane.MembraneVoltage) { - // they are exported as globals - targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(TokenMangler.mangleToSName(ste.getName()), ParameterAttribute.value)); - } else { - logger.error("redundant error log: "+"Entity should be SpeciesContext, Structure, ModelParameter, ReserverdSymbol, KineticsParameter, or MembraneVoltage : " + ste.getClass()); - throw new RuntimeException("Unsupported entity in SBML model export: "+ste.getClass()); - } - return targetXpath; - } - - - private XPathTarget getVCMLTargetXPath(SymbolTableEntry ste, SimulationContext simContext) { - XPathTarget targetXpath = null; - if (ste instanceof SpeciesContextSpecParameter) { - String paramXpath = ""; - SpeciesContextSpecParameter scsp = (SpeciesContextSpecParameter)ste; - String baseXpath = VCMLSupport.getXPathForSpeciesContextSpec(simContext.getName(), scsp.getSpeciesContextSpec().getSpeciesContext().getName()); - int role = scsp.getRole(); - if (role == SpeciesContextSpec.ROLE_InitialConcentration) { - paramXpath = "/vcml:InitialConcentration"; - } - if (role == SpeciesContextSpec.ROLE_InitialCount) { - paramXpath = "/vcml:InitialCount"; - } - if(role == SpeciesContextSpec.ROLE_DiffusionRate) { - paramXpath = "/vcml:Diffusion"; - } - targetXpath = new XPathTarget(baseXpath+paramXpath); - } else if (ste instanceof ModelParameter) { - // can only change parameter value. - targetXpath = new XPathTarget(VCMLSupport.getXPathForModelParameter(ste.getName())); - } else if (ste instanceof Structure || ste instanceof Structure.StructureSize || ste instanceof StructureMappingParameter) { - // can change compartment size or spatial dimension, but in vcell, we cannot change compartment dimension. - String attributeName = "Size"; - Structure struct = null; - if (ste instanceof Structure) { - struct = (Structure) ste; - } - if (ste instanceof Structure.StructureSize) { - struct = ((StructureSize)ste).getStructure(); - } - if (ste instanceof StructureMappingParameter) { - StructureMappingParameter smp = (StructureMappingParameter)ste; - struct = smp.getStructure(); - int role = smp.getRole(); - if(role == StructureMapping.ROLE_AreaPerUnitArea) { - attributeName = "AreaPerUnitArea"; - } else if (role == StructureMapping.ROLE_VolumePerUnitVolume) { - attributeName = "VolumePerUnitVolume"; - } - } - if (struct instanceof Feature) { - targetXpath = new XPathTarget(VCMLSupport.getXPathForFeatureMappingAttribute(simContext.getName(), - struct.getName(), attributeName)); - } else { - targetXpath = new XPathTarget(VCMLSupport.getXPathForMembraneMappingAttribute(simContext.getName(), - struct.getName(), attributeName)); - } - } else if (ste instanceof KineticsParameter) { - KineticsParameter kp = (KineticsParameter) ste; - targetXpath = new XPathTarget(VCMLSupport.getXPathForKineticLawParameter(kp.getKinetics().getReactionStep().getName(), kp.getName())); - } else if (ste instanceof Membrane.MembraneVoltage) { - targetXpath = new XPathTarget(VCMLSupport.getXPathForMembraneMappingAttribute(simContext.getName(), - ((Membrane.MembraneVoltage)ste).getMembrane().getName(), "InitialVoltage")); - } else { - logger.error("redundant error log: "+"Entity should be SpeciesContext, Structure, ModelParameter, KineticsParameter, or MembraneVoltage : " + ste.getClass()); - throw new RuntimeException("Unsupported entity in VCML model export: "+ste.getClass()); - } - return targetXpath; - } - - public void addSedmlFileToList(String sedmlFileName) { - if(sedmlFileName != null && !sedmlFileName.isEmpty()) { - sedmlFilePathStrAbsoluteList.add(sedmlFileName); - } - } - - - public boolean createOmexArchive(String srcFolder, String sFileName) { - // writing into combine archive, deleting file if already exists with same name - String omexPath = Paths.get(srcFolder, sFileName + ".omex").toString(); - File omexFile = new File(omexPath); - if(omexFile.exists()) { - omexFile.delete(); - } - - try ( CombineArchive archive = new CombineArchive(omexFile); ) { - - - for (String sd : sedmlFilePathStrAbsoluteList) { - File s = Paths.get(srcFolder, sd).toFile(); - archive.addEntry( - s, - "./" + sd, // target file name - new URI("http://identifiers.org/combine.specifications/sed-ml"), - true // mark file as master - ); - } - for (String sd : modelFilePathStrAbsoluteList) { - archive.addEntry( - Paths.get(srcFolder, sd).toFile(), - "./" + sd, // target file name - new URI("http://identifiers.org/combine.specifications/sbml"), - false // mark file as master - ); - } - - archive.addEntry( - Paths.get(srcFolder, sFileName + ".vcml").toFile(), - "./" + sFileName + ".vcml", - new URI("http://purl.org/NET/mediatypes/application/vcml+xml"), - false - ); - - File dir = new File(srcFolder); - String[] files = dir.list(); - if (files == null) { - throw new RuntimeException("createZipArchive: No files found in directory: " + srcFolder); - } - Path rdfFilePath = null; - for(String sd : files) { - Path filePath = Paths.get(srcFolder, sd); - if (sd.endsWith(".rdf")) { - rdfFilePath = filePath; - // the CombineArchive library does not allow to directly write to the /metadata.rdf file - // instead, we copy the file to /metadata.rdf later after the archive is closed - // - // archive.addEntry( - // filePath.toFile(), - // "./metadata.rdf", - // new URI("http://identifiers.org/combine.specifications/omex-metadata"), - // false - // ); - } - if (sd.endsWith(".png")) { - archive.addEntry( - filePath.toFile(), - "./" + sd, - new URI("http://purl.org/NET/mediatypes/image/png"), - false - ); - } - } - if (rdfFilePath != null) { - // create temporary /metadata.rdf file so that an entry for /metadata.rdf is included in the Manifest - OmexDescription omexDescription = new OmexDescription(); - omexDescription.setDescription("VCell Simulation Archive"); - omexDescription.modified.add(new Date()); - archive.addDescription(new OmexMetaDataObject(omexDescription)); - } - - archive.pack(); - archive.close(); - - if (rdfFilePath != null) { - // now that the OMEX archive is closed and written to disk, open it as a regular zip file - // and replace the generated metadata.rdf file with the one we created. - replaceMetadataRdfFileInArchive(omexFile.toPath(), rdfFilePath); - } - - if (OperatingSystemInfo.getInstance().isWindows()) repairManifestEntry(omexFile.toPath()); - removeOtherFiles(srcFolder, files); - - } catch (Exception e) { - throw new RuntimeException("createZipArchive threw exception: " + e.getMessage()); - } - return true; - } - - private static void replaceMetadataRdfFileInArchive(Path zipFilePath, Path newFilePath) throws IOException { - String pathInZip = "./metadata.rdf"; - try( FileSystem fs = FileSystems.newFileSystem(zipFilePath) ) { - Path fileInsideZipPath = fs.getPath(pathInZip); - Files.delete(fileInsideZipPath); - Files.copy(newFilePath, fileInsideZipPath); - } - } - - private static void repairManifestEntry(Path zipFilePath) throws IOException { - try (FileSystem fs = FileSystems.newFileSystem(zipFilePath)) { - Path manifestPath = fs.getPath("/", "manifest.xml"); - if (!Files.exists(manifestPath)) throw new IOException("The manifest file in " + zipFilePath + " is missing"); - - List rawLines, correctedLines = new ArrayList<>(); - try (BufferedReader reader = new BufferedReader(Files.newBufferedReader(manifestPath))) { - rawLines = reader.lines().toList(); - } - for (String rawLine : rawLines) { - correctedLines.add(rawLine.contains(".\\") ? rawLine.replace(".\\", "./") : rawLine); - } - Path tmpFilePath = Files.createTempFile("fixedManifest", ""); - try (BufferedWriter writer = new BufferedWriter(Files.newBufferedWriter(tmpFilePath))) { - for (String line : correctedLines) writer.write(line + "\n"); - } - Files.copy(tmpFilePath, manifestPath, StandardCopyOption.REPLACE_EXISTING); - } - } - - private static void removeOtherFiles(String outputDir, String[] files) { - boolean isDeleted = false; - for (String sd : files) { - if (sd.endsWith(".sedml") || sd.endsWith(".sbml") || sd.endsWith("xml") || sd.endsWith(".vcml") || sd.endsWith(".rdf") || sd.endsWith(".png")) { - isDeleted = Paths.get(outputDir, sd).toFile().delete(); - if (!isDeleted) { - throw new RuntimeException("Unable to remove intermediate file '" + sd + "'."); - } - } - } - } - - public static Map getUnsupportedApplicationMap(BioModel bioModel, ModelFormat modelFormat) { - HashMap unsupportedApplicationMap = new HashMap<>(); - Arrays.stream(bioModel.getSimulationContexts()).forEach(simContext -> { - if (modelFormat == ModelFormat.SBML) { - try { - SBMLExporter.validateSimulationContextSupport(simContext); - } catch (UnsupportedSbmlExportException e) { - unsupportedApplicationMap.put(simContext.getName(), e.getMessage()); - } - } - }); - return unsupportedApplicationMap; - } - - public static class SEDMLExportException extends Exception { - public SEDMLExportException(String message) { - super(message); - } - public SEDMLExportException(String message, Exception cause) { - super(message, cause); - } - } - - public static List writeBioModel(BioModel bioModel, - Optional publicationMetadata, - File exportFileOrDirectory, - ModelFormat modelFormat, - Predicate simContextExportFilter, - boolean bHasPython, - boolean bValidation, - boolean bCreateOmexArchive - ) throws SEDMLExportException, OmexPythonUtils.OmexValidationException, IOException { - Predicate simulationExportFilter = s -> true; - SEDMLEventLog sedmlEventLog = (String entry) -> {}; - Optional jsonReportFile = Optional.empty(); - return writeBioModel( - bioModel, publicationMetadata, jsonReportFile, exportFileOrDirectory, simulationExportFilter, simContextExportFilter, - modelFormat, sedmlEventLog, bHasPython, bValidation, bCreateOmexArchive); - } - - public static List writeBioModel(File vcmlFilePath, - BioModelInfo bioModelInfo, - File outputDir, - Predicate simulationExportFilter, - ModelFormat modelFormat, - SEDMLEventLog eventLogWriter, - boolean bAddPublicationInfo, - boolean bSkipUnsupportedApps, - boolean bHasPython, - boolean bValidate - ) throws SEDMLExportException, OmexPythonUtils.OmexValidationException, IOException { - - // get VCML name from VCML path - String vcmlName = FilenameUtils.getBaseName(vcmlFilePath.getName()); // platform independent, strips extension too - Optional jsonReportFile = Optional.of(Paths.get( - outputDir.getAbsolutePath(), "json_reports" ,vcmlName + ".json").toFile()); - File omexOutputFile = Paths.get(outputDir.getAbsolutePath(), vcmlName + ".omex").toFile(); - eventLogWriter.writeEntry(vcmlName); - - // Create biomodel - BioModel bioModel; - try { - bioModel = XmlHelper.XMLToBioModel(new XMLSource(vcmlFilePath)); - bioModel.updateAll(false); - bioModel.refreshDependencies(); - eventLogWriter.writeEntry(vcmlName + ",VCML,SUCCEEDED\n"); - } catch (XmlParseException | MappingException e1) { - String msg = vcmlName + " VCML failed to parse and generate math: "+e1.getMessage(); - logger.error(msg, e1); - eventLogWriter.writeEntry(vcmlName + ",VCML,FAILED"+e1.getMessage() + "\n"); - throw new SEDMLExportException(msg, e1); - } - - Predicate simContextExportFilter = sc -> true; - if (bSkipUnsupportedApps){ - Map unsupportedApplications = SEDMLExporter.getUnsupportedApplicationMap(bioModel, modelFormat); - simContextExportFilter = (SimulationContext sc) -> !unsupportedApplications.containsKey(sc.getName()); - } - - Optional publicationMetadata = Optional.empty(); - if (bioModelInfo!=null && bioModelInfo.getPublicationInfos()!=null && bioModelInfo.getPublicationInfos().length>0) { - if (bAddPublicationInfo) { - publicationMetadata = Optional.of(PublicationMetadata.fromPublicationInfoAndWeb(bioModelInfo.getPublicationInfos()[0])); - }else{ - publicationMetadata = Optional.of(PublicationMetadata.fromPublicationInfo(bioModelInfo.getPublicationInfos()[0])); - } - } - - boolean bCreateOmexArchive = true; - return writeBioModel( - bioModel, publicationMetadata, jsonReportFile, omexOutputFile, simulationExportFilter, simContextExportFilter, - modelFormat, eventLogWriter, bHasPython, bValidate, bCreateOmexArchive); - } - - private static List writeBioModel(BioModel bioModel, - Optional publicationMetadata, - Optional jsonReportFile, - File exportFileOrDirectory, - Predicate simulationExportFilter, - Predicate simContextExportFilter, - ModelFormat modelFormat, - SEDMLEventLog sedmlEventLog, - boolean bHasPython, - boolean bValidate, - boolean bCreateOmexArchive - ) throws SEDMLExportException, OmexPythonUtils.OmexValidationException, IOException { - try { - // export the entire biomodel to a SEDML file (all supported applications) - int sedmlLevel = 1; - int sedmlVersion = 2; - String sOutputDirPath = FileUtils.getFullPathNoEndSeparator(exportFileOrDirectory.getAbsolutePath()); - String sBaseFileName = FileUtils.getBaseName(exportFileOrDirectory.getAbsolutePath()); - - List simsToExport = Arrays.stream(bioModel.getSimulations()).filter(simulationExportFilter).collect(Collectors.toList()); - - // we replace the obsolete solver with the fully supported equivalent - for (Simulation simulation : simsToExport) { - if (simulation.getSolverTaskDescription().getSolverDescription().equals(SolverDescription.FiniteVolume)) { - try { - // try to replace with the fully supported equivalent (do we need to reset solver parameters?) - simulation.getSolverTaskDescription().setSolverDescription(SolverDescription.SundialsPDE); - } catch (PropertyVetoException e) { - String msg1 = "Failed to replace obsolete PDE solver '"+SolverDescription.FiniteVolume.name()+"' " + - "with fully supported equivalent PDE solver '"+SolverDescription.SundialsPDE.name()+"'"; - logger.error(msg1,e); - try { - simulation.getSolverTaskDescription().setSolverDescription(SolverDescription.FiniteVolumeStandalone); - } catch (PropertyVetoException e1) { - String msg2 = "Failed to replace obsolete PDE solver '"+SolverDescription.FiniteVolume.name()+"' " + - "with equivalent PDE solver '"+SolverDescription.FiniteVolumeStandalone.name()+"'"; - logger.error(msg2, e1); - throw new RuntimeException(msg2, e1); - } - } - } - } - - // convert biomodel to vcml and save to file. - String vcmlString = XmlHelper.bioModelToXML(bioModel); - String vcmlFileName = Paths.get(sOutputDirPath, sBaseFileName + ".vcml").toString(); - File vcmlFile = new File(vcmlFileName); - XmlUtil.writeXMLStringToFile(vcmlString, vcmlFile.getAbsolutePath(), true); - - String jsonReportPath = null; - if (jsonReportFile.isPresent()){ - jsonReportPath = jsonReportFile.get().getAbsolutePath(); - } - SEDMLExporter sedmlExporter = new SEDMLExporter(sBaseFileName, bioModel, sedmlLevel, sedmlVersion, simsToExport, jsonReportPath); - String sedmlString = sedmlExporter.getSEDMLDocument(sOutputDirPath, sBaseFileName, modelFormat, bValidate, simContextExportFilter).writeDocumentToString(); - - if (bCreateOmexArchive) { - - String sedmlFileName = Paths.get(sOutputDirPath, sBaseFileName + ".sedml").toString(); - XmlUtil.writeXMLStringToFile(sedmlString, sedmlFileName, true); - sedmlExporter.addSedmlFileToList(sBaseFileName + ".sedml"); - - String diagramName = XmlRdfUtil.diagramBaseName + XmlRdfUtil.diagramExtension; - Path diagramPath = Paths.get(sOutputDirPath, diagramName); - XmlRdfUtil.writeModelDiagram(bioModel, diagramPath.toFile()); - - Optional bioModelVersion = Optional.ofNullable(bioModel.getVersion()); - String rdfString = XmlRdfUtil.getMetadata(sBaseFileName, diagramPath.toFile(), bioModelVersion, publicationMetadata); - XmlUtil.writeXMLStringToFile(rdfString, String.valueOf(Paths.get(sOutputDirPath, "metadata.rdf")), true); - - sedmlExporter.createOmexArchive(sOutputDirPath, sBaseFileName); - - if (bValidate && bHasPython) { - OmexPythonUtils.validateOmex(Paths.get(sOutputDirPath, sBaseFileName + ".omex")); - } - } else { - XmlUtil.writeXMLStringToFile(sedmlString, exportFileOrDirectory.getAbsolutePath(), true); - } - return sedmlExporter.sedmlRecorder.getRecords(); - } catch (OmexPythonUtils.OmexValidationException e){ - throw e; - } catch (IOException e){ - throw e; - } catch (InterruptedException | XmlParseException e){ - throw new SEDMLExportException("failed to export biomodel", e); - } - } - - public SEDMLRecorder getSedmlLogger() { - return sedmlRecorder; - } -} - - diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImportException.java b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImportException.java index 14b325bcb7..49622d2b07 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImportException.java +++ b/vcell-core/src/main/java/org/vcell/sedml/SEDMLImportException.java @@ -7,4 +7,8 @@ public SEDMLImportException(String message){ public SEDMLImportException(String message, Exception exception){ super(message, exception); } + + public SEDMLImportException(Exception exception){ + super(exception); + } } diff --git a/vcell-core/src/main/java/org/vcell/sedml/SedMLExporter.java b/vcell-core/src/main/java/org/vcell/sedml/SedMLExporter.java new file mode 100644 index 0000000000..bde80405fd --- /dev/null +++ b/vcell-core/src/main/java/org/vcell/sedml/SedMLExporter.java @@ -0,0 +1,1573 @@ +package org.vcell.sedml; + +import cbit.util.xml.XmlRdfUtil; +import cbit.util.xml.XmlUtil; +import cbit.vcell.biomodel.BioModel; +import cbit.vcell.biomodel.ModelUnitConverter; +import cbit.vcell.geometry.GeometryClass; +import cbit.vcell.mapping.*; +import cbit.vcell.mapping.SimulationContext.Application; +import cbit.vcell.mapping.SpeciesContextSpec.SpeciesContextSpecParameter; +import cbit.vcell.mapping.StructureMapping.StructureMappingParameter; +import cbit.vcell.math.Constant; +import cbit.vcell.math.MathUtilities; +import cbit.vcell.model.*; +import cbit.vcell.model.Kinetics.KineticsParameter; +import cbit.vcell.model.Model.ModelParameter; +import cbit.vcell.model.Model.ReservedSymbol; +import cbit.vcell.model.Structure.StructureSize; +import cbit.vcell.parser.*; +import cbit.vcell.resource.OperatingSystemInfo; +import cbit.vcell.solver.*; +import cbit.vcell.solver.Simulation; +import cbit.vcell.solver.MathOverridesResolver.SymbolReplacement; +import cbit.vcell.xml.*; +import de.unirostock.sems.cbarchive.CombineArchive; +import de.unirostock.sems.cbarchive.meta.OmexMetaDataObject; +import de.unirostock.sems.cbarchive.meta.omex.OmexDescription; +import org.apache.commons.io.FilenameUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Element; +import org.jdom2.Namespace; +import org.jlibsedml.components.*; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.components.algorithm.AlgorithmParameter; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.model.ChangeAttribute; +import org.jlibsedml.components.model.ComputeChange; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.*; +import org.jlibsedml.components.output.*; +import org.jlibsedml.components.task.UniformRange.UniformType; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.task.*; +import org.jlibsedml.modelsupport.SBMLSupport; +import org.jlibsedml.modelsupport.SBMLSupport.CompartmentAttribute; +import org.jlibsedml.modelsupport.SBMLSupport.ParameterAttribute; +import org.jlibsedml.modelsupport.SBMLSupport.SpeciesAttribute; +import org.jlibsedml.modelsupport.SUPPORTED_LANGUAGE; +import org.jmathml.ASTNode; +import org.sbml.jsbml.Annotation; +import org.sbml.jsbml.Parameter; +import org.sbml.jsbml.SBMLDocument; +import org.sbml.jsbml.SBMLReader; +import org.sbml.jsbml.xml.XMLNode; +import org.vcell.sbml.OmexPythonUtils; +import org.vcell.sbml.SbmlException; +import org.vcell.sbml.SimSpec; +import org.vcell.sbml.UnsupportedSbmlExportException; +import org.vcell.sbml.vcell.SBMLExporter; +import org.vcell.util.FileUtils; +import org.vcell.util.ISize; +import org.vcell.util.Pair; +import org.vcell.util.TokenMangler; +import org.vcell.util.document.BioModelInfo; +import org.vcell.util.document.Version; + +import javax.xml.stream.XMLStreamException; +import java.beans.PropertyVetoException; +import java.io.*; +import java.net.URI; +import java.nio.file.*; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + + +public class SedMLExporter { + private final static Logger logger = LogManager.getLogger(SedMLExporter.class); + + private final int sedmlLevel; + private final int sedmlVersion; + private SedMLDataContainer sedmlModel = null; + private cbit.vcell.biomodel.BioModel vcBioModel; + private final String jobId; + private final List modelFilePathStrAbsoluteList = new ArrayList<>(); + private final List sedmlFilePathStrAbsoluteList = new ArrayList<>(); + private List simsToExport = new ArrayList<>(); + + private static final String DATA_GENERATOR_TIME_NAME = "time"; + private static final String DATA_GENERATOR_TIME_SYMBOL = "t"; + + private final String sbmlLanguageURN = SUPPORTED_LANGUAGE.SBML_GENERIC.getURN(); + private final String vcmlLanguageURN = SUPPORTED_LANGUAGE.VCELL_GENERIC.getURN(); + + private final SedMLRecorder sedmlRecorder; + private int simCount; + private int overrideCount; + + private final SBMLSupport sbmlSupport = new SBMLSupport(); + + + public SedMLExporter(String argJobId, BioModel argBiomodel, int argLevel, int argVersion, List argSimsToExport) { + this(argJobId, argBiomodel, argLevel, argVersion, argSimsToExport, null); + } + + public SedMLExporter(String argJobId, BioModel argBiomodel, int argLevel, int argVersion, List argSimsToExport, String jsonFilePath) { + + super(); + + this.jobId = argJobId; + this.vcBioModel = argBiomodel; + this.sedmlLevel = argLevel; + this.sedmlVersion = argVersion; + + this.sedmlRecorder = new SedMLRecorder(argJobId, SEDMLConversion.EXPORT, jsonFilePath); + // we need to collect simulation names to be able to match sims in BioModel clone + if (argSimsToExport != null && !argSimsToExport.isEmpty()) { + for (Simulation sim : argSimsToExport) { + this.simsToExport.add(sim.getName()); + } + } else { + this.simsToExport = null; + } + } + + public SedMLDocument getSEDMLDocument(String sPath, String sBaseFileName, ModelFormat modelFormat, + boolean bRoundTripSBMLValidation, Predicate simContextExportFilter) { + double start = System.currentTimeMillis(); + + // Create an SEDMLDocument and create the SEDMLModel from the document, so that other details can be added to it in translateBioModel() + SedMLDocument sedmlDocument = new SedMLDocument(this.sedmlLevel, this.sedmlVersion); + + final String VCML_NS = "http://sourceforge.net/projects/vcell/vcml"; + final String VCML_NS_PREFIX = "vcml"; + + List nsList = new ArrayList<>(); + Namespace ns = Namespace.getNamespace(SedMLTags.MATHML_NS_PREFIX, SedMLTags.MATHML_NS); + nsList.add(ns); + ns = Namespace.getNamespace(VCML_NS_PREFIX, VCML_NS); + nsList.add(ns); + + if (modelFormat.equals(ModelFormat.SBML)) { + final String SBML_NS = "http://www.sbml.org/sbml/level3/version2/core"; + final String SBML_NS_PREFIX = "sbml"; + final String SPATIAL_NS = "https://sbml.org/documents/specifications/level-3/version-1/spatial"; + final String SPATIAL_NS_PREFIX = "spatial"; + ns = Namespace.getNamespace(SBML_NS_PREFIX, SBML_NS); + nsList.add(ns); + SimulationContext[] simContexts = this.vcBioModel.getSimulationContexts(); + for (SimulationContext sc : simContexts) { + if (sc.getGeometry() != null && sc.getGeometry().getDimension() > 0) { + ns = Namespace.getNamespace(SPATIAL_NS_PREFIX, SPATIAL_NS); + nsList.add(ns); + break; + } + } + } + this.sedmlModel = sedmlDocument.getSedMLModel(); + this.sedmlModel.addAllAdditionalNamespaces(nsList); + + + this.translateBioModelToSedML(sPath, sBaseFileName, modelFormat, bRoundTripSBMLValidation, simContextExportFilter); + + double stop = System.currentTimeMillis(); + Exception timer = new Exception(((stop - start) / 1000) + " seconds"); + // update overall status + if (this.sedmlRecorder.hasErrors()) { + this.sedmlRecorder.addTaskRecord(this.vcBioModel.getName(), TaskType.BIOMODEL, TaskResult.FAILED, timer); + } else { + this.sedmlRecorder.addTaskRecord(this.vcBioModel.getName(), TaskType.BIOMODEL, TaskResult.SUCCEEDED, timer); + } + // should never bomb out just because we fail to export to json... + try { + this.sedmlRecorder.exportToJSON(); + } catch (Exception e) { + logger.error("Failed to export to JSON", e); + } + return sedmlDocument; + } + + private void translateBioModelToSedML(String savePath, String sBaseFileName, ModelFormat modelFormat, + boolean bRoundTripSBMLValidation, Predicate simContextExportFilter) { + SedML sedml = this.sedmlModel.getSedML(); + this.modelFilePathStrAbsoluteList.clear(); + try { + + if (modelFormat == ModelFormat.VCML) { + BioModel prunedBM = XmlHelper.cloneBioModel(this.vcBioModel); + for (Simulation sim : prunedBM.getSimulations()) { + prunedBM.removeSimulation(sim); + } + String vcmlString = XmlHelper.bioModelToXML(prunedBM); + String modelFileNameRel = sBaseFileName + "_sedml.vcml"; + String modelFileNameAbs = Paths.get(savePath, modelFileNameRel).toString(); + XmlUtil.writeXMLStringToFile(vcmlString, modelFileNameAbs, false); + this.modelFilePathStrAbsoluteList.add(modelFileNameRel); + for (int i = 0; i < this.vcBioModel.getSimulationContexts().length; i++) { + this.writeModelVCML(modelFileNameRel, this.vcBioModel.getSimulationContext(i)); + this.sedmlRecorder.addTaskRecord(this.vcBioModel.getSimulationContext(i).getName(), TaskType.SIMCONTEXT, TaskResult.SUCCEEDED, null); + this.exportSimulations(i, this.vcBioModel.getSimulationContext(i), null, null, this.vcmlLanguageURN); + } + } + if (modelFormat == ModelFormat.SBML) { + try { +// // TODO: uncomment the for loop below to only export non-spatial +// for(Simulation sim : vcBioModel.getSimulations()) { +// if(sim.isSpatial()) { +// sedmlRecorder.addTaskLog(vcBioModel.getName(), TaskType.MODEL, TaskResult.FAILED, new RuntimeException("spatial")); +// return; +// } +// } + + // convert to SBML units; this also ensures we will use a clone + this.vcBioModel = ModelUnitConverter.createBioModelWithSBMLUnitSystem(this.vcBioModel); + this.sedmlRecorder.addTaskRecord(this.vcBioModel.getName(), TaskType.UNITS, TaskResult.SUCCEEDED, null); + } catch (Exception e1) { + String msg = "unit conversion failed for BioModel '" + this.vcBioModel.getName() + "': " + e1.getMessage(); + logger.error(msg, e1); + this.sedmlRecorder.addTaskRecord(this.vcBioModel.getName(), TaskType.UNITS, TaskResult.FAILED, e1); + throw e1; + } + SimulationContext[] simContexts = Arrays.stream(this.vcBioModel.getSimulationContexts()) + .filter(simContextExportFilter).toArray(SimulationContext[]::new); + + if (simContexts.length == 0) { + this.sedmlRecorder.addTaskRecord(this.vcBioModel.getName(), TaskType.MODEL, TaskResult.FAILED, new Exception("Model has no Applications")); + } else { + int simContextCnt = 0; // for model count, task subcount + for (SimulationContext simContext : simContexts) { + // Export the application itself to SBML, with default values (overrides will become model changes or repeated tasks) + String sbmlString = null; + Map, String> l2gMap = null; // local to global translation map + boolean sbmlExportFailed = false; + Exception simContextException = null; + try { + SBMLExporter.validateSimulationContextSupport(simContext); + boolean isSpatial = simContext.getGeometry().getDimension() > 0; + Pair, String>> pair = XmlHelper.exportSBMLwithMap(this.vcBioModel, 3, 2, 0, isSpatial, simContext, bRoundTripSBMLValidation); + sbmlString = pair.one; + l2gMap = pair.two; + this.writeModelSBML(savePath, sBaseFileName, sbmlString, simContext); + this.sedmlRecorder.addTaskRecord(simContext.getName(), TaskType.SIMCONTEXT, TaskResult.SUCCEEDED, null); + } catch (Exception e) { + String msg = "SBML export failed for simContext '" + simContext.getName() + "': " + e.getMessage(); + logger.error(msg, e); + sbmlExportFailed = true; + simContextException = e; + this.sedmlRecorder.addTaskRecord(simContext.getName(), TaskType.SIMCONTEXT, TaskResult.FAILED, e); + } + + if (!sbmlExportFailed) { + // simContext was exported succesfully, now we try to export its simulations + this.exportSimulations(simContextCnt, simContext, sbmlString, l2gMap, this.sbmlLanguageURN); + } else { + System.err.println(this.sedmlRecorder.getRecordsAsCSV()); + throw new Exception("SimContext '" + simContext.getName() + "' could not be exported to SBML :" + simContextException.getMessage(), simContextException); + } + simContextCnt++; + } + } + } + if (sedml.getModels() != null && !sedml.getModels().isEmpty()) + logger.trace("Number of models in the sedml is " + sedml.getModels().size()); + + if (this.sedmlRecorder.hasErrors()) { + System.err.println(this.sedmlRecorder.getRecordsAsCSV()); + } else { + System.out.println(this.sedmlRecorder.getRecordsAsCSV()); + } + } catch (Exception e) { + // this only happens if not from CLI, we need to pass this down the calling thread + throw new RuntimeException("Error adding model to SEDML document : " + e.getMessage(), e); + } + } + + private void writeModelVCML(String filePathStrRelative, SimulationContext simContext) { + String simContextName = simContext.getName(); + String simContextId = TokenMangler.mangleToSName(simContextName); + this.sedmlModel.getSedML().addModel(new Model(new SId(simContextId), simContextName, this.vcmlLanguageURN, filePathStrRelative + "#" + VCMLSupport.getXPathForSimContext(simContextName))); + } + + private void exportSimulations(int simContextCnt, SimulationContext simContext, + String sbmlString, Map, String> l2gMap, String languageURN) throws Exception { + // ------- + // create sedml objects (simulation, task, datagenerators, report, plot) for each simulation in simcontext + // ------- + String simContextName = simContext.getName(); + String simContextId = TokenMangler.mangleToSName(simContextName); + this.simCount = 0; + this.overrideCount = 0; + simContext.getSimulations(); + for (Simulation vcSimulation : simContext.getSimulations()) { + try { + // if we have a hash containing a subset of simulations to export + // skip simulations not present in hash + if (this.simsToExport != null && !this.simsToExport.contains(vcSimulation.getName())) continue; + + // 1 -------> check compatibility + // if simContext is non-spatial stochastic, check if sim is histogram; if so, skip it, it can't be encoded in sedml 1.x + SolverTaskDescription simTaskDesc = vcSimulation.getSolverTaskDescription(); + if (simContext.getGeometry().getDimension() == 0 && simContext.isStoch()) { + long numOfTrials = simTaskDesc.getStochOpt().getNumOfTrials(); + if (numOfTrials > 1) { + String msg = simContextName + " ( " + vcSimulation.getName() + " ) : export of non-spatial stochastic simulation with histogram option to SEDML not supported at this time."; + throw new Exception(msg); + } + } + + // 2 -------> + // create sedmlSimulation (UniformTimeCourse) with specs and algorithm parameters + UniformTimeCourse utcSim = this.createSedMLSim(simTaskDesc); + + // 3 -------> + // create Tasks + Set dataGeneratorTasksSet = new LinkedHashSet<>(); // tasks not referenced as subtasks by any other (repeated) task; only these will have data generators + MathOverrides mathOverrides = vcSimulation.getMathOverrides(); // need to clone so we can manipulate expressions + this.createSedMLTasks(simContextCnt, l2gMap, simContextName, simContextId, + vcSimulation, utcSim, dataGeneratorTasksSet, mathOverrides, languageURN); + + // 4 -------> + // Create DataGenerators + + List dataGeneratorsOfSim = this.createSEDMLDataGens(sbmlString, simContext, dataGeneratorTasksSet); + + // 5 -------> + // create Report and Plot + + for (SId taskRef : dataGeneratorTasksSet) { + this.createSedMLOutputs(simContext, vcSimulation, dataGeneratorsOfSim, taskRef); + } + this.sedmlRecorder.addTaskRecord(vcSimulation.getName(), TaskType.SIMULATION, TaskResult.SUCCEEDED, null); + } catch (Exception e) { + String msg = "SEDML export failed for simulation '" + vcSimulation.getName() + "': " + e.getMessage(); + logger.error(msg, e); + this.sedmlRecorder.addTaskRecord(vcSimulation.getName(), TaskType.SIMULATION, TaskResult.FAILED, e); + System.err.println(this.sedmlRecorder.getRecordsAsCSV()); + throw e; + } + this.simCount++; + } + } + + private void createSedMLOutputs(SimulationContext simContext, Simulation vcSimulation, List dataGeneratorsOfSim, SId taskRef) { + // add output to sedml Model : 1 plot2d for each non-spatial simulation with all vars (species/output functions) vs time (1 curve per var) + // ignoring output for spatial deterministic (spatial stochastic is not exported to SEDML) and non-spatial stochastic applications with histogram + if (!(simContext.getGeometry().getDimension() > 0)) { + String plot2dId = "plot2d_" + TokenMangler.mangleToSName(vcSimulation.getName()); + String reportId = "report_" + TokenMangler.mangleToSName(vcSimulation.getName()); + // String reportId = "__plot__" + plot2dId; + String plotName = simContext.getName() + "_" + vcSimulation.getName() + "_plot"; + Plot2D sedmlPlot2d = new Plot2D(new SId(plot2dId), plotName); + Report sedmlReport = new Report(new SId(reportId), plotName); + + sedmlPlot2d.setNotes(this.createNotesElement("Plot of all variables and output functions from application '" + simContext.getName() + "' ; simulation '" + vcSimulation.getName() + "' in VCell model")); + sedmlReport.setNotes(this.createNotesElement("Report of all variables and output functions from application '" + simContext.getName() + "' ; simulation '" + vcSimulation.getName() + "' in VCell model")); + DataGenerator dgTime = this.sedmlModel.findDataGeneratorById(new SId(DATA_GENERATOR_TIME_NAME + "_" + taskRef.string())); + if (null == dgTime) throw new RuntimeException("DataGenerator referring time could not be found (sim context: '" + simContext.getName() + "')"); + SId xDataRef = dgTime.getId(); + SId xDatasetXId = new SId("__data_set__" + plot2dId + dgTime.getIdAsString()); + DataSet dataSet = new DataSet(xDatasetXId, DATA_GENERATOR_TIME_NAME, "time", xDataRef); // id, name, label, data generator reference + sedmlReport.addDataSet(dataSet); + + // add a curve for each dataGenerator in SEDML model + int curveCnt = 0; + // String id, String name, ASTNode math + for (DataGenerator dg : dataGeneratorsOfSim) { + // no curve for time, since time is xDateReference + if (dg.getId().equals(xDataRef)) { + continue; + } + SId curveId = new SId("curve_" + plot2dId + "_" + dg.getIdAsString()); + SId datasetYId = new SId("__data_set__" + plot2dId + dg.getIdAsString()); + Curve curve = new Curve(curveId, dg.getName(), xDataRef, dg.getId()); + sedmlPlot2d.addCurve(curve); + // // id, name, label, dataRef + // // dataset id <- unique id + // // dataset name <- data generator name + // // dataset label <- dataset id + DataSet yDataSet = new DataSet(datasetYId, dg.getName(), dg.getIdAsString(), dg.getId()); + sedmlReport.addDataSet(yDataSet); + curveCnt++; + } + this.sedmlModel.getSedML().addOutput(sedmlPlot2d); + this.sedmlModel.getSedML().addOutput(sedmlReport); + } else { // spatial deterministic + if (simContext.getApplicationType().equals(Application.NETWORK_DETERMINISTIC)) { // we ignore spatial stochastic (Smoldyn) + // TODO: add curves/surfaces to the plots + SId plot3dId = new SId("plot3d_" + TokenMangler.mangleToSName(vcSimulation.getName())); + SId reportId = new SId("report_" + TokenMangler.mangleToSName(vcSimulation.getName())); + String plotName = simContext.getName() + "plots"; + Plot3D sedmlPlot3d = new Plot3D(plot3dId, plotName); + Report sedmlReport = new Report(reportId, plotName); + + sedmlPlot3d.setNotes(this.createNotesElement("Plot of all variables and output functions from application '" + simContext.getName() + "' ; simulation '" + vcSimulation.getName() + "' in VCell model")); + sedmlReport.setNotes(this.createNotesElement("Report of all variables and output functions from application '" + simContext.getName() + "' ; simulation '" + vcSimulation.getName() + "' in VCell model")); + DataGenerator dgTime = this.sedmlModel.findDataGeneratorById(new SId(DATA_GENERATOR_TIME_NAME + "_" + taskRef.string())); + if (null == dgTime) throw new RuntimeException("DataGenerator referring time could not be found (sim context: '" + simContext.getName() + "')"); + SId xDataRef = dgTime.getId(); + SId xDatasetXId = new SId("__data_set__" + plot3dId.string() + dgTime.getIdAsString()); + DataSet dataSet = new DataSet(xDatasetXId, DATA_GENERATOR_TIME_NAME, "time", xDataRef); // id, name, label, data generator reference + sedmlReport.addDataSet(dataSet); + + // add a curve for each dataGenerator in SEDML model + int curveCnt = 0; + // String id, String name, ASTNode math + for (DataGenerator dg : dataGeneratorsOfSim) { + // no curve for time, since time is xDateReference + if (dg.getId().equals(xDataRef)) { + continue; + } + SId curveId = new SId("curve_" + plot3dId.string() + "_" + dg.getIdAsString()); + SId datasetYId = new SId("__data_set__" + plot3dId.string() + dg.getIdAsString()); + + DataSet yDataSet = new DataSet(datasetYId, dg.getName(), dg.getIdAsString(), dg.getId()); + sedmlReport.addDataSet(yDataSet); + curveCnt++; + } + this.sedmlModel.getSedML().addOutput(sedmlReport); + } + } + } + + private List createSEDMLDataGens(String sbmlString, SimulationContext simContext, Set dataGeneratorTasksSet) + throws IOException, SbmlException, XMLStreamException { + List dataGeneratorsOfSim = new ArrayList<>(); + for (SId taskRef : dataGeneratorTasksSet) { + // add one DataGenerator for 'time' + SId timeDataGenId = new SId(DATA_GENERATOR_TIME_NAME + "_" + taskRef.string()); + SId timeVarId = new SId(DATA_GENERATOR_TIME_SYMBOL + "_" + taskRef.string()); + Variable timeVar = new Variable(timeVarId, DATA_GENERATOR_TIME_SYMBOL, taskRef, VariableSymbol.TIME); + ASTNode math = Libsedml.parseFormulaString(timeVarId.string()); + DataGenerator timeDataGen = new DataGenerator(timeDataGenId, timeDataGenId.string(), math); + timeDataGen.addVariable(timeVar); + dataGeneratorsOfSim.add(timeDataGen); + + String dataGenIdPrefix = "dataGen_" + taskRef.string(); + + // add dataGenerators for species + // get species list from SBML model. + ArrayList varNamesList = new ArrayList<>(); + if (sbmlString != null) { + String[] sbmlVars = SimSpec.fromSBML(sbmlString).getVarsList(); + Collections.addAll(varNamesList, sbmlVars); + } else { + SpeciesContextSpec[] scSpecs = simContext.getReactionContext().getSpeciesContextSpecs(); + for (SpeciesContextSpec scs : scSpecs) { + varNamesList.add(scs.getSpeciesContext().getName()); + } + } + for (String varName : varNamesList) { + SId varId = new SId(TokenMangler.mangleToSName(varName) + "_" + taskRef.string()); + String xPathForSpecies = sbmlString != null ? this.sbmlSupport.getXPathForSpecies(varName) : VCMLSupport.getXPathForSpeciesContextSpec(simContext.getName(), varName); + Variable sedmlVar = new Variable(varId, varName, taskRef, xPathForSpecies); + ASTNode varMath = Libsedml.parseFormulaString(varId.string()); + SId dataGenId = new SId(dataGenIdPrefix + "_" + TokenMangler.mangleToSName(varName)); //"dataGen_" + varCount; - old code + DataGenerator dataGen = new DataGenerator(dataGenId, varName, varMath); + dataGen.addVariable(sedmlVar); + dataGeneratorsOfSim.add(dataGen); + } + // add dataGenerators for output functions + // get output function list from SBML model + varNamesList = new ArrayList<>(); + if (sbmlString != null) { + SBMLDocument sbmlDoc = new SBMLReader().readSBMLFromString(sbmlString); + List listofGlobalParams = sbmlDoc.getModel().getListOfParameters(); + for (Parameter sbmlGlobalParam : listofGlobalParams) { + // check whether it is a vcell-exported output function + Annotation paramAnnotation = sbmlGlobalParam.getAnnotation(); + if (paramAnnotation != null && paramAnnotation.getNonRDFannotation() != null) { + XMLNode paramElement = paramAnnotation.getNonRDFannotation().getChildElement(XMLTags.SBML_VCELL_OutputFunctionTag, "*"); + if (paramElement != null) { + String varName = sbmlGlobalParam.getId(); + varNamesList.add(varName); + } + } + } + } else { + List ofs = simContext.getOutputFunctionContext().getOutputFunctionsList(); + for (AnnotatedFunction of : ofs) { + varNamesList.add(of.getName()); + } + } + for (String varName : varNamesList) { + SId varId = new SId(TokenMangler.mangleToSName(varName) + "_" + taskRef.string()); + String xPathForSpecies = sbmlString != null ? this.sbmlSupport.getXPathForGlobalParameter(varName) : VCMLSupport.getXPathForOutputFunction(simContext.getName(), varName); + Variable sedmlVar = new Variable(varId, varName, taskRef, xPathForSpecies); + ASTNode varMath = Libsedml.parseFormulaString(varId.string()); + SId dataGenId = new SId(dataGenIdPrefix + "_" + TokenMangler.mangleToSName(varName)); //"dataGen_" + varCount; - old code + DataGenerator dataGen = new DataGenerator(dataGenId, varName, varMath); + dataGen.addVariable(sedmlVar); + dataGeneratorsOfSim.add(dataGen); + } + } + for (DataGenerator dataGen : dataGeneratorsOfSim) { + this.sedmlModel.getSedML().addDataGenerator(dataGen); + } + return dataGeneratorsOfSim; + } + + private UniformTimeCourse createSedMLSim(SolverTaskDescription simTaskDesc) { + // list of kisao terms in vcell-core/src/main/resources/kisao_algs.obo + SolverDescription vcSolverDesc = simTaskDesc.getSolverDescription(); + String kiSAOIdStr = vcSolverDesc.getKisao(); + Algorithm sedmlAlgorithm = new Algorithm(kiSAOIdStr); + Notes an = this.createNotesElement(""); // we show the description of kisao terms for AlgorithmParameters as notes + // for L1V4 and up, AlgorithmParameters has a "name" field we can use instead + sedmlAlgorithm.setNotes(an); + TimeBounds vcSimTimeBounds = simTaskDesc.getTimeBounds(); + double startingTime = vcSimTimeBounds.getStartingTime(); + String simName = simTaskDesc.getSimulation().getName(); + UniformTimeCourse utcSim = new UniformTimeCourse(new SId(TokenMangler.mangleToSName(simName)), simName, startingTime, startingTime, + vcSimTimeBounds.getEndingTime(), (int) simTaskDesc.getExpectedNumTimePoints(), sedmlAlgorithm); + + boolean enableAbsoluteErrorTolerance; // --------- deal with error tolerance + boolean enableRelativeErrorTolerance; + if (vcSolverDesc.isSemiImplicitPdeSolver() || vcSolverDesc.isChomboSolver()) { + enableAbsoluteErrorTolerance = false; + enableRelativeErrorTolerance = true; + } else if (vcSolverDesc.hasErrorTolerance()) { + enableAbsoluteErrorTolerance = true; + enableRelativeErrorTolerance = true; + } else { + enableAbsoluteErrorTolerance = false; + enableRelativeErrorTolerance = false; + } + if (enableAbsoluteErrorTolerance) { + ErrorTolerance et = simTaskDesc.getErrorTolerance(); + String kisaoStr = ErrorTolerance.ErrorToleranceDescription.Absolute.getKisao(); + String kisaoDesc = ErrorTolerance.ErrorToleranceDescription.Absolute.getDescription(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, et.getAbsoluteErrorTolerance() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + if (enableRelativeErrorTolerance) { + ErrorTolerance et = simTaskDesc.getErrorTolerance(); + String kisaoStr = ErrorTolerance.ErrorToleranceDescription.Relative.getKisao(); + String kisaoDesc = ErrorTolerance.ErrorToleranceDescription.Relative.getDescription(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, et.getRelativeErrorTolerance() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + + boolean enableDefaultTimeStep; // ---------- deal with time step (code adapted from TimeSpecPanel.refresh() + boolean enableMinTimeStep; + boolean enableMaxTimeStep; + if (vcSolverDesc.compareEqual(SolverDescription.StochGibson)) { // stochastic time + enableDefaultTimeStep = false; + enableMinTimeStep = false; + enableMaxTimeStep = false; + } else if (vcSolverDesc.compareEqual(SolverDescription.NFSim)) { + enableDefaultTimeStep = false; + enableMinTimeStep = false; + enableMaxTimeStep = false; + } else { + // fixed time step solvers and non spatial stochastic solvers only show default time step. + if (!vcSolverDesc.hasVariableTimestep() || vcSolverDesc.isNonSpatialStochasticSolver()) { + enableDefaultTimeStep = true; + enableMinTimeStep = false; + enableMaxTimeStep = false; + } else { + // variable time step solvers shows min and max, but sundials solvers don't show min + enableDefaultTimeStep = false; + enableMinTimeStep = true; + enableMaxTimeStep = true; + if (vcSolverDesc.hasSundialsTimeStepping()) { + enableMinTimeStep = false; + } + } + } + if (vcSolverDesc == SolverDescription.SundialsPDE) { + String kisaoStr = SolverDescription.AlgorithmParameterDescription.PDEMeshSize.getKisao(); + String kisaoDesc = SolverDescription.AlgorithmParameterDescription.PDEMeshSize.getDescription(); + ISize meshSize = simTaskDesc.getSimulation().getMeshSpecification().getSamplingSize(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, meshSize.toTemporaryKISAOvalue()); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + TimeStep ts = simTaskDesc.getTimeStep(); + if (enableDefaultTimeStep) { + String kisaoStr = TimeStep.TimeStepDescription.Default.getKisao(); + String kisaoDesc = TimeStep.TimeStepDescription.Default.getDescription(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, ts.getDefaultTimeStep() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + if (enableMinTimeStep) { + String kisaoStr = TimeStep.TimeStepDescription.Minimum.getKisao(); + String kisaoDesc = TimeStep.TimeStepDescription.Minimum.getDescription(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, ts.getMinimumTimeStep() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + if (enableMaxTimeStep) { + String kisaoStr = TimeStep.TimeStepDescription.Maximum.getKisao(); + String kisaoDesc = TimeStep.TimeStepDescription.Maximum.getDescription(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, ts.getMaximumTimeStep() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + + if (simTaskDesc.getSimulation().getMathDescription().isNonSpatialStoch()) { // ------- deal with seed + NonspatialStochSimOptions nssso = simTaskDesc.getStochOpt(); + if (nssso.isUseCustomSeed()) { + String kisaoStr = SolverDescription.AlgorithmParameterDescription.Seed.getKisao(); // 488 + String kisaoDesc = SolverDescription.AlgorithmParameterDescription.Seed.getDescription(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssso.getCustomSeed() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + } else { + // (... isRuleBased(), isSpatial(), isMovingMembrane(), isSpatialHybrid() ... + } + + if (vcSolverDesc == SolverDescription.HybridEuler || // -------- deal with hybrid solvers (non-spatial) + vcSolverDesc == SolverDescription.HybridMilAdaptive || + vcSolverDesc == SolverDescription.HybridMilstein) { + NonspatialStochHybridOptions nssho = simTaskDesc.getStochHybridOpt(); + + String kisaoStr = SolverDescription.AlgorithmParameterDescription.Epsilon.getKisao(); + String kisaoDesc = SolverDescription.AlgorithmParameterDescription.Epsilon.getDescription(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssho.getEpsilon() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + + kisaoStr = SolverDescription.AlgorithmParameterDescription.Lambda.getKisao(); + kisaoDesc = SolverDescription.AlgorithmParameterDescription.Lambda.getDescription(); + sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssho.getLambda() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + + kisaoStr = SolverDescription.AlgorithmParameterDescription.MSRTolerance.getKisao(); + kisaoDesc = SolverDescription.AlgorithmParameterDescription.MSRTolerance.getDescription(); + sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssho.getMSRTolerance() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + if (vcSolverDesc == SolverDescription.HybridMilAdaptive) { // --------- one more param for hybrid-adaptive + NonspatialStochHybridOptions nssho = simTaskDesc.getStochHybridOpt(); + + String kisaoStr = SolverDescription.AlgorithmParameterDescription.SDETolerance.getKisao(); + String kisaoDesc = SolverDescription.AlgorithmParameterDescription.SDETolerance.getDescription(); + AlgorithmParameter sedmlAlgorithmParameter = new AlgorithmParameter(kisaoStr, nssho.getSDETolerance() + ""); + sedmlAlgorithm.addAlgorithmParameter(sedmlAlgorithmParameter); + this.addNotesChild(an, TokenMangler.mangleToSName(kisaoStr), kisaoDesc); + } + + // add a note to utcSim to indicate actual solver name + String simNotesStr = "Actual Solver Name : '" + vcSolverDesc.getDisplayLabel() + "'."; + utcSim.setNotes(this.createNotesElement(simNotesStr)); + this.sedmlModel.getSedML().addSimulation(utcSim); + return utcSim; + } + + private void createSedMLTasks(int simContextCnt, Map, String> l2gMap, String simContextName, + String simContextId, Simulation vcSimulation, UniformTimeCourse utcSim, + Set dataGeneratorTasksSet, MathOverrides mathOverrides, String languageURN) + throws ExpressionException, MappingException { + if (mathOverrides == null || !mathOverrides.hasOverrides()) { + // no math overrides, add basic task. + SId taskId = new SId("tsk_" + simContextCnt + "_" + this.simCount); + Task sedmlTask = new Task(taskId, vcSimulation.getName(), new SId(simContextId), utcSim.getId()); + dataGeneratorTasksSet.add(sedmlTask.getId()); + this.sedmlModel.getSedML().addTask(sedmlTask); +// taskRef = taskId; // to be used later to add dataGenerators : one set of DGs per model (simContext). + return; + } + + String[] overriddenConstantNames = mathOverrides.getOverridenConstantNames(); + String[] scannedConstantsNames = mathOverrides.getScannedConstantNames(); + HashMap scannedParamHash = new HashMap<>(); + HashMap unscannedParamHash = new HashMap<>(); + + VariableSymbolTable varST = new VariableSymbolTable(); + String[] constantNames = mathOverrides.getAllConstantNames(); + final HashMap substitutedConstants = new HashMap<>(); + { + final ArrayList overrides = new ArrayList<>(); + for (String constantName : constantNames) { + overrides.add(new Constant(constantName, new Expression(mathOverrides.getActualExpression(constantName, MathOverrides.ScanIndex.ZERO)))); + } + for (Constant override : overrides) { + varST.addVar(override); + } + for (Constant override : overrides) { + override.bind(varST); + } + for (Constant override : overrides) { + Expression flattened = MathUtilities.substituteFunctions(override.getExpression(), varST, true); + substitutedConstants.put(override.getName(), new Expression(flattened)); + } + } + + // need to check for "leftover" overrides from parameter renaming or other model editing + HashMap missingParamHash = new HashMap<>(); + for (String name : scannedConstantsNames) { + if (!mathOverrides.isUnusedParameter(name)) { + scannedParamHash.put(name, new SId(name)); + } else { + missingParamHash.put(name, name); + } + } + for (String name : overriddenConstantNames) { + if (!scannedParamHash.containsKey(name)) { + if (!mathOverrides.isUnusedParameter(name)) { + unscannedParamHash.put(name, name); + } else { + missingParamHash.put(name, name); + } + } + } + if (!missingParamHash.isEmpty()) { + for (String missingParamName : missingParamHash.values()) { + logger.error("ERROR: there is an override entry for non-existent parameter " + missingParamName); + throw new MappingException("MathOverrides has entry for non-existent parameter " + missingParamName); + } + } + + SimulationContext simContext = (SimulationContext) vcSimulation.getSimulationOwner(); + MathSymbolMapping mathSymbolMapping = (MathSymbolMapping) simContext.getMathDescription().getSourceSymbolMapping(); + if (!unscannedParamHash.isEmpty() && scannedParamHash.isEmpty()) { + // only parameters with simple overrides (numeric/expression) no scans + // create new model with change for each parameter that has override; add simple task + SId overriddenSimContextId = new SId(simContextId + "_" + this.overrideCount); + String overriddenSimContextName = simContextName + " modified"; + Model sedModel = new Model(overriddenSimContextId, overriddenSimContextName, languageURN, "#" + simContextId); + this.overrideCount++; + + int variableCount = 0; + for (String unscannedParamName : unscannedParamHash.values()) { + SymbolTableEntry ste = getSymbolTableEntryForModelEntity(mathSymbolMapping, unscannedParamName); + Expression unscannedParamExpr = mathOverrides.getActualExpression(unscannedParamName, MathOverrides.ScanIndex.ZERO); + unscannedParamExpr = this.adjustIfRateParam(vcSimulation, ste, unscannedParamExpr); + if (unscannedParamExpr.isNumeric()) { + // if expression is numeric, add ChangeAttribute to model created above + XPathTarget targetXpath = this.getTargetAttributeXPath(ste, l2gMap, simContext); + ChangeAttribute changeAttribute = new ChangeAttribute(targetXpath, unscannedParamExpr.infix()); + sedModel.addChange(changeAttribute); + } else { + Map symbolToTargetMap = new LinkedHashMap<>(); + // create setValue for unscannedParamName (which contains a scanned param in its expression) + String[] symbols = unscannedParamExpr.getSymbols(); + for (String symbol : symbols) { + SymbolTableEntry entry = getSymbolTableEntryForModelEntity(mathSymbolMapping, symbol); + XPathTarget target = this.getTargetAttributeXPath(entry, l2gMap, simContext); + symbolToTargetMap.put(symbol, target); + } + + // non-numeric expression : add 'computeChange' to modified model + XPathTarget targetXpath = this.getTargetAttributeXPath(ste, l2gMap, simContext); + ComputeChange computeChange = new ComputeChange(targetXpath); + + Expression expr = new Expression(unscannedParamExpr); + String[] exprSymbols = expr.getSymbols(); + for (String symbol : exprSymbols) { + SId varName = new SId(overriddenSimContextId.string() + "_" + symbol + "_" + variableCount); + Variable sedmlVar = new Variable(varName, varName.string(), symbolToTargetMap.get(symbol).toString(), overriddenSimContextId); + expr.substituteInPlace(new Expression(symbol), new Expression(varName.string())); + computeChange.addVariable(sedmlVar); + variableCount++; + } + ASTNode math = Libsedml.parseFormulaString(expr.infix()); + computeChange.setMath(math); + sedModel.addChange(computeChange); + } + } + this.sedmlModel.getSedML().addModel(sedModel); + + SId taskId = new SId("tsk_" + simContextCnt + "_" + this.simCount); + Task sedmlTask = new Task(taskId, vcSimulation.getName(), sedModel.getId(), utcSim.getId()); + dataGeneratorTasksSet.add(sedmlTask.getId()); + this.sedmlModel.getSedML().addTask(sedmlTask); + } else if (!scannedParamHash.isEmpty() && unscannedParamHash.isEmpty()) { + // only parameters with scans + SId taskId = new SId("tsk_" + simContextCnt + "_" + this.simCount); + Task sedmlTask = new Task(taskId, vcSimulation.getName(), new SId(simContextId), utcSim.getId()); + dataGeneratorTasksSet.add(sedmlTask.getId()); + this.sedmlModel.getSedML().addTask(sedmlTask); + + int repeatedTaskIndex = 0; + SId ownerTaskId = taskId; + for (String scannedConstName : scannedConstantsNames) { + SId repeatedTaskId = new SId("repTsk_" + simContextCnt + "_" + this.simCount + "_" + repeatedTaskIndex); + SId rangeId = new SId("range_" + simContextCnt + "_" + this.simCount + "_" + scannedConstName); + RepeatedTask rt = this.createSedMLRepeatedTask(rangeId, l2gMap, simContext, dataGeneratorTasksSet, mathOverrides, ownerTaskId, scannedConstName, repeatedTaskId, new SId(simContextId)); + ownerTaskId = repeatedTaskId; + repeatedTaskIndex++; + this.sedmlModel.getSedML().addTask(rt); + } + + } else { + // both scanned and simple parameters : create new model with change for each simple override; add RepeatedTask + Map rangeIdToOwningRepeatedTaskHash = new LinkedHashMap<>(); + List repeatedTasksList = new ArrayList<>(); + + // create new model with change for each unscanned parameter that has override + SId overriddenSimContextId = new SId(simContextId + "_" + this.overrideCount); + String overriddenSimContextName = simContextName + " modified"; + Model sedModel = new Model(overriddenSimContextId, overriddenSimContextName, languageURN, "#" + simContextId); + this.overrideCount++; + + SId taskId = new SId("tsk_" + simContextCnt + "_" + this.simCount); + Task sedmlTask = new Task(taskId, vcSimulation.getName(), overriddenSimContextId, utcSim.getId()); + dataGeneratorTasksSet.add(sedmlTask.getId()); + this.sedmlModel.getSedML().addTask(sedmlTask); + + // scanned parameters + int repeatedTaskIndex = 0; + int variableCount = 0; + SId ownerTaskId = taskId; + for (String scannedConstName : scannedConstantsNames) { + SId repeatedTaskId = new SId("repTsk_" + simContextCnt + "_" + this.simCount + "_" + repeatedTaskIndex); + SId rangeId = new SId("range_" + simContextCnt + "_" + this.simCount + "_" + scannedConstName); + RepeatedTask rt = this.createSedMLRepeatedTask(rangeId, l2gMap, simContext, dataGeneratorTasksSet, mathOverrides, ownerTaskId, scannedConstName, repeatedTaskId, overriddenSimContextId); + ownerTaskId = repeatedTaskId; + repeatedTaskIndex++; + + // use scannedParamHash to store rangeId for that param, since it might be needed if unscanned param has a scanned param in expr. + if (scannedParamHash.get(scannedConstName).string().equals(scannedConstName)) { + // the hash was originally populated as . Replace 'value' with rangeId for scannedParam + scannedParamHash.put(scannedConstName, rangeId); + rangeIdToOwningRepeatedTaskHash.put(rangeId, rt); // we'll need the right repeated task for this range later on, in the unscanned loop + } + // add to local list; will be added to sedml doc later + repeatedTasksList.add(rt); + } + + // for unscanned parameter overrides + for (String unscannedParamName : unscannedParamHash.values()) { + SymbolTableEntry ste = getSymbolTableEntryForModelEntity(mathSymbolMapping, unscannedParamName); + Expression unscannedParamExpr = mathOverrides.getActualExpression(unscannedParamName, MathOverrides.ScanIndex.ZERO); + unscannedParamExpr = this.adjustIfRateParam(vcSimulation, ste, unscannedParamExpr); + if (unscannedParamExpr.isNumeric()) { + // if expression is numeric, add ChangeAttribute to model created above + XPathTarget targetXpath = this.getTargetAttributeXPath(ste, l2gMap, simContext); + ChangeAttribute changeAttribute = new ChangeAttribute(targetXpath, unscannedParamExpr.infix()); + sedModel.addChange(changeAttribute); + } else { + // check for any scanned parameter in unscanned parameter expression + String[] exprSymbols = unscannedParamExpr.getSymbols(); + boolean bHasScannedParameter = false; + List scannedParamNameInUnscannedParamExpList = new ArrayList<>(); + for (String symbol : exprSymbols) { + if (scannedParamHash.get(symbol) != null) { + bHasScannedParameter = true; + scannedParamNameInUnscannedParamExpList.add(symbol); + } + } + // (scanned parameter in expr) ? (add setValue for unscanned param in repeatedTask) : (add computeChange to modifiedModel) + Map symbolToTargetMap = new LinkedHashMap<>(); + String[] symbols = unscannedParamExpr.getSymbols(); + for (String symbol : symbols) { + SymbolTableEntry entry = getSymbolTableEntryForModelEntity(mathSymbolMapping, symbol); + XPathTarget target = this.getTargetAttributeXPath(entry, l2gMap, simContext); + symbolToTargetMap.put(symbol, target); + } + if (bHasScannedParameter) { + // create setValue for unscannedParamName (which contains a scanned param in its expression) + SymbolTableEntry entry = getSymbolTableEntryForModelEntity(mathSymbolMapping, unscannedParamName); + XPathTarget target = this.getTargetAttributeXPath(entry, l2gMap, simContext); + Set rangeIdSet = new HashSet<>(); + for (String scannedParamNameInUnscannedParamExp : scannedParamNameInUnscannedParamExpList) { + SId rangeId = scannedParamHash.get(scannedParamNameInUnscannedParamExp); + rangeIdSet.add(rangeId); // all the ranges referred in the scannedParamNameInUnscannedParamExpList + } + for (SId rangeId : rangeIdSet) { + SetValue setValue = new SetValue(target, rangeId, overriddenSimContextId); // @TODO: we have no range?? + Expression expr = new Expression(unscannedParamExpr); + for (String symbol : symbols) { + SId symbolName = new SId(rangeId.string() + "_" + symbol + "_" + variableCount); + Variable sedmlVar = new Variable(symbolName, symbolName.string(), symbolToTargetMap.get(symbol).toString(), overriddenSimContextId); // sbmlSupport.getXPathForSpecies(symbol)); + setValue.addVariable(sedmlVar); + expr.substituteInPlace(new Expression(symbol), new Expression(symbolName.string())); + variableCount++; + } + ASTNode math = Libsedml.parseFormulaString(expr.infix()); + setValue.setMath(math); + RepeatedTask rtRecovered = rangeIdToOwningRepeatedTaskHash.get(rangeId); + rtRecovered.addChange(setValue); + } + } else { + // non-numeric expression : add 'computeChange' to modified model + XPathTarget targetXpath = this.getTargetAttributeXPath(ste, l2gMap, simContext); + ComputeChange computeChange = new ComputeChange(targetXpath); + Expression expr = new Expression(unscannedParamExpr); + for (String symbol : exprSymbols) { + SId varName = new SId(overriddenSimContextId.string() + "_" + symbol + "_" + variableCount); + Variable sedmlVar = new Variable(varName, varName.string(), symbolToTargetMap.get(symbol).toString(), overriddenSimContextId); + expr.substituteInPlace(new Expression(symbol), new Expression(varName.string())); + computeChange.addVariable(sedmlVar); + variableCount++; + } + ASTNode math = Libsedml.parseFormulaString(expr.infix()); + computeChange.setMath(math); + sedModel.addChange(computeChange); + } + } + } + this.sedmlModel.getSedML().addModel(sedModel); + for (RepeatedTask rt : repeatedTasksList) { + this.sedmlModel.getSedML().addTask(rt); + } + } + } + + private Expression adjustIfRateParam(Simulation vcSimulation, SymbolTableEntry ste, Expression unscannedParamExpr) + throws ExpressionException { + if (ste instanceof KineticsParameter kp) { + if (kp.getKinetics().getAuthoritativeParameter() == kp) { + SimulationContext simulationContext = (SimulationContext) vcSimulation.getSimulationOwner(); + boolean bSpatial = simulationContext.getGeometry().getDimension() > 0; + boolean bLumped = kp.getKinetics() instanceof LumpedKinetics; + if (!bLumped && !bSpatial) { + MathSymbolMapping msm = (MathSymbolMapping) simulationContext.getMathDescription().getSourceSymbolMapping(); + cbit.vcell.math.Variable structSize = msm.getVariable(simulationContext.getGeometryContext().getStructureMapping(kp.getKinetics().getReactionStep().getStructure()).getSizeParameter()); + unscannedParamExpr = Expression.mult(unscannedParamExpr, new Expression(structSize.getName())); + } + } + } + return unscannedParamExpr; + } + + private RepeatedTask createSedMLRepeatedTask(SId rangeId, Map, String> l2gMap, + SimulationContext simContext, Set dataGeneratorTasksSet, + MathOverrides mathOverrides, SId ownerTaskId, String scannedConstName, SId repeatedTaskId, SId modelReferenceId) + throws ExpressionException, MappingException { + RepeatedTask rt = new RepeatedTask(repeatedTaskId, mathOverrides.getSimulation().getName(), true, rangeId); + dataGeneratorTasksSet.add(repeatedTaskId); + SubTask subTask = new SubTask(0, ownerTaskId); + dataGeneratorTasksSet.remove(ownerTaskId); + rt.addSubtask(subTask); + ConstantArraySpec constantArraySpec = mathOverrides.getConstantArraySpec(scannedConstName); + MathSymbolMapping mathSymbolMapping = (MathSymbolMapping) simContext.getMathDescription().getSourceSymbolMapping(); + // list of Ranges, if sim is parameter scan. + Range r = this.createSedMLRange(rangeId, rt, constantArraySpec, scannedConstName, simContext, l2gMap, modelReferenceId, mathOverrides.getSimulation()); + // list of Changes + SymbolTableEntry ste = getSymbolTableEntryForModelEntity(mathSymbolMapping, scannedConstName); + XPathTarget target = this.getTargetAttributeXPath(ste, l2gMap, simContext); + //ASTNode math1 = new ASTCi(r.getId()); // was scannedConstName + ASTNode math1 = Libsedml.parseFormulaString(r.getId().string()); // here the math is always the range id expression + SetValue setValue = new SetValue(target, r.getId(), modelReferenceId); + setValue.setMath(math1); + rt.addChange(setValue); + return rt; + } + + private Range createSedMLRange(SId rangeId, RepeatedTask rt, ConstantArraySpec constantArraySpec, String scannedConstantName, SimulationContext simContext, Map, String> l2gMap, SId modelReferenceId, Simulation vcSim) + throws ExpressionException, MappingException { + Range r; + SimulationContext sc = (SimulationContext) vcSim.getSimulationOwner(); + SymbolReplacement sr = sc.getMathOverridesResolver().getSymbolReplacement(scannedConstantName, true); + String cName = sr != null ? sr.newName : scannedConstantName; + MathSymbolMapping msm = (MathSymbolMapping) simContext.getMathDescription().getSourceSymbolMapping(); + SymbolTableEntry ste = msm.getBiologicalSymbol(vcSim.getMathOverrides().getConstant(cName))[0]; + if (constantArraySpec.getType() == ConstantArraySpec.TYPE_INTERVAL) { + // ------ Uniform Range + UniformType type = constantArraySpec.isLogInterval() ? UniformType.LOG : UniformType.LINEAR; + if (constantArraySpec.getMinValue().isNumeric() && constantArraySpec.getMaxValue().isNumeric()) { + r = new UniformRange(rangeId, constantArraySpec.getMinValue().evaluateConstant(), + constantArraySpec.getMaxValue().evaluateConstant(), constantArraySpec.getNumValues(), type); + rt.addRange(r); + return r; + } else { + r = new UniformRange(rangeId, 1, 2, constantArraySpec.getNumValues(), type); + rt.addRange(r); + // now make a FunctionalRange with expressions + FunctionalRange fr = new FunctionalRange(new SId("fr_" + rangeId.string()), rangeId); + Expression expMin = constantArraySpec.getMinValue(); + expMin = this.adjustIfRateParam(vcSim, ste, expMin); + Expression expMax = constantArraySpec.getMaxValue(); + expMax = this.adjustIfRateParam(vcSim, ste, expMax); + Expression trans = Expression.add(new Expression(rangeId.string()), new Expression("-1")); + Expression func = Expression.add(expMax, Expression.negate(expMin)); + func = Expression.mult(func, trans); + func = Expression.add(expMin, func); + this.createFunctionalRangeElements(fr, func, simContext, l2gMap, modelReferenceId); + rt.addRange(fr); + return fr; + } + } else { + // ----- Vector Range + // we try to preserve symbolic values coming from unit transforms... + cbit.vcell.math.Constant[] cs = constantArraySpec.getConstants(); + ArrayList values = new ArrayList<>(); + Expression expFact = null; + for (Constant c : cs) { + if (!(c.getExpression().evaluateConstant() == 0)) { + expFact = c.getExpression(); + break; + } + } + // compute list of numeric multipliers + for (Constant c : cs) { + Expression exp = c.getExpression(); + exp = Expression.div(exp, expFact).simplifyJSCL(); + values.add(exp.evaluateConstant()); + } + r = new VectorRange(rangeId, values); + rt.addRange(r); + // now make a FunctionalRange with expressions + FunctionalRange fr = new FunctionalRange(new SId("fr_" + rangeId.string()), rangeId); + expFact = Expression.mult(new Expression(rangeId.string()), expFact); + expFact = this.adjustIfRateParam(vcSim, ste, expFact); + this.createFunctionalRangeElements(fr, expFact, simContext, l2gMap, modelReferenceId); + rt.addRange(fr); + return fr; + } + } + + private void createFunctionalRangeElements(FunctionalRange fr, Expression func, SimulationContext simContext, + Map, String> l2gMap, SId modelReferenceId) throws ExpressionException, MappingException { + String[] symbols = func.getSymbols(); + MathSymbolMapping msm = (MathSymbolMapping) simContext.getMathDescription().getSourceSymbolMapping(); + for (String symbol : symbols) { + if (symbol.equals(fr.getRange().string())) continue; + SymbolTableEntry entry = getSymbolTableEntryForModelEntity(msm, symbol); + XPathTarget target = this.getTargetAttributeXPath(entry, l2gMap, simContext); + SId symbolName = new SId(fr.getRange().string() + "_" + symbol); + Variable sedmlVar = new Variable(symbolName, symbolName.string(), target.toString(), modelReferenceId); // sbmlSupport.getXPathForSpecies(symbol)); + fr.addVariable(sedmlVar); + func.substituteInPlace(new Expression(symbol), new Expression(symbolName.string())); + } + ASTNode math = Libsedml.parseFormulaString(func.infix()); + fr.setMath(math); + } + + private void writeModelSBML(String savePath, String sBaseFileName, String sbmlString, SimulationContext simContext) throws IOException { + String simContextName = simContext.getName(); + String simContextId = TokenMangler.mangleToSName(simContextName); + String filePathStrAbsolute = Paths.get(savePath, sBaseFileName + "_" + simContextId + ".xml").toString(); + String filePathStrRelative = sBaseFileName + "_" + simContextId + ".xml"; + XmlUtil.writeXMLStringToFile(sbmlString, filePathStrAbsolute, true); + this.modelFilePathStrAbsoluteList.add(filePathStrRelative); + this.sedmlModel.getSedML().addModel(new Model(new SId(simContextId), simContextName, this.sbmlLanguageURN, filePathStrRelative)); + } + + private Notes createNotesElement(String notesStr) { + // create some xhtml. E.g., + org.jdom2.Element para = new org.jdom2.Element("p"); + para.setText(notesStr); + // create a notes element + return new Notes(para); + } + + private void addNotesChild(Notes note, String kisao, String desc) { + Element sub = new Element("AlgorithmParameter", "http://www.biomodels.net/kisao/KISAO_FULL#"); + sub.setAttribute(TokenMangler.mangleToSName(kisao), desc); + note.addNote(sub); + } + + public static SymbolTableEntry getSymbolTableEntryForModelEntity(MathSymbolMapping mathSymbolMapping, String paramName) throws MappingException { + cbit.vcell.math.Variable mathVar = mathSymbolMapping.findVariableByName(paramName); + if (mathVar == null) { + throw new MappingException("No variable found for parameter: " + paramName); + } + SymbolTableEntry[] stEntries = mathSymbolMapping.getBiologicalSymbol(mathVar); + if (stEntries == null) { + throw new MappingException("No matching biological symbol for variable: " + mathVar); + } + + // if the extra stes in the array are KineticsProxyParameters/ModelQuantities, remove them from array. Should be left with only one entry for overriddenConstantName + if (stEntries.length > 1) { + // + // If there are more than one stEntries, usually, it is a regular ste (species, global parameter, structure, etc) together with + // kineticsProxyParameters (that have the regular ste as target) or Model quantities (structure size, membrane voltage). + // So filtering out the kinticProxyParametes should leave only the regular parameter, + // which is what we want. If there are more, then there is a problem. + // + List steList = new ArrayList<>(Arrays.asList(stEntries)); + for (SymbolTableEntry stEntry : stEntries) { + if (stEntry instanceof ProxyParameter) { + SymbolTableEntry ppTargetSte = ((ProxyParameter) stEntry).getTarget(); + if (steList.contains(ppTargetSte) || ppTargetSte instanceof ModelQuantity) { + steList.remove(stEntry); + } + } + if (stEntry instanceof ModelQuantity) { + steList.remove(stEntry); + } + } + // after removing proxy parameters, cannot have more than one ste in list + if (steList.isEmpty()) { + throw new MappingException("No mapping entry for constant : '" + paramName + "'."); + } + if (steList.size() > 1) { + throw new MappingException("Cannot have more than one mapping entry for constant : '" + paramName + "'."); + } + SymbolTableEntry[] stes = steList.toArray(SymbolTableEntry[]::new); + return stes[0]; + } else { + return stEntries[0]; + } + } + + private XPathTarget getTargetAttributeXPath(SymbolTableEntry ste, Map, String> l2gMap, SimulationContext simContext) { + // VCML model format + if (l2gMap == null) return this.getVCMLTargetXPath(ste, simContext); + // SBML model format + SBMLSupport sbmlSupport = new SBMLSupport(); // to get Xpath string for variables. + XPathTarget targetXpath ; + if (ste instanceof SpeciesContext || ste instanceof SpeciesContextSpecParameter) { + String speciesId = TokenMangler.mangleToSName(ste.getName()); + // can change species initial concentration or amount + String speciesAttr = ""; + if (ste instanceof SpeciesContextSpecParameter scsp) { + speciesId = TokenMangler.mangleToSName((scsp).getSpeciesContext().getName()); + int role = scsp.getRole(); + if (role == SpeciesContextSpec.ROLE_InitialConcentration) { + speciesAttr = scsp.getName(); + } + if (role == SpeciesContextSpec.ROLE_InitialCount) { + speciesAttr = scsp.getName(); + } + if (role == SpeciesContextSpec.ROLE_DiffusionRate) { + speciesAttr = scsp.getName(); + } + } + if (speciesAttr.isEmpty()) { + targetXpath = new XPathTarget(sbmlSupport.getXPathForCompartment(speciesId)); + } else if (speciesAttr.equalsIgnoreCase("initialConcentration") || speciesAttr.equalsIgnoreCase("initConc")) { + targetXpath = new XPathTarget(sbmlSupport.getXPathForSpecies(speciesId, SpeciesAttribute.initialConcentration)); + } else if (speciesAttr.equalsIgnoreCase("initialCount") || speciesAttr.equalsIgnoreCase("initCount")) { + targetXpath = new XPathTarget(sbmlSupport.getXPathForSpecies(speciesId, SpeciesAttribute.initialAmount)); + } else if (speciesAttr.equalsIgnoreCase("diff")) { + targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(speciesId + "_" + speciesAttr, ParameterAttribute.value)); + } else { + throw new RuntimeException("Unknown species attribute '" + speciesAttr + "'; cannot get xpath target for species '" + speciesId + "'."); + } + + } else if (ste instanceof ModelParameter || ste instanceof ReservedSymbol) { + // can only change parameter value. + targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(ste.getName(), ParameterAttribute.value)); + // use Ion's sample 3, with spatial app + } else if (ste instanceof Structure || ste instanceof Structure.StructureSize || ste instanceof StructureMappingParameter) { + String compartmentId = TokenMangler.mangleToSName(ste.getName()); + // can change compartment size or spatial dimension, but in vcell, we cannot change compartment dimension. + String compartmentAttr = ""; + String mappingId = ""; + if (ste instanceof Structure.StructureSize) { + compartmentId = TokenMangler.mangleToSName(((StructureSize) ste).getStructure().getName()); + compartmentAttr = ste.getName(); + } + if (ste instanceof StructureMappingParameter smp) { + compartmentId = TokenMangler.mangleToSName(smp.getStructure().getName()); + int role = smp.getRole(); + if (role == StructureMapping.ROLE_Size) { + compartmentAttr = smp.getName(); + } else if (role == StructureMapping.ROLE_AreaPerUnitArea || role == StructureMapping.ROLE_VolumePerUnitVolume) { + compartmentAttr = smp.getName(); + Structure structure = smp.getStructure(); + GeometryClass gc = smp.getSimulationContext().getGeometryContext().getStructureMapping(structure).getGeometryClass(); + mappingId = TokenMangler.mangleToSName(gc.getName() + structure.getName()); + } + } + if (compartmentAttr.isEmpty()) { + targetXpath = new XPathTarget(sbmlSupport.getXPathForCompartment(compartmentId)); + } else if (compartmentAttr.equalsIgnoreCase("size")) { + targetXpath = new XPathTarget(sbmlSupport.getXPathForCompartment(compartmentId, CompartmentAttribute.size)); + } else if (compartmentAttr.equalsIgnoreCase("AreaPerUnitArea") || compartmentAttr.equalsIgnoreCase("VolPerUnitVol")) { + targetXpath = new XPathTarget(sbmlSupport.getXPathForCompartmentMapping(compartmentId, mappingId, CompartmentAttribute.unitSize)); + } else { + throw new RuntimeException("Unknown compartment attribute '" + compartmentAttr + "'; cannot get xpath target for compartment '" + compartmentId + "'."); + } + } else if (ste instanceof KineticsParameter kp) { + String reactionID = kp.getKinetics().getReactionStep().getName(); + String parameterID = kp.getName(); + Pair key = new Pair<>(reactionID, parameterID); + String value = l2gMap.get(key); + if (value == null) { + // stays as local parameter + targetXpath = new XPathTarget(sbmlSupport.getXPathForKineticLawParameter(reactionID, parameterID, ParameterAttribute.value)); + } else { + // became a global in SBML, we need to refer to that global + targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(value, ParameterAttribute.value)); + } + } else if (ste instanceof Membrane.MembraneVoltage) { + // they are exported as globals + targetXpath = new XPathTarget(sbmlSupport.getXPathForGlobalParameter(TokenMangler.mangleToSName(ste.getName()), ParameterAttribute.value)); + } else { + logger.error("redundant error log: " + "Entity should be SpeciesContext, Structure, ModelParameter, ReserverdSymbol, KineticsParameter, or MembraneVoltage : " + ste.getClass()); + throw new RuntimeException("Unsupported entity in SBML model export: " + ste.getClass()); + } + return targetXpath; + } + + + private XPathTarget getVCMLTargetXPath(SymbolTableEntry ste, SimulationContext simContext) { + XPathTarget targetXpath; + if (ste instanceof SpeciesContextSpecParameter scsp) { + String paramXpath = ""; + String baseXpath = VCMLSupport.getXPathForSpeciesContextSpec(simContext.getName(), scsp.getSpeciesContextSpec().getSpeciesContext().getName()); + int role = scsp.getRole(); + if (role == SpeciesContextSpec.ROLE_InitialConcentration) { + paramXpath = "/vcml:InitialConcentration"; + } + if (role == SpeciesContextSpec.ROLE_InitialCount) { + paramXpath = "/vcml:InitialCount"; + } + if (role == SpeciesContextSpec.ROLE_DiffusionRate) { + paramXpath = "/vcml:Diffusion"; + } + targetXpath = new XPathTarget(baseXpath + paramXpath); + } else if (ste instanceof ModelParameter) { + // can only change parameter value. + targetXpath = new XPathTarget(VCMLSupport.getXPathForModelParameter(ste.getName())); + } else if (ste instanceof Structure || ste instanceof Structure.StructureSize || ste instanceof StructureMappingParameter) { + // can change compartment size or spatial dimension, but in vcell, we cannot change compartment dimension. + String attributeName = "Size"; + Structure struct = null; + if (ste instanceof Structure) { + struct = (Structure) ste; + } + if (ste instanceof Structure.StructureSize) { + struct = ((StructureSize) ste).getStructure(); + } + if (ste instanceof StructureMappingParameter smp) { + struct = smp.getStructure(); + int role = smp.getRole(); + if (role == StructureMapping.ROLE_AreaPerUnitArea) { + attributeName = "AreaPerUnitArea"; + } else if (role == StructureMapping.ROLE_VolumePerUnitVolume) { + attributeName = "VolumePerUnitVolume"; + } + } + if (struct instanceof Feature) { + targetXpath = new XPathTarget(VCMLSupport.getXPathForFeatureMappingAttribute(simContext.getName(), + struct.getName(), attributeName)); + } else { + targetXpath = new XPathTarget(VCMLSupport.getXPathForMembraneMappingAttribute(simContext.getName(), + struct.getName(), attributeName)); + } + } else if (ste instanceof KineticsParameter kp) { + targetXpath = new XPathTarget(VCMLSupport.getXPathForKineticLawParameter(kp.getKinetics().getReactionStep().getName(), kp.getName())); + } else if (ste instanceof Membrane.MembraneVoltage) { + targetXpath = new XPathTarget(VCMLSupport.getXPathForMembraneMappingAttribute(simContext.getName(), + ((Membrane.MembraneVoltage) ste).getMembrane().getName(), "InitialVoltage")); + } else { + logger.error("redundant error log: " + "Entity should be SpeciesContext, Structure, ModelParameter, KineticsParameter, or MembraneVoltage : " + ste.getClass()); + throw new RuntimeException("Unsupported entity in VCML model export: " + ste.getClass()); + } + return targetXpath; + } + + public void addSedmlFileToList(String sedmlFileName) { + if (sedmlFileName != null && !sedmlFileName.isEmpty()) { + this.sedmlFilePathStrAbsoluteList.add(sedmlFileName); + } + } + + + public boolean createOmexArchive(String srcFolder, String sFileName) { + // writing into combine archive, deleting file if already exists with same name + String omexPath = Paths.get(srcFolder, sFileName + ".omex").toString(); + File omexFile = new File(omexPath); + if (omexFile.exists()) { + omexFile.delete(); + } + + try (CombineArchive archive = new CombineArchive(omexFile)) { + + + for (String sd : this.sedmlFilePathStrAbsoluteList) { + File s = Paths.get(srcFolder, sd).toFile(); + archive.addEntry( + s, + "./" + sd, // target file name + new URI("http://identifiers.org/combine.specifications/sed-ml"), + true // mark file as master + ); + } + for (String sd : this.modelFilePathStrAbsoluteList) { + archive.addEntry( + Paths.get(srcFolder, sd).toFile(), + "./" + sd, // target file name + new URI("http://identifiers.org/combine.specifications/sbml"), + false // mark file as master + ); + } + + archive.addEntry( + Paths.get(srcFolder, sFileName + ".vcml").toFile(), + "./" + sFileName + ".vcml", + new URI("http://purl.org/NET/mediatypes/application/vcml+xml"), + false + ); + + File dir = new File(srcFolder); + String[] files = dir.list(); + if (files == null) { + throw new RuntimeException("createZipArchive: No files found in directory: " + srcFolder); + } + Path rdfFilePath = null; + for (String sd : files) { + Path filePath = Paths.get(srcFolder, sd); + if (sd.endsWith(".rdf")) { + rdfFilePath = filePath; + // the CombineArchive library does not allow to directly write to the /metadata.rdf file + // instead, we copy the file to /metadata.rdf later after the archive is closed + // + // archive.addEntry( + // filePath.toFile(), + // "./metadata.rdf", + // new URI("http://identifiers.org/combine.specifications/omex-metadata"), + // false + // ); + } + if (sd.endsWith(".png")) { + archive.addEntry( + filePath.toFile(), + "./" + sd, + new URI("http://purl.org/NET/mediatypes/image/png"), + false + ); + } + } + if (rdfFilePath != null) { + // create temporary /metadata.rdf file so that an entry for /metadata.rdf is included in the Manifest + OmexDescription omexDescription = new OmexDescription(); + omexDescription.setDescription("VCell Simulation Archive"); + omexDescription.modified.add(new Date()); + archive.addDescription(new OmexMetaDataObject(omexDescription)); + } + + archive.pack(); + archive.close(); + + if (rdfFilePath != null) { + // now that the OMEX archive is closed and written to disk, open it as a regular zip file + // and replace the generated metadata.rdf file with the one we created. + replaceMetadataRdfFileInArchive(omexFile.toPath(), rdfFilePath); + } + + if (OperatingSystemInfo.getInstance().isWindows()) repairManifestEntry(omexFile.toPath()); + removeOtherFiles(srcFolder, files); + + } catch (Exception e) { + throw new RuntimeException("createZipArchive threw exception: " + e.getMessage()); + } + return true; + } + + private static void replaceMetadataRdfFileInArchive(Path zipFilePath, Path newFilePath) throws IOException { + String pathInZip = "./metadata.rdf"; + try (FileSystem fs = FileSystems.newFileSystem(zipFilePath)) { + Path fileInsideZipPath = fs.getPath(pathInZip); + Files.delete(fileInsideZipPath); + Files.copy(newFilePath, fileInsideZipPath); + } + } + + private static void repairManifestEntry(Path zipFilePath) throws IOException { + try (FileSystem fs = FileSystems.newFileSystem(zipFilePath)) { + Path manifestPath = fs.getPath("/", "manifest.xml"); + if (!Files.exists(manifestPath)) + throw new IOException("The manifest file in " + zipFilePath + " is missing"); + + List rawLines, correctedLines = new ArrayList<>(); + try (BufferedReader reader = new BufferedReader(Files.newBufferedReader(manifestPath))) { + rawLines = reader.lines().toList(); + } + for (String rawLine : rawLines) { + correctedLines.add(rawLine.contains(".\\") ? rawLine.replace(".\\", "./") : rawLine); + } + Path tmpFilePath = Files.createTempFile("fixedManifest", ""); + try (BufferedWriter writer = new BufferedWriter(Files.newBufferedWriter(tmpFilePath))) { + for (String line : correctedLines) writer.write(line + "\n"); + } + Files.copy(tmpFilePath, manifestPath, StandardCopyOption.REPLACE_EXISTING); + } + } + + private static void removeOtherFiles(String outputDir, String[] files) { + boolean isDeleted; + for (String sd : files) { + if (sd.endsWith(".sedml") || sd.endsWith(".sbml") || sd.endsWith("xml") || sd.endsWith(".vcml") || sd.endsWith(".rdf") || sd.endsWith(".png")) { + isDeleted = Paths.get(outputDir, sd).toFile().delete(); + if (!isDeleted) { + throw new RuntimeException("Unable to remove intermediate file '" + sd + "'."); + } + } + } + } + + public static Map getUnsupportedApplicationMap(BioModel bioModel, ModelFormat modelFormat) { + HashMap unsupportedApplicationMap = new HashMap<>(); + Arrays.stream(bioModel.getSimulationContexts()).forEach(simContext -> { + if (modelFormat == ModelFormat.SBML) { + try { + SBMLExporter.validateSimulationContextSupport(simContext); + } catch (UnsupportedSbmlExportException e) { + unsupportedApplicationMap.put(simContext.getName(), e.getMessage()); + } + } + }); + return unsupportedApplicationMap; + } + + public static class SEDMLExportException extends Exception { + public SEDMLExportException(String message) { + super(message); + } + + public SEDMLExportException(String message, Exception cause) { + super(message, cause); + } + } + + public static List writeBioModel(BioModel bioModel, + Optional publicationMetadata, + File exportFileOrDirectory, + ModelFormat modelFormat, + Predicate simContextExportFilter, + boolean bHasPython, + boolean bValidation, + boolean bCreateOmexArchive + ) throws SEDMLExportException, OmexPythonUtils.OmexValidationException, IOException { + Predicate simulationExportFilter = s -> true; + SEDMLEventLog sedmlEventLog = (String entry) -> { + }; + Optional jsonReportFile = Optional.empty(); + return writeBioModel( + bioModel, publicationMetadata, jsonReportFile, exportFileOrDirectory, simulationExportFilter, simContextExportFilter, + modelFormat, sedmlEventLog, bHasPython, bValidation, bCreateOmexArchive); + } + + public static List writeBioModel(File vcmlFilePath, + BioModelInfo bioModelInfo, + File outputDir, + Predicate simulationExportFilter, + ModelFormat modelFormat, + SEDMLEventLog eventLogWriter, + boolean bAddPublicationInfo, + boolean bSkipUnsupportedApps, + boolean bHasPython, + boolean bValidate + ) throws SEDMLExportException, OmexPythonUtils.OmexValidationException, IOException { + + // get VCML name from VCML path + String vcmlName = FilenameUtils.getBaseName(vcmlFilePath.getName()); // platform independent, strips extension too + Optional jsonReportFile = Optional.of(Paths.get( + outputDir.getAbsolutePath(), "json_reports", vcmlName + ".json").toFile()); + File omexOutputFile = Paths.get(outputDir.getAbsolutePath(), vcmlName + ".omex").toFile(); + eventLogWriter.writeEntry(vcmlName); + + // Create biomodel + BioModel bioModel; + try { + bioModel = XmlHelper.XMLToBioModel(new XMLSource(vcmlFilePath)); + bioModel.updateAll(false); + bioModel.refreshDependencies(); + eventLogWriter.writeEntry(vcmlName + ",VCML,SUCCEEDED\n"); + } catch (XmlParseException | MappingException e1) { + String msg = vcmlName + " VCML failed to parse and generate math: " + e1.getMessage(); + logger.error(msg, e1); + eventLogWriter.writeEntry(vcmlName + ",VCML,FAILED" + e1.getMessage() + "\n"); + throw new SEDMLExportException(msg, e1); + } + + Predicate simContextExportFilter = sc -> true; + if (bSkipUnsupportedApps) { + Map unsupportedApplications = SedMLExporter.getUnsupportedApplicationMap(bioModel, modelFormat); + simContextExportFilter = (SimulationContext sc) -> !unsupportedApplications.containsKey(sc.getName()); + } + + Optional publicationMetadata = Optional.empty(); + if (bioModelInfo != null && bioModelInfo.getPublicationInfos() != null && bioModelInfo.getPublicationInfos().length > 0) { + if (bAddPublicationInfo) { + publicationMetadata = Optional.of(PublicationMetadata.fromPublicationInfoAndWeb(bioModelInfo.getPublicationInfos()[0])); + } else { + publicationMetadata = Optional.of(PublicationMetadata.fromPublicationInfo(bioModelInfo.getPublicationInfos()[0])); + } + } + + boolean bCreateOmexArchive = true; + return writeBioModel( + bioModel, publicationMetadata, jsonReportFile, omexOutputFile, simulationExportFilter, simContextExportFilter, + modelFormat, eventLogWriter, bHasPython, bValidate, bCreateOmexArchive); + } + + private static List writeBioModel(BioModel bioModel, + Optional publicationMetadata, + Optional jsonReportFile, + File exportFileOrDirectory, + Predicate simulationExportFilter, + Predicate simContextExportFilter, + ModelFormat modelFormat, + SEDMLEventLog sedmlEventLog, + boolean bHasPython, + boolean bValidate, + boolean bCreateOmexArchive + ) throws SEDMLExportException, OmexPythonUtils.OmexValidationException, IOException { + try { + // export the entire biomodel to a SEDML file (all supported applications) + int sedmlLevel = 1; + int sedmlVersion = 2; + String sOutputDirPath = FileUtils.getFullPathNoEndSeparator(exportFileOrDirectory.getAbsolutePath()); + String sBaseFileName = FileUtils.getBaseName(exportFileOrDirectory.getAbsolutePath()); + + List simsToExport = Arrays.stream(bioModel.getSimulations()).filter(simulationExportFilter).collect(Collectors.toList()); + + // we replace the obsolete solver with the fully supported equivalent + for (Simulation simulation : simsToExport) { + if (simulation.getSolverTaskDescription().getSolverDescription().equals(SolverDescription.FiniteVolume)) { + try { + // try to replace with the fully supported equivalent (do we need to reset solver parameters?) + simulation.getSolverTaskDescription().setSolverDescription(SolverDescription.SundialsPDE); + } catch (PropertyVetoException e) { + String msg1 = "Failed to replace obsolete PDE solver '" + SolverDescription.FiniteVolume.name() + "' " + + "with fully supported equivalent PDE solver '" + SolverDescription.SundialsPDE.name() + "'"; + logger.error(msg1, e); + try { + simulation.getSolverTaskDescription().setSolverDescription(SolverDescription.FiniteVolumeStandalone); + } catch (PropertyVetoException e1) { + String msg2 = "Failed to replace obsolete PDE solver '" + SolverDescription.FiniteVolume.name() + "' " + + "with equivalent PDE solver '" + SolverDescription.FiniteVolumeStandalone.name() + "'"; + logger.error(msg2, e1); + throw new RuntimeException(msg2, e1); + } + } + } + } + + // convert biomodel to vcml and save to file. + String vcmlString = XmlHelper.bioModelToXML(bioModel); + String vcmlFileName = Paths.get(sOutputDirPath, sBaseFileName + ".vcml").toString(); + File vcmlFile = new File(vcmlFileName); + XmlUtil.writeXMLStringToFile(vcmlString, vcmlFile.getAbsolutePath(), true); + + String jsonReportPath = null; + if (jsonReportFile.isPresent()) { + jsonReportPath = jsonReportFile.get().getAbsolutePath(); + } + SedMLExporter sedmlExporter = new SedMLExporter(sBaseFileName, bioModel, sedmlLevel, sedmlVersion, simsToExport, jsonReportPath); + String sedmlString = sedmlExporter.getSEDMLDocument(sOutputDirPath, sBaseFileName, modelFormat, bValidate, simContextExportFilter).writeDocumentToString(); + + if (bCreateOmexArchive) { + + String sedmlFileName = Paths.get(sOutputDirPath, sBaseFileName + ".sedml").toString(); + XmlUtil.writeXMLStringToFile(sedmlString, sedmlFileName, true); + sedmlExporter.addSedmlFileToList(sBaseFileName + ".sedml"); + + String diagramName = XmlRdfUtil.diagramBaseName + XmlRdfUtil.diagramExtension; + Path diagramPath = Paths.get(sOutputDirPath, diagramName); + XmlRdfUtil.writeModelDiagram(bioModel, diagramPath.toFile()); + + Optional bioModelVersion = Optional.ofNullable(bioModel.getVersion()); + String rdfString = XmlRdfUtil.getMetadata(sBaseFileName, diagramPath.toFile(), bioModelVersion, publicationMetadata); + XmlUtil.writeXMLStringToFile(rdfString, String.valueOf(Paths.get(sOutputDirPath, "metadata.rdf")), true); + + sedmlExporter.createOmexArchive(sOutputDirPath, sBaseFileName); + + if (bValidate && bHasPython) { + OmexPythonUtils.validateOmex(Paths.get(sOutputDirPath, sBaseFileName + ".omex")); + } + } else { + XmlUtil.writeXMLStringToFile(sedmlString, exportFileOrDirectory.getAbsolutePath(), true); + } + return sedmlExporter.sedmlRecorder.getRecords(); + } catch (OmexPythonUtils.OmexValidationException e) { + throw e; + } catch (IOException e) { + throw e; + } catch (InterruptedException | XmlParseException e) { + throw new SEDMLExportException("failed to export biomodel", e); + } + } + + public SedMLRecorder getSedmlLogger() { + return this.sedmlRecorder; + } +} + + diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java b/vcell-core/src/main/java/org/vcell/sedml/SedMLImporter.java similarity index 52% rename from vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java rename to vcell-core/src/main/java/org/vcell/sedml/SedMLImporter.java index 2f05be032c..1d7b278211 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLImporter.java +++ b/vcell-core/src/main/java/org/vcell/sedml/SedMLImporter.java @@ -1,10 +1,12 @@ package org.vcell.sedml; +import cbit.image.ImageException; import cbit.util.xml.VCLogger; import cbit.util.xml.VCLoggerException; import cbit.util.xml.XmlUtil; import cbit.vcell.biomodel.BioModel; +import cbit.vcell.geometry.GeometryException; import cbit.vcell.mapping.*; import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.SimulationContext.MathMappingCallback; @@ -30,10 +32,25 @@ import org.apache.logging.log4j.Logger; import org.jdom2.Document; import org.jdom2.Element; -import org.jlibsedml.Model; -import org.jlibsedml.Parameter; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.algorithm.Algorithm; +import org.jlibsedml.components.algorithm.AlgorithmParameter; +import org.jlibsedml.components.dataGenerator.DataGenerator; +import org.jlibsedml.components.model.Change; +import org.jlibsedml.components.model.ChangeAttribute; +import org.jlibsedml.components.model.ComputeChange; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.Parameter; import org.jlibsedml.*; -import org.jlibsedml.UniformRange.UniformType; +import org.jlibsedml.components.output.DataSet; +import org.jlibsedml.components.simulation.OneStep; +import org.jlibsedml.components.task.UniformRange.UniformType; +import org.jlibsedml.components.output.Output; +import org.jlibsedml.components.output.Report; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.task.*; import org.jlibsedml.execution.ArchiveModelResolver; import org.jlibsedml.execution.FileModelResolver; import org.jlibsedml.execution.ModelResolver; @@ -56,71 +73,68 @@ import java.nio.file.Files; import java.util.*; +import java.util.stream.Collectors; /** * Serves as a means to convert sedml documents into VCell BioModels */ -public class SEDMLImporter { - private final static Logger logger = LogManager.getLogger(SEDMLImporter.class); - private SedML sedml; - private final boolean exactMatchOnly; - +public class SedMLImporter { + private final static Logger logger = LogManager.getLogger(SedMLImporter.class); + + private final StrictnessPolicy strictnessPolicy; private final VCLogger transLogger; + private final Map kisaoToSolverMapping; + private final HashMap importMap; + + private SedMLDataContainer sedmlContainer; + private String bioModelBaseName; - private ArchiveComponents ac; + private ArchiveComponents archiveComponents; private ModelResolver resolver; private SBMLSupport sbmlSupport; - - private final HashMap importMap = new HashMap<>(); + /** * Builds the importer for future initialization * * @param transLogger the VC logger to use - * @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. + * @param policy settings for how import should react to unsupported cases */ - public SEDMLImporter(VCLogger transLogger, File fileWithSedmlToProcess, SedML sedml, boolean exactMatchOnly) - throws XMLException, IOException { - this(transLogger, exactMatchOnly); - this.initialize(fileWithSedmlToProcess, sedml); + public SedMLImporter(VCLogger transLogger, StrictnessPolicy policy) { + this.transLogger = transLogger; + this.strictnessPolicy = policy; + this.kisaoToSolverMapping = new HashMap<>(); + this.importMap = new LinkedHashMap<>(); + this.sedmlContainer = null; } /** - * Initialize the importer to process a specific set of SedML within a document or archive. + * Initialize the importer to process a specific set of SedML within a document or archive. The importer will attempt + * to find any unsupported sedml during initialization, and in such case will fail except if the + * allowModifiedImport flag is set to true * @param fileWithSedmlToProcess the file containing SedML - * @param sedml the SedML to be processed (since the file may have more than 1 sedml) + * @param sedml the SedML to be processed + * * @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. + * @return the sedml data container to be used for this importer */ - public void initialize(File fileWithSedmlToProcess, SedML sedml) throws XMLException, IOException { - // extract bioModel name from sedml (or sedml) file + public SedMLDataContainer initialize(File fileWithSedmlToProcess, SedMLDataContainer sedml) throws XMLException, IOException { 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; + // Determine sedml to import off of. + this.sedmlContainer = this.verifyOrModifyOrRejectSedml(sedml); + // extract bioModel name from sedml (or sedml) file 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.archiveComponents = Libsedml.readSEDMLArchive(Files.newInputStream(fileWithSedmlToProcess.toPath())); } - this.resolver = new ModelResolver(this.sedml); - if(this.ac != null) { - ArchiveModelResolver amr = new ArchiveModelResolver(this.ac); - amr.setSedmlPath(this.sedml.getPathForURI()); + this.resolver = new ModelResolver(this.sedmlContainer); + if(this.archiveComponents != null) { + ArchiveModelResolver amr = new ArchiveModelResolver(this.archiveComponents); + amr.setSedmlPath(this.sedmlContainer.getPathForURI()); this.resolver.add(amr); } else { this.resolver.add(new FileModelResolver()); // assumes absolute paths @@ -128,207 +142,36 @@ public void initialize(File fileWithSedmlToProcess, SedML sedml) throws XMLExcep this.resolver.add(new RelativeFileModelResolver(sedmlRelativePrefix)); // in case model URIs are relative paths } this.sbmlSupport = new SBMLSupport(); + return this.sedmlContainer; } - /** - * Get the list of biomodels from the sedml initialized at construction time. - */ - public List getBioModels() { - List bioModelList = new LinkedList<>(); - List modelList; - List simulationList; - List abstractTaskList; - List dataGeneratorList; - List outputList; - - Map bioModelMap; // Holds all entries for all SEDML Models where some may reference the same BioModel - Map vcSimulations = new HashMap<>(); // We will parse all tasks and create Simulations in BioModels - + /** + * Get the list of biomodels from the sedml initialized at construction time. + */ + public Map getBioModels(){ + if (this.sedmlContainer == null) throw new IllegalStateException("Importer has not yet been initialized!"); + Set bioModels = new LinkedHashSet<>(); + + Map bioModelMap; // Holds all entries for all SEDML Models where some may reference the same BioModel + Map vcSimulations; // We will parse all tasks and create Simulations in BioModels + SedMLDataContainer matchingSedmlContainer = this.sedmlContainer; + SedML matchingSedml = matchingSedmlContainer.getSedML(); try { // iterate through all the elements and show them at the console - modelList = this.sedml.getModels(); - if (modelList.isEmpty()) return new LinkedList<>(); // nothing to import - simulationList = this.sedml.getSimulations(); - abstractTaskList = this.sedml.getTasks(); - dataGeneratorList = this.sedml.getDataGenerators(); - outputList = this.sedml.getOutputs(); - - this.printSEDMLSummary(modelList, simulationList, abstractTaskList, dataGeneratorList, outputList); + this.printSEDMLSummary(matchingSedml.getModels(), matchingSedml.getSimulations(), matchingSedml.getTasks(), matchingSedml.getDataGenerators(), matchingSedml.getOutputs()); + // If we don't have models, we don't have anything to do + if (this.sedmlContainer.getSedML().getModels().isEmpty()) return new HashMap<>(); // nothing to import // NB: We don't know how many BioModels we'll end up with, // as some model changes may be translatable as simulations with overrides - bioModelMap = this.createBioModels(modelList); + bioModelMap = this.createBioModels(matchingSedml.getModels()); Set uniqueBioModels = new HashSet<>(bioModelMap.values()); - // Creating one VCell Simulation for each SED-ML actual Task - // (RepeatedTasks get added either on top of or separately as parameter scan overrides) - for (AbstractTask selectedTask : abstractTaskList) { - if (selectedTask instanceof RepeatedTask) continue; // Repeated tasks refer to regular tasks, so first we need to create simulations for all regular tasks - if (!(selectedTask instanceof Task baseTask)) throw new RuntimeException("Unsupported task " + selectedTask); - - // the SedML simulation will become the vCell simulation - org.jlibsedml.Simulation sedmlSimulation = this.sedml.getSimulation(baseTask.getSimulationReference()); - if(!(sedmlSimulation instanceof UniformTimeCourse utcSimulation)) { // only UTC sims supported - String baseTaskName = String.format("%s(%s)", baseTask.getName() == null ? "" : baseTask.getName(), baseTask.getId()); - logger.error("task '{}' is being skipped, it references an unsupported simulation type: {}", baseTaskName, sedmlSimulation); - continue; - } - - // the "original" model referred to by the task; almost always sbml we can import as physiology - org.jlibsedml.Model sedmlModel = this.sedml.getModelWithId(baseTask.getModelReference()); - if (sedmlModel == null) throw new RuntimeException("We somehow got a null sedml model!!"); - // can be sbml or vcml - String sedmlOriginalModelLanguage = sedmlModel.getLanguage(); - // this will be used in the BioModel name - String sedmlOriginalModelName = sedmlModel.getName() != null ? sedmlModel.getName() : ""; - - // at this point we assume that the sedml simulation, algorithm and kisaoID are all valid - - // identify the vCell solvers that would match best the sedml solver kisao id - String kisaoID = utcSimulation.getAlgorithm().getKisaoID(); - // try to find a match in the ontology tree - SolverDescription solverDescription = SolverUtilities.matchSolverWithKisaoId(kisaoID, this.exactMatchOnly); - if (solverDescription != null) { - logger.info("Task (id='{}') is compatible, solver match found in ontology: '{}' matched to {}", baseTask.getId(), kisaoID, solverDescription); - } else { - logger.warn("Task (id='{})' is not compatible, no equivalent solver found in ontology for requested algorithm '{}'.", selectedTask.getId(), kisaoID); - if (this.exactMatchOnly){ - logger.error("Unable to continue; \"Exact Match Only\" mode is enabled; no substitute can be applied."); - throw new RuntimeException("No appropriate solver could be found; \"Exact Match Only\" mode is enabled"); - } - // give it a try anyway with our deterministic default solver - solverDescription = SolverDescription.CombinedSundials; - logger.info("Attempting to solve Task (id='{})' with deterministic default solver {}", selectedTask.getId(), solverDescription); - } - // find out everything else we need about the application we're going to use, - // as some more info will be needed when we parse the sbml file - boolean bSpatial = false; - Application appType = Application.NETWORK_DETERMINISTIC; - Set sfList = solverDescription.getSupportedFeatures(); - for(SolverDescription.SolverFeature sf : sfList) { - switch(sf) { - case Feature_Rulebased: - if(appType != Application.SPRINGSALAD) { - // springs(alad) type takes precedence - appType = Application.RULE_BASED_STOCHASTIC; - } - break; - case Feature_Stochastic: - appType = Application.NETWORK_STOCHASTIC; - break; - case Feature_Deterministic: - appType = Application.NETWORK_DETERMINISTIC; - break; - case Feature_Springs: - appType = Application.SPRINGSALAD; - break; - case Feature_Spatial: - bSpatial = true; - break; - default: - break; - } - } - - BioModel bioModel = bioModelMap.get(sedmlModel.getId()); - - // if language is VCML, we don't need to create Applications and Simulations in BioModel - // we allow a subset of SED-ML Simulation settings (may have been edited) to override existing BioModel Simulation settings - - if(sedmlOriginalModelLanguage.contentEquals(SUPPORTED_LANGUAGE.VCELL_GENERIC.getURN())) { - Simulation theSimulation = null; - for (Simulation sim : bioModel.getSimulations()) { - if (sim.getName().equals(baseTask.getName())) { - logger.trace(" --- selected task - name: " + baseTask.getName() + ", id: " + baseTask.getId()); - sim.setImportedTaskID(baseTask.getId()); - theSimulation = sim; - break; // found the one, no point to continue the for loop - } - }if(theSimulation == null) { - logger.error("Couldn't match sedml task '" + baseTask.getName() + "' with any biomodel simulation"); - // TODO: should we throw an exception? - continue; // should never happen - } - - SolverTaskDescription simTaskDesc = theSimulation.getSolverTaskDescription(); - this.translateTimeBounds(simTaskDesc, utcSimulation); - continue; - } - - // if language is SBML, we must create Simulations - // we may need to also create Applications (if the default one from SBML import is not the right type) - - // see first if there is a suitable application type for the specified kisao - // if not, we add one by doing a "copy as" to the right type - SimulationContext[] existingSimulationContexts = bioModel.getSimulationContexts(); - SimulationContext matchingSimulationContext = null; - for (SimulationContext simContext : existingSimulationContexts) { - if (simContext.getApplicationType().equals(appType) && ((simContext.getGeometry().getDimension() > 0) == bSpatial)) { - matchingSimulationContext = simContext; - break; - } - } - if (matchingSimulationContext == null) { - // this happens if we need a NETWORK_STOCHASTIC application - String modelPlusContextName = String.format("%s_%d", sedmlOriginalModelName, existingSimulationContexts.length); - matchingSimulationContext = SimulationContext.copySimulationContext( - bioModel.getSimulationContext(0), - modelPlusContextName, bSpatial, appType); - bioModel.addSimulationContext(matchingSimulationContext); - try { - String importedSCName = bioModel.getSimulationContext(0).getName(); - bioModel.getSimulationContext(0).setName("original_imported_"+importedSCName); - matchingSimulationContext.setName(importedSCName); - } catch (PropertyVetoException e) { - // we should never bomb out just for trying to set a pretty name - logger.warn("could not set pretty name on application from name of model "+sedmlModel); - } - } - matchingSimulationContext.refreshDependencies(); - MathMappingCallback callback = new MathMappingCallbackTaskAdapter(null); - matchingSimulationContext.refreshMathDescription(callback, NetworkGenerationRequirements.ComputeFullStandardTimeout); - - // making the new vCell simulation based on the sedml simulation - Simulation newSimulation = new Simulation(matchingSimulationContext.getMathDescription(), matchingSimulationContext); - - // See note below this immediately following section - String newSimName = baseTask.getId(); - if(SEDMLUtil.getName(baseTask) != null) { - newSimName += "_" + SEDMLUtil.getName(baseTask); - } - newSimulation.setName(newSimName); - newSimulation.setImportedTaskID(baseTask.getId()); - vcSimulations.put(baseTask.getId(), newSimulation); - - /* NOTE: Before, we checked if the selected task was an instance of a task; we no longer need to check - that because we logically confirm that earlier on. If we ever get to this point and need an 'else': - - newSimulation.setName(SEDMLUtil.getName(utcSimulation)+"_"+SEDMLUtil.getName(baseTask)); - */ - - // we identify the type of sedml simulation (uniform time course, etc.) - // and set the vCell simulation parameters accordingly - SolverTaskDescription simTaskDesc = newSimulation.getSolverTaskDescription(); - simTaskDesc.setSolverDescription(solverDescription); - - - this.translateTimeBounds(simTaskDesc, utcSimulation); - this.translateAlgorithmParams(simTaskDesc, utcSimulation); - - newSimulation.setSolverTaskDescription(simTaskDesc); - newSimulation.setDescription(SEDMLUtil.getName(baseTask)); - bioModel.addSimulation(newSimulation); - newSimulation.refreshDependencies(); - - // finally, add MathOverrides if referenced model has specified compatible changes - if (!sedmlModel.getListOfChanges().isEmpty() && this.canTranslateToOverrides(bioModel, sedmlModel)) { - this.createOverrides(newSimulation, sedmlModel.getListOfChanges()); - } - - } + // First, process tasks (a.k.a "base tasks", "actual tasks", "basic tasks", etc. NOT repeated tasks) + vcSimulations = this.addStandardTasks(matchingSedmlContainer, bioModelMap); // now process repeated tasks, if any - this.addRepeatedTasks(abstractTaskList, vcSimulations); + this.addRepeatedTasks(matchingSedml.getTasks(), vcSimulations); // we may have added simulations in addRepeatedTasks() {method above}, so let's make sure we add them. @@ -337,19 +180,17 @@ public List getBioModels() { Simulation[] sims = bm.getSimulations(); for (Simulation sim : sims) { String taskId = sim.getImportedTaskID(); - AbstractTask task = this.sedml.getTaskWithId(taskId); - if (task != null && task.getName() != null) { + AbstractTask task = this.sedmlContainer.findAbstractTaskById(new SId(taskId)); + if (null == task) throw new RuntimeException("Unexpected non-task"); + if (task.getName() != null) { try { sim.setName(task.getName()); } catch (PropertyVetoException e) { // we should never bomb out just for trying to set a pretty name - logger.warn("could not set pretty name for simulation " + - sim.getDisplayName() + " from task " + task); + logger.warn("could not set pretty name for simulation {} from task {}", sim.getDisplayName(), task); } - } else if (task == null){ - throw new RuntimeException("We somehow got a null task!!"); } - } + } } // purge unused biomodels and applications @@ -361,27 +202,174 @@ public List getBioModels() { i--; } } - if (doc.getSimulationContexts().length > 0) bioModelList.add(doc); + if (doc.getSimulationContexts().length > 0) bioModels.add(doc); } // try to consolidate SimContexts into fewer (possibly just one) BioModels // unlikely to happen from SED-MLs not originating from VCell, but very useful for round-tripping if so // TODO: maybe try to detect that and only try if of VCell origin - bioModelList = this.mergeBioModels(bioModelList); + bioModels = this.mergeBioModels(bioModels); // TODO: make imported BioModel(s) VCell-friendly // TODO: apply TransformMassActions to usedBiomodels // cannot do this for now, as it can be very expensive (hours!!) // also has serious memory issues (runs out of memory even with bumping up to Xmx12G - return bioModelList; + Map mappings = new LinkedHashMap<>(); + for (BioModel bm : bioModels) mappings.put(bm, this.getSBMLSymbolMapping(bm)); + return mappings; } catch (Exception e) { Tracer.failure(e,"Unable to initialize bioModel for the given selection: "+e.getMessage()); throw new RuntimeException("Unable to initialize bioModel for the given selection\n"+e.getMessage(), e); } } - private List mergeBioModels(List bioModels) { - if (bioModels.size() <=1) return bioModels; + private SedMLDataContainer verifyOrModifyOrRejectSedml(SedMLDataContainer providedSedmlContainer) { + /* + These are the cases we currently track: + 1) SedML requests a simulation type we don't currently support (e.g. SteadyState & Analysis) + 2) SedML requests a solver algorithm that we do not have exactly + 3) SedML requests repeated task with multiple subtasks + */ + boolean shouldPrune = false; + StrictnessPolicy policy; + SedMLDataContainer copiedSedmlContainer; + + if (this.strictnessPolicy.isConsideredStrict()){ + copiedSedmlContainer = providedSedmlContainer; + policy = this.strictnessPolicy; + } else { + SedMLDataContainer tmpContainer; + StrictnessPolicy tmpPolicy; + try { + tmpContainer = new SedMLDataContainer(providedSedmlContainer, true); + tmpPolicy = this.strictnessPolicy; + } catch (CloneNotSupportedException e){ + logger.warn("could not clone sedml in provided SedMLDataContainer; will still attempt flexible import."); + tmpContainer = providedSedmlContainer; + tmpPolicy = StrictnessPolicy.elevateToStrictSettings(this.strictnessPolicy); + } + copiedSedmlContainer = tmpContainer; + policy = tmpPolicy; + } + SedML copiedSedml = copiedSedmlContainer.getSedML(); + + // Case 1: Verify all valid simulations + logger.info("Simulation Type check: STARTED"); + Set validSimulations = new HashSet<>(); + validSimulations.addAll(copiedSedml.getSimulations().stream().filter(UniformTimeCourse.class::isInstance).toList()); + validSimulations.addAll(copiedSedml.getSimulations().stream().filter(OneStep.class::isInstance).toList()); + if (validSimulations.size() != copiedSedml.getSimulations().size()){ + // We have some simulations we can't support + Set badSims = new HashSet<>(copiedSedml.getSimulations()); + badSims.removeAll(validSimulations); + if (!policy.skipUnsupportedSimulationTypes){ + String badSimsString = badSims.stream().map(Object::toString).collect(Collectors.joining("\n\t")); + String errMsg = "Provided SedML contains disallowed Simulations:\n\t" + badSimsString; + logger.error("Simulation Type check: FAILED ...throwing exception..."); + throw new IllegalArgumentException(errMsg); + } + logger.info("Removing unsupported simulations from SedML"); + // Safe to modify `copiedSedmlContainer` / "copiedSedml" for rest of this scope; if statement ensures it's a deep copy since if skipUnsupportedSimulationTypes is true, it can't be strict + for (org.jlibsedml.components.simulation.Simulation simToRemove: badSims){ + copiedSedml.getListOfSimulations().removeContent(simToRemove); + } + shouldPrune = true; + } + logger.info("Simulation Type check: PASSED"); + + // Case 2: Solver Algorithm Check + logger.info("Solver Algorithm check: STARTED"); + java.util.function.Function SimulationDotGetAlgorithm = org.jlibsedml.components.simulation.Simulation::getAlgorithm; + Set requestedAlgorithms = copiedSedml.getSimulations().stream().map(SimulationDotGetAlgorithm).map(Algorithm::getKisaoID).collect(Collectors.toSet()); + Map solverMatches = new HashMap<>(); + boolean strictKisao = StrictnessPolicy.SolverMatchPolicy.STRICT_MATCH_OR_REJECT == policy.solverMatchPolicy; + for (String kisao : requestedAlgorithms) solverMatches.put(kisao, SolverUtilities.matchSolverWithKisaoId(kisao, strictKisao)); + Set badMatches = new HashSet<>(); + for (String kisao : solverMatches.keySet()) if (solverMatches.get(kisao) == null) badMatches.add(kisao); + if (!badMatches.isEmpty()){ + switch (policy.solverMatchPolicy){ + case STRICT_MATCH_OR_REJECT: + case FLEXIBLE_MATCH_OR_REJECT: + String badSimsString = String.join("\n\t", badMatches); + String errMsg = "Under selected settings, provided SedML contains unmatchable Kisao Algorithms:\n\t" + badSimsString; + logger.error("Simulation Algorithm check: FAILED ...throwing exception..."); + throw new IllegalArgumentException(errMsg); + case SUNDIALS_AS_LAST_RESORT: + // give it a try anyway with our deterministic default solver + logger.warn("Attempting to solve incompatible kisao algorithms with deterministic default solver: {}", SolverDescription.CombinedSundials); + for (String kisao : requestedAlgorithms) solverMatches.putIfAbsent(kisao, SolverDescription.CombinedSundials); + break; + case REMOVE_UNSUPPORTED: + // remove the offending simulations (and their unsupported algorithms) + for (String badKisao : badMatches) solverMatches.remove(badKisao); + logger.info("Removing simulations with unsupported kisao from SedML"); + for (org.jlibsedml.components.simulation.Simulation sim: copiedSedml.getSimulations()) { + String kisaoAlg = sim.getAlgorithm().getKisaoID(); + if (!badMatches.contains(kisaoAlg)) continue; + copiedSedml.getListOfSimulations().removeContent(sim); + } + shouldPrune = true; + break; + } + } + this.kisaoToSolverMapping.putAll(solverMatches); + logger.info("Solver Algorithm check: PASSED"); + + // Case 3: Multi-SubTask Check + // -> In essence, a repeated task can apply to multiple "simulations" at once. This is problematic for us. + logger.info("Multi-SubTask check: STARTED"); + List repeatedTasks = copiedSedml.getTasks().stream().filter(RepeatedTask.class::isInstance).map(RepeatedTask.class::cast).toList(); + Queue badRepeatedTasks = repeatedTasks.stream().filter((task)->task.getSubTasks().size() > 1).collect(Collectors.toCollection(LinkedList::new)); + if (!badRepeatedTasks.isEmpty()){ + // Uh-oh, multi-subTasks detected! + if (policy.multipleSubTaskPolicy != StrictnessPolicy.MultipleSubTaskPolicy.REJECT_IMMEDIATELY){ + while (!badRepeatedTasks.isEmpty()){ + boolean success = this.successfullyReducedRedundantSubTasks(badRepeatedTasks.poll()); + if (!success) break; // + } + } + // check again! + if (!badRepeatedTasks.isEmpty()){ + if (policy.multipleSubTaskPolicy != StrictnessPolicy.MultipleSubTaskPolicy.CONDENSE_ELSE_REMOVE){ + String badSimsString = badRepeatedTasks.stream().map(Object::toString).collect(Collectors.joining("\n\t")); + String baseErrMsg = "Provided SedML contains disallowed RepeatedTasks with multi-subTasks"; + String addendum = policy.multipleSubTaskPolicy == StrictnessPolicy.MultipleSubTaskPolicy.CONDENSE_ELSE_REJECT ? "; sub-tasks are not redundant:" : ""; + logger.info("Multi-SubTask check: FAILED ...throwing exception..."); + throw new IllegalArgumentException(baseErrMsg + addendum + badSimsString); + } + + // Remove what we can't do + while (!badRepeatedTasks.isEmpty()){ + copiedSedml.getListOfTasks().removeContent(badRepeatedTasks.poll()); + } + shouldPrune = true; + } + } + logger.info("Multi-SubTask check: PASSED"); + + if (shouldPrune) providedSedmlContainer.pruneSedML(); + return providedSedmlContainer; + } + //!!!!! + //!!! WARNING! DO NOT CALL UNLESS SEDML CONTAINING RepeatedTask CAN BE EDITED!!!!! + //!!!!! + private boolean successfullyReducedRedundantSubTasks(RepeatedTask badRepeatedTask){ + // There one way we can keep the repeated task: + // if the repeated task does something silly, like repeat the same task multiple times...for no good reason..., + // we can just keep one single subtask, and keep the repeated task! + Set uniqueSubTaskReferences = badRepeatedTask.getSubTasks().stream().map(SubTask::getId).collect(Collectors.toSet()); + if (uniqueSubTaskReferences.size() != 1) return false; + SubTask subTaskToKeep = badRepeatedTask.getSubTasks().iterator().next(); + for (SubTask subTask : badRepeatedTask.getSubTasks()){ + if (subTask == subTaskToKeep) continue; + badRepeatedTask.getListOfSubTasks().removeContent(subTask); + } + return true; + } + + private Set mergeBioModels(Set bioModelSet) { + if (bioModelSet.size() <= 1) return bioModelSet; + List bioModels = new ArrayList<>(bioModelSet); // for now just try if they *ALL* have matchable model // this should be the case if dealing with exported SEDML/OMEX from VCell BioModel with multiple applications @@ -432,7 +420,7 @@ private List mergeBioModels(List bioModels) { bioModel.refreshDependencies(); } catch (XmlParseException | PropertyVetoException e) { logger.error(e); - return bioModels; + return bioModelSet; } } } @@ -444,7 +432,7 @@ private List mergeBioModels(List bioModels) { RelationVisitor rvNotStrict = new ModelRelationVisitor(false); boolean equivalent = bioModels.get(i).getModel().relate(bm0.getModel(),rvNotStrict); logger.debug("Equivalent => {}", equivalent); - if (!equivalent) return bioModels; + if (!equivalent) return bioModelSet; } // all have matchable model, try to merge by pooling SimContexts Document dom; @@ -464,7 +452,7 @@ private List mergeBioModels(List bioModels) { } } catch (Exception e) { logger.error(e.getMessage(), e); - return bioModels; + return bioModelSet; } // re-read XML into a single BioModel and replace docs List BioModel mergedBioModel; @@ -473,10 +461,10 @@ private List mergeBioModels(List bioModels) { mergedBioModel = XmlHelper.XMLToBioModel(new XMLSource(mergedXML)); } catch (Exception e) { logger.error(e.getMessage(), e); - return bioModels; + return bioModelSet; } // merge succeeded, replace the list - return Arrays.asList(mergedBioModel); + return Set.of(mergedBioModel); // TODO more work here if we want to generalize } @@ -494,7 +482,7 @@ private void createOverrides(Simulation newSimulation, List changes) thr convertedSimcontext = null; } Variable vcVar = this.resolveMathVariable(importedSimcontext, convertedSimcontext, targetID); - if (!this.isMathVariableConstantValued(vcVar)) { + if (this.isMathVariableNonConstantValued(vcVar)) { logger.error("target in change "+change+" could not be resolved to Constant, overrides not applied"); continue; } @@ -508,22 +496,22 @@ private void createOverrides(Simulation newSimulation, List changes) thr exp = new ExpressionMathMLParser(null).fromMathML(math, "t"); // Substitute SED-ML parameters - List params = cc.getListOfParameters(); + List params = cc.getParameters(); System.out.println(params); for (Parameter param : params) { - exp.substituteInPlace(new Expression(param.getId()), new Expression(param.getValue())); + exp.substituteInPlace(new Expression(param.getIdAsString()), new Expression(param.getValue())); } // Substitute SED-ML variables (which reference SBML entities) - List vars = cc.getListOfVariables(); + List vars = cc.getVariables(); System.out.println(vars); - for (org.jlibsedml.Variable var : vars) { + for (org.jlibsedml.components.Variable var : vars) { String sbmlID = this.sbmlSupport.getIdFromXPathIdentifer(var.getTarget()); vcVar = this.resolveMathVariable(importedSimcontext, convertedSimcontext, sbmlID); - if (!isMathVariableConstantValued(vcVar)){ + if (this.isMathVariableNonConstantValued(vcVar)){ throw new SEDMLImportException("could not evaluate var '"+vcVar.getName()+" as a constant"); } - exp.substituteInPlace(new Expression(var.getId()), new Expression(vcVar.getName())); + exp.substituteInPlace(new Expression(var.getIdAsString()), new Expression(vcVar.getName())); } } else { logger.error("unsupported change "+change+" encountered, overrides not applied"); @@ -552,7 +540,7 @@ private Expression scaleIfChanged (Expression exp, String targetID, SimulationCo if (symbols != null) { for (String sbString : symbols) { Variable vcVar = this.resolveMathVariable(importedSC, convertedSC, sbString); - if (!isMathVariableConstantValued(vcVar)){ + if (this.isMathVariableNonConstantValued(vcVar)){ throw new SEDMLImportException("cannot solve for constant valued scale factor, '"+vcVar+"' is not constant"); } factor.substituteInPlace(new Expression(sbString), new Expression(vcVar.getName())); @@ -566,7 +554,7 @@ private Expression scaleIfChanged (Expression exp, String targetID, SimulationCo return exp; } - private boolean isMathVariableConstantValued(Variable var) { + private boolean isMathVariableNonConstantValued(Variable var) { boolean varIsConstant = (var instanceof Constant); if (var instanceof cbit.vcell.math.Function) { try { @@ -576,7 +564,7 @@ private boolean isMathVariableConstantValued(Variable var) { logger.warn("Substituted constant evaluation failed", e); } } - return varIsConstant; + return !varIsConstant; } private Variable resolveMathVariable(SimulationContext importedSimContext, SimulationContext convertedSimContext, @@ -602,9 +590,8 @@ private Variable resolveMathVariable(SimulationContext importedSimContext, Simul } } // if simcontext was converted to stochastic then species init constants use different names - if (convertedSimContext != null && biologicalSymbolTableEntry instanceof SpeciesContextSpecParameter) { - SpeciesContextSpecParameter speciesContextSpecParameter = (SpeciesContextSpecParameter)biologicalSymbolTableEntry; - if (speciesContextSpecParameter.getRole() == SpeciesContextSpec.ROLE_InitialConcentration + if (convertedSimContext != null && biologicalSymbolTableEntry instanceof SpeciesContextSpecParameter speciesContextSpecParameter) { + if (speciesContextSpecParameter.getRole() == SpeciesContextSpec.ROLE_InitialConcentration || speciesContextSpecParameter.getRole() == SpeciesContextSpec.ROLE_InitialCount) { String spcName = speciesContextSpecParameter.getSpeciesContext().getName(); SpeciesContextSpec scs = null; @@ -623,12 +610,179 @@ private Variable resolveMathVariable(SimulationContext importedSimContext, Simul return var; } - private void addRepeatedTasks(List listOfTasks, Map vcSimulations) throws ExpressionException, PropertyVetoException, SEDMLImportException { + private Map addStandardTasks(SedMLDataContainer sedmlContainer, Map bioModelMap) + throws PropertyVetoException, SEDMLImportException, ImageException, GeometryException, ExpressionException, MappingException { + Map vcSimulations = new HashMap<>(); + SedML sedml = sedmlContainer.getSedML(); + // Creating one VCell Simulation for each SED-ML actual Task + // (RepeatedTasks get added either on top of or separately as parameter scan overrides) + for (AbstractTask selectedTask : sedml.getTasks()) { + if (selectedTask instanceof RepeatedTask) continue; // Repeated tasks refer to regular tasks, so first we need to create simulations for all regular tasks + if (!(selectedTask instanceof Task baseTask)) throw new RuntimeException("Unsupported task " + selectedTask); + + // the SedML simulation will become the vCell simulation + SedBase sedBaseSimFound = sedml.searchInSimulationsFor(baseTask.getSimulationReference()); + if(!(sedBaseSimFound instanceof UniformTimeCourse utcSimulation)) { // only UTC sims supported + String baseTaskName = String.format("%s(%s)", baseTask.getName() == null ? "" : baseTask.getName(), baseTask.getId()); + logger.error("task '{}' is being skipped, it references an unsupported simulation type: {}", baseTaskName, sedBaseSimFound.getClass().getSimpleName()); + continue; + } + + // the "original" model referred to by the task; almost always sbml we can import as physiology + Model sedmlModel = sedmlContainer.findModelById(baseTask.getModelReference()); + if(null == sedmlModel) { + String baseTaskName = String.format("%s(%s)", baseTask.getName() == null ? "" : baseTask.getName(), baseTask.getId()); + logger.error("Model reference of task `{}` is invalid", baseTaskName); + continue; + } + // can be sbml or vcml + String sedmlOriginalModelLanguage = sedmlModel.getLanguage(); + // this will be used in the BioModel name + String sedmlOriginalModelName = sedmlModel.getName() != null ? sedmlModel.getName() : ""; + + // at this point we assume that the sedml simulation, algorithm and kisaoID are all valid + + // identify the vCell solvers that would match best the sedml solver kisao id + String kisaoID = utcSimulation.getAlgorithm().getKisaoID(); + SolverDescription solverDescription = this.kisaoToSolverMapping.get(kisaoID); + if (solverDescription == null) + throw new IllegalStateException("kisao id was not properly mapped at initialization time!"); + + // find out everything else we need about the application we're going to use, + // as some more info will be needed when we parse the sbml file + boolean bSpatial = false; + Application appType = Application.NETWORK_DETERMINISTIC; + Set sfList = solverDescription.getSupportedFeatures(); + for(SolverDescription.SolverFeature sf : sfList) { + switch(sf) { + case Feature_Rulebased: + if(appType != Application.SPRINGSALAD) { + // springs(alad) type takes precedence + appType = Application.RULE_BASED_STOCHASTIC; + } + break; + case Feature_Stochastic: + appType = Application.NETWORK_STOCHASTIC; + break; + case Feature_Deterministic: + appType = Application.NETWORK_DETERMINISTIC; + break; + case Feature_Springs: + appType = Application.SPRINGSALAD; + break; + case Feature_Spatial: + bSpatial = true; + break; + default: + break; + } + } + + BioModel bioModel = bioModelMap.get(sedmlModel.getId()); + + // if language is VCML, we don't need to create Applications and Simulations in BioModel + // we allow a subset of SED-ML Simulation settings (may have been edited) to override existing BioModel Simulation settings + + if(sedmlOriginalModelLanguage.contentEquals(SUPPORTED_LANGUAGE.VCELL_GENERIC.getURN())) { + Simulation theSimulation = null; + for (Simulation sim : bioModel.getSimulations()) { + if (sim.getName().equals(baseTask.getName())) { + logger.trace(" --- selected task - name: " + baseTask.getName() + ", id: " + baseTask.getId()); + sim.setImportedTaskID(baseTask.getId().string()); + theSimulation = sim; + break; // found the one, no point to continue the for loop + } + }if(theSimulation == null) { + logger.error("Couldn't match sedml task '" + baseTask.getName() + "' with any biomodel simulation"); + // TODO: should we throw an exception? + continue; // should never happen + } + + SolverTaskDescription simTaskDesc = theSimulation.getSolverTaskDescription(); + this.translateTimeBounds(simTaskDesc, utcSimulation); + continue; + } + + // if language is SBML, we must create Simulations + // we may need to also create Applications (if the default one from SBML import is not the right type) + + // see first if there is a suitable application type for the specified kisao + // if not, we add one by doing a "copy as" to the right type + SimulationContext[] existingSimulationContexts = bioModel.getSimulationContexts(); + SimulationContext matchingSimulationContext = null; + for (SimulationContext simContext : existingSimulationContexts) { + if (simContext.getApplicationType().equals(appType) && ((simContext.getGeometry().getDimension() > 0) == bSpatial)) { + matchingSimulationContext = simContext; + break; + } + } + if (matchingSimulationContext == null) { + // this happens if we need a NETWORK_STOCHASTIC application + String modelPlusContextName = String.format("%s_%d", sedmlOriginalModelName, existingSimulationContexts.length); + matchingSimulationContext = SimulationContext.copySimulationContext( + bioModel.getSimulationContext(0), + modelPlusContextName, bSpatial, appType); + bioModel.addSimulationContext(matchingSimulationContext); + try { + String importedSCName = bioModel.getSimulationContext(0).getName(); + bioModel.getSimulationContext(0).setName("original_imported_"+importedSCName); + matchingSimulationContext.setName(importedSCName); + } catch (PropertyVetoException e) { + // we should never bomb out just for trying to set a pretty name + logger.warn("could not set pretty name on application from name of model "+sedmlModel); + } + } + matchingSimulationContext.refreshDependencies(); + MathMappingCallback callback = new MathMappingCallbackTaskAdapter(null); + matchingSimulationContext.refreshMathDescription(callback, NetworkGenerationRequirements.ComputeFullStandardTimeout); + + // making the new vCell simulation based on the sedml simulation + Simulation newSimulation = new Simulation(matchingSimulationContext.getMathDescription(), matchingSimulationContext); + + // See note below this immediately following section + String newSimName = baseTask.getId().string(); + if(SedMLUtil.getName(baseTask) != null) { + newSimName += "_" + SedMLUtil.getName(baseTask); + } + newSimulation.setName(newSimName); + newSimulation.setImportedTaskID(baseTask.getId().string()); + vcSimulations.put(baseTask.getId(), newSimulation); + + /* NOTE: Before, we checked if the selected task was an instance of a task; we no longer need to check + that because we logically confirm that earlier on. If we ever get to this point and need an 'else': + + newSimulation.setName(SEDMLUtil.getName(utcSimulation)+"_"+SEDMLUtil.getName(baseTask)); + */ + + // we identify the type of sedml simulation (uniform time course, etc.) + // and set the vCell simulation parameters accordingly + SolverTaskDescription simTaskDesc = newSimulation.getSolverTaskDescription(); + simTaskDesc.setSolverDescription(solverDescription); + + + this.translateTimeBounds(simTaskDesc, utcSimulation); + this.translateAlgorithmParams(simTaskDesc, utcSimulation); + + newSimulation.setSolverTaskDescription(simTaskDesc); + newSimulation.setDescription(SedMLUtil.getName(baseTask)); + bioModel.addSimulation(newSimulation); + newSimulation.refreshDependencies(); + + // finally, add MathOverrides if referenced model has specified compatible changes + if (!sedmlModel.getChanges().isEmpty() && this.canTranslateToOverrides(bioModel, sedmlModel)) { + this.createOverrides(newSimulation, sedmlModel.getChanges()); + } + + } + return vcSimulations; + } + + private void addRepeatedTasks(List listOfTasks, Map vcSimulations) throws ExpressionException, PropertyVetoException, SEDMLImportException { for (AbstractTask abstractedRepeatedTask : listOfTasks) { if (!(abstractedRepeatedTask instanceof RepeatedTask repeatedTask)) continue; if (!repeatedTask.getResetModel() || repeatedTask.getSubTasks().size() != 1) { // if removed, see RunUtils.prepareNonspatialHdf5() - logger.error("sequential RepeatedTask not yet supported, task "+SEDMLUtil.getName(abstractedRepeatedTask)+" is being skipped"); + logger.error("sequential RepeatedTask not yet supported, task {} is being skipped", SedMLUtil.getName(abstractedRepeatedTask)); continue; } @@ -657,61 +811,57 @@ private void addRepeatedTasks(List listOfTasks, Map params = fr.getParameters(); + List params = fr.getParameters(); System.out.println(params); - for (String paramId : params.keySet()) { - frExpMin.substituteInPlace(new Expression(params.get(paramId).getId()), new Expression(((Parameter)params.get(paramId)).getValue())); - frExpMax.substituteInPlace(new Expression(params.get(paramId).getId()), new Expression(((Parameter)params.get(paramId)).getValue())); + for (Parameter param : params) { + frExpMin.substituteInPlace(new Expression(param.getIdAsString()), new Expression(param.getValue())); + frExpMax.substituteInPlace(new Expression(param.getIdAsString()), new Expression(param.getValue())); } // Substitute SED-ML variables (which reference SBML entities) - Map vars = fr.getVariables(); + List vars = fr.getVariables(); System.out.println(vars); - for (String varId : vars.keySet()) { - String sbmlID = this.sbmlSupport.getIdFromXPathIdentifer(((org.jlibsedml.Variable)vars.get(varId)).getTarget()); + for (org.jlibsedml.components.Variable var : vars) { + String sbmlID = this.sbmlSupport.getIdFromXPathIdentifer(var.getTarget()); Variable vcVar = this.resolveMathVariable(importedSimcontext, convertedSimcontext, sbmlID); - if (!isMathVariableConstantValued(vcVar)){ + if (this.isMathVariableNonConstantValued(vcVar)){ throw new SEDMLImportException("expecting vcell var '"+constantValuedVar.getName()+"' " + "mapped to SBML target '"+sbmlID+"' to be constant valued"); } - frExpMin.substituteInPlace(new Expression(varId), new Expression(vcVar.getName())); - frExpMax.substituteInPlace(new Expression(varId), new Expression(vcVar.getName())); + frExpMin.substituteInPlace(new Expression(var.getIdAsString()), new Expression(vcVar.getName())); + frExpMax.substituteInPlace(new Expression(var.getIdAsString()), new Expression(vcVar.getName())); } frExpMin = this.scaleIfChanged(frExpMin, targetID, importedSimcontext, convertedSimcontext); frExpMax = this.scaleIfChanged(frExpMax, targetID, importedSimcontext, convertedSimcontext); @@ -719,35 +869,34 @@ private void addRepeatedTasks(List listOfTasks, Map params = fr.getParameters(); + List params = fr.getParameters(); System.out.println(params); - for (String paramId : params.keySet()) { - expFact.substituteInPlace(new Expression(params.get(paramId).getId()), new Expression(((Parameter)params.get(paramId)).getValue())); + for (Parameter param : params) { + expFact.substituteInPlace(new Expression(param.getIdAsString()), new Expression(param.getValue())); } // Substitute SED-ML variables (which reference SBML entities) - Map vars = fr.getVariables(); + List vars = fr.getVariables(); System.out.println(vars); - for (String varId : vars.keySet()) { - String sbmlID = this.sbmlSupport.getIdFromXPathIdentifer(((org.jlibsedml.Variable)vars.get(varId)).getTarget()); + for (org.jlibsedml.components.Variable var : vars) { + String sbmlID = this.sbmlSupport.getIdFromXPathIdentifer(var.getTarget()); Variable vcVar = this.resolveMathVariable(importedSimcontext, convertedSimcontext, sbmlID); - if (!isMathVariableConstantValued(vcVar)){ + if (this.isMathVariableNonConstantValued(vcVar)){ throw new SEDMLImportException("expecting vcell var '"+constantValuedVar.getName()+"' " + "mapped to SBML target '"+sbmlID+"' to be constant valued"); } - expFact.substituteInPlace(new Expression(varId), new Expression(vcVar.getName())); + expFact.substituteInPlace(new Expression(var.getIdAsString()), new Expression(vcVar.getName())); } expFact = expFact.simplifyJSCL(); String[] values = new String[vr.getNumElements()]; for (int i = 0; i < values.length; i++) { Expression expFinal = new Expression(expFact); // Substitute Range values - expFinal.substituteInPlace(new Expression(vr.getId()), new Expression(vr.getElementAt(i))); + expFinal.substituteInPlace(new Expression(vr.getIdAsString()), new Expression(vr.getElementAt(i))); expFinal = this.scaleIfChanged(expFinal, targetID, importedSimcontext, convertedSimcontext); expFinal = expFinal.simplifyJSCL(); values[i] = expFinal.infix(); @@ -755,13 +904,11 @@ private void addRepeatedTasks(List listOfTasks, Map listOfTasks, Map listOfTasks, Map createBioModels(List models) throws SEDMLImportException { + private Map createBioModels(List models) { final String MODEL_RESOLUTION_ERROR = "Unresolvable Model(s) encountered. Either there is incompatible " + "/ unsupported SED-ML features, or there are unresolvable references."; // Order which type of models to process first based on least-to-greatest priority - Map idToBiomodelMap = new HashMap<>(); + Map idToBiomodelMap = new HashMap<>(); List basicModels = new LinkedList<>(); // No overrides, no references. These come first! LinkedList> advancedModelsList = new LinkedList<>(); // works as both queue and list! // Initialize the advanced models list (effectively a "2D-Array") for (ADVANCED_MODEL_TYPES amt : ADVANCED_MODEL_TYPES.values()){ - logger.trace("Initializing " + amt.toString()); + logger.trace("Initializing {}", amt.toString()); advancedModelsList.add(new LinkedList<>()); } // Group models by type for processing order for (Model model : models){ - if (model.getSourcePathOrURIString().startsWith("#")){ + if (model.getSourceAsString().startsWith("#")){ advancedModelsList.get(ADVANCED_MODEL_TYPES.REFERENCING_MODELS.ordinal()).add(model); - } else if (!model.getListOfChanges().isEmpty()) { + } else if (!model.getChanges().isEmpty()) { advancedModelsList.get(ADVANCED_MODEL_TYPES.CHANGED_MODELS.ordinal()).add(model); } else { basicModels.add(model); @@ -845,7 +992,7 @@ private Map createBioModels(List models) throws SEDMLIm // Process basic models for (Model model : basicModels){ - String referenceId = this.getReferenceId(model); + SId referenceId = SedMLImporter.getSedMLReferenceId(model); idToBiomodelMap.put(model.getId(), this.getModelReference(referenceId, model, idToBiomodelMap)); } @@ -869,7 +1016,7 @@ private Map createBioModels(List models) throws SEDMLIm // Try and process the model Model nextModel = advancedModels.remove(); - String referenceId = this.getReferenceId(nextModel); + SId referenceId = SedMLImporter.getSedMLReferenceId(nextModel); BioModel bioModel; try { bioModel = this.getModelReference(referenceId, nextModel, idToBiomodelMap); @@ -887,29 +1034,27 @@ private Map createBioModels(List models) throws SEDMLIm return idToBiomodelMap; } - private String getReferenceId(Model model){ - String referenceId = model.getSourcePathOrURIString(); - return referenceId.startsWith("#") ? referenceId.substring(1) : referenceId; + private static SId getSedMLReferenceId(Model model){ + String referenceId = model.getSourceAsString(); + return referenceId.startsWith("#") ? new SId(referenceId.substring(1)) : null; } - private BioModel getModelReference(String referenceId, Model model, Map idToBiomodelMap) throws SEDMLImportException { + private BioModel getModelReference(SId sedmlReference, Model model, Map idToBiomodelMap) { // Were we given a reference ID? We need to check if the parent was processed yet. - if (referenceId != null && !model.getSourcePathOrURIString().equals(referenceId)){ - boolean canTranslate; - BioModel parentBiomodel = idToBiomodelMap.get(referenceId); + if (sedmlReference == null) return this.importModel(model); + boolean canTranslate; + BioModel parentBiomodel = idToBiomodelMap.get(sedmlReference); - if (parentBiomodel == null) - throw new RuntimeException("The parent hasn't been processed yet!"); + if (parentBiomodel == null) throw new RuntimeException("The parent hasn't been processed yet!"); + + canTranslate = this.canTranslateToOverrides(parentBiomodel, model); + if (canTranslate) return parentBiomodel; - canTranslate = this.canTranslateToOverrides(parentBiomodel, model); - if (canTranslate) - return parentBiomodel; - } return this.importModel(model); } - private boolean canTranslateToOverrides(BioModel refBM, Model mm) throws SEDMLImportException { - List changes = mm.getListOfChanges(); + private boolean canTranslateToOverrides(BioModel refBM, Model mm) { + List changes = mm.getChanges(); // XML changes can't be translated to math overrides, only attribute changes and compute changes can for (Change change : changes) { if (change.isAddXML() || change.isChangeXML() || change.isRemoveXML()) return false; @@ -919,7 +1064,7 @@ private boolean canTranslateToOverrides(BioModel refBM, Model mm) throws SEDMLIm String sbmlID = this.sbmlSupport.getIdFromXPathIdentifer(change.getTargetXPath().toString()); SimulationContext simulationContext = refBM.getSimulationContext(0); Variable vcVar = this.resolveMathVariable(simulationContext, null, sbmlID); - if (!isMathVariableConstantValued(vcVar)) { + if (this.isMathVariableNonConstantValued(vcVar)) { logger.warn("mapped changeAttribute for SBML ID "+sbmlID+" mapped to non-constant VCell variable '"+vcVar.getName()+"'"); return false; } @@ -929,13 +1074,13 @@ private boolean canTranslateToOverrides(BioModel refBM, Model mm) throws SEDMLIm private BioModel importModel(Model mm) { BioModel bioModel; - String sedmlOriginalModelName = mm.getId(); + SId sedmlOriginalModelId = mm.getId(); String sedmlOriginalModelLanguage = mm.getLanguage(); - String modelXML = this.resolver.getModelString(mm); // source with all the changes applied + String modelXML = this.resolver.getXMLStringRepresentationOfModel(mm); // source with all the changes applied if (modelXML == null) { throw new RuntimeException("Resolver could not find model: "+mm.getId()); } - String bioModelName = this.bioModelBaseName + "_" + this.sedml.getFileName() + "_" + sedmlOriginalModelName; + String bioModelName = this.bioModelBaseName + "_" + this.sedmlContainer.getFileName() + "_" + sedmlOriginalModelId.string(); try { if(sedmlOriginalModelLanguage.contentEquals(SUPPORTED_LANGUAGE.VCELL_GENERIC.getURN())) { // vcml XMLSource vcmlSource = new XMLSource(modelXML); @@ -952,7 +1097,7 @@ private BioModel importModel(Model mm) { SBMLImporter sbmlImporter = new SBMLImporter(sbmlSource, this.transLogger, bValidateSBML); bioModel = sbmlImporter.getBioModel(); bioModel.setName(bioModelName); - bioModel.getSimulationContext(0).setName(mm.getName() != null? mm.getName() : mm.getId()); + bioModel.getSimulationContext(0).setName(mm.getName() != null? mm.getName() : mm.getIdAsString()); bioModel.updateAll(false); this.importMap.put(bioModel, sbmlImporter); } @@ -978,14 +1123,14 @@ private BioModel importModel(Model mm) { } } - private void printSEDMLSummary(List mmm, List sss, - List ttt, List ddd, List ooo) { + private void printSEDMLSummary(List mmm, List sss, + List ttt, List ddd, List ooo) { for(Model mm : mmm) { logger.trace("sedml model: "+mm.toString()); - List listOfChanges = mm.getListOfChanges(); + List listOfChanges = mm.getChanges(); logger.debug("There are " + listOfChanges.size() + " changes in model "+mm.getId()); } - for(org.jlibsedml.Simulation ss : sss) { + for(org.jlibsedml.components.simulation.Simulation ss : sss) { logger.trace("sedml simulation: "+ss.toString()); } for(AbstractTask tt : ttt) { @@ -999,7 +1144,7 @@ private void printSEDMLSummary(List mmm, List sedmlAlgorithmParameters = algorithm.getListOfAlgorithmParameters(); + List sedmlAlgorithmParameters = algorithm.getAlgorithmParameters(); for (AlgorithmParameter sedmlAlgorithmParameter : sedmlAlgorithmParameters) { String apKisaoID = sedmlAlgorithmParameter.getKisaoID(); @@ -1100,41 +1245,36 @@ private void translateAlgorithmParams(SolverTaskDescription simTaskDesc, org.jli private Task getBaseTask(RepeatedTask repeatedTask){ // Can we not have multiple base Tasks? If we have multiple sub-tasks, // then couldn't there be more than one base Task? - AbstractTask referredTask; - SubTask st = repeatedTask.getSubTasks().entrySet().iterator().next().getValue(); // single subtask - String taskId = st.getTaskId(); + SubTask st = repeatedTask.getSubTasks().iterator().next(); // single subtask + SId taskId = st.getTask(); // find the base-task, by recursively checking the task being referenced until it's not a repeated task - referredTask = this.sedml.getTaskWithId(taskId); - if (referredTask == null) throw new RuntimeException("Unexpected null-Task"); - if (referredTask instanceof RepeatedTask) return this.getBaseTask((RepeatedTask) referredTask); - return (Task) referredTask; + SedBase sedmlElement = this.sedmlContainer.getSedML().searchInTasksFor(taskId); + if (sedmlElement instanceof Task basicTask) return basicTask; + if (sedmlElement instanceof RepeatedTask referredTask) return this.getBaseTask(referredTask); + throw new RuntimeException("Unexpected non-Task"); } private boolean simulationIsNeededAsOutput(Simulation sim){ // We need to do a bit of digging to get to the bottom of this: if our simulation isn't connected to an output // we do not need it for output purposes. This is important in the import step, as we get all types of models. + SedML sedml = this.sedmlContainer.getSedML(); - List reportList = new LinkedList<>(); - for (Output output : this.sedml.getOutputs()){ - if (!(output instanceof Report report)) continue; - reportList.add(report); - } + List reportList = sedml.getOutputs().stream().filter(Report.class::isInstance).map(Report.class::cast).toList(); for (Report report : reportList){ - Set neededTaskReferences = new HashSet<>(); - for (DataSet ds : report.getListOfDataSets()){ - for (DataGenerator dataGenerator : this.sedml.getDataGenerators()){ + Set neededTaskReferences = new HashSet<>(); + for (DataSet ds : report.getDataSets()){ + for (DataGenerator dataGenerator : sedml.getDataGenerators()){ if (!ds.getDataReference().equals(dataGenerator.getId())) continue; - for (org.jlibsedml.Variable var : dataGenerator.getListOfVariables()){ - neededTaskReferences.add(var.getReference()); + for (org.jlibsedml.components.Variable var : dataGenerator.getVariables()){ + neededTaskReferences.add(var.getTaskReference()); } } } - for (AbstractTask task : this.sedml.getTasks()){ - if (neededTaskReferences.contains(task.getId()) && task.getId().equals(sim.getImportedTaskID())) - return true; + for (AbstractTask task : sedml.getTasks()){ + if (neededTaskReferences.contains(task.getId()) && task.getIdAsString().equals(sim.getImportedTaskID())) return true; } } @@ -1144,13 +1284,41 @@ private boolean simulationIsNeededAsOutput(Simulation sim){ public SBMLSymbolMapping getSBMLSymbolMapping(BioModel bioModel){ SBMLImporter sbmlImporter = this.importMap.get(bioModel); - if (sbmlImporter != null) { - return sbmlImporter.getSymbolMapping(); - } else { - return null; - } + return (sbmlImporter != null) ? sbmlImporter.getSymbolMapping() : null; } + public record StrictnessPolicy(boolean skipUnsupportedSimulationTypes, MultipleSubTaskPolicy multipleSubTaskPolicy, SolverMatchPolicy solverMatchPolicy){ + public enum MultipleSubTaskPolicy { + REJECT_IMMEDIATELY, + CONDENSE_ELSE_REJECT, + CONDENSE_ELSE_REMOVE + } + + public enum SolverMatchPolicy { + STRICT_MATCH_OR_REJECT, + FLEXIBLE_MATCH_OR_REJECT, + SUNDIALS_AS_LAST_RESORT, + REMOVE_UNSUPPORTED + } + + public boolean isConsideredStrict(){ + // WARNING: Changing this method has repercussions for code using it. Check and understand usage context's side effects before editing. + return !this.skipUnsupportedSimulationTypes + && this.multipleSubTaskPolicy == MultipleSubTaskPolicy.REJECT_IMMEDIATELY + && this.solverMatchPolicy != SolverMatchPolicy.REMOVE_UNSUPPORTED; + } + + public static StrictnessPolicy elevateToStrictSettings(StrictnessPolicy startingPolicy){ + // WARNING: Changing this method has repercussions for code using it. Check and understand usage context's side effects before editing. + if (startingPolicy.isConsideredStrict()) return startingPolicy; + // Because only `REMOVE_UNSUPPORTED` can be considered "unstrict", we don't want to unnecessarily lower the setting + SolverMatchPolicy newMatchPolicy = startingPolicy.solverMatchPolicy != SolverMatchPolicy.REMOVE_UNSUPPORTED ? startingPolicy.solverMatchPolicy : SolverMatchPolicy.SUNDIALS_AS_LAST_RESORT; + StrictnessPolicy complyingPolicy = new StrictnessPolicy(false, MultipleSubTaskPolicy.REJECT_IMMEDIATELY, newMatchPolicy); + assert complyingPolicy.isConsideredStrict(); // This is to ensure we never edit `isConsideredStrict` without considering this method too. + return complyingPolicy; + } + } + private enum ADVANCED_MODEL_TYPES { CHANGED_MODELS, REFERENCING_MODELS } diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLRecorder.java b/vcell-core/src/main/java/org/vcell/sedml/SedMLRecorder.java similarity index 94% rename from vcell-core/src/main/java/org/vcell/sedml/SEDMLRecorder.java rename to vcell-core/src/main/java/org/vcell/sedml/SedMLRecorder.java index 5ddd900cbd..74358e5192 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLRecorder.java +++ b/vcell-core/src/main/java/org/vcell/sedml/SedMLRecorder.java @@ -21,9 +21,9 @@ /** * Sedml-specific recorder class */ -public class SEDMLRecorder extends Recorder { +public class SedMLRecorder extends Recorder { - private final static Logger logger = LogManager.getLogger(SEDMLRecorder.class); + private final static Logger logger = LogManager.getLogger(SedMLRecorder.class); private String identifier; private SEDMLConversion operation; @@ -38,7 +38,7 @@ public class SEDMLRecorder extends Recorder { * @param jobName name of the job this recorder is recording * @param conversion whether the sedml is imported or exported */ - public SEDMLRecorder(String jobName, SEDMLConversion conversion) { + public SedMLRecorder(String jobName, SEDMLConversion conversion) { this(jobName, conversion, null); } @@ -49,8 +49,8 @@ public SEDMLRecorder(String jobName, SEDMLConversion conversion) { * @param conversion whether the sedml is imported or exported * @param jsonFilePath path to the json file to be created. */ - public SEDMLRecorder(String jobName, SEDMLConversion conversion, String jsonFilePath) { - super(SEDMLRecorder.class); + public SedMLRecorder(String jobName, SEDMLConversion conversion, String jsonFilePath) { + super(SedMLRecorder.class); this.identifier = jobName; this.operation = conversion; this.exceptionTypes = new HashSet<>(); diff --git a/vcell-core/src/main/java/org/vcell/sedml/SEDMLUtil.java b/vcell-core/src/main/java/org/vcell/sedml/SedMLUtil.java similarity index 50% rename from vcell-core/src/main/java/org/vcell/sedml/SEDMLUtil.java rename to vcell-core/src/main/java/org/vcell/sedml/SedMLUtil.java index 4c9cde2156..990047c006 100644 --- a/vcell-core/src/main/java/org/vcell/sedml/SEDMLUtil.java +++ b/vcell-core/src/main/java/org/vcell/sedml/SedMLUtil.java @@ -1,10 +1,10 @@ package org.vcell.sedml; -import org.jlibsedml.AbstractIdentifiableElement; +import org.jlibsedml.components.SedBase; -public class SEDMLUtil { +public class SedMLUtil { - public static String getName(AbstractIdentifiableElement thing) { + public static String getName(SedBase thing) { if(thing == null) { return null; } @@ -12,7 +12,7 @@ public static String getName(AbstractIdentifiableElement thing) { return thing.getName(); } if(thing.getId() != null) { - return thing.getId(); + return thing.getId().string(); } return null; } 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 f68b0a640e..4bb40aa3bd 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 @@ -7,6 +7,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jlibsedml.*; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.output.*; +import org.jlibsedml.components.task.AbstractTask; import java.io.File; import java.io.FileInputStream; @@ -275,44 +278,45 @@ public void setExceptionMessage(String sedmlAbsolutePath, String entityId, Strin private static ArchiveLog initializeLogFromOmex(String omexFile) throws IOException, XMLException { // read sedml files from omex ArchiveComponents ac = Libsedml.readSEDMLArchive(new FileInputStream(omexFile)); - List sedmlDocs = ac.getSedmlDocuments(); + List sedmlDocs = ac.getSedmlDocuments(); List sedDocumentLogs = new ArrayList<>(); - for (SEDMLDocument sedmlDoc : sedmlDocs) { - SedML sedmlModel = sedmlDoc.getSedMLModel(); + for (SedMLDocument sedmlDoc : sedmlDocs) { + SedMLDataContainer sedmlModel = sedmlDoc.getSedMLModel(); + SedML sedml = sedmlModel.getSedML(); SedDocumentLog sedDocumentLog = new SedDocumentLog(); sedDocumentLog.location = sedmlModel.getFileName(); sedDocumentLog.status = Status.QUEUED; - List tasks = sedmlModel.getTasks(); + List tasks = sedml.getTasks(); for (AbstractTask task : tasks) { TaskLog taskItem = new TaskLog(); - taskItem.id = task.getId(); + taskItem.id = task.getIdAsString(); taskItem.status = Status.QUEUED; sedDocumentLog.tasks.add(taskItem); } - List outputs = sedmlModel.getOutputs(); + List outputs = sedml.getOutputs(); for (Output output : outputs) { OutputLog outputItem = new OutputLog(); - outputItem.id = output.getId(); + outputItem.id = output.getIdAsString(); outputItem.status = Status.QUEUED; - if (output instanceof Plot2D) { - for (Curve curve : ((Plot2D) output).getListOfCurves()) { + if (output instanceof Plot2D p2d) { + for (AbstractCurve curve : p2d.getCurves()) { CurveLog curveItem = new CurveLog(); - curveItem.id = curve.getId(); + curveItem.id = curve.getIdAsString(); curveItem.status = Status.QUEUED; if (outputItem.curves == null) { outputItem.curves = new ArrayList<>(); } outputItem.curves.add(curveItem); } - } else if (output instanceof Report) { - for (DataSet dataSet : ((Report) output).getListOfDataSets()) { + } else if (output instanceof Report report) { + for (DataSet dataSet : report.getDataSets()) { DataSetLog dataSetItem = new DataSetLog(); - dataSetItem.id = dataSet.getId(); + dataSetItem.id = dataSet.getIdAsString(); dataSetItem.status = Status.QUEUED; if (outputItem.dataSets == null) { outputItem.dataSets = new ArrayList<>(); diff --git a/vcell-core/src/main/resources/schema.xsd b/vcell-core/src/main/resources/schema.xsd index 98e1303acc..d00bc44098 100644 --- a/vcell-core/src/main/resources/schema.xsd +++ b/vcell-core/src/main/resources/schema.xsd @@ -63,7 +63,7 @@ - + diff --git a/vcell-core/src/main/resources/sed-ml-L1-V2.xsd b/vcell-core/src/main/resources/sed-ml-L1-V2.xsd index 385623a88f..08d5c0f0a9 100644 --- a/vcell-core/src/main/resources/sed-ml-L1-V2.xsd +++ b/vcell-core/src/main/resources/sed-ml-L1-V2.xsd @@ -62,7 +62,7 @@ - + diff --git a/vcell-core/src/main/resources/sed-ml-L1-V3.xsd b/vcell-core/src/main/resources/sed-ml-L1-V3.xsd index 80ebb8f56a..4d85df5f1a 100644 --- a/vcell-core/src/main/resources/sed-ml-L1-V3.xsd +++ b/vcell-core/src/main/resources/sed-ml-L1-V3.xsd @@ -64,7 +64,7 @@ - + diff --git a/vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideApplyTest.java b/vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideApplyTest.java index b4a3d15d3c..bc50a61cb9 100644 --- a/vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideApplyTest.java +++ b/vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideApplyTest.java @@ -178,9 +178,9 @@ public void test_mathOverrideApply(String filename_colon_appname) throws Excepti // for now, if it doesn't throw an exception, then it passes if (knownFaults().contains(filename)){ // some applications may pass and others fail, e.g. 'biomodel_55178308.vcml:Spatial 1 - 3D - electrophysiology' passes but rest fail -// Assert.fail("applying math overrides succeeded, but '"+filename_colon_appname+"' in known faults list, remove from known faults list"); +// fail("applying math overrides succeeded, but '"+filename_colon_appname+"' in known faults list, remove from known faults list"); } - }catch (Exception e){ + } catch (Exception e){ if (!knownFaults().contains(filename)){ e.printStackTrace(); fail("applying math overrides failed unexpectedly for '" + filename_colon_appname + "', add to known faults: " + e.getMessage()); diff --git a/vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideRoundTipTest.java b/vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideRoundTripTest.java similarity index 96% rename from vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideRoundTipTest.java rename to vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideRoundTripTest.java index 56365fea5f..0f6bc254fa 100644 --- a/vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideRoundTipTest.java +++ b/vcell-core/src/test/java/cbit/vcell/biomodel/MathOverrideRoundTripTest.java @@ -2,7 +2,6 @@ import cbit.util.xml.XmlUtil; import cbit.vcell.mapping.SimulationContext; -import cbit.vcell.resource.NativeLib; import cbit.vcell.resource.PropertyLoader; import cbit.vcell.xml.XMLSource; import cbit.vcell.xml.XmlHelper; @@ -18,7 +17,7 @@ import org.vcell.sbml.vcell.SBMLExporter; import org.vcell.sedml.ModelFormat; import org.vcell.sedml.PublicationMetadata; -import org.vcell.sedml.SEDMLExporter; +import org.vcell.sedml.SedMLExporter; import java.io.File; import java.io.FileNotFoundException; @@ -33,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @Tag("Fast") -public class MathOverrideRoundTipTest { +public class MathOverrideRoundTripTest { private final boolean bDebug = true; @@ -92,7 +91,7 @@ public void test_General_Kinetics_Override_Roundtrip(String filename) throws Exc boolean bWriteOmexArchive = true; Optional publicationMetadata = Optional.empty(); Predicate simContextFilter = (sc) -> true; - SEDMLExporter.writeBioModel(bioModel, publicationMetadata, omexFile, ModelFormat.SBML, simContextFilter, bHasPython, bRoundTripSBMLValidation, bWriteOmexArchive); + SedMLExporter.writeBioModel(bioModel, publicationMetadata, omexFile, ModelFormat.SBML, simContextFilter, bHasPython, bRoundTripSBMLValidation, bWriteOmexArchive); SBMLExporter.MemoryVCLogger memoryVCLogger = new SBMLExporter.MemoryVCLogger(); List bioModels = XmlHelper.readOmex(omexFile, memoryVCLogger); @@ -147,7 +146,7 @@ public void test_General_Kinetics_Override_Roundtrip(String filename) throws Exc // System.out.println("parsed: " + constant.getName() + "=" + constant.getExpression().infix()); // } // } -// Assert.assertTrue("expected math overrides to match", equiv); +// SedGeneralClass.assertTrue("expected math overrides to match", equiv); // } @@ -184,7 +183,7 @@ public void test_General_Kinetics_Override_Roundtrip(String filename) throws Exc // System.out.println("parsed: " + constant.getName() + "=" + constant.getExpression().infix()); // } // } -// Assert.assertTrue("expected math overrides to match", equiv); +// SedGeneralClass.assertTrue("expected math overrides to match", equiv); // } // BioModel bioModel_sbmlUnits = ModelUnitConverter.createBioModelWithSBMLUnitSystem(bioModel_vcellUnits); // { @@ -217,14 +216,14 @@ public void test_General_Kinetics_Override_Roundtrip(String filename) throws Exc // System.out.println("parsed: " + constant.getName() + "=" + constant.getExpression().flattenSafe().infix()); // } // } -// Assert.assertTrue("expected math overrides to match", equiv); +// SedGeneralClass.assertTrue("expected math overrides to match", equiv); // } // // } private static BioModel getBioModelFromResource(String fileName) throws IOException, XmlParseException { - InputStream inputStream = MathOverrideRoundTipTest.class.getResourceAsStream(fileName); + InputStream inputStream = MathOverrideRoundTripTest.class.getResourceAsStream(fileName); if (inputStream == null) { throw new FileNotFoundException("file not found! " + fileName); } else { diff --git a/vcell-core/src/test/java/org/jlibsedml/TestComponents.java b/vcell-core/src/test/java/org/jlibsedml/TestComponents.java new file mode 100644 index 0000000000..a60b1612b6 --- /dev/null +++ b/vcell-core/src/test/java/org/jlibsedml/TestComponents.java @@ -0,0 +1,66 @@ +package org.jlibsedml; + +import cbit.util.xml.VCLogger; +import cbit.util.xml.VCLoggerException; +import org.jdom2.JDOMException; +import org.jlibsedml.components.SId; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.output.Axis; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +@Tag("Fast") +public class TestComponents { + + + private static SedMLDataContainer sedmlContainer; + + @BeforeAll + public static void loadSedMLContainer() { + try (InputStream stream = TestComponents.class.getResourceAsStream("example.sedml")){ + TestComponents.sedmlContainer = SedMLReader.readFile(stream); + } catch (IOException | XMLException | JDOMException e) { + Assertions.fail(e); + } + + } + + @Test + public void testImport() { + SId xAxisId = new SId("x_axis_for_autogen_plot_for_task1"); + Axis xAxis = TestComponents.sedmlContainer.findAxisById(xAxisId); + if (null == xAxis){Assertions.fail(String.format("id `%s` does not map to the expected xAxis element", xAxisId.string())); return; } + Assertions.assertEquals(xAxisId, xAxis.getId()); + Assertions.assertEquals("Time", xAxis.getName()); + Assertions.assertEquals(Axis.Type.LINEAR, xAxis.getType()); + + SId yAxisId = new SId("y_axis_for_autogen_plot_for_task1"); + Axis yAxis = TestComponents.sedmlContainer.findAxisById(yAxisId); + if (null == yAxis){ Assertions.fail(String.format("id `%s` does not map to the expected yAxis element", yAxisId.string())); return; } + Assertions.assertEquals(yAxisId, yAxis.getId()); + Assertions.assertEquals("Species", yAxis.getName()); + Assertions.assertEquals(Axis.Type.LINEAR, yAxis.getType()); + } + + // TODO: Complete this logger and use it for whole CLI + private static class MockLogger extends VCLogger { + @Override + public void sendMessage(Priority p, ErrorType et, String message) throws VCLoggerException { + + } + + public void sendAllMessages() {} + + public boolean hasMessages() { + return false; + } + } + +} diff --git a/vcell-core/src/test/java/org/vcell/optimization/CopasiOptimizationSolverTest.java b/vcell-core/src/test/java/org/vcell/optimization/CopasiOptimizationSolverTest.java index e513fdc40c..2d1302376a 100644 --- a/vcell-core/src/test/java/org/vcell/optimization/CopasiOptimizationSolverTest.java +++ b/vcell-core/src/test/java/org/vcell/optimization/CopasiOptimizationSolverTest.java @@ -103,7 +103,7 @@ public void testLocalOptimization() throws ExpressionException, IOException, Xml // OptimizationResultSet optimizationResultSet = copasiOptimizationSolver.solveRemoteApi( // parameterEstimationTask, optSolverCallbacks, clientTaskStatusSupport, clientServerInfo); // -// Assert.assertNotNull(optimizationResultSet); +// SedGeneralClass.assertNotNull(optimizationResultSet); // } private static class TestClientTaskStatusSupport implements ClientTaskStatusSupport { diff --git a/vcell-core/src/test/java/org/vcell/sbml/SBMLSpatialTest.java b/vcell-core/src/test/java/org/vcell/sbml/SBMLSpatialTest.java index b7d4d13bdb..be1c60a56d 100644 --- a/vcell-core/src/test/java/org/vcell/sbml/SBMLSpatialTest.java +++ b/vcell-core/src/test/java/org/vcell/sbml/SBMLSpatialTest.java @@ -4,8 +4,13 @@ import cbit.vcell.mapping.SimulationContext; import cbit.vcell.resource.PropertyLoader; import cbit.vcell.solver.*; -import org.apache.log4j.ConsoleAppender; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.layout.PatternLayout; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -26,7 +31,29 @@ public class SBMLSpatialTest { @BeforeAll public static void before() throws IOException { PropertyLoader.setProperty(PropertyLoader.installationRoot, ".."); - Logger.getLogger(SBMLExporter.class).addAppender(new ConsoleAppender()); + PropertyLoader.setProperty(PropertyLoader.installationRoot, ".."); + Logger logger = LogManager.getLogger(SBMLExporter.class); + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName()); + + // If this logger doesn't have its own config, create one + if (!loggerConfig.getName().equals(logger.getName())) { + loggerConfig = new LoggerConfig(logger.getName(), Level.DEBUG, true); + config.addLogger(logger.getName(), loggerConfig); + } + // Make a Console Appender + org.apache.logging.log4j.core.appender.ConsoleAppender appender = org.apache.logging.log4j.core.appender.ConsoleAppender.newBuilder() + .setName("DefaultConsole") + .setTarget(org.apache.logging.log4j.core.appender.ConsoleAppender.Target.SYSTEM_OUT) + .setLayout(PatternLayout.newBuilder().withPattern("%d{HH:mm:ss} %-5level %c - %msg%n").withConfiguration(config).build()) + .setConfiguration(config) + .build(); + appender.start(); + + loggerConfig.addAppender(appender, logger.getLevel(), null); + // Update loggers + context.updateLoggers(); // create temporary working directory workingDir = Files.createTempDirectory("sbml-test-suite-working-dir-").toFile(); } diff --git a/vcell-core/src/test/java/org/vcell/sbml/SBMLTestSuiteTest.java b/vcell-core/src/test/java/org/vcell/sbml/SBMLTestSuiteTest.java index 5bbcd51e16..98eb53ec3a 100644 --- a/vcell-core/src/test/java/org/vcell/sbml/SBMLTestSuiteTest.java +++ b/vcell-core/src/test/java/org/vcell/sbml/SBMLTestSuiteTest.java @@ -11,8 +11,14 @@ import cbit.vcell.solver.ExplicitOutputTimeSpec; import cbit.vcell.solver.Simulation; import cbit.vcell.solver.TimeBounds; -import org.apache.log4j.ConsoleAppender; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.ConsoleAppender; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.layout.PatternLayout; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; @@ -39,7 +45,28 @@ public class SBMLTestSuiteTest { @BeforeAll public static void before() { PropertyLoader.setProperty(PropertyLoader.installationRoot, ".."); - Logger.getLogger(SBMLExporter.class).addAppender(new ConsoleAppender()); + Logger logger = LogManager.getLogger(SBMLExporter.class); + LoggerContext context = (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName()); + + // If this logger doesn't have its own config, create one + if (!loggerConfig.getName().equals(logger.getName())) { + loggerConfig = new LoggerConfig(logger.getName(), Level.DEBUG, true); + config.addLogger(logger.getName(), loggerConfig); + } + // Make a Console Appender + ConsoleAppender appender = ConsoleAppender.newBuilder() + .setName("DefaultConsole") + .setTarget(ConsoleAppender.Target.SYSTEM_OUT) + .setLayout(PatternLayout.newBuilder().withPattern("%d{HH:mm:ss} %-5level %c - %msg%n").withConfiguration(config).build()) + .setConfiguration(config) + .build(); + appender.start(); + + loggerConfig.addAppender(appender, logger.getLevel(), null); + // Update loggers + context.updateLoggers(); } public static Collection testCases() { diff --git a/vcell-core/src/test/java/org/vcell/sbml/SBMLUnitTranslatorTest.java b/vcell-core/src/test/java/org/vcell/sbml/SBMLUnitTranslatorTest.java index 9b966fe93c..aa59c04865 100644 --- a/vcell-core/src/test/java/org/vcell/sbml/SBMLUnitTranslatorTest.java +++ b/vcell-core/src/test/java/org/vcell/sbml/SBMLUnitTranslatorTest.java @@ -145,14 +145,14 @@ public void testKMOLE_Derivation() { assertFalse(vcFluxUnits.isCompatible(vcMemReactUnits), "flux and membrane units are incompatible because moles/molecules are different"); System.out.println("["+vcFluxUnits.getSymbol()+"] / ["+vcMemReactUnits.getSymbol()+"] = ["+vcFluxUnits.divideBy(vcMemReactUnits).getSymbol()+"]"); // VCUnitDefinition KMOLE_expected_units = vcFluxUnits.divideBy(vcMemReactUnits); -// Assert.assertTrue("KMOLE units should be equivalent to flux/membrane units", ); +// SedGeneralClass.assertTrue("KMOLE units should be equivalent to flux/membrane units", ); // // VCUnitDefinition fluxToMembraneReactUnits = vcFluxUnits.convertTo(1, vcMemReactUnits); // VCUnitDefinition fluxToMembraneReactUnits = vcFluxUnits.convertTo(1, vcMemReactUnits); // UnitDefinition sbmlUnit = new SBMLReader().readSBMLFromString(getSbmlUnitDefinition_KMOLE).getModel().getListOfUnitDefinitions().get("unitid"); // VCUnitDefinition vcUnit = SBMLUnitTranslator.getVCUnitDefinition(sbmlUnit, vcUnitSystem); // -// Assert.assertTrue("expected=["+expectedUnit.getSymbol()+"], parsed=["+vcUnit.getSymbol()+"] are not equivalent", expectedUnit.isEquivalent(vcUnit)); +// SedGeneralClass.assertTrue("expected=["+expectedUnit.getSymbol()+"], parsed=["+vcUnit.getSymbol()+"] are not equivalent", expectedUnit.isEquivalent(vcUnit)); } @Disabled diff --git a/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterCommon.java b/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterCommon.java index 0292e41e64..f38e3ff800 100644 --- a/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterCommon.java +++ b/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterCommon.java @@ -14,6 +14,8 @@ import cbit.vcell.xml.XMLSource; import cbit.vcell.xml.XmlHelper; import com.google.common.io.Files; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -28,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.*; public abstract class SEDMLExporterCommon { + private static final Logger logger = LogManager.getLogger(SEDMLExporterCommon.class); record UnsupportedApplication(String filename, String applicationName, String reason) { } @@ -40,6 +43,11 @@ public TestCase(String filename, ModelFormat modelFormat){ this.filename = filename; this.modelFormat = modelFormat; } + + @Override + public String toString(){ + return String.format("[%s] %s (%s)", this.getClass().getSimpleName(), this.filename, this.modelFormat); + } } @@ -138,7 +146,7 @@ void sedml_roundtrip_common(TestCase testCase) throws Exception { bioModel.updateAll(false); Predicate simulationExportFilter = sim -> true; - List simsToExport = Arrays.stream(bioModel.getSimulations()).filter(simulationExportFilter).collect(Collectors.toList()); + List simsToExport = Arrays.stream(bioModel.getSimulations()).filter(simulationExportFilter).toList(); // we replace the obsolete solver with the fully supported equivalent for (Simulation simulation : simsToExport) { @@ -148,22 +156,23 @@ void sedml_roundtrip_common(TestCase testCase) throws Exception { } File outputDir = Files.createTempDir(); String jsonFullyQualifiedName = new File(outputDir, test_case_name + ".json").getAbsolutePath(); - System.out.println(jsonFullyQualifiedName); + logger.info(jsonFullyQualifiedName); boolean bHasPython = true; boolean bRoundTripSBMLValidation = true; boolean bWriteOmexArchive = true; File omexFile = new File(outputDir, test_case_name + ".omex"); Optional publicationMetadata = Optional.empty(); - Set unsupportedApplications = SEDMLExporter.getUnsupportedApplicationMap(bioModel, testCase.modelFormat) + Set unsupportedApplications = SedMLExporter.getUnsupportedApplicationMap(bioModel, testCase.modelFormat) .entrySet().stream().map(e -> new UnsupportedApplication(testCase.filename, e.getKey(), e.getValue())).collect(Collectors.toSet()); Set declaredUnsupportedApplications = unsupportedApplications().stream() .filter(ua -> ua.filename.equals(testCase.filename)).collect(Collectors.toSet()); if (!declaredUnsupportedApplications.equals(unsupportedApplications)){ - System.err.println("declared unsupported applications for model "+test_case_name+" do not match actual, add the following to unsupportedApplications():"); + StringBuilder errMsg = new StringBuilder(); for (UnsupportedApplication ua : unsupportedApplications){ - System.err.println("unsupportedApplications.add(new UnsupportedApplication(\""+ua.filename+"\",\""+ua.applicationName+"\",\""+ua.reason+"\"));"); + errMsg.append(String.format("unsupportedApplications.add(new UnsupportedApplication(\"%s\",\"%s\",\"%s\"\n\t", ua.filename, ua.applicationName,ua.reason)); } + logger.error("declared unsupported applications for model "+test_case_name+" do not match actual, add the following to unsupportedApplications():\n\t" + errMsg.toString()); assertEquals(declaredUnsupportedApplications, unsupportedApplications, "declared unsupported applications for model "+test_case_name+" do not match actual:\ndeclared:\n"+declaredUnsupportedApplications+"\nfound\n"+unsupportedApplications); } @@ -171,7 +180,7 @@ void sedml_roundtrip_common(TestCase testCase) throws Exception { "declared unsupported applications for model "+test_case_name+" do not match actual:\ndeclared:\n"+declaredUnsupportedApplications+"\nfound\n"+unsupportedApplications); Predicate simContextFilter = (SimulationContext sc) -> unsupportedApplications.stream().noneMatch(ua -> ua.applicationName.equals(sc.getName())); try { - List sedmlTaskRecords = SEDMLExporter.writeBioModel( + List sedmlTaskRecords = SedMLExporter.writeBioModel( bioModel, publicationMetadata, omexFile, testCase.modelFormat, simContextFilter, bHasPython, bRoundTripSBMLValidation, bWriteOmexArchive); boolean bAnyFailures = false; @@ -207,7 +216,7 @@ void sedml_roundtrip_common(TestCase testCase) throws Exception { } if (testCase.modelFormat == ModelFormat.VCML){ - System.err.println("skipping re-importing SEDML for this test case, not yet supported for VCML"); + logger.warn("skipping re-importing SEDML for this test case, not yet supported for VCML"); return; } SBMLExporter.MemoryVCLogger memoryVCLogger = new SBMLExporter.MemoryVCLogger(); @@ -224,7 +233,7 @@ void sedml_roundtrip_common(TestCase testCase) throws Exception { String rereadVcmlPath = new File(tempDir, "reread_" + i + ".vcml").getAbsolutePath(); XmlUtil.writeXMLStringToFile(XmlHelper.bioModelToXML(bioModels.get(i)), rereadVcmlPath, true); } - System.err.println("wrote original and final BioModel VCML files to " + tempDir.getAbsolutePath()); + logger.debug("wrote original and final BioModel VCML files to {}", tempDir.getAbsolutePath()); } assertEquals(1, bioModels.size(), "expecting 1 biomodel in round trip"); @@ -248,7 +257,7 @@ void sedml_roundtrip_common(TestCase testCase) throws Exception { assertNotNull(simRoundTripped, "roundtripped simulation not found with name '" + simName + "'"); boolean mathOverrideEquiv = simToExport.getMathOverrides().compareEquivalent(simRoundTripped.getMathOverrides()); if (simToExport.getNumTrials()>1){ - throw new SEDMLExporter.SEDMLExportException("trials not suppported for SEDML export"); + throw new SedMLExporter.SEDMLExportException("trials not suppported for SEDML export"); } if (!mathOverrideEquiv){ // @@ -258,7 +267,7 @@ void sedml_roundtrip_common(TestCase testCase) throws Exception { List newOverrideNames = Arrays.stream(simRoundTripped.getMathOverrides().getOverridenConstantNames()).sorted().collect(Collectors.toList()); if (!oldOverrideNames.equals(newOverrideNames) && (simToExport.getScanCount() == simRoundTripped.getScanCount())){ // simulation scan counts are the same, but overridden constants have different names, try substituting them into the math and compare the maths. - System.out.println("old names: "+oldOverrideNames+", new names: "+newOverrideNames); + logger.info("old names: "+oldOverrideNames+", new names: "+newOverrideNames); for (int scan = 0; scan < simToExport.getScanCount(); scan++){ MathOverrides.ScanIndex scanIndex = new MathOverrides.ScanIndex(scan); MathDescription oldMathDescription = new SimulationSymbolTable(simToExport, scanIndex).getMathDescription(); @@ -281,92 +290,92 @@ void sedml_roundtrip_common(TestCase testCase) throws Exception { if (validationException.errors.stream() .anyMatch(err -> err.type == OmexPythonUtils.OmexValidationErrorType.OMEX_PARSE_ERROR)){ if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.OMEX_PARSER_ERRORS) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; }else{ - System.err.println("add SEDML_FAULT.OMEX_PARSER_ERRORS to "+test_case_name+": "+e.getMessage()); + logger.error("add SEDML_FAULT.OMEX_PARSER_ERRORS to {}: {}", test_case_name, e.getMessage()); } } if (validationException.errors.stream() .anyMatch(err -> err.type == OmexPythonUtils.OmexValidationErrorType.OMEX_VALIDATION_ERROR)){ if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.OMEX_VALIDATION_ERRORS) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; }else{ - System.err.println("add SEDML_FAULT.OMEX_VALIDATION_ERRORS to "+test_case_name+": "+e.getMessage()); + logger.error("add SEDML_FAULT.OMEX_VALIDATION_ERRORS to {}: {}", test_case_name, e.getMessage()); } } } if (e.getMessage()!=null && e.getMessage().contains("There are no models in ")){ if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.NO_MODELS_IN_OMEX) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; }else{ - System.err.println("add SEDML_FAULT.NO_MODELS_IN_OMEX to "+test_case_name); + logger.error("add SEDML_FAULT.NO_MODELS_IN_OMEX to {}", test_case_name); } } if (e.getMessage()!=null && e.getMessage().contains("expecting 1 biomodel in round trip")){ if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.DIFF_NUMBER_OF_BIOMODELS) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; }else{ - System.err.println("add SEDML_FAULT.DIFF_NUMBER_OF_BIOMODELS to "+test_case_name); + logger.error("add SEDML_FAULT.DIFF_NUMBER_OF_BIOMODELS to {}", test_case_name); } } if (e.getMessage()!=null && e.getMessage().contains("Error constructing a new simulation context")) { if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.ERROR_CONSTRUCTING_SIMCONTEXT) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; } else { - System.err.println("add SEDML_FAULT.ERROR_CONSTRUCTING_SIMCONTEXT to " + test_case_name); + logger.error("add SEDML_FAULT.ERROR_CONSTRUCTING_SIMCONTEXT to {}", test_case_name); } } if (e.getMessage()!=null && e.getMessage().contains("non-spatial stochastic simulation with histogram option to SEDML not supported")) { if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.NONSPATIAL_STOCH_HISTOGRAM) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; } else { - System.err.println("add SEDML_FAULT.NONSPATIAL_STOCH_HISTOGRAM to " + test_case_name); + logger.error("add SEDML_FAULT.NONSPATIAL_STOCH_HISTOGRAM to {}", test_case_name); } } if (e.getMessage()!=null && e.getMessage().contains("math overrides not equivalent for simulation")) { if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.MATH_OVERRIDE_NOT_EQUIVALENT) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; } else { - System.err.println("add SEDML_FAULT.MATH_OVERRIDE_NOT_EQUIVALENT to " + test_case_name); + logger.error("add SEDML_FAULT.MATH_OVERRIDE_NOT_EQUIVALENT to {}", test_case_name); } } if (e.getMessage()!=null && e.getMessage().contains("math overrides names not equivalent for simulation")) { if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.MATH_OVERRIDE_NAMES_DIFFERENT) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; } else { - System.err.println("add SEDML_FAULT.MATH_OVERRIDE_NAMES_DIFFERENT to " + test_case_name); + logger.error("add SEDML_FAULT.MATH_OVERRIDE_NAMES_DIFFERENT to {}", test_case_name); } } if (e.getMessage()!=null && e.getMessage().contains("roundtripped simulationContext not found with name")) { if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.SIMCONTEXT_NOT_FOUND_BY_NAME) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; } else { - System.err.println("add SEDML_FAULT.SIMCONTEXT_NOT_FOUND_BY_NAME to " + test_case_name); + logger.error("add SEDML_FAULT.SIMCONTEXT_NOT_FOUND_BY_NAME to {}", test_case_name); } } if (e.getMessage()!=null && e.getMessage().contains("roundtripped simulation not found with name")) { if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.SIMULATION_NOT_FOUND_BY_NAME) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; } else { - System.err.println("add SEDML_FAULT.SIMULATION_NOT_FOUND_BY_NAME to " + test_case_name); + logger.error("add SEDML_FAULT.SIMULATION_NOT_FOUND_BY_NAME to {}", test_case_name); } } if (e.getMessage()!=null && e.getMessage().contains("could not be exported to SBML :MathDescriptions not equivalent after VCML->SBML->VCML")) { if (knownSEDMLFaults().get(testCase.filename) == SEDML_FAULT.MATH_DIFFERENT) { - System.err.println("Expected error: "+e.getMessage()); + logger.info("Expected error: {}", e.getMessage()); return; } else { - System.err.println("add SEDML_FAULT.MATH_DIFFERENT to " + test_case_name); + logger.error("add SEDML_FAULT.MATH_DIFFERENT to {}", test_case_name); } } throw e; diff --git a/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterSBMLTest.java b/vcell-core/src/test/java/org/vcell/sbml/SedMLExporterSBMLTest.java similarity index 99% rename from vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterSBMLTest.java rename to vcell-core/src/test/java/org/vcell/sbml/SedMLExporterSBMLTest.java index 1e30acf859..18457cb9d6 100644 --- a/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterSBMLTest.java +++ b/vcell-core/src/test/java/org/vcell/sbml/SedMLExporterSBMLTest.java @@ -11,7 +11,7 @@ import java.util.stream.Stream; @Tag("SEDML_SBML_IT") -public class SEDMLExporterSBMLTest extends SEDMLExporterCommon { +public class SedMLExporterSBMLTest extends SEDMLExporterCommon { /** * each file in the slowTestSet takes > 10s on disk and is not included in the unit test (move to integration testing) diff --git a/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterVCMLTest.java b/vcell-core/src/test/java/org/vcell/sbml/SedMLExporterVCMLTest.java similarity index 96% rename from vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterVCMLTest.java rename to vcell-core/src/test/java/org/vcell/sbml/SedMLExporterVCMLTest.java index e571097840..b45b60facd 100644 --- a/vcell-core/src/test/java/org/vcell/sbml/SEDMLExporterVCMLTest.java +++ b/vcell-core/src/test/java/org/vcell/sbml/SedMLExporterVCMLTest.java @@ -11,7 +11,7 @@ import java.util.stream.Stream; @Tag("SEDML_VCML_IT") -public class SEDMLExporterVCMLTest extends SEDMLExporterCommon { +public class SedMLExporterVCMLTest extends SEDMLExporterCommon { static Set slowTestSet(){ Set slowModels = new HashSet<>(); diff --git a/vcell-core/src/test/java/org/vcell/sedml/StandaloneSEDMLTest.java b/vcell-core/src/test/java/org/vcell/sedml/StandaloneSEDMLTest.java index fda9b4ea22..c01a611ad6 100644 --- a/vcell-core/src/test/java/org/vcell/sedml/StandaloneSEDMLTest.java +++ b/vcell-core/src/test/java/org/vcell/sedml/StandaloneSEDMLTest.java @@ -6,18 +6,21 @@ import java.util.List; import java.util.Set; -import org.jlibsedml.AbstractTask; -import org.jlibsedml.Algorithm; +import org.jlibsedml.SedMLDataContainer; +import org.jlibsedml.components.SedBase; +import org.jlibsedml.components.SedML; +import org.jlibsedml.components.task.AbstractTask; +import org.jlibsedml.components.algorithm.Algorithm; import org.jlibsedml.ArchiveComponents; -import org.jlibsedml.DataGenerator; +import org.jlibsedml.components.dataGenerator.DataGenerator; import org.jlibsedml.Libsedml; -import org.jlibsedml.Model; -import org.jlibsedml.OneStep; -import org.jlibsedml.Output; -import org.jlibsedml.SEDMLDocument; -import org.jlibsedml.SedML; -import org.jlibsedml.SteadyState; -import org.jlibsedml.UniformTimeCourse; +import org.jlibsedml.components.model.Model; +import org.jlibsedml.components.simulation.OneStep; +import org.jlibsedml.components.output.Output; +import org.jlibsedml.SedMLDocument; +import org.jlibsedml.components.simulation.SteadyState; +import org.jlibsedml.components.simulation.UniformTimeCourse; +import org.jlibsedml.components.task.Task; import org.jlibsedml.execution.ArchiveModelResolver; import org.jlibsedml.execution.ModelResolver; import org.jlibsedml.modelsupport.BioModelsModelsRetriever; @@ -35,7 +38,6 @@ import cbit.vcell.mapping.SimulationContext.Application; import cbit.vcell.mapping.SimulationContext.MathMappingCallback; import cbit.vcell.mapping.SimulationContext.NetworkGenerationRequirements; -import cbit.vcell.resource.ResourceUtil; import cbit.vcell.solver.Simulation; import cbit.vcell.solver.SolverDescription; import cbit.vcell.xml.XMLSource; @@ -98,14 +100,16 @@ public static void doit(File archiveFile) throws Exception{ ArchiveComponents ac = null; ac = Libsedml.readSEDMLArchive(new FileInputStream(archiveFile)); - SEDMLDocument sedmlDoc = ac.getSedmlDocuments().get(0); + SedMLDocument sedmlDoc = ac.getSedmlDocuments().get(0); - SedML sedml = sedmlDoc.getSedMLModel(); - if(sedml == null || sedml.getModels().isEmpty()) { - throw new RuntimeException("sedml null or empty"); - } + SedMLDataContainer sedmlContainer = sedmlDoc.getSedMLModel(); + if(sedmlContainer == null) throw new RuntimeException("sedml container null"); + + SedML sedml = sedmlContainer.getSedML(); + if(sedml == null || sedml.getModels().isEmpty()) throw new RuntimeException("sedml container null or empty"); + - ModelResolver resolver = new ModelResolver(sedml); + ModelResolver resolver = new ModelResolver(sedmlContainer); // resolver.add(new FileModelResolver()); resolver.add(new ArchiveModelResolver(ac)); resolver.add(new BioModelsModelsRetriever()); @@ -115,12 +119,12 @@ public static void doit(File archiveFile) throws Exception{ // // iterate through all the elements and show them at the console // - List mmm = sedml.getModels(); + List mmm = sedml.getModels(); for(Model mm : mmm) { System.out.println(mm.toString()); } - List sss = sedml.getSimulations(); - for(org.jlibsedml.Simulation ss : sss) { + List sss = sedml.getSimulations(); + for(org.jlibsedml.components.simulation.Simulation ss : sss) { System.out.println(ss.toString()); } List ttt = sedml.getTasks(); @@ -143,14 +147,17 @@ public static void doit(File archiveFile) throws Exception{ // HashMap flattenedModels = new HashMap(); List taskList = sedml.getTasks(); for (AbstractTask task : taskList){ - String modelReference = task.getModelReference(); - org.jlibsedml.Model sedmlOriginalModel = sedml.getModelWithId(modelReference); + Task baseTask = sedmlContainer.getBaseTask(task.getId()); + if (baseTask == null) throw new RuntimeException("baseTask is null"); + Model sedmlOriginalModel = sedmlContainer.findModelById(baseTask.getModelReference()); + if (null == sedmlOriginalModel) throw new RuntimeException("Unable to determine source model"); - String sbmlModelString = resolver.getModelString(sedmlOriginalModel); + String sbmlModelString = resolver.getXMLStringRepresentationOfModel(sedmlOriginalModel); XMLSource sbmlSource = new XMLSource(sbmlModelString); // sbmlSource with all the changes applied - - org.jlibsedml.Simulation sedmlSimulation = sedml.getSimulation(task.getSimulationReference()); + + org.jlibsedml.components.simulation.Simulation sedmlSimulation = sedmlContainer.findSimulationById(baseTask.getSimulationReference()); + if (null == sedmlSimulation) throw new RuntimeException("Unable to determine simulation"); Algorithm algorithm = sedmlSimulation.getAlgorithm(); KisaoTerm sedmlKisao = kisaoInstance.getTermById(algorithm.getKisaoID()); diff --git a/vcell-core/src/test/resources/org/jlibsedml/example.sedml b/vcell-core/src/test/resources/org/jlibsedml/example.sedml new file mode 100644 index 0000000000..118ef95e02 --- /dev/null +++ b/vcell-core/src/test/resources/org/jlibsedml/example.sedml @@ -0,0 +1,2999 @@ + + + + + + + + + + + + + + + + + auto_time_for_task1_var + + + + + + + + auto_dg_for_task1_x1__x + + + + + + + + auto_dg_for_task1_x2__x + + + + + + + + auto_dg_for_task1_x3__x + + + + + + + + auto_dg_for_task1_x4__x + + + + + + + + auto_dg_for_task1_x5__x + + + + + + + + auto_dg_for_task1_x6__x + + + + + + + + auto_dg_for_task1_x7__x + + + + + + + + auto_dg_for_task1_x8__x + + + + + + + + auto_dg_for_task1_x9__x + + + + + + + + auto_dg_for_task1_x10__x + + + + + + + + auto_dg_for_task1_x11__x + + + + + + + + auto_dg_for_task1_x12__x + + + + + + + + auto_dg_for_task1_x13__x + + + + + + + + auto_dg_for_task1_x14__x + + + + + + + + auto_dg_for_task1_x15__x + + + + + + + + auto_dg_for_task1_x16__x + + + + + + + + auto_dg_for_task1_x17__x + + + + + + + + auto_dg_for_task1_x18__x + + + + + + + + auto_dg_for_task1_x19__x + + + + + + + + auto_dg_for_task1_x20__x + + + + + + + + auto_dg_for_task1_x21__x + + + + + + + + auto_dg_for_task1_x22__x + + + + + + + + auto_dg_for_task1_x23__x + + + + + + + + auto_dg_for_task1_x24__x + + + + + + + + auto_dg_for_task1_x25__x + + + + + + + + auto_dg_for_task1_x26__x + + + + + + + + auto_dg_for_task1_x27__x + + + + + + + + auto_dg_for_task1_x28__x + + + + + + + + auto_dg_for_task1_x29__x + + + + + + + + auto_dg_for_task1_x30__x + + + + + + + + auto_dg_for_task1_x31__x + + + + + + + + auto_dg_for_task1_x32__x + + + + + + + + auto_dg_for_task1_x33__x + + + + + + + + auto_dg_for_task1_x34__x + + + + + + + + auto_dg_for_task1_x35__x + + + + + + + + auto_dg_for_task1_x36__x + + + + + + + + auto_dg_for_task1_x37__x + + + + + + + + auto_dg_for_task1_x38__x + + + + + + + + auto_dg_for_task1_x39__x + + + + + + + + auto_dg_for_task1_x40__x + + + + + + + + auto_dg_for_task1_x41__x + + + + + + + + auto_dg_for_task1_x42__x + + + + + + + + auto_dg_for_task1_x43__x + + + + + + + + auto_dg_for_task1_x44__x + + + + + + + + auto_dg_for_task1_x45__x + + + + + + + + auto_dg_for_task1_x46__x + + + + + + + + auto_dg_for_task1_x47__x + + + + + + + + auto_dg_for_task1_x48__x + + + + + + + + auto_dg_for_task1_x49__x + + + + + + + + auto_dg_for_task1_x50__x + + + + + + + + auto_dg_for_task1_x51__x + + + + + + + + auto_dg_for_task1_x52__x + + + + + + + + auto_dg_for_task1_x53__x + + + + + + + + auto_dg_for_task1_x54__x + + + + + + + + auto_dg_for_task1_x55__x + + + + + + + + auto_dg_for_task1_x56__x + + + + + + + + auto_dg_for_task1_x57__x + + + + + + + + auto_dg_for_task1_x58__x + + + + + + + + auto_dg_for_task1_x59__x + + + + + + + + auto_dg_for_task1_x60__x + + + + + + + + auto_dg_for_task1_x61__x + + + + + + + + auto_dg_for_task1_x62__x + + + + + + + + auto_dg_for_task1_x63__x + + + + + + + + auto_dg_for_task1_x64__x + + + + + + + + auto_dg_for_task1_x65__x + + + + + + + + auto_dg_for_task1_x66__x + + + + + + + + auto_dg_for_task1_x67__x + + + + + + + + auto_dg_for_task1_x68__x + + + + + + + + auto_dg_for_task1_x69__x + + + + + + + + auto_dg_for_task1_x70__x + + + + + + + + auto_dg_for_task1_x71__x + + + + + + + + auto_dg_for_task1_x72__x + + + + + + + + auto_dg_for_task1_x73__x + + + + + + + + auto_dg_for_task1_x74__x + + + + + + + + auto_dg_for_task1_x75__x + + + + + + + + auto_dg_for_task1_x76__x + + + + + + + + auto_dg_for_task1_x77__x + + + + + + + + auto_dg_for_task1_x78__x + + + + + + + + auto_dg_for_task1_x79__x + + + + + + + + auto_dg_for_task1_x80__x + + + + + + + + auto_dg_for_task1_x81__x + + + + + + + + auto_dg_for_task1_x82__x + + + + + + + + auto_dg_for_task1_x83__x + + + + + + + + auto_dg_for_task1_x84__x + + + + + + + + auto_dg_for_task1_x85__x + + + + + + + + auto_dg_for_task1_x86__x + + + + + + + + auto_dg_for_task1_x87__x + + + + + + + + auto_dg_for_task1_x88__x + + + + + + + + auto_dg_for_task1_x89__x + + + + + + + + auto_dg_for_task1_x90__x + + + + + + + + auto_dg_for_task1_x91__x + + + + + + + + auto_dg_for_task1_x92__x + + + + + + + + auto_dg_for_task1_x93__x + + + + + + + + auto_dg_for_task1_x94__x + + + + + + + + auto_dg_for_task1_Raf_act__x + + + + + + + + auto_dg_for_task1_Ras_GTP__x + + + + + + + + auto_dg_for_task1_MEK_PP__x + + + + + + + + auto_dg_for_task1_ERK_PP__x + + + + + + + + auto_dg_for_task1_SHC_P_t__x + + + + + + + + auto_dg_for_task1_EGF_EGFR_act__x + + + + + + + + auto_dg_for_task1_k1__x + + + + + + + + auto_dg_for_task1_kr1__x + + + + + + + + auto_dg_for_task1_kr2__x + + + + + + + + auto_dg_for_task1_k2__x + + + + + + + + auto_dg_for_task1_k3__x + + + + + + + + auto_dg_for_task1_kr3__x + + + + + + + + auto_dg_for_task1_k4__x + + + + + + + + auto_dg_for_task1_kr4__x + + + + + + + + auto_dg_for_task1_k5__x + + + + + + + + auto_dg_for_task1_k6__x + + + + + + + + auto_dg_for_task1_kr6__x + + + + + + + + auto_dg_for_task1_k7__x + + + + + + + + auto_dg_for_task1_k8__x + + + + + + + + auto_dg_for_task1_kr8__x + + + + + + + + auto_dg_for_task1_k10__x + + + + + + + + auto_dg_for_task1_kr10__x + + + + + + + + auto_dg_for_task1_kr11__x + + + + + + + + auto_dg_for_task1_k11__x + + + + + + + + auto_dg_for_task1_kr12__x + + + + + + + + auto_dg_for_task1_k12__x + + + + + + + + auto_dg_for_task1_k13__x + + + + + + + + auto_dg_for_task1_k14__x + + + + + + + + auto_dg_for_task1_kr14__x + + + + + + + + auto_dg_for_task1_k15__x + + + + + + + + auto_dg_for_task1_kr16__x + + + + + + + + auto_dg_for_task1_k16__x + + + + + + + + auto_dg_for_task1_kr17__x + + + + + + + + auto_dg_for_task1_k17__x + + + + + + + + auto_dg_for_task1_kr18__x + + + + + + + + auto_dg_for_task1_k18__x + + + + + + + + auto_dg_for_task1_kr19__x + + + + + + + + auto_dg_for_task1_k19__x + + + + + + + + auto_dg_for_task1_kr20__x + + + + + + + + auto_dg_for_task1_k20__x + + + + + + + + auto_dg_for_task1_k21__x + + + + + + + + auto_dg_for_task1_kr21__x + + + + + + + + auto_dg_for_task1_k22__x + + + + + + + + auto_dg_for_task1_kr22__x + + + + + + + + auto_dg_for_task1_k23__x + + + + + + + + auto_dg_for_task1_kr23__x + + + + + + + + auto_dg_for_task1_k24__x + + + + + + + + auto_dg_for_task1_kr24__x + + + + + + + + auto_dg_for_task1_kr25__x + + + + + + + + auto_dg_for_task1_k25__x + + + + + + + + auto_dg_for_task1_k28__x + + + + + + + + auto_dg_for_task1_kr28__x + + + + + + + + auto_dg_for_task1_k29__x + + + + + + + + auto_dg_for_task1_kr29__x + + + + + + + + auto_dg_for_task1_kr32__x + + + + + + + + auto_dg_for_task1_k32__x + + + + + + + + auto_dg_for_task1_k33__x + + + + + + + + auto_dg_for_task1_kr33__x + + + + + + + + auto_dg_for_task1_k34__x + + + + + + + + auto_dg_for_task1_kr34__x + + + + + + + + auto_dg_for_task1_k35__x + + + + + + + + auto_dg_for_task1_kr35__x + + + + + + + + auto_dg_for_task1_Vm36__x + + + + + + + + auto_dg_for_task1_Km36__x + + + + + + + + auto_dg_for_task1_k37__x + + + + + + + + auto_dg_for_task1_kr37__x + + + + + + + + auto_dg_for_task1_k40__x + + + + + + + + auto_dg_for_task1_kr40__x + + + + + + + + auto_dg_for_task1_kr41__x + + + + + + + + auto_dg_for_task1_k41__x + + + + + + + + auto_dg_for_task1_k42__x + + + + + + + + auto_dg_for_task1_kr42__x + + + + + + + + auto_dg_for_task1_k43__x + + + + + + + + auto_dg_for_task1_kr44__x + + + + + + + + auto_dg_for_task1_k44__x + + + + + + + + auto_dg_for_task1_k45__x + + + + + + + + auto_dg_for_task1_k47__x + + + + + + + + auto_dg_for_task1_kr48__x + + + + + + + + auto_dg_for_task1_k48__x + + + + + + + + auto_dg_for_task1_k49__x + + + + + + + + auto_dg_for_task1_kr50__x + + + + + + + + auto_dg_for_task1_k50__x + + + + + + + + auto_dg_for_task1_k52__x + + + + + + + + auto_dg_for_task1_kr52__x + + + + + + + + auto_dg_for_task1_k53__x + + + + + + + + auto_dg_for_task1_k55__x + + + + + + + + auto_dg_for_task1_kr56__x + + + + + + + + auto_dg_for_task1_k56__x + + + + + + + + auto_dg_for_task1_k57__x + + + + + + + + auto_dg_for_task1_kr58__x + + + + + + + + auto_dg_for_task1_k58__x + + + + + + + + auto_dg_for_task1_k59__x + + + + + + + + auto_dg_for_task1_k60__x + + + + + + + + auto_dg_for_task1_k61__x + + + + + + + + auto_dg_for_task1_C__x + + + + + + + + auto_dg_for_task1_RT__x + + + + + + + + auto_dg_for_task1_c1__x + + + + + + + + auto_dg_for_task1_c2__x + + + + + + + + auto_dg_for_task1_c3__x + + + + + + + + auto_dg_for_task1_v1__x + + + + + + + + auto_dg_for_task1_v2__x + + + + + + + + auto_dg_for_task1_v3__x + + + + + + + + auto_dg_for_task1_v4__x + + + + + + + + auto_dg_for_task1_v5__x + + + + + + + + auto_dg_for_task1_v6__x + + + + + + + + auto_dg_for_task1_v7__x + + + + + + + + auto_dg_for_task1_v8__x + + + + + + + + auto_dg_for_task1_v9__x + + + + + + + + auto_dg_for_task1_v10__x + + + + + + + + auto_dg_for_task1_v11__x + + + + + + + + auto_dg_for_task1_v12__x + + + + + + + + auto_dg_for_task1_v13__x + + + + + + + + auto_dg_for_task1_v14__x + + + + + + + + auto_dg_for_task1_v15__x + + + + + + + + auto_dg_for_task1_v16__x + + + + + + + + auto_dg_for_task1_v17__x + + + + + + + + auto_dg_for_task1_v18__x + + + + + + + + auto_dg_for_task1_v19__x + + + + + + + + auto_dg_for_task1_v20__x + + + + + + + + auto_dg_for_task1_v21__x + + + + + + + + auto_dg_for_task1_v22__x + + + + + + + + auto_dg_for_task1_v23__x + + + + + + + + auto_dg_for_task1_v24__x + + + + + + + + auto_dg_for_task1_v25__x + + + + + + + + auto_dg_for_task1_v26__x + + + + + + + + auto_dg_for_task1_v27__x + + + + + + + + auto_dg_for_task1_v28__x + + + + + + + + auto_dg_for_task1_v29__x + + + + + + + + auto_dg_for_task1_v30__x + + + + + + + + auto_dg_for_task1_v31__x + + + + + + + + auto_dg_for_task1_v32__x + + + + + + + + auto_dg_for_task1_v33__x + + + + + + + + auto_dg_for_task1_v34__x + + + + + + + + auto_dg_for_task1_v35__x + + + + + + + + auto_dg_for_task1_v36__x + + + + + + + + auto_dg_for_task1_v37__x + + + + + + + + auto_dg_for_task1_v38__x + + + + + + + + auto_dg_for_task1_v39__x + + + + + + + + auto_dg_for_task1_v40__x + + + + + + + + auto_dg_for_task1_v41__x + + + + + + + + auto_dg_for_task1_v42__x + + + + + + + + auto_dg_for_task1_v43__x + + + + + + + + auto_dg_for_task1_v44__x + + + + + + + + auto_dg_for_task1_v45__x + + + + + + + + auto_dg_for_task1_v46__x + + + + + + + + auto_dg_for_task1_v47__x + + + + + + + + auto_dg_for_task1_v48__x + + + + + + + + auto_dg_for_task1_v49__x + + + + + + + + auto_dg_for_task1_v50__x + + + + + + + + auto_dg_for_task1_v51__x + + + + + + + + auto_dg_for_task1_v52__x + + + + + + + + auto_dg_for_task1_v53__x + + + + + + + + auto_dg_for_task1_v54__x + + + + + + + + auto_dg_for_task1_v55__x + + + + + + + + auto_dg_for_task1_v56__x + + + + + + + + auto_dg_for_task1_v57__x + + + + + + + + auto_dg_for_task1_v58__x + + + + + + + + auto_dg_for_task1_v59__x + + + + + + + + auto_dg_for_task1_v60__x + + + + + + + + auto_dg_for_task1_v61__x + + + + + + + + auto_dg_for_task1_v62__x + + + + + + + + auto_dg_for_task1_v63__x + + + + + + + + auto_dg_for_task1_v64__x + + + + + + + + auto_dg_for_task1_v65__x + + + + + + + + auto_dg_for_task1_v66__x + + + + + + + + auto_dg_for_task1_v67__x + + + + + + + + auto_dg_for_task1_v68__x + + + + + + + + auto_dg_for_task1_v69__x + + + + + + + + auto_dg_for_task1_v70__x + + + + + + + + auto_dg_for_task1_v71__x + + + + + + + + auto_dg_for_task1_v72__x + + + + + + + + auto_dg_for_task1_v73__x + + + + + + + + auto_dg_for_task1_v74__x + + + + + + + + auto_dg_for_task1_v75__x + + + + + + + + auto_dg_for_task1_v76__x + + + + + + + + auto_dg_for_task1_v77__x + + + + + + + + auto_dg_for_task1_v78__x + + + + + + + + auto_dg_for_task1_v79__x + + + + + + + + auto_dg_for_task1_v80__x + + + + + + + + auto_dg_for_task1_v81__x + + + + + + + + auto_dg_for_task1_v82__x + + + + + + + + auto_dg_for_task1_v83__x + + + + + + + + auto_dg_for_task1_v84__x + + + + + + + + auto_dg_for_task1_v85__x + + + + + + + + auto_dg_for_task1_v86__x + + + + + + + + auto_dg_for_task1_v87__x + + + + + + + + auto_dg_for_task1_v88__x + + + + + + + + auto_dg_for_task1_v89__x + + + + + + + + auto_dg_for_task1_v90__x + + + + + + + + auto_dg_for_task1_v91__x + + + + + + + + auto_dg_for_task1_v92__x + + + + + + + + auto_dg_for_task1_v93__x + + + + + + + + auto_dg_for_task1_v94__x + + + + + + + + auto_dg_for_task1_v95__x + + + + + + + + auto_dg_for_task1_v96__x + + + + + + + + auto_dg_for_task1_v97__x + + + + + + + + auto_dg_for_task1_v98__x + + + + + + + + auto_dg_for_task1_v99__x + + + + + + + + auto_dg_for_task1_v100__x + + + + + + + + auto_dg_for_task1_v101__x + + + + + + + + auto_dg_for_task1_v102__x + + + + + + + + auto_dg_for_task1_v103__x + + + + + + + + auto_dg_for_task1_v104__x + + + + + + + + auto_dg_for_task1_v105__x + + + + + + + + auto_dg_for_task1_v106__x + + + + + + + + auto_dg_for_task1_v107__x + + + + + + + + auto_dg_for_task1_v108__x + + + + + + + + auto_dg_for_task1_v109__x + + + + + + + + auto_dg_for_task1_v110__x + + + + + + + + auto_dg_for_task1_v111__x + + + + + + + + auto_dg_for_task1_v112__x + + + + + + + + auto_dg_for_task1_v113__x + + + + + + + + auto_dg_for_task1_v114__x + + + + + + + + auto_dg_for_task1_v115__x + + + + + + + + auto_dg_for_task1_v116__x + + + + + + + + auto_dg_for_task1_v117__x + + + + + + + + auto_dg_for_task1_v118__x + + + + + + + + auto_dg_for_task1_v119__x + + + + + + + + auto_dg_for_task1_v120__x + + + + + + + + auto_dg_for_task1_v121__x + + + + + + + + auto_dg_for_task1_v122__x + + + + + + + + auto_dg_for_task1_v123__x + + + + + + + + auto_dg_for_task1_v124__x + + + + + + + + auto_dg_for_task1_v125__x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +