Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.vcell.cli.run.hdf5;

public class Hdf5DataSet {


public Hdf5DataSet (){

}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
package org.vcell.cli.run.hdf5;

import org.jlibsedml.Variable;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.LinkedHashMap;

import org.jlibsedml.Variable;

/**
* Struct-class to hold list of nonspacial variable data
* Class
*/
public class Hdf5DataSourceNonspatial extends Hdf5DataSource {
public Hdf5DataSourceNonspatial() {}

/**
* List of all data contained within a job relevant to HDF5 formatted files
*/
public List<Hdf5JobData> jobData = new ArrayList<>();
public List<Hdf5JobData> jobData = new LinkedList<>();

/**
* Struct-Subclass for holding job data
Expand All @@ -28,6 +29,4 @@ public static class Hdf5JobData {
*/
public Map<Variable, double[]> varData = new LinkedHashMap<>();
}


}
113 changes: 98 additions & 15 deletions vcell-cli/src/main/java/org/vcell/cli/run/hdf5/Hdf5WrapperFactory.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package org.vcell.cli.run.hdf5;

import cbit.vcell.solver.Simulation;
import cbit.vcell.parser.DivideByZeroException;
import cbit.vcell.parser.Expression;
import cbit.vcell.parser.ExpressionException;
import cbit.vcell.parser.SimpleSymbolTable;
import cbit.vcell.parser.SymbolTable;
import cbit.vcell.solver.ode.ODESolverResultSet;
import ncsa.hdf.hdf5lib.exceptions.HDF5Exception;

Expand All @@ -27,6 +31,7 @@
import java.io.*;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;


/**
Expand Down Expand Up @@ -65,20 +70,23 @@ public Hdf5DataWrapper generateHdf5File(Map<TaskJob, ODESolverResultSet> nonSpat
Hdf5DataWrapper hdf5FileWrapper = new Hdf5DataWrapper();
Exception nonSpatialException = null, spatialException = null;

// Nonspacial Collection
try {
wrappers.addAll(this.collectNonspatialDatasets(this.sedml, nonSpatialResults, this.taskToSimulationMap, this.sedmlLocation));
} catch (Exception e){
logger.warn("Collection of nonspatial datasets failed for " + this.sedml.getFileName(), e);
nonSpatialException = e;
}

// Spacial Collection
try {
wrappers.addAll(this.collectSpatialDatasets(this.sedml, spatialResults, this.taskToSimulationMap, this.sedmlLocation));
} catch (Exception e){
logger.warn("Collection of spatial datasets failed for " + this.sedml.getFileName(), e);
spatialException = e;
}

// Error Checking
if (nonSpatialException != null && nonSpatialException != null){
throw new RuntimeException("Encountered complete dataset collection failure;\nNonSpatial Reported:\n" + nonSpatialException.getMessage()
+ "\nSpatial Reported:\n" + spatialException.getMessage());
Expand All @@ -100,9 +108,9 @@ private List<Hdf5DatasetWrapper> collectNonspatialDatasets(SedML sedml, Map<Task

// go through each entry (dataset)
for (DataSet dataset : report.getListOfDataSets()) {
List<String> varIDs = new ArrayList<>();
Map<Variable, NonspatialValueHolder> values = new HashMap<>();
int maxLengthOfAllData = 0; // We have to pad up to this value
Map<String, List<Double>> idToDataMap = new LinkedHashMap<>();
//List<String> varIDs = new ArrayList<>();
//List<Double> values = new ArrayList<>();

// use the data reference to obtain the data generator
DataGenerator datagen = sedml.getDataGeneratorWithId(dataset.getDataReference()); assert datagen != null;
Expand Down Expand Up @@ -140,7 +148,7 @@ private List<Hdf5DatasetWrapper> collectNonspatialDatasets(SedML sedml, Map<Task

if (taskJobs.isEmpty()) continue;

varIDs.add(var.getId());
//varIDs.add(var.getId());

if (!(sedmlSim instanceof UniformTimeCourse)){
logger.error("only uniform time course simulations are supported");
Expand All @@ -150,12 +158,11 @@ private List<Hdf5DatasetWrapper> collectNonspatialDatasets(SedML sedml, Map<Task
// we want to keep the last outputNumberOfPoints only
int outputNumberOfPoints = ((UniformTimeCourse) sedmlSim).getNumberOfPoints();
double outputStartTime = ((UniformTimeCourse) sedmlSim).getOutputStartTime();
NonspatialValueHolder variablesList;
List<Double> formattedData;

for (TaskJob taskJob : taskJobs) {
ODESolverResultSet results = nonspatialResultsHash.get(taskJob);
int column = results.findColumn(sbmlVarId);
double[] data = results.extractColumn(column);
double[] data = results.extractColumn(results.findColumn(sbmlVarId));

if (outputStartTime > 0){
double[] correctiveData = new double[outputNumberOfPoints + 1];
Expand All @@ -165,17 +172,38 @@ private List<Hdf5DatasetWrapper> collectNonspatialDatasets(SedML sedml, Map<Task
data = correctiveData;
}

maxLengthOfAllData = Integer.max(maxLengthOfAllData, data.length);
if (initialTask instanceof RepeatedTask && values.containsKey(var)) { // double[] exists
variablesList = values.get(var);
/*if (initialTask instanceof RepeatedTask && idToDataMap.containsKey(var.getId())) { // double[] exists
variablesList = idToDataMap.get(var.getId());
} else { // this is the first double[]
variablesList = new NonspatialValueHolder(taskToSimulationMap.get(initialTask));
}
variablesList.values.add(data);
values.put(var, variablesList);
variablesList = new LinkedList<>();
}*/
// really ugly convert from double[] to List<Double>
formattedData = Arrays.asList(Arrays.stream(data).boxed().toArray(Double[]::new));
idToDataMap.put(var.getId(), formattedData);
}
}

