package eu.quanticol.carma.core.cli;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import org.apache.commons.math3.stat.descriptive.AggregateSummaryStatistics;
import org.apache.commons.math3.stat.descriptive.StatisticalSummary;
import org.apache.commons.math3.stat.descriptive.StatisticalSummaryValues;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.cmg.ml.sam.sim.sampling.SimulationTimeSeries;
import eu.quanticol.carma.core.ModelLoader;
import eu.quanticol.carma.core.ui.data.MeasureData;
import eu.quanticol.carma.core.ui.data.SimulationOutcome;
import eu.quanticol.carma.simulator.CarmaComponent;
import eu.quanticol.carma.simulator.CarmaModel;
import eu.quanticol.carma.simulator.CarmaSystem;
public class CARMACommandLine {
static String experimentFile;
static String outputFolder = null;
static boolean fileEnded = false;
static boolean verbose = true;
static boolean multithreaded = false;
static int nThreads = 1;
static boolean seedSet = false;
static long baseSeed;
static boolean deadlineSet = false;
static double deadline;
static boolean replicationsSet = false;
static int nReplications;
private static long timeElapsed = 0; // time in nanoseconds
private static enum Mode {Simulation, Multivesta, Help, Summary, None};
private static Mode getMode(String[] args) {
if (args.length == 0) {
return Mode.Help;
}
switch(args[0]) {
case "multivesta":
return Mode.Multivesta;
case "simulate":
case "sim":
return Mode.Simulation;
case "summary":
return Mode.Summary;
case "help":
case "-help":
case "--help":
case "h":
case "-h":
case "--h":
return Mode.Help;
default:
return Mode.None;
}
}
private static void parseSimulationArguments(String[] args, boolean skipFirst) {
boolean unrecognised = false;
if (args.length == 0 || "-help".equals(args[0]) || "-h".equals(args[0])) {
printHelp();
System.exit(0);
}
int i = skipFirst ? 1 : 0;
experimentFile = args[i++];
for (; i < args.length; i++) {
switch(args[i]) {
case "-o":
case "-out":
case "-output":
if (i+1 <= args.length) {
outputFolder = args[++i];
}
else
System.out.println("Output flag was used but no output folder given.");
break;
case "-m":
case "-multi":
case "-multithreaded":
if (i+1 <= args.length) {
try {
nThreads = Integer.parseInt(args[++i]);
if (nThreads <= 0) {
System.out.println("Number of threads must be at least 1. "
+ "Running single-threaded instead.");
multithreaded = false;
} else {
multithreaded = true;
}
}
catch (Exception e) {
System.out.println("Could not understand number of cores (" + args[i] +
"). Running single-threaded instead.");
// multithreaded = false;
}
} else {
System.out.println("Multithreaded option was used but no number of threads given.");
}
break;
case "-seed":
if (i+1 <= args.length) {
try {
baseSeed = Long.parseLong(args[++i]);
seedSet = true;
}
catch (NumberFormatException e) {
System.out.println("Could not understand initial seed (" + args[i] +
"). Ignoring.");
}
} else {
System.out.println("Seed option was used but no initial seed given.");
}
break;
case "-t":
case "-time":
case "-deadline":
if (i+1 <= args.length) {
try {
deadline = Double.parseDouble(args[++i]);
deadlineSet = true;
}
catch (NumberFormatException e) {
System.out.println("Could not understand simulation final time (" + args[i] +
"). Ignoring.");
}
} else {
System.out.println("Final time option was used but no final time given.");
}
break;
case "-reps":
case "-replications":
if (i+1 <= args.length) {
try {
nReplications = Integer.parseInt(args[++i]);
replicationsSet = true;
}
catch (NumberFormatException e) {
System.out.println("Could not understand number of replications (" + args[i] +
"). Ignoring.");
}
} else {
System.out.println("Replications option was used but no number given.");
}
break;
case "-quiet":
case "-q":
verbose = false;
break;
default:
System.out.println("Unrecognised option: " + args[i] + " (ignoring).");
unrecognised = true;
}
}
if (unrecognised)
printHelp();
if (seedSet || deadlineSet || replicationsSet) {
warn("overridden simulation parameters (seed, stop time, number of replications)"
+ " will apply to all experiments in the file!");
}
}
/*
private static MultivestaExperiment parseMultivestaArguments(String[] args) {
boolean unrecognised = false;
String modelFile = args[1];
String queryFile = args[2];
for (int i = 3; i < args.length; i++) {
switch(args[i]) {
case "-o":
case "-out":
case "-output":
if (i+1 <= args.length) {
outputFolder = args[++i];
}
else
System.out.println("Output flag was used but no output folder given.");
break;
case "-quiet":
case "-q":
verbose = false;
break;
default: {
System.out.println("Unrecognised option: " + args[i] + " (ignoring).");
unrecognised = true;
}
}
}
if (unrecognised)
printHelp();
return new MultivestaExperiment(modelFile,queryFile,outputFolder);
}
*/
private static List<CommandLineSimulation> readExperimentsFile(String filename)
throws IOException {
List<CommandLineSimulation> experiments = new ArrayList<CommandLineSimulation>();
BufferedReader reader;
try {
reader = new BufferedReader(new FileReader(filename));
}
catch (Exception e) {
System.out.println("Error when reading experiments file (details below). Exiting.");
System.out.println("Error encountered:");
e.printStackTrace();
return experiments;
}
while (!fileEnded) {
CommandLineSimulation sim = readSingleExperiment(reader);
if (sim != null)
experiments.add(sim);
else if (!fileEnded)
System.out.println("Disregarding experiment.");
}
reader.close();
report("Read " + experiments.size() + " experiment specification(s).");
return experiments;
}
private static CommandLineSimulation readSingleExperiment(BufferedReader reader) throws IOException {
String name;
// disregard any empty lines between the previous experiment and this, also ensuring
// that there is still an experiment description left (ie we are not at EOF)
do {
name = reader.readLine();
if (name == null) {
fileEnded = true;
return null;
}
} while (name.isEmpty());
String modelName = reader.readLine();
CarmaModel model = getCARMAModel(modelName);
if (model == null) {
System.out.println("Could not read model from " + modelName + ".");
// continue reading until you find blank line or EOF (disregard experiment)
String line;
do {
line = reader.readLine();
} while (line != null && !line.isEmpty());
return null;
}
String system = reader.readLine();
int reps = Integer.parseInt(reader.readLine());
double stopTime = Double.parseDouble(reader.readLine());
int samplings = Integer.parseInt(reader.readLine());
List<MeasureData> measures = readMeasures(reader,model);
CommandLineSimulation sim = new CommandLineSimulation(name, model, system, reps,
stopTime, samplings, measures, modelName);
return sim;
}
private static CarmaModel getCARMAModel(String name) {
if (name.endsWith(".java")) {
warn("the compiled file " + name +
" may be out of date. Consider using the original CARMA model.");
return loadCARMAModelFromJava(name);
}
else if (name.endsWith(".carma")) {
return loadCARMAModelFromCARMA(name);
}
else {
System.out.println("Could not read model file. Please specify either a .carma or .java file.");
return null;
}
}
private static CarmaModel loadCARMAModelFromCARMA(String name) {
try {
ModelLoader loader = new ModelLoader();
return loader.load(name);
} catch (Exception e) {
System.out.println("Problem when loading model from file " + name + ":");
e.printStackTrace();
return null;
}
}
private static CarmaModel loadCARMAModelFromJava(String name) {
Path modelPath = Paths.get(name);
String modelName = modelPath.getFileName().toString();
// filename of log just replaces .java extension with .log
String logName = modelName.substring(0,modelName.lastIndexOf(".")) + ".log";
OutputStream stream = null;
try {
stream = new FileOutputStream(logName);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
System.exit(-1);
}
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int res = compiler.run(null, null, stream, name, "-source","1.8");
//int res = compiler.run(null, null, stream, name);
if(res==0) {
report("Java file successfully compiled.");
}
else {
System.out.println("Compilation failed: " + res + ". See log file for details.");
return null;
}
// Load and instantiate compiled class.
CarmaModel model = null;
try {
//avoid having null parent if the path is relative and doesn't specify a parent
File root = modelPath.toAbsolutePath().getParent().toFile();
URL rootUrl = root.getParentFile().toURI().toURL();
//TODO can this be done more elegantly?
// (both manipulating the classloader, and assuming that the package name is ms)
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { rootUrl },
CarmaModel.class.getClassLoader());
String className = "ms." + modelName.replace(".java", "");
Class<?> cls = Class.forName(className, true, classLoader);
Object inst = cls.newInstance();
model = (CarmaModel) inst;
return model;
} catch (MalformedURLException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
private static List<MeasureData> readMeasures(BufferedReader reader, CarmaModel model)
throws IOException {
List<String> allMeasures = Arrays.asList(model.getMeasures());
List<MeasureData> measureList = new ArrayList<MeasureData>();
String measureSpec = reader.readLine();
while (measureSpec != null && !measureSpec.isEmpty()) {
// measure specifications should be written as:
// measureName (if no parameters are required)
// or: measureName : parName = parValue
// or: measureName : parName1 = parValue1, parName2 = parValue2, ...
String[] parts = measureSpec.split(":");
String measureName = parts[0].trim();
if (!allMeasures.contains(measureName)) {
System.out.println("Measure " + measureName + " not found (ignoring).");
}
else if (parts.length > 2) {
System.out.println("Invalid measure specification " + measureSpec + "(ignoring)");
}
else {
// parse the specification
HashMap<String,Object> measurePars = new HashMap<String,Object>();
if (parts.length == 2) {
// the measure has parameters, read them one by one
String[] parSpecs = parts[1].split(",");
Map<String,Class<?>> parClasses = model.getParametersType(measureName);
for (String parSpec : parSpecs) {
int eqInd = parSpec.indexOf("=");
String parName = parSpec.substring(0, eqInd).trim();
String parValue = parSpec.substring(eqInd+1).trim();
if (!parClasses.containsKey(parName)) {
System.out.println("Unrecognised parameter " + parName +
" for measure " + measureName + " (ignoring).");
continue;
}
Object valueToPut;
if (parClasses.get(parName).equals(Integer.class))
valueToPut = Integer.parseInt(parValue);
else
valueToPut = Double.parseDouble(parValue);
measurePars.put(parName, valueToPut);
}
}
// add the measure if all the necessary parameters have been given
if (measurePars.size() != model.getMeasureParameters(measureName).length)
System.out.println("Measure " + measureName + " requires more parameters.");
else
measureList.add(new MeasureData(measureName,measurePars));
}
measureSpec = reader.readLine();
}
if (measureSpec == null) {
fileEnded = true;
}
return measureList;
}
private static void performSimulation() {
// Set up experiments:
List<CommandLineSimulation> allSims = null;
try {
allSims = readExperimentsFile(experimentFile);
}
catch (IOException e) {
e.printStackTrace();
}
// Run each experiment:
for (CommandLineSimulation sim : allSims) {
report("\nStarting experiment " + sim.getName() + ".");
// apply any additional parameters specified in the arguments...
overrideSimulation(sim);
long startTime = System.nanoTime();
// ...perform simulation...
if (!multithreaded) {
if (seedSet) {
sim.setSeed(baseSeed);
}
sim.execute(verbose);
}
else {
sim = performMultithreadedSimulation(sim);
}
long stopTime = System.nanoTime();
timeElapsed = stopTime - startTime;
report(String.format("Time elapsed: %s for %d replications",
formatTime(timeElapsed),sim.getReplications()));
// ...and save output
writeOutput(sim);
report("Finished experiment.");
}
}
private static CommandLineSimulation performMultithreadedSimulation(CommandLineSimulation sim) {
// split simulation and assign to each thread
List<CommandLineSimulation> subtasks = new ArrayList<CommandLineSimulation>();
int replicationsEach = sim.getReplications() / nThreads;
CommandLineSimulation subtask = sim.copy();
subtask.setReplications(replicationsEach);
for(int i = 0; i < nThreads - 1; i++) {
CommandLineSimulation newTask = subtask.copy();
newTask.setTaskName("Thread " + i);
if (seedSet) {
newTask.setSeed((i+1) * baseSeed);
}
subtasks.add(newTask);
}
// int replicationsRemaining = replicationsEach + sim.getReplications() % nThreads;
subtask.setReplications(replicationsEach + sim.getReplications() % nThreads);
subtask.setTaskName("Thread " + (nThreads-1));
if (seedSet) {
subtask.setSeed(nThreads * baseSeed);
}
subtasks.add(subtask);
// create threads / assign to cores
//TODO consider workStealingPool instead?
ExecutorService executor = Executors.newFixedThreadPool(nThreads);
// Perhaps:
//calls = subtasks.stream().map(t -> makeCallable(t))
// .collect(Collectors.toList());
// or simply:
List<? extends Callable<Object>> calls = new ArrayList<Callable<Object>>();
calls = subtasks;
try {
executor.invokeAll(calls);
}
catch (InterruptedException e) {
System.out.println("Error encountered while executing subtasks:");
e.printStackTrace();
System.out.println("Quitting.");
System.exit(-1);
}
executor.shutdown();
// merge results
int nResults = sim.getMeasures().size();
List<SimulationTimeSeries> allResults = new ArrayList<SimulationTimeSeries>(nResults);
for (int i = 0; i < nResults; i++) {
//create aggregate
int nPoints = sim.getSamplings() + 1;
List<List<SummaryStatistics>> allStatistics =
new ArrayList<List<SummaryStatistics>>(nPoints);
//allStatistics(j,k) will hold results for the k-th thread at the j-th sampling time
for (int j = 0; j < nPoints; j++) {
allStatistics.add(new ArrayList<SummaryStatistics>());
}
for (CommandLineSimulation task : subtasks) {
StatisticalSummary[] data = task.getResult(0).getCollectedData().get(i).getData();
for (int j = 0; j < data.length; j++) {
allStatistics.get(j).add((SummaryStatistics) data[j]);
}
}
//TODO This might be better to do in a static method of SimulationTimeSeries?
StatisticalSummaryValues[] aggregate = new StatisticalSummaryValues[nPoints];
for (int j = 0; j < aggregate.length; j++) {
aggregate[j] = AggregateSummaryStatistics.aggregate(allStatistics.get(j));
}
String name = subtasks.get(0).getResult(0).getCollectedData().get(i).getName();
double dt = sim.getSimulationTime() / sim.getSamplings();
allResults.add(new SimulationTimeSeries(name,dt,sim.getReplications(),aggregate));
}
// and create the final summary simulation
CommandLineSimulation finalSim = sim.copy();
//TODO change starting, total and average time in constructor of outcome?
SimulationOutcome outcome = new SimulationOutcome("",0,0,allResults);
List<SimulationOutcome> finalResult = new ArrayList<SimulationOutcome>(1);
finalResult.add(outcome);
finalSim.setResults(finalResult);
report("Aggregated results");
return finalSim;
}
private static void overrideSimulation(CommandLineSimulation sim) {
if (deadlineSet) {
sim.setSimulationTime(deadline);
}
if (replicationsSet) {
sim.setReplications(nReplications);
}
}
private static void writeOutput(CommandLineSimulation sim) {
LocalDateTime finishDate = LocalDateTime.now();
//TODO Check that this never happens
if (sim.getResults().size() > 1) {
System.out.println("Too many results: " + sim.getResults().size());
return;
}
if (sim.getResults() == null || sim.getResults().isEmpty()) {
System.out.println("No results found.");
return;
}
SimulationOutcome result = sim.getResult(0);
Path outputBase;
if (outputFolder == null)
outputBase = Paths.get("results").resolve(sim.getName());
else
outputBase = Paths.get(outputFolder).resolve("results").resolve(sim.getName());
if (Files.exists(outputBase)) {
warn("folder for experiment " + sim.getName() + " already exists.");
}
try {
Files.createDirectories(outputBase);
// } catch (FileAlreadyExistsException e) {
// System.out.println("Warning: folder for experiment " + sim.getName() +
// " already exists.");
} catch (IOException e) {
System.out.println("Could not create output folder: " + outputBase + ".");
//e.printStackTrace();
return;
}
// Write simulation results:
for (SimulationTimeSeries ts : result.getCollectedData()) {
Path measureFile = outputBase.resolve(ts.getName() + ".csv");
if (Files.exists(measureFile)) {
warn("will overwrite file " + measureFile + ".");
}
try (PrintWriter writer = new PrintWriter(measureFile.toFile())) {
ts.writeToCSV(writer);
} catch (FileNotFoundException e) {
System.err.println("Could not create file: " + measureFile + ".");
}
}
// Write time taken:
Path timeFile = outputBase.resolve("timeInSeconds");
if (Files.exists(timeFile)) {
warn("will overwrite file " + timeFile + ".");
}
try (PrintStream out = new PrintStream(timeFile.toFile())) {
double time_s = timeElapsed / 1e9;
out.print(String.format("%.5f",time_s));
} catch (FileNotFoundException e) {
System.err.println("Could not create file: " + timeFile + ".");
}
// Copy original model:
Path originalPath = Paths.get(sim.getModelLocation());
Path copyPath = outputBase.resolve(originalPath.getFileName());
if (Files.exists(copyPath)) {
warn("will overwrite file " + copyPath + ".");
}
boolean madeCopy = true;
try {
Files.copy(originalPath, copyPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e1) {
System.err.println("Error when trying to copy model file.");
madeCopy = false;
}
// Write experiment segment:
Path experimentFile = outputBase.resolve("experimentFile");
if (Files.exists(experimentFile)) {
warn("will overwrite file " + experimentFile + ".");
}
try (PrintStream out = new PrintStream(experimentFile.toFile())) {
out.println(sim.getName());
out.println(sim.getModelLocation());
out.println(sim.getSystem());
out.println(sim.getReplications());
out.println(sim.getSimulationTime());
out.println(sim.getSamplings());
for (MeasureData md : sim.getMeasures()) {
out.print(md.getMeasureName());
if (md.getParameters().size() > 0) {
List<String> pars = new ArrayList<String>(md.getParameters().size());
for (Map.Entry<String, Object> e : md.getParameters().entrySet()) {
pars.add(e.getKey() + " = " + e.getValue());
}
out.print(": " + String.join(", ", pars));
}
out.println();
}
} catch (FileNotFoundException e) {
System.err.println("Could not create file: " + timeFile + ".");
}
// Make gnuplot script:
Path plotFile = outputBase.resolve("plot.gp");
try (PrintStream out = new PrintStream(plotFile.toFile())) {
out.println("set title \"Measures for " + sim.getName() + "\"");
out.println("set xlabel \"Time\"");
out.println("set datafile separator \";\"");
out.println();
out.println("# To change the bounds of the yaxis, edit the following line:");
out.println("set yrange [0:*]");
out.println("# To change the bounds of the xaxis, uncomment and edit the following line:");
out.println("#set xrange [0:*]");
out.println();
//out.println("set offset graph 0.01,0.01,0,0");
out.println("# You can control the position of the legend with the following line:");
out.println("#set key outside right center");
out.println();
out.println("# To save a png figure of the plot, uncomment the next two lines:");
out.println("#set terminal png");
out.println(String.format("#set output \"%s_plot.png\"",sim.getName()));
out.println("# Or for PostScript output, uncomment the next two lines instead:");
out.println("#set terminal postscript color");
out.println(String.format("#set output \"%s_plot.ps\"",sim.getName()));
out.println();
out.println("# To change the font size on the axes, uncomment and edit the following line:");
out.println("#set size 0.5,0.5");
out.println();
List<String> measureList = sim.getMeasures().stream()
.map(t -> sim.getCarmaModel().getMeasure(t.getMeasureName(),t.getParameters())
.getName()).collect(Collectors.toList());
out.println("# The below lines will print the mean value of each measure in the experiment.");
out.println("# The confidence intervals at each point will be shown as error bars.");
out.println("# If you want to show standard deviations as error bars instead,");
out.println("# change \"using 1:2:4\" to \"using 1:2:3\"");
for (int i = 0; i < measureList.size(); i++) {
//output will have the form:
//plot "measure1.csv" using 1:2:4 title "measure1" with yerrorlines lw 4, \
// "measure2.csv" using 1:2:4 title "measure2" with yerrorlines lw 4
//etc. The ?: switching just ensures the right elements are printed at the right places
out.print((i == 0) ? "plot " : "\t");
out.print(String.format("\"%1$s.csv\" using 1:2:4 title \"%1$s\" with yerrorlines lw 4",
measureList.get(i)));
out.println((i < measureList.size() - 1) ? ", \\" : "");
}
out.println();
out.println("# For faster modification, the equivalent lines below may be useful:");
out.println("#measures = \"" + String.join(" ", measureList) + "\"");
out.println("#plot for [measure in measures] measure.\".csv\" using 1:2:4 lw 4"
+ " title measure with yerrorlines");
out.println();
out.println("# To only print mean values without error bars, "
+ "change \"1:2:4\" to \"1:2\" and \"yerrorlines\" to \"lines\".");
} catch (FileNotFoundException e) {
System.err.println("Could not create file: " + plotFile + ".");
}
// Write summary:
Path summaryFile = outputBase.resolve("info");
if (Files.exists(summaryFile)) {
warn("will overwrite file " + summaryFile + ".");
}
try(PrintWriter writer = new PrintWriter(summaryFile.toFile())) {
String firstLine = "Summary for experiment " + sim.getName() + ":";
writer.println(firstLine);
writer.println(new String(new char[firstLine.length()-1]).replace('\0', '-'));
writer.print("This experiment used the model " + sim.getModelLocation() + ".");
if (madeCopy) {
writer.println(" A copy has been saved in this directory.");
} else {
// just in case, e.g. the original file is deleted during the simulation
writer.println("I failed to make a copy of the original model.");
}
writer.println("The scenario considered was " + sim.getSystem() + ".");
writer.print("The experiment tracked the following measures: ");
// writer.println(sim.getMeasures().stream().map(MeasureData::toString)
// .collect(Collectors.joining(", ")) + ".");
writer.println(sim.getMeasures().stream()
.map(t -> sim.getCarmaModel().getMeasure(t.getMeasureName(),t.getParameters())
.getName())
.collect(Collectors.joining(", ")) + ".");
writer.println(String.format("The final time of the simulation was %.3f "
+ "and %d samplings were taken (sampling interval: %.5f).",
sim.getSimulationTime(),
sim.getSamplings(),
sim.getSimulationTime()/sim.getSamplings()));
writer.println(sim.getReplications() + " replications were performed in "
+ formatTime(timeElapsed) + " using the CARMA simulator.");
if (multithreaded) {
writer.println("The simulations were divided into " + nThreads + " batches.");
}
if (seedSet) {
writer.println("The seed for the simulations was set to " + baseSeed + ".");
}
writer.println("The data from individual replications were combined and statistics"
+ " (mean, variance) were computed using the Apache Commons Mathematics library.");
writer.println(String.format("This experiment finished at %s on %s.",
finishDate.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM)),
finishDate.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG))));
if (writer.checkError()) {
System.err.println("Error when writing to file " + summaryFile);
}
} catch (FileNotFoundException e) {
System.err.println("Could not create file: " + summaryFile + ".");
}
report("Wrote experiment results at " + outputBase.toAbsolutePath() + ".");
}
private static void report(String msg) {
if (verbose) {
System.out.println(msg);
}
}
private static void warn(String msg) {
System.out.println("Warning: " + msg);
}
private static void printHelp() {
String helpMessage = "Usage: java CARMACommandLine <experiments_file> "
+ "[-output <output_directory>] [-quiet] [-multithreaded N] "
+ "[-seed N] [-deadline t] [-replications N]\n\n"
+ "Optional parameters:\n"
+ "-output (-out, -o) : specify a location to store experiment results.\n"
+ "-quiet (-q) : only print warning and error messages.\n"
+ "-multithreaded (-m) : spread the task into the specified number of threads.\n"
+ "-seed : specify the random seed for the simulations.\n"
+ "-deadline (-time, -t): specify the final time for the simulations.\n"
+ "-replications (-reps): specify the number of replications.\n";
System.out.println(helpMessage);
}
public static void main(String[] args) {
// read arguments
Mode mode = getMode(args);
// based on the arguments, choose the right option
switch(mode) {
case Help:
printHelp();
break;
case Simulation:
parseSimulationArguments(args,true);
performSimulation();
break;
case Multivesta:
/*
MultivestaExperiment exp = parseMultivestaArguments(args);
try {
exp.run();
} catch (Exception e) {
System.out.println("Could not run MultiVeStA experiment.\nError:");
e.printStackTrace();
}
*/
System.out.println("MultiVeStA integration coming soon.");
break;
case Summary:
printSummary(args);
break;
case None:
parseSimulationArguments(args,false);
performSimulation();
}
}
private static void printSummary(String[] args) {
//parseSummaryArguments(args);
if (args.length < 3) {
System.out.println("No model given.\n");
printHelp();
return;
}
String name = args[2];
boolean create_latex = false;
boolean unrecognised = false;
for(int i = 3; i < args.length; i++) {
switch(args[i]) {
case "-latex":
case "-LaTeX":
create_latex = true;
break;
case "-quiet":
case "-q":
verbose = false;
break;
default:
System.out.println("Unrecognised option: " + args[i] + " (ignoring).");
unrecognised = true;
}
}
if (unrecognised) {
printHelp();
}
CarmaModel model = getCARMAModel(name);
if (model == null) {
System.out.println("Could not read model from file " + name);
return;
}
if (verbose) {
printModelOutline(model);
}
if (create_latex) {
String outputFile = null;
printLatex(model,outputFile);
}
}
private static void printModelOutline(CarmaModel m) {
for(String sys_name : m.getSystems()) {
System.out.println("System " + sys_name + ":");
CarmaSystem system = m.getFactory(sys_name).getModel();
System.out.println("Initial state:");
for (CarmaComponent c : system.getCollective()) {
System.out.print("Component " + c.getName() +
((c.getLocation() != null) ? ("at " + c.getLocation()) : ""));
}
}
}
private static void printLatex(CarmaModel m, String filename) {
System.out.println("LaTeX generation not implemented yet.");
}
public static long getTimeElapsed() {
return timeElapsed;
}
private static String formatTime(long time) {
long time_ms = TimeUnit.NANOSECONDS.toMillis(time);
if (time_ms < 1000) { // if up to 1 second, print time in milliseconds
return time_ms + " ms";
} else {
double time_s = (double) time_ms / 1000;
if (time_s < 300) { // if up to 5 minutes, print seconds
return String.format("%.5f s", time_s);
} else { // if more than 5 minutes, print minutes and seconds rounded down
int minFull = (int) time_s / 60;
int secRem = (int) time_s % 60;
return String.format("%d m %d s",minFull,secRem);
}
}
}
/**
* Resets the fields of the class, so it can be called repeatedly from other code.
*/
public static void reset() {
experimentFile = null;
outputFolder = null;
fileEnded = false;
verbose = true;
multithreaded = false;
nThreads = 1;
timeElapsed = 0;
seedSet = false;
deadlineSet = false;
replicationsSet = false;
}
}