blob: 967e8639f1eb24064e012565e3954daeccc3864c [file] [log] [blame]
package org.eclipse.stem.solvers.rk.impl;
/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.stem.core.graph.DynamicLabel;
import org.eclipse.stem.core.graph.IntegrationLabel;
import org.eclipse.stem.core.graph.IntegrationLabelValue;
import org.eclipse.stem.core.graph.LabelValue;
import org.eclipse.stem.core.graph.SimpleDataExchangeLabelValue;
import org.eclipse.stem.core.model.Decorator;
import org.eclipse.stem.core.model.IntegrationDecorator;
import org.eclipse.stem.core.model.STEMTime;
import org.eclipse.stem.core.solver.impl.SolverImpl;
import org.eclipse.stem.core.trigger.Trigger;
import org.eclipse.stem.solvers.rk.RkPackage;
import org.eclipse.stem.solvers.rk.RungeKutta;
import org.eclipse.stem.ui.Activator;
/**
* <!-- begin-user-doc -->
* An implementation of the model object '<em><b>Runge Kutta</b></em>'.
* <!-- end-user-doc -->
* <p>
* The following features are implemented:
* <ul>
* <li>{@link org.eclipse.stem.solvers.rk.impl.RungeKuttaImpl#getRelativeTolerance <em>Relative Tolerance</em>}</li>
* </ul>
* </p>
*
* @generated
*/
@SuppressWarnings("synthetic-access")
public class RungeKuttaImpl extends SolverImpl implements RungeKutta {
// Number of threads
private short num_threads = 2;// Preferences will override
// Jobs
private RkJob [] jobs;
// Used to synchronize worker threads to agree on step size
private CyclicBarrier stepSizeBarrier;
// Used to synchronize worker threads to proceed after all threads have
// updated the current temporary value to the new position
private CyclicBarrier updateDoneBarrier;
// Smallest step size required by any thread to advance one step
private double smallestH;
private double maximumError;
// The step size and the current position (x)
private double stepSize = 1.0;
private double currentX;
// Constants used in Runge Kutta Cash Karp
static double a2=0.2, a3=0.3, a4=0.6, a5=1.0, a6=0.875;
static double b21 = 0.2, b31=3.0/40, b32=9.0/40.0, b41=0.3, b42=-0.9, b43=1.2;
static double b51=-11.0/54.0, b52=2.5, b53=-70.0/27.0, b54=35.0/27.0, b61=1631.0/55296.0, b62=175.0/512.0, b63=575.0/13824.0, b64=44275.0/110592.0, b65=253.0/4096.0;
static double c1 = 37.0/378.0, c3=250.0/621.0, c4=125.0/594.0, c6=512.0/1771.0;
static double dc5 = -277.0/14336.0;
static double dc1 = c1-2825.0/27648.0, dc3=c3-18575.0/48384.0, dc4=c4-13525.0/55296.0, dc6=c6-0.25;
static double SAFETY=0.9, PGROW=-0.2, PSHRNK=-0.25, ERRCON=1.89E-4;
static double TINY = 1E-30;
private static int MAX_PROGRESS_REPORTS = 5;
/**
* The default value of the '{@link #getRelativeTolerance() <em>Relative Tolerance</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getRelativeTolerance()
* @generated
* @ordered
*/
protected static final double RELATIVE_TOLERANCE_EDEFAULT = 1.0E-9;
/**
* The cached value of the '{@link #getRelativeTolerance() <em>Relative Tolerance</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getRelativeTolerance()
* @generated
* @ordered
*/
protected double relativeTolerance = RELATIVE_TOLERANCE_EDEFAULT;
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
public RungeKuttaImpl() {
super();
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated NOT
*/
@Override
public boolean step(STEMTime time, long timeDelta, int cycle) {
// Validate all decorators that return deltas to make sure
// they are of deterministic nature. The Runge Kutta integratio
// can only handle determininistic variants
for(Decorator decorator:this.getDecorators())
if(decorator instanceof IntegrationDecorator) {
IntegrationDecorator idec = (IntegrationDecorator)decorator;
if(!idec.isDeterministic()) {
Activator.logError("Error, decorator: "+idec+" is not deterministic. The Runge Kutta Integrator can only handle deterministic models.", new Exception());
return false;
}
}
if(!this.isInitialized())
this.initialize(time);
Activator act = org.eclipse.stem.ui.Activator.getDefault();
if(act != null) {
final Preferences preferences = act.getPluginPreferences();
num_threads = (short)preferences.getInt(org.eclipse.stem.ui.preferences.PreferenceConstants.SIMULATION_THREADS);
} else num_threads = 2; // Just so we can run inside junit test
final int c = cycle;
// Initialize latches
stepSizeBarrier = new CyclicBarrier(num_threads, new Runnable() {
public void run() {
// All threads successfully advanced time by some step h.
// Find the smallest
smallestH = Double.MAX_VALUE;
maximumError = -Double.MAX_VALUE;
for(int i=0;i<num_threads;++i) {
if(jobs[i].h <= smallestH) {
if(jobs[i].h == smallestH && maximumError < jobs[i].maxerror) maximumError = jobs[i].maxerror;
else if(jobs[i].h != smallestH) maximumError = jobs[i].maxerror;
smallestH = jobs[i].h;
}
}
}
});
updateDoneBarrier = new CyclicBarrier(num_threads);
// Find triggers and make sure they are invoked
for(Decorator decorator:this.getDecorators()) {
if(decorator instanceof Trigger) {
decorator.updateLabels(time, timeDelta, cycle);
}
}
// First initialize the probe and temp label values from the current
// label values.
for(Decorator decorator:this.getDecorators()) {
EList<DynamicLabel>allLabels = decorator.getLabelsToUpdate();
for (final Iterator<DynamicLabel> currentStateLabelIter = allLabels
.iterator(); currentStateLabelIter.hasNext();) {
if(decorator instanceof IntegrationDecorator) {
// It's a standard disease model with a standard disease model label
final IntegrationLabel iLabel = (IntegrationLabel) currentStateLabelIter.next();
((IntegrationLabelValue)iLabel.getProbeValue()).set((IntegrationLabelValue)iLabel.getCurrentValue());
((IntegrationLabelValue)iLabel.getTempValue()).set((IntegrationLabelValue)iLabel.getCurrentValue());
} else currentStateLabelIter.next();
}
}
if(jobs == null || jobs.length != num_threads) {
// Initialize the jobs if not done yet or of the number of threads changes
jobs = new RkJob[num_threads];
for(short i=0;i<num_threads;++i) {
final short threadnum = i;
jobs[i] = new RkJob("Worker "+i, threadnum, this);
} // For each job
} // If not initialized
// Initialize
int thread = 0;
for(RkJob j:jobs) {
j.cycle = c;
j.time = time;
j.timeDelta = timeDelta;
}
// Schedule. Jobs can be rescheduled after finished
for(RkJob j:jobs)
j.schedule();
// Wait until all jobs completed
for(RkJob j : jobs) {
try {
j.join();
} catch(InterruptedException ie) {
Activator.logError(ie.getMessage(), ie);
}
}
// Set the common time and step size here and validate everything is right
double minStep = Double.MAX_VALUE;
double currentT = jobs[0].t;
for(RkJob j : jobs) {
// The jobs have calculated new step sizes after they finished. Pick the
// smallest one for the next cycle
if(j.h < minStep) minStep = j.h;
if(j.t != currentT) Activator.logError("Error, one thread was in misstep with other threads, its time was "+j.t+" versus "+currentT, new Exception());
}
return true;
}
/**
* _step Do the step for a single thread
*
* @param time
* @param timeDelta
* @param cycle
* @param threadnum
*/
protected void _step(STEMTime time, long timeDelta, int cycle, short threadnum) {
// this.setProgress(0.0);
// We only deal with standard disease model decorators
ArrayList<Decorator> iDecorators = new ArrayList<Decorator>();
for(Decorator d:getDecorators()) {
if(d instanceof IntegrationDecorator)
iDecorators.add(d);
}
// First we get the step size, either the default step size
// (initially 1.0) or the last step size used.
double h = this.getStepSize();
// x is to keep track of how far we have advanced in the solution. It is essentially
// a double cycle representation
double x = this.getCurrentX();
// Substantial performance can be gained here. Basically if the current cycle
// is greater than the cycle requested by the simulation, we are done. This
// means that the error tolerance between last step and this step is small
// enough so we don't need to update the labels. The error tolerance is
// specified in the disease model
//*** OBSERVE: Since we limit h to max 1 below, this code is never invoked. It's kept
//*** around in case we want to allow time to be calculated far out in the future if
//*** the error is small enough
if(x >= cycle) {
// Just copy the next value the same as the current value for all labels
for(Decorator sdm:iDecorators) {
EList<DynamicLabel>myLabels = sdm.getLabelsToUpdate(threadnum, num_threads);
int numLabels = myLabels.size();
double n = 0.0;
int setProgressEveryNthNode = num_threads * numLabels/(MAX_PROGRESS_REPORTS);
if(setProgressEveryNthNode == 0) setProgressEveryNthNode = 1;
for (final Iterator<DynamicLabel> currentStateLabelIter = myLabels
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
// The estimated disease value contains the value calculated at position x
IntegrationLabelValue nextValueAtX = (IntegrationLabelValue)EcoreUtil.copy(diseaseLabel.getProbeValue());
IntegrationLabelValue currentValueAtCycle = (IntegrationLabelValue)diseaseLabel.getCurrentValue();
IntegrationLabelValue nextState = (IntegrationLabelValue)diseaseLabel.getNextValue();
adjustValuesToCycle(currentValueAtCycle, nextValueAtX, x, cycle);
// NextValueAtX has been modified here to the correct value for this cycle.
nextState.set(nextValueAtX);
// The next value is valid now.
diseaseLabel.setNextValueValid(true);
double progress = n/numLabels;
jobs[threadnum].setProgress(progress);
if(n%setProgressEveryNthNode==0) {
// Get the progress for all threads
for(int i=0;i<num_threads;++i) if(i!=threadnum && jobs[i] != null) progress+=jobs[i].getProgress();
progress /= num_threads;
sdm.setProgress(progress);
}
n+=1.0;
}
} // For each decorator
// So that validation code above is happy
jobs[threadnum].h = h;
jobs[threadnum].t = x;
return;
}
// Make sure we actually have labels to update
boolean workToDo=false;
for(Decorator sdm:iDecorators)
if(sdm.getLabelsToUpdate(threadnum, num_threads).size() > 0) {workToDo=true;break;}
if(!workToDo) {
// Nothing to do, just advance x and set h
jobs[threadnum].h = h;
jobs[threadnum].t = x;
// Be nice and walk in step with others until done
while(x < cycle) {
try {
// Set to a large number to make sure it's larger than any step size reported
// by another thread
do {
jobs[threadnum].h = Double.MAX_VALUE;
stepSizeBarrier.await();
} while(this.maximumError > 1.0);
updateDoneBarrier.await();
} catch(InterruptedException ie) {
// Should never happen
Activator.logError(ie.getMessage(), ie);
} catch(BrokenBarrierException bbe) {
// Should never happen
Activator.logError(bbe.getMessage(), bbe);
}
// Set to the smallest value reported by another thread
h = this.smallestH;
x += h;
jobs[threadnum].h = h;
jobs[threadnum].t = x;
}
return;
}
// We use the Runge Kutta Kash Carp method to advance to the next
// step in the simulation. Two estimates of the disease deltas
// are calculated and compared to each other. If they differ
// by more than a maximum error (determined by a parameter for
// the disease model), we reduce the step size until an acceptable
// error is reached.
// These are used during Runge Kutta calculations:
Map<IntegrationLabel, IntegrationLabelValue> k1map = new HashMap<IntegrationLabel, IntegrationLabelValue>();
Map<IntegrationLabel, IntegrationLabelValue> k2map = new HashMap<IntegrationLabel, IntegrationLabelValue>();
Map<IntegrationLabel, IntegrationLabelValue> k3map = new HashMap<IntegrationLabel, IntegrationLabelValue>();
Map<IntegrationLabel, IntegrationLabelValue> k4map = new HashMap<IntegrationLabel, IntegrationLabelValue>();
Map<IntegrationLabel, IntegrationLabelValue> k5map = new HashMap<IntegrationLabel, IntegrationLabelValue>();
Map<IntegrationLabel, IntegrationLabelValue> k6map = new HashMap<IntegrationLabel, IntegrationLabelValue>();
// Used below as temporary place holder, one for each decorator
IntegrationLabelValue _k1[], _k2[], _k3[], _k4[], _k5[], _k6[];
int numDecorators = iDecorators.size();
_k1 = new IntegrationLabelValue[numDecorators];
_k2 = new IntegrationLabelValue[numDecorators];
_k3 = new IntegrationLabelValue[numDecorators];
_k4 = new IntegrationLabelValue[numDecorators];
_k5 = new IntegrationLabelValue[numDecorators];
_k6 = new IntegrationLabelValue[numDecorators];
// The final estimates for label values are stored here
Map<IntegrationLabel, IntegrationLabelValue> finalEstimate = new HashMap<IntegrationLabel, IntegrationLabelValue>();
// Delta is used to scale the step (h)
double delta = 0.0;
int n=0;
for(Decorator sdm:iDecorators) {
Iterator<DynamicLabel> iter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator();
IntegrationLabel firstLabel = (IntegrationLabel)iter.next();
// Initialize temporary place holders just by creating dups of the first label available
_k1[n] = (IntegrationLabelValue)EcoreUtil.copy(firstLabel.getCurrentValue());
_k2[n] = (IntegrationLabelValue)EcoreUtil.copy(firstLabel.getCurrentValue());
_k3[n] = (IntegrationLabelValue)EcoreUtil.copy(firstLabel.getCurrentValue());
_k4[n] = (IntegrationLabelValue)EcoreUtil.copy(firstLabel.getCurrentValue());
_k5[n] = (IntegrationLabelValue)EcoreUtil.copy(firstLabel.getCurrentValue());
_k6[n++] = (IntegrationLabelValue)EcoreUtil.copy(firstLabel.getCurrentValue());
}
double end = Math.floor(this.getCurrentX())+1.0;
// Keep track if whether anyone want to stop
// or pause updating labels
boolean interrupt=false, pause = false;
// We keep these around to determine when to call setProgress(...) on the decorators.
// If we call too frequently we can too many callbacks which affects performance.
double nextProgressReportStep = num_threads*(end-x)/MAX_PROGRESS_REPORTS;
double nextProgressReport = x+nextProgressReportStep;
// HashMap<StandardDiseaseModelLabel, StandardDiseaseModelLabelValue> validate = new
// HashMap<StandardDiseaseModelLabel, StandardDiseaseModelLabelValue>();
// This is the main loop we keep iterating over until we are done with the step
while(x < end) {
k1map.clear();
k2map.clear();
k3map.clear();
k4map.clear();
k5map.clear();
k6map.clear();
finalEstimate.clear();
// Validation code kept here if needed in the future
/*
HashMap<StandardDiseaseModelLabel, StandardDiseaseModelLabelValue> validate = new HashMap<StandardDiseaseModelLabel, StandardDiseaseModelLabelValue>();
if(!redo)
for(StandardDiseaseModelImpl sdm:diseaseModelDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final StandardDiseaseModelLabel diseaseLabel = (StandardDiseaseModelLabel) currentStateLabelIter
.next();
final StandardDiseaseModelLabelValue val = (StandardDiseaseModelLabelValue)diseaseLabel.getCurrentDiseaseModelTempLabelValue();
validate.put(diseaseLabel, val);
}
}
else {
for(StandardDiseaseModelImpl sdm:diseaseModelDecorators)
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final StandardDiseaseModelLabel diseaseLabel = (StandardDiseaseModelLabel) currentStateLabelIter
.next();
final SIRLabelValue val = (SIRLabelValue)diseaseLabel.getCurrentDiseaseModelTempLabelValue();
validate.put(diseaseLabel, val);
final SIRLabelValue oldVal = (SIRLabelValue)validate.get(diseaseLabel);
if(val.getI() != oldVal.getI() ||
val.getS() != oldVal.getS() ||
val.getR() != oldVal.getR() ||
//val.getE() != oldVal.getE() ||
val.getBirths() != oldVal.getBirths() ||
val.getDeaths() != oldVal.getDeaths() ||
val.getDiseaseDeaths() != oldVal.getDiseaseDeaths()
)
Activator.logError("Error, old and new value not the same label: "+diseaseLabel, new Exception());
}
}
*/
// ToDo: We should check if a maximum number of iterations have been
// exceeded here and throw an error.
// First, get the delta values at the current state
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).calculateDelta(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).applyExternalDeltas(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
// Set the scaling factor for disease parameters for each decorator and location
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
IntegrationLabelValue scale = (IntegrationLabelValue)diseaseLabel.getErrorScale();
scale.set((IntegrationLabelValue)diseaseLabel.getTempValue());
IntegrationLabelValue dt = (IntegrationLabelValue)EcoreUtil.copy(diseaseLabel.getDeltaValue());
dt.scale(h);
dt.abs();
dt.add(TINY);
scale.abs();
scale.add(dt);
}
}
// Step 1 in Runge Kutta Fehlberg.
// Get the delta values out of each node label and
// build a first estimate of the next value'
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
IntegrationLabelValue deltaLabel = (IntegrationLabelValue)diseaseLabel.getDeltaValue();
k1map.put(diseaseLabel, (IntegrationLabelValue)EcoreUtil.copy(deltaLabel));
deltaLabel.scale(h);
deltaLabel.scale(b21);
((IntegrationLabelValue)diseaseLabel.getProbeValue()).set(
deltaLabel.add((IntegrationLabelValue)
diseaseLabel.
getTempValue()));
}
}
// Now get the next delta values
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).calculateDelta(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).applyExternalDeltas(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
// Step 2 in Runge Kutta Fehlberg.
// Get the delta values out of each node label and
// build a second estimate of the next value
n = 0;
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
IntegrationLabelValue deltaLabel = (IntegrationLabelValue)diseaseLabel.getDeltaValue();
k2map.put(diseaseLabel,(IntegrationLabelValue)EcoreUtil.copy(deltaLabel));
_k1[n].set(k1map.get(diseaseLabel));
_k2[n].set(deltaLabel);
IntegrationLabelValue estDelta =
_k1[n].scale(b31);
_k2[n].scale(b32);
estDelta.add(_k2[n]);
estDelta.scale(h);
((IntegrationLabelValue)diseaseLabel.getProbeValue()).set(estDelta.add(
(IntegrationLabelValue)diseaseLabel.
getTempValue()));
}
++n;
}
// Now get the next delta values
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).calculateDelta(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).applyExternalDeltas(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
// Step 3 in Runge Kutta Fehlberg.
// Get the delta values out of each node label and
// build a third estimate of the next value
n = 0;
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
IntegrationLabelValue deltaLabel = (IntegrationLabelValue)diseaseLabel.getDeltaValue();
k3map.put(diseaseLabel, (IntegrationLabelValue)EcoreUtil.copy(deltaLabel));
_k1[n].set(k1map.get(diseaseLabel));
_k2[n].set(k2map.get(diseaseLabel));
_k3[n].set(deltaLabel);
_k1[n].scale(b41);
_k2[n].scale(b42);
_k3[n].scale(b43);
IntegrationLabelValue estDelta = _k1[n];
estDelta.add(_k2[n]);
estDelta.add(_k3[n]);
estDelta.scale(h);
((IntegrationLabelValue)diseaseLabel.getProbeValue()).set(estDelta.add(
(IntegrationLabelValue)diseaseLabel.
getTempValue()));
}
++n;
}
// Now get the next delta values
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).calculateDelta(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).applyExternalDeltas(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
// Step 4 in Runge Kutta Fehlberg.
// Get the delta values out of each node label and
// build a fourth estimate of the next value
n = 0;
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
IntegrationLabelValue deltaLabel = (IntegrationLabelValue)diseaseLabel.getDeltaValue();
k4map.put(diseaseLabel,(IntegrationLabelValue)EcoreUtil.copy(deltaLabel));
_k1[n].set(k1map.get(diseaseLabel));
_k2[n].set(k2map.get(diseaseLabel));
_k3[n].set(k3map.get(diseaseLabel));
_k4[n].set(deltaLabel);
_k1[n].scale(b51);
_k2[n].scale(b52);
_k3[n].scale(b53);
_k4[n].scale(b54);
IntegrationLabelValue estDelta = _k1[n];
estDelta.add(_k2[n]);
estDelta.add(_k3[n]);
estDelta.add(_k4[n]);
estDelta.scale(h);
((IntegrationLabelValue)diseaseLabel.getProbeValue()).set(estDelta.add(
(IntegrationLabelValue)diseaseLabel.
getTempValue()));
}
++n;
}
// Now get the next delta values
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).calculateDelta(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).applyExternalDeltas(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
// Step 5 in Runge Kutta Fehlberg.
// Get the delta values out of each node label and
// build a fifth estimate of the next value
n = 0;
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
IntegrationLabelValue deltaLabel = (IntegrationLabelValue)diseaseLabel.getDeltaValue();
k5map.put(diseaseLabel,(IntegrationLabelValue)EcoreUtil.copy(deltaLabel));
_k1[n].set(k1map.get(diseaseLabel));
_k2[n].set(k2map.get(diseaseLabel));
_k3[n].set(k3map.get(diseaseLabel));
_k4[n].set(k4map.get(diseaseLabel));
_k5[n].set(deltaLabel);
_k1[n].scale(b61);
_k2[n].scale(b62);
_k3[n].scale(b63);
_k4[n].scale(b64);
_k5[n].scale(b65);
IntegrationLabelValue estDelta = _k1[n];
estDelta.add(_k2[n]);
estDelta.add(_k3[n]);
estDelta.add(_k4[n]);
estDelta.add(_k5[n]);
estDelta.scale(h);
((IntegrationLabelValue)diseaseLabel.getProbeValue()).set(estDelta.add(
(IntegrationLabelValue)diseaseLabel.
getTempValue()));
}
++n;
}
// Now get the next delta values
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).calculateDelta(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
for(Decorator sdm:iDecorators)
((IntegrationDecorator)sdm).applyExternalDeltas(time, timeDelta, sdm.getLabelsToUpdate(threadnum, num_threads));
// Step 6 in Runge Kutta Fehlberg.
// Calculate k6
n = 0;
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
IntegrationLabelValue deltaLabel = (IntegrationLabelValue)diseaseLabel.getDeltaValue();
k6map.put(diseaseLabel,(IntegrationLabelValue)EcoreUtil.copy(deltaLabel));
}
++n;
}
// Step 7 in Runge Kutta Fehlberg
// Calculate the two estimates from k1, .. k6 values
// and determine the maximum difference (error) between them.
boolean success = true; // Were we able to update all labels without a large enough error?
double maxerror = 0.0;
n = 0;
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
IntegrationLabelValue currentValue = (IntegrationLabelValue)diseaseLabel.getTempValue();
_k1[n].set(k1map.get(diseaseLabel));
_k3[n].set(k3map.get(diseaseLabel));
_k4[n].set(k4map.get(diseaseLabel));
_k5[n].set(k5map.get(diseaseLabel));
_k6[n].set(k6map.get(diseaseLabel));
_k1[n].scale(c1);
_k3[n].scale(c3);
_k4[n].scale(c4);
_k6[n].scale(c6);
// New Y
IntegrationLabelValue yout = (IntegrationLabelValue)
EcoreUtil.copy(_k1[n].add(_k3[n]).add(_k4[n]).add(_k6[n]));
yout.scale(h);
yout.add(currentValue);
// Get the error
_k1[n].set(k1map.get(diseaseLabel));
_k3[n].set(k3map.get(diseaseLabel));
_k4[n].set(k4map.get(diseaseLabel));
_k5[n].set(k5map.get(diseaseLabel));
_k6[n].set(k6map.get(diseaseLabel));
_k1[n].scale(dc1);
_k3[n].scale(dc3);
_k4[n].scale(dc4);
_k5[n].scale(dc5);
_k6[n].scale(dc6);
IntegrationLabelValue yerror = (IntegrationLabelValue) EcoreUtil.copy(_k1[n].add(_k3[n]).add(_k4[n]).add(_k5[n]).add(_k6[n]));
yerror.scale(h);
yerror.divide((IntegrationLabelValue)diseaseLabel.getErrorScale());
double error = yerror.max();
error /= relativeTolerance;
if(error > maxerror) {
maxerror = error;
}
if(error <= 1.0)
finalEstimate.put(diseaseLabel,
(IntegrationLabelValue)EcoreUtil.copy(yout));
}
++n;
}
jobs[threadnum].h = h;
jobs[threadnum].maxerror = maxerror;
try {
stepSizeBarrier.await();
} catch(InterruptedException ie) {
// Should never happen
Activator.logError(ie.getMessage(), ie);
} catch(BrokenBarrierException bbe) {
// Should never happen
Activator.logError(bbe.getMessage(), bbe);
}
// At least one of the threads had to large of an error, fail
if(this.maximumError > 1.0)
success = false;
// Are we done?
if(success) {
// Check to make sure
if(this.smallestH > h)
Activator.logError("Error, h was less than the smallest, perhaps barrier process failed to execute? h:"+h+" vs "+this.smallestH, new Exception());
// Yes, hurrah, advance x using the step size h
x+=h;
if(this.maximumError > ERRCON)
h = SAFETY*h*Math.pow(this.maximumError, PGROW);
else
h = 5.0*h;
// Limit to max 1
if(h > 1.0) h = 1.0;
// Make sure we don't overshoot
if(x < end && x+h > end) h = (end-x);
// Update the current value to the new position
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter.next();
((IntegrationLabelValue)diseaseLabel.getTempValue()).set(finalEstimate.get(diseaseLabel));
((IntegrationLabelValue)diseaseLabel.getProbeValue()).set(finalEstimate.get(diseaseLabel));
}
}
// Wait until all other threads have updated the current value
try {
updateDoneBarrier.await();
} catch(InterruptedException ie) {
// Should never happen
Activator.logError(ie.getMessage(), ie);
} catch(BrokenBarrierException bbe) {
// Should never happen
Activator.logError(bbe.getMessage(), bbe);
}
double progress = (end-x < 0.0)? 1.0:1.0-(end-x);
jobs[threadnum].setProgress(progress);
if(x > nextProgressReport) {
// Get the progress for all threads
for(int i=0;i<num_threads;++i) if(i!=threadnum && jobs[i] != null) progress+=jobs[i].getProgress();
progress /= num_threads;
for(Decorator sdm:iDecorators)
sdm.setProgress(progress);
nextProgressReport += nextProgressReportStep;
}
} else {
// At least one thread failed, change the step size
// Problem, error too big, we need to reduce the step size
delta = SAFETY*h*Math.pow(this.maximumError,PSHRNK);
if(h > 0.0)
h = (delta > 0.1*h)? delta:0.1*h;
else
h = (delta > 0.1*h)? 0.1*h:delta;
// We didn't succeed.
//Reset the estimated value back to the original, the step size
// has been reduced so we well try again.
// Set the estimated value back to the current original value
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter.next();
((IntegrationLabelValue)diseaseLabel.getProbeValue()).set((IntegrationLabelValue)diseaseLabel.getTempValue());
}
}
}
} // While x < end
jobs[threadnum].t = x;
jobs[threadnum].h = h;
// Remember the step size and position in the solver
this.setStepSize(h);
this.setCurrentX(x);
// We're done
for(Decorator sdm:iDecorators) {
for (final Iterator<DynamicLabel> currentStateLabelIter = sdm.getLabelsToUpdate(threadnum, num_threads)
.iterator(); currentStateLabelIter.hasNext();) {
final IntegrationLabel diseaseLabel = (IntegrationLabel) currentStateLabelIter
.next();
// This is the next state for the label
IntegrationLabelValue nextState = (IntegrationLabelValue)diseaseLabel.getNextValue();
// This is the original current state at the previous cycle
IntegrationLabelValue originalState = (IntegrationLabelValue)diseaseLabel.getCurrentValue();
// This is the final value calculated at position x.
IntegrationLabelValue newValue = finalEstimate.get(diseaseLabel);
// x could be larger than the requested cycle, so we do a linear interpolation
// to fit it exactly to the requested cycle
// *** Not needed since we always end exactly at the requested cycle
//adjustValuesToCycle(originalState, newValue, x, cycle);
// New value has been modified here to fit the requested cycle
nextState.set(newValue);
// Do any model specific work for instance add noise
((IntegrationDecorator)sdm).doModelSpecificAdjustments((LabelValue)nextState);
// The next value is valid now.
diseaseLabel.setNextValueValid(true);
}
}
}
/**
* Adjust the returned label so that it matches the exact value at the requested cycle
* instead of the value at x. We do this by using the difference between x and the current
* cycle to adjust the label. nextValueAtX is modified by this function
*
* @param currentValue The current value
* @param nextValueAtX The next value at position x
* @param x Current position
* @param cycle Current cycle
*/
void adjustValuesToCycle(IntegrationLabelValue currentValue, IntegrationLabelValue nextValueAtX, double x, int cycle) {
IntegrationLabelValue result = (IntegrationLabelValue)EcoreUtil.copy(currentValue);
nextValueAtX.sub(currentValue); // difference between new value and old now in nextValueAtX
nextValueAtX.scale(1.0/(x-cycle+1));
nextValueAtX.set(result.add(nextValueAtX));
}
/**
* initialize before simulation begins. Rewind/forward any population model
* values to the start time of the
* @param time
*/
private void initialize(STEMTime time) {
EList<Decorator> redoList = new BasicEList<Decorator>();
boolean redo = false;
for(Decorator d:this.getDecorators()) {
if(d instanceof IntegrationDecorator) {
EList<DynamicLabel> labels = d.getLabelsToUpdate();
for(DynamicLabel l:labels) {
if(l instanceof IntegrationLabel) {
IntegrationLabel il = (IntegrationLabel)l;
il.reset(time);
if(((SimpleDataExchangeLabelValue)il.getDeltaValue()).getAdditions() > 0.0 ||
((SimpleDataExchangeLabelValue)il.getDeltaValue()).getSubstractions() > 0.0)
redo = true;
}
}
}
if(!redo)redoList.add(d);
}
// Fix decorators with unapplied deltas
if(redo) {
for(Decorator d:redoList) {
if(d instanceof IntegrationDecorator) {
EList<DynamicLabel> labels = d.getLabelsToUpdate();
for(DynamicLabel l:labels) {
if(l instanceof IntegrationLabel) {
IntegrationLabel il = (IntegrationLabel)l;
il.reset(time);
}
}
}
}
}
this.setInitialized(true);
}
/**
* Reset the solver
* @generated NOT
*/
@Override
public void reset() {
this.setStepSize(1.0);
this.setCurrentX(0.0);
this.setInitialized(false);
}
protected double getStepSize() {
return stepSize;
}
protected double getCurrentX() {
return currentX;
}
protected void setStepSize(double d) {
stepSize = d;
}
protected void setCurrentX(double d) {
currentX = d;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
protected EClass eStaticClass() {
return RkPackage.Literals.RUNGE_KUTTA;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public double getRelativeTolerance() {
return relativeTolerance;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public void setRelativeTolerance(double newRelativeTolerance) {
double oldRelativeTolerance = relativeTolerance;
relativeTolerance = newRelativeTolerance;
if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, RkPackage.RUNGE_KUTTA__RELATIVE_TOLERANCE, oldRelativeTolerance, relativeTolerance));
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case RkPackage.RUNGE_KUTTA__RELATIVE_TOLERANCE:
return new Double(getRelativeTolerance());
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case RkPackage.RUNGE_KUTTA__RELATIVE_TOLERANCE:
setRelativeTolerance(((Double)newValue).doubleValue());
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eUnset(int featureID) {
switch (featureID) {
case RkPackage.RUNGE_KUTTA__RELATIVE_TOLERANCE:
setRelativeTolerance(RELATIVE_TOLERANCE_EDEFAULT);
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public boolean eIsSet(int featureID) {
switch (featureID) {
case RkPackage.RUNGE_KUTTA__RELATIVE_TOLERANCE:
return relativeTolerance != RELATIVE_TOLERANCE_EDEFAULT;
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public String toString() {
if (eIsProxy()) return super.toString();
StringBuffer result = new StringBuffer(super.toString());
result.append(" (relativeTolerance: ");
result.append(relativeTolerance);
result.append(')');
return result.toString();
}
} //RungeKuttaImpl