// Missing functionality!!! We need to computer the variables into the proper values
if (idToDataMap.isEmpty()) continue;
int maxLengthOfData = 0;
List<Double> data = new LinkedList<>();

SimpleDataGenCalculator calc = new SimpleDataGenCalculator(datagen);

for (List<Double> varData : idToDataMap.values())
if (varData.size() > maxLengthOfData)
maxLengthOfData = varData.size();

for (int i = 0; i < maxLengthOfData; i++){
for (String varId : idToDataMap.keySet()){
List<Double> varData = idToDataMap.get(varId);
Double value = i >= varData.size() ? Double.NaN : varData.get(i);
calc.setArgument(varId, value);
}
data.add(calc.evaluateWithCurrentArguments());
}
dataSetValues.put(dataset, values);

//dataSetValues.put(dataset, data.stream().toArray(Double[]::new));

} // end of dataset

Expand Down Expand Up @@ -353,6 +381,11 @@ private List<Report> getReports(List<Output> outputs){
return reports;
}

/**
* Goes though references of repeated tasks, finding the "base" task that isn't repeated.
* @param task the task to check if repeated
* @return the base task (not a repeated task)
*/
private AbstractTask getOriginalTask(AbstractTask task){
while (task instanceof RepeatedTask) { // We need to find the original task burried beneath.
// We assume that we can never have a sequential repeated task at this point, we check for that in SEDMLImporter
Expand Down Expand Up @@ -450,4 +483,54 @@ private static String removeVCellPrefixes(String s, String sedmlId){

return s;
}

private class SimpleDataGenCalculator {
private Expression equation;
private Map<String, Double> 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);
SymbolTable symTable = new SimpleSymbolTable(variableArray);
this.equation.bindExpression(symTable);

for (String var : variableArray){
bindingMap.put(var, Double.NaN);
}
}

public void setArgument(String parameter, Double argument){
if (!this.bindingMap.containsKey(parameter)) throw new IllegalArgumentException(String.format("\"%s\" is not a parameter of the expression", parameter));
this.bindingMap.put(parameter, argument);
}

public void setArguments(Map<String, Double> parameterToArgumentMap){
for (String param : parameterToArgumentMap.keySet()){
this.bindingMap.put(param, parameterToArgumentMap.get(param));
}
}

public double evaluateWithCurrentArguments() throws ExpressionException, DivideByZeroException {
Double[] args = this.bindingMap.values().toArray(new Double[0]);
return this.equation.evaluateVector(Stream.of(args).mapToDouble(Double::doubleValue).toArray());
}

public double evaluateWithProvidedArguments(Map<String, Double> parameterToArgumentMap) throws ExpressionException, DivideByZeroException {
List<Double> args = new LinkedList<>();

// Confirm we have the correct params
if (parameterToArgumentMap.size() != this.bindingMap.size()) throw new IllegalArgumentException("Incorrect number of entries.");
if (!parameterToArgumentMap.keySet().equals(this.bindingMap.keySet())) throw new IllegalArgumentException("Parameter 'keys' don't match");

// Prepare args
for (String param : this.bindingMap.keySet()){ // binding map, because keys are set-similar but only binding map preserves order for sure!
args.add(parameterToArgumentMap.get(param));
}

// Solve
return this.equation.evaluateVector(args.stream().mapToDouble(Double::doubleValue).toArray());
}
}
}