use of gov.sandia.n2a.backend.internal.InternalBackendData.EventSource in project n2a by frothga.
the class JobC method generateDefinitionsLocal.
public void generateDefinitionsLocal(RendererC context) throws Exception {
EquationSet s = context.part;
BackendDataC bed = context.bed;
StringBuilder result = context.result;
context.global = false;
String ns = prefix(s) + "::";
// Unit ctor
if (bed.needLocalCtor) {
result.append(ns + prefix(s) + " ()\n");
result.append("{\n");
if (bed.needLocalDerivative) {
result.append(" stackDerivative = 0;\n");
}
if (bed.needLocalPreserve) {
result.append(" preserve = 0;\n");
}
for (EquationSet p : s.parts) {
result.append(" " + mangle(p.name) + ".container = this;\n");
BackendDataC pbed = (BackendDataC) p.backendData;
if (pbed.singleton) {
result.append(" " + mangle(p.name) + ".instance.container = this;\n");
}
}
if (s.accountableConnections != null) {
for (EquationSet.AccountableConnection ac : s.accountableConnections) {
result.append(" int " + prefix(ac.connection) + "_" + mangle(ac.alias) + "_count = 0;\n");
}
}
if (bed.refcount) {
result.append(" refcount = 0;\n");
}
if (bed.index != null) {
// -1 indicates that an index needs to be assigned. This should only be done once.
result.append(" __24index = -1;\n");
}
if (bed.localMembers.size() > 0) {
result.append(" clear ();\n");
}
result.append("}\n");
result.append("\n");
}
// Unit dtor
if (bed.needLocalDtor) {
result.append(ns + "~" + prefix(s) + " ()\n");
result.append("{\n");
if (bed.needLocalDerivative) {
result.append(" while (stackDerivative)\n");
result.append(" {\n");
result.append(" Derivative * temp = stackDerivative;\n");
result.append(" stackDerivative = stackDerivative->next;\n");
result.append(" delete temp;\n");
result.append(" }\n");
}
if (bed.needLocalPreserve) {
result.append(" if (preserve) delete preserve;\n");
}
result.append("}\n");
result.append("\n");
}
// Unit clear
if (bed.localMembers.size() > 0) {
result.append("void " + ns + "clear ()\n");
result.append("{\n");
for (Variable v : bed.localMembers) {
result.append(" " + zero(mangle(v), v) + ";\n");
}
result.append("}\n");
result.append("\n");
}
// Unit setPeriod
if (// instance of top-level population, so set period on wrapper whenever our period changes
s.container == null) {
result.append("void " + ns + "setPeriod (" + T + " dt)\n");
result.append("{\n");
result.append(" PartTime<" + T + ">::setPeriod (dt);\n");
result.append(" if (container->visitor->event != visitor->event) container->setPeriod (dt);\n");
result.append("}\n");
result.append("\n");
}
// Unit die
if (bed.needLocalDie) {
result.append("void " + ns + "die ()\n");
result.append("{\n");
if (s.metadata.getFlag("backend", "all", "fastExit")) {
result.append(" Simulator<" + T + ">::instance.stop = true;\n");
} else {
// tag part as dead
if (// $live is stored in this part
bed.liveFlag >= 0) {
result.append(" flags &= ~((" + bed.localFlagType + ") 0x1 << " + bed.liveFlag + ");\n");
}
// instance counting
if (bed.n != null && !bed.singleton)
result.append(" container->" + mangle(s.name) + ".n--;\n");
for (String alias : bed.accountableEndpoints) {
result.append(" " + mangle(alias) + "->" + prefix(s) + "_" + mangle(alias) + "_count--;\n");
}
// release event monitors
for (EventTarget et : bed.eventTargets) {
for (EventSource es : et.sources) {
String part = "";
if (es.reference != null)
part = resolveContainer(es.reference, context, "");
String eventMonitor = "eventMonitor_" + prefix(s);
if (es.monitorIndex > 0)
eventMonitor += "_" + es.monitorIndex;
result.append(" removeMonitor (" + part + eventMonitor + ", this);\n");
}
}
}
result.append("}\n");
result.append("\n");
}
// Unit enterSimulation
if (bed.localReference.size() > 0) {
result.append("void " + ns + "enterSimulation ()\n");
result.append("{\n");
// String rather than EquationSet, because we may have references to several different instances of the same EquationSet, and all must be accounted
TreeSet<String> touched = new TreeSet<String>();
for (VariableReference r : bed.localReference) {
String container = resolveContainer(r, context, "");
if (touched.add(container))
result.append(" " + container + "refcount++;\n");
}
result.append("}\n");
result.append("\n");
}
// Unit leaveSimulation
{
result.append("void " + ns + "leaveSimulation ()\n");
result.append("{\n");
if (!bed.singleton) {
result.append(" " + containerOf(s, false, "") + mangle(s.name) + ".remove (this);\n");
}
TreeSet<String> touched = new TreeSet<String>();
for (VariableReference r : bed.localReference) {
String container = resolveContainer(r, context, "");
if (touched.add(container))
result.append(" " + container + "refcount--;\n");
}
result.append("}\n");
result.append("\n");
}
// Unit isFree
if (bed.refcount) {
result.append("bool " + ns + "isFree ()\n");
result.append("{\n");
result.append(" return refcount == 0;\n");
result.append("}\n");
result.append("\n");
}
// Unit init
if (bed.needLocalInit) {
result.append("void " + ns + "init ()\n");
result.append("{\n");
s.setInit(1);
for (Variable v : bed.localBufferedExternal) {
// Clear both buffered and regular values, so we can use a proper combiner during init.
result.append(" " + clearAccumulator(mangle("next_", v), v, context) + ";\n");
result.append(" " + clearAccumulator(mangle(v), v, context) + ";\n");
}
for (EventTarget et : bed.eventTargets) {
if (et.timeIndex >= 0) {
// Normal values are modulo 1 second. This initial value guarantees no match.
result.append(" eventTime" + et.timeIndex + " = 10;\n");
}
// Auxiliary variables get initialized as part of the regular list below.
}
if (!bed.localFlagType.isEmpty()) {
if (bed.liveFlag >= 0) {
if (bed.newborn >= 0) {
result.append(" flags |= (" + bed.localFlagType + ") 0x1 << " + bed.liveFlag + ";\n");
} else {
result.append(" flags = (" + bed.localFlagType + ") 0x1 << " + bed.liveFlag + ";\n");
}
} else {
if (bed.newborn < 0) {
result.append(" flags = 0;\n");
}
// else flags has already been initialized by Population::add()
}
}
// Initialize static objects
for (// non-optimized list, so hopefully all variables are covered
Variable v : // non-optimized list, so hopefully all variables are covered
bed.localInit) {
for (EquationEntry e : v.equations) {
prepareStaticObjects(e.expression, context, " ");
if (e.condition != null)
prepareStaticObjects(e.condition, context, " ");
}
}
// Compute variables
if (bed.lastT) {
result.append(" lastT = Simulator<" + T + ">::instance.currentEvent->t;\n");
}
s.simplify("$init", bed.localInit);
if (T.equals("int"))
EquationSet.determineExponentsSimplified(bed.localInit);
EquationSet.determineOrderInit(bed.localInit);
if (bed.localInit.contains(bed.dt)) {
result.append(" EventStep<" + T + "> * event = getEvent ();\n");
context.hasEvent = true;
result.append(" " + type(bed.dt) + " " + mangle(bed.dt) + ";\n");
}
for (// optimized list: only variables with equations that actually fire during init
Variable v : // optimized list: only variables with equations that actually fire during init
bed.localInit) {
multiconditional(v, context, " ");
}
// TODO: may need to deal with REPLACE for buffered variables. See internal.Part
if (bed.localInit.contains(bed.dt)) {
result.append(" if (" + mangle(bed.dt) + " != event->dt) setPeriod (" + mangle(bed.dt) + ");\n");
} else if (// implies that bed.dt exists and is constant
bed.setDt) {
result.append(" setPeriod (" + resolve(bed.dt.reference, context, false) + ");\n");
}
// instance counting
if (bed.trackN)
result.append(" " + containerOf(s, false, "") + mangle(s.name) + ".n++;\n");
for (String alias : bed.accountableEndpoints) {
result.append(" " + mangle(alias) + "->" + prefix(s) + "_" + mangle(alias) + "_count++;\n");
}
// Request event monitors
for (EventTarget et : bed.eventTargets) {
for (EventSource es : et.sources) {
String part = "";
if (es.reference != null)
part = resolveContainer(es.reference, context, "");
String eventMonitor = "eventMonitor_" + prefix(s);
if (es.monitorIndex > 0)
eventMonitor += "_" + es.monitorIndex;
result.append(" " + part + eventMonitor + ".push_back (this);\n");
}
}
// contained populations
if (s.parts.size() > 0) {
// If there are parts at all, then orderedParts must be filled in correctly. Otherwise it may be null.
for (EquationSet e : s.orderedParts) {
if (((BackendDataC) e.backendData).needGlobalInit) {
result.append(" " + mangle(e.name) + ".init ();\n");
}
}
}
s.setInit(0);
context.hasEvent = false;
result.append("}\n");
result.append("\n");
}
// Unit integrate
if (bed.needLocalIntegrate) {
result.append("void " + ns + "integrate ()\n");
result.append("{\n");
push_region(result, ns + "integrate()");
if (bed.localIntegrated.size() > 0) {
if (bed.lastT) {
result.append(" " + T + " dt = Simulator<" + T + ">::instance.currentEvent->t - lastT;\n");
} else {
result.append(" EventStep<" + T + "> * event = getEvent ();\n");
context.hasEvent = true;
result.append(" " + T + " dt = event->dt;\n");
}
// Note the resolve() call on the left-hand-side below has lvalue==false.
// Integration always takes place in the primary storage of a variable.
String pad = "";
if (bed.needLocalPreserve) {
pad = " ";
result.append(" if (preserve)\n");
result.append(" {\n");
for (Variable v : bed.localIntegrated) {
result.append(" " + resolve(v.reference, context, false) + " = preserve->" + mangle(v) + " + ");
int shift = v.derivative.exponent + bed.dt.exponent - Operator.MSB - v.exponent;
if (shift != 0 && T.equals("int")) {
result.append("(int) ((int64_t) " + resolve(v.derivative.reference, context, false) + " * dt" + context.printShift(shift) + ");\n");
} else {
result.append(resolve(v.derivative.reference, context, false) + " * dt;\n");
}
}
result.append(" }\n");
result.append(" else\n");
result.append(" {\n");
}
for (Variable v : bed.localIntegrated) {
result.append(pad + " " + resolve(v.reference, context, false) + " += ");
int shift = v.derivative.exponent + bed.dt.exponent - Operator.MSB - v.exponent;
if (shift != 0 && T.equals("int")) {
result.append("(int) ((int64_t) " + resolve(v.derivative.reference, context, false) + " * dt" + context.printShift(shift) + ");\n");
} else {
result.append(resolve(v.derivative.reference, context, false) + " * dt;\n");
}
}
if (bed.needLocalPreserve)
result.append(" }\n");
}
// contained populations
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalIntegrate) {
result.append(" " + mangle(e.name) + ".integrate ();\n");
}
}
context.hasEvent = false;
pop_region(result);
result.append("}\n");
result.append("\n");
}
// Unit update
if (bed.needLocalUpdate) {
result.append("void " + ns + "update ()\n");
result.append("{\n");
push_region(result, ns + "update()");
for (Variable v : bed.localBufferedInternalUpdate) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
s.simplify("$live", bed.localUpdate);
if (T.equals("int"))
EquationSet.determineExponentsSimplified(bed.localUpdate);
for (Variable v : bed.localUpdate) {
multiconditional(v, context, " ");
}
for (Variable v : bed.localBufferedInternalUpdate) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
// contained populations
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalUpdate) {
result.append(" " + mangle(e.name) + ".update ();\n");
}
}
pop_region(result);
result.append("}\n");
result.append("\n");
}
// Unit finalize
if (bed.needLocalFinalize) {
result.append("bool " + ns + "finalize ()\n");
result.append("{\n");
// contained populations
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalFinalize) {
// ignore return value
result.append(" " + mangle(e.name) + ".finalize ();\n");
}
}
// Early-out if we are already dead
if (// $live is stored in this part
bed.liveFlag >= 0) {
// early-out if we are already dead, to avoid another call to die()
result.append(" if (! (flags & (" + bed.localFlagType + ") 0x1 << " + bed.liveFlag + ")) return false;\n");
}
// Preemptively fetch current event
boolean needT = bed.eventSources.size() > 0 || s.lethalP || bed.localBufferedExternal.contains(bed.dt);
if (needT) {
result.append(" EventStep<" + T + "> * event = getEvent ();\n");
context.hasEvent = true;
}
// Events
for (EventSource es : bed.eventSources) {
EventTarget et = es.target;
String eventMonitor = "eventMonitor_" + prefix(et.container);
if (es.monitorIndex > 0)
eventMonitor += "_" + es.monitorIndex;
if (es.testEach) {
result.append(" for (Part * p : " + eventMonitor + ")\n");
result.append(" {\n");
result.append(" if (! p->eventTest (" + et.valueIndex + ")) continue;\n");
eventGenerate(" ", et, context, false);
result.append(" }\n");
} else // All monitors share same condition, so only test one.
{
result.append(" if (! " + eventMonitor + ".empty () && " + eventMonitor + "[0]->eventTest (" + et.valueIndex + "))\n");
result.append(" {\n");
if (// Each target instance may require a different delay.
es.delayEach) {
result.append(" for (auto p : " + eventMonitor + ")\n");
result.append(" {\n");
eventGenerate(" ", et, context, false);
result.append(" }\n");
} else // All delays are the same.
{
eventGenerate(" ", et, context, true);
}
result.append(" }\n");
}
}
int eventCount = bed.eventTargets.size();
if (eventCount > 0) {
result.append(" flags &= ~(" + bed.localFlagType + ") 0 << " + eventCount + ";\n");
}
// Finalize variables
if (bed.lastT) {
result.append(" lastT = Simulator<" + T + ">::instance.currentEvent->t;\n");
}
for (Variable v : bed.localBufferedExternal) {
if (v == bed.dt) {
result.append(" if (" + mangle("next_", v) + " != event->dt) setPeriod (" + mangle("next_", v) + ");\n");
} else {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
}
for (Variable v : bed.localBufferedExternalWrite) {
result.append(" " + clearAccumulator(mangle("next_", v), v, context) + ";\n");
}
if (bed.type != null) {
result.append(" switch (" + mangle("$type") + ")\n");
result.append(" {\n");
// Each "split" is one particular set of new parts to transform into.
// Each combination requires a separate piece of code. Thus, the outer
// structure here is a switch statement. Each case within the switch implements
// a particular combination of new parts. At this point, $type merely indicates
// which combination to process. Afterward, it will be set to an index within that
// combination, per the N2A language document.
int countSplits = s.splits.size();
for (int i = 0; i < countSplits; i++) {
ArrayList<EquationSet> split = s.splits.get(i);
// Check if $type = me. Ignore this particular case, since it is a null operation
if (split.size() == 1 && split.get(0) == s) {
continue;
}
result.append(" case " + (i + 1) + ":\n");
result.append(" {\n");
// indicates that this instance is one of the resulting parts
boolean used = false;
int countParts = split.size();
for (int j = 0; j < countParts; j++) {
EquationSet to = split.get(j);
if (to == s && !used) {
used = true;
result.append(" " + mangle("$type") + " = " + (j + 1) + ";\n");
} else {
result.append(" " + containerOf(s, false, "") + mangle(s.name) + "_2_" + mangle(to.name) + " (this, " + (j + 1) + ");\n");
}
}
if (used) {
result.append(" break;\n");
} else {
result.append(" die ();\n");
result.append(" return false;\n");
}
result.append(" }\n");
}
result.append(" }\n");
}
if (s.lethalP) {
// lethalP implies that $p exists, so no need to check for null
if (bed.p.hasAttribute("constant")) {
double pvalue = ((Scalar) ((Constant) bed.p.equations.first().expression).value).value;
if (pvalue != 0) {
// If $t' is exactly 1, then pow() is unnecessary here. However, that is a rare situation.
result.append(" if (pow (" + resolve(bed.p.reference, context, false) + ", " + resolve(bed.dt.reference, context, false));
if (context.useExponent) {
// second operand must have exponent=15
result.append(context.printShift(bed.dt.exponent - 15));
// exponentA
result.append(", " + bed.p.exponent);
// exponentResult
result.append(", " + bed.p.exponent);
}
result.append(") < uniform<" + T + "> ()");
// -1 is hard-coded from the Uniform function.
if (context.useExponent)
result.append(context.printShift(-1 - bed.p.exponent));
result.append(")\n");
}
} else {
if (bed.p.hasAttribute("temporary")) {
// Assemble a minimal set of expressions to evaluate $p
List<Variable> list = new ArrayList<Variable>();
for (Variable t : s.ordered) if (t.hasAttribute("temporary") && bed.p.dependsOn(t) != null)
list.add(t);
list.add(bed.p);
s.simplify("$live", list, bed.p);
if (T.equals("int"))
EquationSet.determineExponentsSimplified(list);
for (Variable v : list) {
multiconditional(v, context, " ");
}
}
result.append(" if (" + mangle("$p") + " <= 0 || " + mangle("$p") + " < " + context.print(1, bed.p.exponent) + " && pow (" + mangle("$p") + ", " + resolve(bed.dt.reference, context, false));
if (context.useExponent) {
result.append(context.printShift(bed.dt.exponent - 15));
result.append(", " + bed.p.exponent);
result.append(", " + bed.p.exponent);
}
result.append(") < uniform<" + T + "> ()");
if (context.useExponent)
result.append(context.printShift(-1 - bed.p.exponent));
result.append(")\n");
}
result.append(" {\n");
result.append(" die ();\n");
result.append(" return false;\n");
result.append(" }\n");
}
if (s.lethalConnection) {
for (ConnectionBinding c : s.connectionBindings) {
VariableReference r = s.resolveReference(c.alias + ".$live");
if (!r.variable.hasAttribute("constant")) {
result.append(" if (" + resolve(r, context, false, "", true) + " == 0)\n");
result.append(" {\n");
result.append(" die ();\n");
result.append(" return false;\n");
result.append(" }\n");
}
}
}
if (s.lethalContainer) {
VariableReference r = s.resolveReference("$up.$live");
if (!r.variable.hasAttribute("constant")) {
result.append(" if (" + resolve(r, context, false, "", true) + " == 0)\n");
result.append(" {\n");
result.append(" die ();\n");
result.append(" return false;\n");
result.append(" }\n");
}
}
result.append(" return true;\n");
context.hasEvent = false;
result.append("}\n");
result.append("\n");
}
// Unit updateDerivative
if (bed.needLocalUpdateDerivative) {
result.append("void " + ns + "updateDerivative ()\n");
result.append("{\n");
push_region(result, ns + "updateDerivative()");
for (Variable v : bed.localBufferedInternalDerivative) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
s.simplify("$live", bed.localDerivativeUpdate);
if (T.equals("int"))
EquationSet.determineExponentsSimplified(bed.localDerivativeUpdate);
for (Variable v : bed.localDerivativeUpdate) {
multiconditional(v, context, " ");
}
for (Variable v : bed.localBufferedInternalDerivative) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
// contained populations
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalUpdateDerivative) {
result.append(" " + mangle(e.name) + ".updateDerivative ();\n");
}
}
pop_region(result);
result.append("}\n");
result.append("\n");
}
// Unit finalizeDerivative
if (bed.needLocalFinalizeDerivative) {
result.append("void " + ns + "finalizeDerivative ()\n");
result.append("{\n");
for (Variable v : bed.localBufferedExternalDerivative) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
for (Variable v : bed.localBufferedExternalWriteDerivative) {
result.append(" " + clearAccumulator(mangle("next_", v), v, context) + ";\n");
}
// contained populations
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalFinalizeDerivative) {
result.append(" " + mangle(e.name) + ".finalizeDerivative ();\n");
}
}
result.append("}\n");
result.append("\n");
}
if (bed.needLocalPreserve) {
// Unit snapshot
result.append("void " + ns + "snapshot ()\n");
result.append("{\n");
if (bed.needLocalPreserve) {
result.append(" preserve = new Preserve;\n");
for (Variable v : bed.localIntegrated) {
result.append(" preserve->" + mangle(v) + " = " + mangle(v) + ";\n");
}
for (Variable v : bed.localDerivativePreserve) {
result.append(" preserve->" + mangle(v) + " = " + mangle(v) + ";\n");
}
for (Variable v : bed.localBufferedExternalWriteDerivative) {
result.append(" preserve->" + mangle("next_", v) + " = " + mangle("next_", v) + ";\n");
result.append(" " + clearAccumulator(mangle("next_", v), v, context) + ";\n");
}
}
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalPreserve) {
result.append(" " + mangle(e.name) + ".snapshot ();\n");
}
}
result.append("}\n");
result.append("\n");
// Unit restore
result.append("void " + ns + "restore ()\n");
result.append("{\n");
if (bed.needLocalPreserve) {
for (Variable v : bed.localDerivativePreserve) {
result.append(" " + mangle(v) + " = preserve->" + mangle(v) + ";\n");
}
for (Variable v : bed.localBufferedExternalWriteDerivative) {
result.append(" " + mangle("next_", v) + " = preserve->" + mangle("next_", v) + ";\n");
}
result.append(" delete preserve;\n");
result.append(" preserve = 0;\n");
}
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalPreserve) {
result.append(" " + mangle(e.name) + ".restore ();\n");
}
}
result.append("}\n");
result.append("\n");
}
if (bed.needLocalDerivative) {
// Unit pushDerivative
result.append("void " + ns + "pushDerivative ()\n");
result.append("{\n");
if (bed.localDerivative.size() > 0) {
result.append(" Derivative * temp = new Derivative;\n");
result.append(" temp->next = stackDerivative;\n");
result.append(" stackDerivative = temp;\n");
for (Variable v : bed.localDerivative) {
result.append(" temp->" + mangle(v) + " = " + mangle(v) + ";\n");
}
}
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalDerivative) {
result.append(" " + mangle(e.name) + ".pushDerivative ();\n");
}
}
result.append("}\n");
result.append("\n");
// Unit multiplyAddToStack
result.append("void " + ns + "multiplyAddToStack (" + T + " scalar)\n");
result.append("{\n");
for (Variable v : bed.localDerivative) {
result.append(" stackDerivative->" + mangle(v) + " += ");
if (T.equals("int")) {
result.append("(int) ((int64_t) " + mangle(v) + " * scalar >> " + (Operator.MSB - 1) + ");\n");
} else {
result.append(mangle(v) + " * scalar;\n");
}
}
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalDerivative) {
result.append(" " + mangle(e.name) + ".multiplyAddToStack (scalar);\n");
}
}
result.append("}\n");
result.append("\n");
// Unit multiply
result.append("void " + ns + "multiply (" + T + " scalar)\n");
result.append("{\n");
for (Variable v : bed.localDerivative) {
if (T.equals("int")) {
result.append(" " + mangle(v) + " = (int64_t) " + mangle(v) + " * scalar >> " + (Operator.MSB - 1) + ";\n");
} else {
result.append(" " + mangle(v) + " *= scalar;\n");
}
}
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalDerivative) {
result.append(" " + mangle(e.name) + ".multiply (scalar);\n");
}
}
result.append("}\n");
result.append("\n");
// Unit addToMembers
result.append("void " + ns + "addToMembers ()\n");
result.append("{\n");
if (bed.localDerivative.size() > 0) {
for (Variable v : bed.localDerivative) {
result.append(" " + mangle(v) + " += stackDerivative->" + mangle(v) + ";\n");
}
result.append(" Derivative * temp = stackDerivative;\n");
result.append(" stackDerivative = stackDerivative->next;\n");
result.append(" delete temp;\n");
}
for (EquationSet e : s.parts) {
if (((BackendDataC) e.backendData).needGlobalDerivative) {
result.append(" " + mangle(e.name) + ".addToMembers ();\n");
}
}
result.append("}\n");
result.append("\n");
}
// Unit setPart
if (s.connectionBindings != null) {
result.append("void " + ns + "setPart (int i, Part * part)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
result.append(" case " + c.index + ": " + mangle(c.alias) + " = (" + prefix(c.endpoint) + " *) part; return;\n");
}
result.append(" }\n");
result.append("}\n");
result.append("\n");
}
// Unit getPart
if (s.connectionBindings != null) {
result.append("Part<" + T + "> * " + ns + "getPart (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
result.append(" case " + c.index + ": return " + mangle(c.alias) + ";\n");
}
result.append(" }\n");
result.append(" return 0;\n");
result.append("}\n");
result.append("\n");
}
// Unit getCount
if (bed.accountableEndpoints.size() > 0) {
result.append("int " + ns + "getCount (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
if (bed.accountableEndpoints.contains(c.alias)) {
result.append(" case " + c.index + ": return " + mangle(c.alias) + "->" + prefix(s) + "_" + mangle(c.alias) + "_count;\n");
}
}
result.append(" }\n");
result.append(" return 0;\n");
result.append("}\n");
result.append("\n");
}
// Unit getProject
if (bed.hasProject) {
result.append("void " + ns + "getProject (int i, MatrixFixed<" + T + ",3,1> & xyz)\n");
result.append("{\n");
// $project is evaluated similar to $p. The result is not stored.
s.setConnect(1);
result.append(" switch (i)\n");
result.append(" {\n");
boolean needDefault = false;
for (ConnectionBinding c : s.connectionBindings) {
result.append(" case " + c.index + ":");
Variable project = s.find(new Variable(c.alias + ".$project"));
if (// fetch $xyz from endpoint
project == null) {
VariableReference fromXYZ = s.resolveReference(c.alias + ".$xyz");
if (fromXYZ.variable == null) {
needDefault = true;
} else {
if (// calculated value
fromXYZ.variable.hasAttribute("temporary")) {
result.append(" " + mangle(c.alias) + "->getXYZ (xyz); break;\n");
} else // stored value or "constant"
{
result.append(" xyz = " + resolve(fromXYZ, context, false) + "; break;\n");
}
}
} else // compute $project
{
// to complete the "case" line
result.append("\n");
result.append(" {\n");
if (// it could also be "constant", but no other type
project.hasAttribute("temporary")) {
// Assemble a minimal set of expressions to evaluate $project
List<Variable> list = new ArrayList<Variable>();
for (Variable t : s.ordered) {
if ((t.hasAttribute("temporary") || bed.localMembers.contains(t)) && project.dependsOn(t) != null)
list.add(t);
}
list.add(project);
s.simplify("$connect", list, project);
if (T.equals("int"))
EquationSet.determineExponentsSimplified(list);
for (Variable v : list) {
multiconditional(v, context, " ");
}
}
result.append(" xyz = " + resolve(project.reference, context, false) + ";\n");
result.append(" break;\n");
result.append(" }\n");
}
}
if (needDefault) {
result.append(" default:\n");
result.append(" xyz[0] = 0;\n");
result.append(" xyz[1] = 0;\n");
result.append(" xyz[2] = 0;\n");
}
result.append(" }\n");
result.append("}\n");
s.setConnect(0);
}
// Unit mapIndex
if (s.connectionMatrix != null && s.connectionMatrix.needsMapping) {
result.append("int " + ns + "mapIndex (int i, int rc)\n");
result.append("{\n");
Variable rc = new Variable("rc");
rc.reference = new VariableReference();
rc.reference.variable = rc;
rc.container = s;
rc.addAttribute("preexistent");
AccessVariable av = new AccessVariable();
av.reference = rc.reference;
ConnectionMatrix cm = s.connectionMatrix;
cm.rowMapping.replaceRC(av);
cm.colMapping.replaceRC(av);
result.append(" if (i == " + cm.rows.index + ") return ");
cm.rowMapping.rhs.render(context);
result.append(";\n");
result.append(" return ");
cm.colMapping.rhs.render(context);
result.append(";\n");
result.append("}\n");
result.append("\n");
}
// Unit getNewborn
if (bed.newborn >= 0) {
result.append("bool " + ns + "getNewborn ()\n");
result.append("{\n");
result.append(" return flags & (" + bed.localFlagType + ") 0x1 << " + bed.newborn + ";\n");
result.append("}\n");
result.append("\n");
}
// Unit getLive
if (bed.live != null && !bed.live.hasAttribute("constant")) {
result.append(T + " " + ns + "getLive ()\n");
result.append("{\n");
if (// "accessor" indicates whether or not $value is actually stored
!bed.live.hasAttribute("accessor")) {
result.append(" if (" + resolve(bed.live.reference, context, false, "", true) + " == 0) return 0;\n");
}
if (s.lethalConnection) {
for (ConnectionBinding c : s.connectionBindings) {
VariableReference r = s.resolveReference(c.alias + ".$live");
if (!r.variable.hasAttribute("constant")) {
result.append(" if (" + resolve(r, context, false, "", true) + " == 0) return 0;\n");
}
}
}
if (s.lethalContainer) {
VariableReference r = s.resolveReference("$up.$live");
if (!r.variable.hasAttribute("constant")) {
result.append(" if (" + resolve(r, context, false, "", true) + " == 0) return 0;\n");
}
}
result.append(" return 1;\n");
result.append("}\n");
result.append("\n");
}
// Unit getP
if (// Only connections need to provide an accessor
bed.p != null && s.connectionBindings != null) {
result.append(T + " " + ns + "getP ()\n");
result.append("{\n");
s.setConnect(1);
if (!bed.p.hasAttribute("constant")) {
// Assemble a minimal set of expressions to evaluate $p
List<Variable> list = new ArrayList<Variable>();
for (Variable t : s.ordered) {
if ((t.hasAttribute("temporary") || bed.localMembers.contains(t)) && bed.p.dependsOn(t) != null)
list.add(t);
}
list.add(bed.p);
s.simplify("$connect", list, bed.p);
if (T.equals("int"))
EquationSet.determineExponentsSimplified(list);
for (Variable v : list) {
multiconditional(v, context, " ");
}
}
result.append(" return " + resolve(bed.p.reference, context, false) + ";\n");
s.setConnect(0);
result.append("}\n");
result.append("\n");
}
// Unit getXYZ
if (// Connection targets need to provide an accessor.
bed.xyz != null && s.connected) {
result.append("void " + ns + "getXYZ (MatrixFixed<" + T + ",3,1> & xyz)\n");
result.append("{\n");
// If stored, then simply copy into the return value.
if (bed.xyz.hasAttribute("temporary")) {
// Assemble a minimal set of expressions to evaluate $xyz
List<Variable> list = new ArrayList<Variable>();
for (Variable t : s.ordered) if (t.hasAttribute("temporary") && bed.xyz.dependsOn(t) != null)
list.add(t);
list.add(bed.xyz);
// evaluate in $live phase, because endpoints already exist when connection is evaluated.
s.simplify("$live", list, bed.xyz);
if (T.equals("int"))
EquationSet.determineExponentsSimplified(list);
for (Variable v : list) {
multiconditional(v, context, " ");
}
}
result.append(" xyz = " + resolve(bed.xyz.reference, context, false) + ";\n");
result.append("}\n");
result.append("\n");
}
// Unit events
if (bed.eventTargets.size() > 0) {
result.append("bool " + ns + "eventTest (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (EventTarget et : bed.eventTargets) {
result.append(" case " + et.valueIndex + ":\n");
result.append(" {\n");
// Not safe or useful to simplify et.dependencies before emitting.
for (Variable v : et.dependencies) {
multiconditional(v, context, " ");
}
if (et.edge != EventTarget.NONZERO) {
result.append(" " + T + " before = ");
if (et.trackOne)
result.append(resolve(et.track.reference, context, false));
else
result.append(mangle(et.track.name));
result.append(";\n");
}
if (// This is a single variable, so check its value directly.
et.trackOne) {
result.append(" " + T + " after = " + resolve(et.track.reference, context, true) + ";\n");
} else // This is an expression, so use our private auxiliary variable.
{
result.append(" " + T + " after = ");
et.event.operands[0].render(context);
result.append(";\n");
if (et.edge != EventTarget.NONZERO) {
result.append(" " + mangle(et.track.name) + " = after;\n");
}
}
switch(et.edge) {
case EventTarget.NONZERO:
if (et.timeIndex >= 0) {
// Guard against multiple events in a given cycle.
// Note that other trigger types don't need this because they set the auxiliary variable,
// so the next test in the same cycle will no longer see change.
result.append(" if (after == 0) return false;\n");
if (T.equals("int")) {
// No need for modulo arithmetic. Rather, int time should be wrapped elsewhere.
result.append(" " + T + " moduloTime = Simulator<" + T + ">::instance.currentEvent->t;\n");
} else // float, double
{
// Wrap time at 1 second, to fit in float precision.
result.append(" " + T + " moduloTime = (" + T + ") fmod (Simulator<" + T + ">::instance.currentEvent->t, 1);\n");
}
result.append(" if (eventTime" + et.timeIndex + " == moduloTime) return false;\n");
result.append(" eventTime" + et.timeIndex + " = moduloTime;\n");
result.append(" return true;\n");
} else {
result.append(" return after != 0;\n");
}
break;
case EventTarget.CHANGE:
result.append(" return before != after;\n");
break;
case EventTarget.FALL:
result.append(" return before != 0 && after == 0;\n");
break;
case EventTarget.RISE:
default:
result.append(" return before == 0 && after != 0;\n");
}
result.append(" }\n");
}
result.append(" }\n");
result.append(" return false;\n");
result.append("}\n");
result.append("\n");
if (bed.needLocalEventDelay) {
result.append(T + " " + ns + "eventDelay (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (EventTarget et : bed.eventTargets) {
if (et.delay >= -1)
continue;
// Need to evaluate expression
result.append(" case " + et.valueIndex + ":\n");
result.append(" {\n");
for (Variable v : et.dependencies) {
multiconditional(v, context, " ");
}
result.append(" " + T + " result = ");
et.event.operands[1].render(context);
result.append(";\n");
result.append(" if (result < 0) return -1;\n");
result.append(" return result;\n");
result.append(" }\n");
}
result.append(" }\n");
result.append(" return -1;\n");
result.append("}\n");
result.append("\n");
}
result.append("void " + ns + "setLatch (int i)\n");
result.append("{\n");
result.append(" flags |= (" + bed.localFlagType + ") 0x1 << i;\n");
result.append("}\n");
result.append("\n");
if (bed.eventReferences.size() > 0) {
result.append("void " + ns + "finalizeEvent ()\n");
result.append("{\n");
for (Variable v : bed.eventReferences) {
String current = resolve(v.reference, context, false);
String buffered = resolve(v.reference, context, true);
result.append(" " + current);
switch(v.assignment) {
case Variable.ADD:
result.append(" += " + buffered + ";\n");
result.append(" " + zero(buffered, v) + ";\n");
break;
case Variable.MULTIPLY:
case Variable.DIVIDE:
{
// The current and buffered values of the variable have the same exponent.
// raw = exponentV + exponentV - MSB
// shift = raw - exponentV = exponentV - MSB
int shift = v.exponent - Operator.MSB;
if (shift != 0 && T.equals("int")) {
result.append(" = (int64_t) " + current + " * " + buffered + context.printShift(shift) + ";\n");
} else {
result.append(" *= " + buffered + ";\n");
}
result.append(" " + clear(buffered, v, 1, context) + ";\n");
break;
}
case Variable.MIN:
// TODO: Write elementwise min() and max() for matrices.
result.append(" = min (" + current + ", " + buffered + ");\n");
result.append(" " + clear(buffered, v, Double.POSITIVE_INFINITY, context) + ";\n");
break;
case Variable.MAX:
result.append(" = max (" + current + ", " + buffered + ");\n");
result.append(" " + clear(buffered, v, Double.NEGATIVE_INFINITY, context) + ";\n");
break;
default:
// REPLACE
result.append(" = " + buffered + ";\n");
break;
}
}
result.append("}\n");
result.append("\n");
}
}
// Unit path
if (bed.needLocalPath) {
result.append("void " + ns + "path (String & result)\n");
result.append("{\n");
if (// Not a connection, or a unary connection
s.connectionBindings == null || s.connectionBindings.size() == 1) {
// We assume that result is passed in as the empty string.
if (s.container != null) {
if (// Will our container provide a non-empty path?
((BackendDataC) s.container.backendData).needLocalPath) {
result.append(" container->path (result);\n");
result.append(" result += \"." + s.name + "\";\n");
} else {
result.append(" result = \"" + s.name + "\";\n");
}
}
if (bed.index != null) {
result.append(" result += __24index;\n");
} else if (s.connectionBindings != null) {
ConnectionBinding c = s.connectionBindings.get(0);
BackendDataC cbed = (BackendDataC) c.endpoint.backendData;
if (cbed.index != null)
result.append(" result += " + mangle(c.alias) + "->__24index;\n");
}
} else // binary or higher connection
{
boolean first = true;
boolean temp = false;
for (ConnectionBinding c : s.connectionBindings) {
if (first) {
result.append(" " + mangle(c.alias) + "->path (result);\n");
first = false;
} else {
if (!temp) {
result.append(" String temp;\n");
temp = true;
}
result.append(" " + mangle(c.alias) + "->path (temp);\n");
result.append(" result += \"-\";\n");
result.append(" result += temp;\n");
}
}
}
result.append("}\n");
result.append("\n");
}
// Unit conversions
Set<Conversion> conversions = s.getConversions();
for (Conversion pair : conversions) {
EquationSet source = pair.from;
EquationSet dest = pair.to;
boolean connectionSource = source.connectionBindings != null;
boolean connectionDest = dest.connectionBindings != null;
if (connectionSource != connectionDest) {
Backend.err.get().println("Can't change $type between connection and non-connection.");
throw new Backend.AbortRun();
// Why not? Because a connection *must* know the instances it connects, while
// a compartment cannot know those instances. Thus, one can never be converted
// to the other.
}
// The "2" functions only have local meaning, so they are never virtual.
// Must do everything init() normally does, including increment $n.
// Parameters:
// from -- the source part
// visitor -- the one managing the source part
// $type -- The integer index, in the $type expression, of the current target part. The target part's $type field will be initialized with this number (and zeroed after one cycle).
result.append("void " + ns + mangle(source.name) + "_2_" + mangle(dest.name) + " (" + mangle(source.name) + " * from, int " + mangle("$type") + ")\n");
result.append("{\n");
// if this is a recycled part, then clear() is called
result.append(" " + mangle(dest.name) + " * to = " + mangle(dest.name) + ".allocate ();\n");
if (connectionDest) {
// Match connection bindings
for (ConnectionBinding c : dest.connectionBindings) {
ConnectionBinding d = source.findConnection(c.alias);
if (d == null) {
Backend.err.get().println("Unfulfilled connection binding during $type change.");
throw new Backend.AbortRun();
}
result.append(" to->" + mangle(c.alias) + " = from->" + mangle(c.alias) + ";\n");
}
}
// TODO: Convert contained populations from matching populations in the source part?
result.append(" to->enterSimulation ();\n");
result.append(" getEvent ()->enqueue (to);\n");
// Match variables between the two sets.
// TODO: a match between variables should be marked as a dependency. This might change some "dummy" variables into stored values.
String[] forbiddenAttributes = new String[] { "global", "constant", "accessor", "reference", "temporary", "dummy", "preexistent" };
for (Variable v : dest.variables) {
if (v.name.equals("$type")) {
// initialize new part with its position in the $type split
result.append(" to->" + mangle(v) + " = " + mangle("$type") + ";\n");
continue;
}
if (v.hasAny(forbiddenAttributes))
continue;
Variable v2 = source.find(v);
if (v2 != null) {
result.append(" to->" + mangle(v) + " = " + resolve(v2.reference, context, false, "from->", false) + ";\n");
}
}
// Unless the user qualifies code with $type, the values just copied above will simply be overwritten.
result.append(" to->init ();\n");
result.append("}\n");
result.append("\n");
}
}
use of gov.sandia.n2a.backend.internal.InternalBackendData.EventSource in project n2a by frothga.
the class JobC method generateDeclarationsLocal.
public void generateDeclarationsLocal(EquationSet s, StringBuilder result) {
BackendDataC bed = (BackendDataC) s.backendData;
// Unit class
result.append("class " + prefix(s) + " : public PartTime<" + T + ">\n");
result.append("{\n");
result.append("public:\n");
// Unit buffers
if (bed.needLocalDerivative) {
result.append(" class Derivative\n");
result.append(" {\n");
result.append(" public:\n");
for (Variable v : bed.localDerivative) {
result.append(" " + type(v) + " " + mangle(v) + ";\n");
}
result.append(" Derivative * next;\n");
result.append(" };\n");
result.append("\n");
}
if (bed.needLocalPreserve) {
result.append(" class Preserve\n");
result.append(" {\n");
result.append(" public:\n");
for (Variable v : bed.localIntegrated) {
result.append(" " + type(v) + " " + mangle(v) + ";\n");
}
for (Variable v : bed.localDerivativePreserve) {
result.append(" " + type(v) + " " + mangle(v) + ";\n");
}
for (Variable v : bed.localBufferedExternalWriteDerivative) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
result.append(" };\n");
result.append("\n");
}
// Unit variables
if (bed.needLocalDerivative) {
result.append(" Derivative * stackDerivative;\n");
}
if (bed.needLocalPreserve) {
result.append(" Preserve * preserve;\n");
}
if (bed.pathToContainer == null) {
result.append(" " + prefix(s.container) + " * container;\n");
}
if (s.connectionBindings != null) {
for (ConnectionBinding c : s.connectionBindings) {
// we should be able to assume that s.container is non-null; ie: a connection should always operate in some larger container
result.append(" " + prefix(c.endpoint) + " * " + mangle(c.alias) + ";\n");
}
}
if (s.accountableConnections != null) {
for (EquationSet.AccountableConnection ac : s.accountableConnections) {
result.append(" int " + prefix(ac.connection) + "_" + mangle(ac.alias) + "_count;\n");
}
}
if (bed.refcount) {
result.append(" int refcount;\n");
}
if (bed.index != null) {
result.append(" int __24index;\n");
}
if (bed.lastT) {
// $lastT is for internal use only, so no need for __24 prefix.
result.append(" " + T + " lastT;\n");
}
for (Variable v : bed.localMembers) {
result.append(" " + type(v) + " " + mangle(v) + ";\n");
}
for (Variable v : bed.localBufferedExternal) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
for (EquationSet p : s.parts) {
result.append(" " + prefix(p) + "_Population " + mangle(p.name) + ";\n");
}
for (String columnName : bed.localColumns) {
result.append(" String " + columnName + ";\n");
}
for (EventSource es : bed.eventSources) {
String eventMonitor = "eventMonitor_" + prefix(es.target.container);
if (es.monitorIndex > 0)
eventMonitor += "_" + es.monitorIndex;
result.append(" std::vector<Part<" + T + "> *> " + eventMonitor + ";\n");
}
for (EventTarget et : bed.eventTargets) {
if (!et.trackOne && et.edge != EventTarget.NONZERO) {
result.append(" " + T + " " + mangle(et.track.name) + ";\n");
}
if (et.timeIndex >= 0) {
result.append(" " + T + " eventTime" + et.timeIndex + ";\n");
}
}
if (!bed.localFlagType.isEmpty()) {
result.append(" " + bed.localFlagType + " flags;\n");
}
int i = 0;
for (Delay d : bed.delays) {
d.index = i++;
result.append(" DelayBuffer<" + T + "> delay" + d.index + ";\n");
}
result.append("\n");
// Unit functions
if (bed.needLocalCtor) {
result.append(" " + prefix(s) + " ();\n");
}
if (bed.needLocalDtor) {
result.append(" virtual ~" + prefix(s) + " ();\n");
}
if (bed.localMembers.size() > 0) {
result.append(" virtual void clear ();\n");
}
if (s.container == null) {
result.append(" virtual void setPeriod (" + T + " dt);\n");
}
if (bed.needLocalDie) {
result.append(" virtual void die ();\n");
}
if (bed.localReference.size() > 0) {
result.append(" virtual void enterSimulation ();\n");
}
result.append(" virtual void leaveSimulation ();\n");
if (bed.refcount) {
result.append(" virtual bool isFree ();\n");
}
if (bed.needLocalInit) {
result.append(" virtual void init ();\n");
}
if (bed.needLocalIntegrate) {
result.append(" virtual void integrate ();\n");
}
if (bed.needLocalUpdate) {
result.append(" virtual void update ();\n");
}
if (bed.needLocalFinalize) {
result.append(" virtual bool finalize ();\n");
}
if (bed.needLocalUpdateDerivative) {
result.append(" virtual void updateDerivative ();\n");
}
if (bed.needLocalFinalizeDerivative) {
result.append(" virtual void finalizeDerivative ();\n");
}
if (bed.needLocalPreserve) {
result.append(" virtual void snapshot ();\n");
result.append(" virtual void restore ();\n");
}
if (bed.needLocalDerivative) {
result.append(" virtual void pushDerivative ();\n");
result.append(" virtual void multiplyAddToStack (" + T + " scalar);\n");
result.append(" virtual void multiply (" + T + " scalar);\n");
result.append(" virtual void addToMembers ();\n");
}
if (bed.live != null && !bed.live.hasAttribute("constant")) {
result.append(" virtual " + T + " getLive ();\n");
}
if (bed.xyz != null && s.connected) {
result.append(" virtual void getXYZ (MatrixFixed<" + T + ",3,1> & xyz);\n");
}
if (s.connectionBindings != null) {
if (bed.p != null) {
result.append(" virtual " + T + " getP ();\n");
}
if (bed.hasProject) {
result.append(" virtual void getProject (int i, MatrixFixed<" + T + ",3,1> & xyz);\n");
}
result.append(" virtual void setPart (int i, Part<" + T + "> * part);\n");
result.append(" virtual Part<" + T + "> * getPart (int i);\n");
}
if (bed.newborn >= 0) {
result.append(" virtual bool getNewborn ();\n");
}
if (s.connectionMatrix != null && s.connectionMatrix.needsMapping) {
result.append(" virtual int mapIndex (int i, int rc);\n");
}
if (bed.eventTargets.size() > 0) {
result.append(" virtual bool eventTest (int i);\n");
if (bed.needLocalEventDelay) {
result.append(" virtual " + T + " eventDelay (int i);\n");
}
result.append(" virtual void setLatch (int i);\n");
if (bed.eventReferences.size() > 0) {
result.append(" virtual void finalizeEvent ();\n");
}
}
if (bed.accountableEndpoints.size() > 0) {
result.append(" virtual int getCount (int i);\n");
}
if (bed.needLocalPath) {
result.append(" virtual void path (String & result);\n");
}
// Conversions
Set<Conversion> conversions = s.getConversions();
for (Conversion pair : conversions) {
EquationSet source = pair.from;
EquationSet dest = pair.to;
result.append(" void " + mangle(source.name) + "_2_" + mangle(dest.name) + " (" + mangle(source.name) + " * from, int " + mangle("$type") + ");\n");
}
// Unit class trailer
result.append("};\n");
result.append("\n");
}
use of gov.sandia.n2a.backend.internal.InternalBackendData.EventSource in project n2a by frothga.
the class Part method finish.
public boolean finish(Simulator simulator) {
InternalBackendData bed = (InternalBackendData) equations.backendData;
int populations = equations.parts.size();
for (int i = 0; i < populations; i++) ((Population) valuesObject[i]).finish(simulator);
if (bed.liveStorage == InternalBackendData.LIVE_STORED) {
// early-out if we are already dead, to avoid another call to die()
if (((Scalar) get(bed.live)).value == 0)
return false;
}
// when they are evaluated, which is generally different than the triggering event.
for (EventSource es : bed.eventSources) {
@SuppressWarnings("unchecked") List<Instance> monitors = (ArrayList<Instance>) valuesObject[es.monitorIndex];
if (monitors.size() == 0)
continue;
EventTarget eventType = es.target;
if (es.testEach) {
for (Instance i : monitors) {
if (i == null)
continue;
double delay = eventType.test(i, simulator);
// the trigger condition was not satisfied
if (delay < -1)
continue;
EventSpikeSingle spike;
if (// event was triggered, but timing is no-care
delay < 0) {
spike = new EventSpikeSingleLatch();
// queue immediately after current cycle, so latches get set for next full cycle
spike.t = simulator.currentEvent.t;
} else if (// process as close to current cycle as possible
delay == 0) {
// fully execute the event (not latch it)
spike = new EventSpikeSingle();
// queue immediately
spike.t = simulator.currentEvent.t;
} else {
// Is delay a quantum number of $t' steps?
double ratio = delay / event.dt;
int step = (int) Math.round(ratio);
if (Math.abs(ratio - step) < 1e-3) {
if (simulator.during)
spike = new EventSpikeSingleLatch();
else
spike = new EventSpikeSingle();
delay = step * event.dt;
} else {
spike = new EventSpikeSingle();
}
spike.t = simulator.currentEvent.t + delay;
}
spike.eventType = eventType;
spike.target = i;
simulator.queueEvent.add(spike);
}
} else // All monitors share same condition, so only test one.
{
double delay = -2;
for (Instance i : monitors) {
if (i == null)
continue;
delay = eventType.test(i, simulator);
break;
}
// the trigger condition was not satisfied
if (delay < -1)
continue;
if (// Each target instance may require a different delay.
es.delayEach) {
for (Instance i : monitors) {
if (i == null)
continue;
// This results in one redundant eval, of first entry in monitors. Not clear if it's worth the work to avoid this.
delay = eventType.delay(i, simulator);
EventSpikeSingle spike;
if (delay < 0) {
spike = new EventSpikeSingleLatch();
spike.t = simulator.currentEvent.t;
} else if (delay == 0) {
spike = new EventSpikeSingle();
spike.t = simulator.currentEvent.t;
} else {
double ratio = delay / event.dt;
int step = (int) Math.round(ratio);
if (Math.abs(ratio - step) < 1e-3) {
if (simulator.during)
spike = new EventSpikeSingleLatch();
else
spike = new EventSpikeSingle();
delay = step * event.dt;
} else {
spike = new EventSpikeSingle();
}
spike.t = simulator.currentEvent.t + delay;
}
spike.eventType = eventType;
spike.target = i;
simulator.queueEvent.add(spike);
}
} else // All delays are the same.
{
EventSpikeMulti spike;
if (delay < 0) {
spike = new EventSpikeMultiLatch();
spike.t = simulator.currentEvent.t;
} else if (delay == 0) {
spike = new EventSpikeMulti();
spike.t = simulator.currentEvent.t;
} else {
double ratio = delay / event.dt;
int step = (int) Math.round(ratio);
if (Math.abs(ratio - step) < 1e-3) {
if (simulator.during)
spike = new EventSpikeMultiLatch();
else
spike = new EventSpikeMulti();
delay = step * event.dt;
} else {
spike = new EventSpikeMulti();
}
spike.t = simulator.currentEvent.t + delay;
}
spike.eventType = eventType;
// We don't copy the array, just keep a reference to it. What could go wrong with this?
// If a part dies and tries to remove itself from the list while it is being used to deliver spikes,
// then we could get a null pointer exception. Solution is to synchronize access to the list.
// If a connection is born while the spike is in flight, one could argue that it shouldn't
// receive it, but one could also argue that it should. In nature these two things (spikes
// and synapse creation) occur at vastly different timescales. Wouldn't a nascent synapse
// receive spikes even as it is forming?
spike.targets = monitors;
simulator.queueEvent.add(spike);
}
}
}
// Other stuff
if (bed.lastT != null)
setFinal(bed.lastT, new Scalar(simulator.currentEvent.t));
for (Variable v : bed.localBufferedExternal) setFinal(v, getFinal(v));
clearExternalWriteBuffers(bed.localBufferedExternalWrite);
for (Integer i : bed.eventLatches) valuesFloat[i] = 0;
if (bed.type != null) {
int type = (int) ((Scalar) get(bed.type)).value;
if (type > 0) {
ArrayList<EquationSet> split = equations.splits.get(type - 1);
// indicates that this instance is one of the resulting parts
boolean used = false;
int countParts = split.size();
for (int i = 0; i < countParts; i++) {
EquationSet other = split.get(i);
Scalar splitPosition = new Scalar(i + 1);
if (other == equations && !used) {
used = true;
setFinal(bed.type, splitPosition);
} else {
InternalBackendData otherBed = (InternalBackendData) other.backendData;
// zeroes all variables
Part p = new Part(other, (Part) container);
// If this is a connection, keep the same bindings
Conversion conversion = bed.conversions.get(other);
if (conversion.bindings != null) {
for (int j = 0; j < conversion.bindings.length; j++) {
p.valuesObject[otherBed.endpoints + conversion.bindings[j]] = valuesObject[bed.endpoints + j];
}
}
event.enqueue(p);
p.resolve();
// Copy over variables
int count = conversion.from.size();
for (int v = 0; v < count; v++) {
Variable from = conversion.from.get(v);
Variable to = conversion.to.get(v);
p.setFinal(to, get(from));
}
// sets $type, which will appear during init cycle
p.setFinal(otherBed.type, splitPosition);
p.init(simulator);
}
}
if (!used) {
die();
return false;
}
}
}
if (equations.lethalP) {
double p;
if (bed.p.hasAttribute("temporary")) {
// Probe $p in run phase (as opposed to connect phase).
InstanceTemporaries temp = new InstanceTemporaries(this, simulator);
for (Variable v : bed.PdependenciesTemp) {
Type result = v.eval(temp);
if (result == null)
temp.set(v, v.type);
else
temp.set(v, result);
}
Type result = bed.p.eval(temp);
if (result == null)
p = 1;
else
p = ((Scalar) result).value;
} else {
p = ((Scalar) get(bed.p)).value;
}
if (p <= 0 || p < 1 && Math.pow(p, event.dt) < simulator.random.nextDouble()) {
die();
return false;
}
}
if (equations.lethalConnection) {
int count = equations.connectionBindings.size();
for (int i = 0; i < count; i++) {
if (!getPart(i).getLive()) {
die();
return false;
}
}
}
if (equations.lethalContainer) {
if (!((Part) container).getLive()) {
die();
return false;
}
}
return true;
}
use of gov.sandia.n2a.backend.internal.InternalBackendData.EventSource in project n2a by frothga.
the class Part method die.
public void die() {
InternalBackendData bed = (InternalBackendData) equations.backendData;
if (bed.fastExit) {
Simulator s = Simulator.instance.get();
s.stop = true;
s.fastExit = true;
// The bookkeeping below is no longer relevant.
return;
}
// set $live to false, if it is stored in this part
if (bed.liveStorage == InternalBackendData.LIVE_STORED) {
set(bed.live, new Scalar(0));
}
// update accountable endpoints
if (bed.count != null) {
int length = bed.count.length;
for (int i = 0; i < length; i++) {
if (bed.count[i] >= 0) {
Part p = (Part) valuesObject[bed.endpoints + i];
p.valuesFloat[bed.count[i]]--;
}
}
}
// Release event monitors
for (EventTarget et : bed.eventTargets) {
for (EventSource es : et.sources) {
// Don't bother with self-connection, since we are going away.
if (es.reference == null)
continue;
Part source = (Part) valuesObject[es.reference.index];
@SuppressWarnings("unchecked") ArrayList<Instance> monitors = (ArrayList<Instance>) source.valuesObject[es.monitorIndex];
int index = monitors.indexOf(this);
// Actually removing the element can cause a concurrent modification exception. Instead, the monitors array will get flushed next time an event processes it.
monitors.set(index, null);
}
}
((Population) container.valuesObject[bed.populationIndex]).remove(this);
}
use of gov.sandia.n2a.backend.internal.InternalBackendData.EventSource in project n2a by frothga.
the class JobC method generateDefinitions.
public void generateDefinitions(EquationSet s, StringBuilder result) throws Exception {
for (EquationSet p : s.parts) generateDefinitions(p, result);
CRenderer context = new CRenderer(result, s);
BackendDataC bed = (BackendDataC) s.backendData;
// -------------------------------------------------------------------
context.global = true;
// namespace for all functions associated with part s
String ns = prefix(s) + "_Population::";
// Population ctor
if (bed.needGlobalCtor) {
result.append(ns + prefix(s) + "_Population ()\n");
result.append("{\n");
if (bed.n != null) {
result.append(" n = 0;\n");
}
if (bed.index != null) {
result.append(" nextIndex = 0;\n");
}
if (bed.globalDerivative.size() > 0) {
result.append(" stackDerivative = 0;\n");
}
if (bed.needGlobalPreserve) {
result.append(" preserve = 0;\n");
}
result.append("}\n");
result.append("\n");
}
// Population dtor
if (bed.needGlobalDtor) {
result.append(ns + "~" + prefix(s) + "_Population ()\n");
result.append("{\n");
if (bed.globalDerivative.size() > 0) {
result.append(" while (stackDerivative)\n");
result.append(" {\n");
result.append(" Derivative * temp = stackDerivative;\n");
result.append(" stackDerivative = stackDerivative->next;\n");
result.append(" delete temp;\n");
result.append(" }\n");
}
if (bed.needGlobalPreserve) {
result.append(" if (preserve) delete preserve;\n");
}
result.append("}\n");
result.append("\n");
}
// Population create
result.append("Part * " + ns + "create ()\n");
result.append("{\n");
result.append(" " + prefix(s) + " * p = new " + prefix(s) + ";\n");
if (bed.pathToContainer == null)
result.append(" p->container = container;\n");
result.append(" return p;\n");
result.append("}\n");
result.append("\n");
// Population add / remove
if (bed.index != null) {
result.append("void " + ns + "add (Part * part)\n");
result.append("{\n");
result.append(" " + prefix(s) + " * p = (" + prefix(s) + " *) part;\n");
result.append(" if (p->__24index < 0) p->__24index = nextIndex++;\n");
if (bed.trackInstances) {
result.append(" p->before = &live;\n");
result.append(" p->after = live.after;\n");
result.append(" p->before->after = p;\n");
result.append(" p->after->before = p;\n");
}
result.append("}\n");
result.append("\n");
if (bed.trackInstances) {
result.append("void " + ns + "remove (Part * part)\n");
result.append("{\n");
result.append(" " + prefix(s) + " * p = (" + prefix(s) + " *) part;\n");
result.append(" if (p == old) old = old->after;\n");
result.append(" p->before->after = p->after;\n");
result.append(" p->after->before = p->before;\n");
result.append(" Population::remove (part);\n");
result.append("}\n");
result.append("\n");
}
}
// Population getTarget
if (s.connectionBindings != null) {
result.append("Population * " + ns + "getTarget (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
int i = 0;
for (ConnectionBinding c : s.connectionBindings) {
// TODO: Need a function to permute all descending paths to a class of populations.
// In the simplest form, it is a peer in our current container, so no iteration at all.
result.append(" case " + i++ + ": return & container->" + mangle(c.endpoint.name) + ";\n");
}
result.append(" default: return 0;\n");
result.append(" }\n");
result.append("}\n");
result.append("\n");
}
// Population init
result.append("void " + ns + "init ()\n");
result.append("{\n");
s.setInit(1);
// Zero out members
for (Variable v : bed.globalMembers) {
result.append(" " + mangle(v) + zero(v) + ";\n");
}
for (Variable v : bed.globalBufferedExternal) {
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
// declare buffer variables
for (Variable v : bed.globalBufferedInternal) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
// no separate $ and non-$ phases, because only $variables work at the population level
for (Variable v : bed.globalInit) {
multiconditional(v, context, " ");
}
// finalize
for (Variable v : bed.globalBuffered) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
// clear variables that may be written externally before first finalize()
for (Variable v : bed.globalBufferedExternalWrite) {
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
// create instances
if (bed.n != null) {
if (s.connectionBindings != null) {
Backend.err.get().println("$n is not applicable to connections");
throw new Backend.AbortRun();
}
result.append(" resize (" + resolve(bed.n.reference, context, false) + ");\n");
}
// make connections
if (s.connectionBindings != null) {
// queue to evaluate our connections
result.append(" simulator.connect (this);\n");
}
s.setInit(0);
result.append("};\n");
result.append("\n");
// Population integrate
if (bed.globalIntegrated.size() > 0) {
result.append("void " + ns + "integrate ()\n");
result.append("{\n");
result.append(" EventStep * event = getEvent ();\n");
context.hasEvent = true;
result.append(" float dt = event->dt;\n");
result.append(" if (preserve)\n");
result.append(" {\n");
for (Variable v : bed.globalIntegrated) {
result.append(" " + resolve(v.reference, context, false) + " = preserve->" + mangle(v) + " + " + resolve(v.derivative.reference, context, false) + " * dt;\n");
}
result.append(" }\n");
result.append(" else\n");
result.append(" {\n");
for (Variable v : bed.globalIntegrated) {
result.append(" " + resolve(v.reference, context, false) + " += " + resolve(v.derivative.reference, context, false) + " * dt;\n");
}
result.append(" }\n");
context.hasEvent = false;
result.append("};\n");
result.append("\n");
}
// Population update
if (bed.globalUpdate.size() > 0) {
result.append("void " + ns + "update ()\n");
result.append("{\n");
for (Variable v : bed.globalBufferedInternalUpdate) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
for (Variable v : bed.globalUpdate) {
multiconditional(v, context, " ");
}
for (Variable v : bed.globalBufferedInternalUpdate) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
if (s.connectionBindings == null) {
// TODO: find a better way to keep track of "new" parts. Problem: what if a part is "new" relative to several different connection types, and what if those connections are tested at different times?
result.append(" old = live.after;\n");
}
result.append("};\n");
result.append("\n");
}
// Population finalize
if (bed.needGlobalFinalize) {
result.append("bool " + ns + "finalize ()\n");
result.append("{\n");
if (// $n shares control with other specials, so must coordinate with them
bed.canResize && bed.n.derivative == null && bed.canGrowOrDie) {
if (// $n is explicitly assigned only once, so no need to monitor it for assigned values.
bed.n.hasAttribute("initOnly")) {
// -1 means to update $n from n. This can only be done after other parts are finalized, as they may impose structural dynamics via $p or $type.
result.append(" simulator.resize (this, -1);\n");
} else // $n may be assigned during the regular update cycle, so we need to monitor it.
{
result.append(" if (" + mangle("$n") + " != " + mangle("next_", "$n") + ") simulator.resize (this, " + mangle("next_", "$n") + ");\n");
result.append(" else simulator.resize (this, -1);\n");
}
}
for (Variable v : bed.globalBufferedExternal) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
for (Variable v : bed.globalBufferedExternalWrite) {
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
// Return value is generally ignored, except for top-level population.
boolean returnN = bed.needGlobalFinalizeN;
if (bed.canResize) {
if (bed.canGrowOrDie) {
if (// $n' exists
bed.n.derivative != null) {
// the rate of change in $n is pre-determined, so it relentlessly overrides any other structural dynamics
if (returnN) {
result.append(" if (n == 0) return false;\n");
returnN = false;
}
result.append(" simulator.resize (this, " + mangle("$n") + ");\n");
}
} else // $n is the only kind of structural dynamics, so simply do a resize() when needed
{
if (!bed.n.hasAttribute("initOnly")) {
if (returnN) {
result.append(" if (n == 0) return false;\n");
returnN = false;
}
result.append(" if (n != (int) " + mangle("$n") + ") simulator.resize (this, " + mangle("$n") + ");\n");
}
}
}
if (returnN) {
result.append(" return n;\n");
} else {
result.append(" return true;\n");
}
result.append("};\n");
result.append("\n");
}
// Population resize()
if (bed.n != null) {
result.append("void " + ns + "resize (int n)\n");
result.append("{\n");
if (bed.canResize && bed.canGrowOrDie && bed.n.derivative == null) {
result.append(" if (n < 0)\n");
result.append(" {\n");
result.append(" " + mangle("$n") + " = this->n;\n");
result.append(" return;\n");
result.append(" }\n");
result.append("\n");
}
result.append(" EventStep * event = container->getEvent ();\n");
result.append(" while (this->n < n)\n");
result.append(" {\n");
result.append(" Part * p = allocate ();\n");
result.append(" p->enterSimulation ();\n");
result.append(" event->enqueue (p);\n");
result.append(" p->init ();\n");
result.append(" }\n");
result.append("\n");
result.append(" Part * p = live.before;\n");
result.append(" while (this->n > n)\n");
result.append(" {\n");
result.append(" if (p == &live) N2A_THROW (\"Inconsistent $n\");\n");
result.append(" if (p->getLive ()) p->die ();\n");
result.append(" p = p->before;\n");
result.append(" }\n");
result.append("};\n");
result.append("\n");
}
// Population updateDerivative
if (bed.globalDerivativeUpdate.size() > 0) {
result.append("void " + ns + "updateDerivative ()\n");
result.append("{\n");
for (Variable v : bed.globalBufferedInternalDerivative) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
for (Variable v : bed.globalDerivativeUpdate) {
multiconditional(v, context, " ");
}
for (Variable v : bed.globalBufferedInternalDerivative) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
result.append("};\n");
result.append("\n");
}
// Population finalizeDerivative
if (bed.globalBufferedExternalDerivative.size() > 0) {
result.append("void " + ns + "finalizeDerivative ()\n");
result.append("{\n");
for (Variable v : bed.globalBufferedExternalDerivative) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
for (Variable v : bed.globalBufferedExternalWriteDerivative) {
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
result.append("};\n");
result.append("\n");
}
if (bed.needGlobalPreserve) {
// Population snapshot
result.append("void " + ns + "snapshot ()\n");
result.append("{\n");
result.append(" preserve = new Preserve;\n");
for (Variable v : bed.globalIntegrated) {
result.append(" preserve->" + mangle(v) + " = " + mangle(v) + ";\n");
}
for (Variable v : bed.globalDerivativePreserve) {
result.append(" preserve->" + mangle(v) + " = " + mangle(v) + ";\n");
}
for (Variable v : bed.globalBufferedExternalWriteDerivative) {
result.append(" preserve->" + mangle("next_", v) + " = " + mangle("next_", v) + ";\n");
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
result.append("};\n");
result.append("\n");
// Population restore
result.append("void " + ns + "restore ()\n");
result.append("{\n");
for (Variable v : bed.globalDerivativePreserve) {
result.append(" " + mangle(v) + " = preserve->" + mangle(v) + ";\n");
}
for (Variable v : bed.globalBufferedExternalWriteDerivative) {
result.append(" " + mangle("next_", v) + " = preserve->" + mangle("next_", v) + ";\n");
}
result.append(" delete preserve;\n");
result.append(" preserve = 0;\n");
result.append("};\n");
result.append("\n");
}
if (bed.globalDerivative.size() > 0) {
// Population pushDerivative
result.append("void " + ns + "pushDerivative ()\n");
result.append("{\n");
result.append(" Derivative * temp = new Derivative;\n");
result.append(" temp->_next = stackDerivative;\n");
result.append(" stackDerivative = temp;\n");
for (Variable v : bed.globalDerivative) {
result.append(" temp->" + mangle(v) + " = " + mangle(v) + ";\n");
}
result.append("};\n");
result.append("\n");
// Population multiplyAddToStack
result.append("void " + ns + "multiplyAddToStack (float scalar)\n");
result.append("{\n");
for (Variable v : bed.globalDerivative) {
result.append(" stackDerivative->" + mangle(v) + " += " + mangle(v) + " * scalar;\n");
}
result.append("};\n");
result.append("\n");
// Population multiply
result.append("void " + ns + "multiply (float scalar)\n");
result.append("{\n");
for (Variable v : bed.globalDerivative) {
result.append(" " + mangle(v) + " *= scalar;\n");
}
result.append("};\n");
result.append("\n");
// Population addToMembers
result.append("void " + ns + "addToMembers ()\n");
result.append("{\n");
for (Variable v : bed.globalDerivative) {
result.append(" " + mangle(v) + " += stackDerivative->" + mangle(v) + ";\n");
}
result.append(" Derivative * temp = stackDerivative;\n");
result.append(" stackDerivative = stackDerivative->next;\n");
result.append(" delete temp;\n");
result.append("};\n");
result.append("\n");
}
// Population getK
if (bed.needK) {
result.append("int " + ns + "getK (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
Variable v = s.find(new Variable(c.alias + ".$k"));
EquationEntry e = null;
if (v != null)
e = v.equations.first();
if (e != null) {
result.append(" case " + c.index + ": return ");
e.expression.render(context);
result.append(";\n");
}
}
result.append(" }\n");
result.append(" return 0;\n");
result.append("}\n");
result.append("\n");
}
// Population getMax
if (bed.needMax) {
result.append("int " + ns + "getMax (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
Variable v = s.find(new Variable(c.alias + ".$max"));
EquationEntry e = null;
if (v != null)
e = v.equations.first();
if (e != null) {
result.append(" case " + c.index + ": return ");
e.expression.render(context);
result.append(";\n");
}
}
result.append(" }\n");
result.append(" return 0;\n");
result.append("}\n");
result.append("\n");
}
// Population getMin
if (bed.needMin) {
result.append("int " + ns + "getMin (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
Variable v = s.find(new Variable(c.alias + ".$min"));
EquationEntry e = null;
if (v != null)
e = v.equations.first();
if (e != null) {
result.append(" case " + c.index + ": return ");
e.expression.render(context);
result.append(";\n");
}
}
result.append(" }\n");
result.append(" return 0;\n");
result.append("}\n");
result.append("\n");
}
// Population getRadius
if (bed.needRadius) {
result.append("int " + ns + "getRadius (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
Variable v = s.find(new Variable(c.alias + ".$radius"));
EquationEntry e = null;
if (v != null)
e = v.equations.first();
if (e != null) {
result.append(" case " + c.index + ": return ");
e.expression.render(context);
result.append(";\n");
}
}
result.append(" }\n");
result.append(" return 0;\n");
result.append("}\n");
result.append("\n");
}
if (bed.needGlobalPath) {
result.append("void " + ns + "path (String & result)\n");
result.append("{\n");
if (// Will our container provide a non-empty path?
((BackendDataC) s.container.backendData).needLocalPath) {
result.append(" container->path (result);\n");
result.append(" result += \"." + s.name + "\";\n");
} else {
result.append(" result = \"" + s.name + "\";\n");
}
result.append("}\n");
result.append("\n");
}
// -------------------------------------------------------------------
context.global = false;
ns = prefix(s) + "::";
// Unit ctor
if (bed.needLocalCtor || s.parts.size() > 0) {
result.append(ns + prefix(s) + " ()\n");
result.append("{\n");
if (bed.localDerivative.size() > 0) {
result.append(" stackDerivative = 0;\n");
}
if (bed.needLocalPreserve) {
result.append(" preserve = 0;\n");
}
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".container = this;\n");
}
if (s.accountableConnections != null) {
for (EquationSet.AccountableConnection ac : s.accountableConnections) {
result.append(" int " + prefix(ac.connection) + "_" + mangle(ac.alias) + "_count = 0;\n");
}
}
if (bed.refcount) {
result.append(" refcount = 0;\n");
}
if (bed.index != null) {
// -1 indicates that an index needs to be assigned. This should only be done once.
result.append(" __24index = -1;\n");
}
if (bed.localMembers.size() > 0) {
result.append(" clear ();\n");
}
result.append("}\n");
result.append("\n");
}
// Unit dtor
if (bed.needLocalDtor) {
result.append(ns + "~" + prefix(s) + " ()\n");
result.append("{\n");
if (bed.localDerivative.size() > 0) {
result.append(" while (stackDerivative)\n");
result.append(" {\n");
result.append(" Derivative * temp = stackDerivative;\n");
result.append(" stackDerivative = stackDerivative->next;\n");
result.append(" delete temp;\n");
result.append(" }\n");
}
if (bed.needLocalPreserve) {
result.append(" if (preserve) delete preserve;\n");
}
result.append("}\n");
result.append("\n");
}
// Unit clear
if (bed.localMembers.size() > 0) {
result.append("void " + ns + "clear ()\n");
result.append("{\n");
for (Variable v : bed.localMembers) {
result.append(" " + mangle(v) + zero(v) + ";\n");
}
result.append("}\n");
result.append("\n");
}
// Unit setPeriod
if (// instance of top-level population, so set period on wrapper whenever our period changes
s.container == null) {
result.append("void " + ns + "setPeriod (float dt)\n");
result.append("{\n");
result.append(" PartTime::setPeriod (dt);\n");
result.append(" if (container->visitor->event != visitor->event) container->setPeriod (dt);\n");
result.append("}\n");
result.append("\n");
}
// Unit die
if (bed.needLocalDie) {
result.append("void " + ns + "die ()\n");
result.append("{\n");
// tag part as dead
if (// $live is stored in this part
bed.liveFlag >= 0) {
result.append(" flags &= ~((" + bed.flagType + ") 0x1 << " + bed.liveFlag + ");\n");
}
// instance counting
if (s.connectionBindings == null)
result.append(" container->" + mangle(s.name) + ".n--;\n");
for (String alias : bed.accountableEndpoints) {
result.append(" " + mangle(alias) + "->" + prefix(s) + "_" + mangle(alias) + "_count--;\n");
}
// release event monitors
for (EventTarget et : bed.eventTargets) {
for (EventSource es : et.sources) {
String part = "";
if (es.reference != null)
part = resolveContainer(es.reference, context, "");
result.append(" removeMonitor (" + part + "eventMonitor_" + prefix(s) + ", this);\n");
}
}
result.append("}\n");
result.append("\n");
}
// Unit enterSimulation
if (bed.localReference.size() > 0) {
result.append("void " + ns + "enterSimulation ()\n");
result.append("{\n");
// String rather than EquationSet, because we may have references to several different instances of the same EquationSet, and all must be accounted
TreeSet<String> touched = new TreeSet<String>();
for (VariableReference r : bed.localReference) {
String container = resolveContainer(r, context, "");
if (touched.add(container))
result.append(" " + container + "refcount++;\n");
}
result.append("}\n");
result.append("\n");
}
// Unit leaveSimulation
{
result.append("void " + ns + "leaveSimulation ()\n");
result.append("{\n");
String container = "container->";
if (bed.pathToContainer != null)
container = mangle(bed.pathToContainer) + "->" + container;
result.append(" " + container + mangle(s.name) + ".remove (this);\n");
TreeSet<String> touched = new TreeSet<String>();
for (VariableReference r : bed.localReference) {
container = resolveContainer(r, context, "");
if (touched.add(container))
result.append(" " + container + "refcount--;\n");
}
result.append("}\n");
result.append("\n");
}
// Unit isFree
if (bed.refcount) {
result.append("bool " + ns + "isFree ()\n");
result.append("{\n");
result.append(" return refcount == 0;\n");
result.append("}\n");
result.append("\n");
}
// Unit init
if (bed.needLocalInit || s.parts.size() > 0) {
result.append("void " + ns + "init ()\n");
result.append("{\n");
s.setInit(1);
for (Variable v : bed.localBufferedExternal) {
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
for (EventTarget et : bed.eventTargets) {
if (et.track != null && et.track.name.startsWith("eventAux")) {
result.append(" " + et.track.name + " = 0;\n");
}
if (et.timeIndex >= 0) {
// Normal values are modulo 1 second. This initial value guarantees no match.
result.append(" eventTime" + et.timeIndex + " = 10;\n");
}
}
if (!bed.flagType.isEmpty()) {
if (bed.liveFlag < 0) {
result.append(" flags = 0;\n");
} else {
result.append(" flags = (" + bed.flagType + ") 0x1 << " + bed.liveFlag + ";\n");
}
}
// declare buffer variables
for (Variable v : bed.localBufferedInternal) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
// $variables
for (Variable v : bed.localInit) {
// TODO: This doesn't allow in temporaries that a $variable may depend on. See InternalBackendData sorting section for example of how to handle this better.
if (!v.name.startsWith("$"))
continue;
if (v == bed.live)
continue;
if (v == bed.type) {
// TODO: Work out logic of $type better. This trap should not be here.
Backend.err.get().println("$type must be conditional, and it must never be assigned during init.");
throw new Backend.AbortRun();
}
multiconditional(v, context, " ");
}
// finalize $variables
if (bed.localBuffered.contains(bed.dt)) {
result.append(" EventStep * event = getEvent ();\n");
context.hasEvent = true;
}
if (bed.lastT) {
result.append(" lastT = simulator.currentEvent->t;\n");
}
for (// more than just localBufferedInternal, because we must finalize members as well
Variable v : // more than just localBufferedInternal, because we must finalize members as well
bed.localBuffered) {
if (!v.name.startsWith("$"))
continue;
if (v == bed.dt) {
result.append(" if (" + mangle("next_", v) + " != event->dt) setPeriod (" + mangle("next_", v) + ");\n");
} else {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
}
// non-$variables
for (Variable v : bed.localInit) {
if (v.name.startsWith("$"))
continue;
multiconditional(v, context, " ");
}
// finalize non-$variables
for (Variable v : bed.localBuffered) {
if (v.name.startsWith("$"))
continue;
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
// clear variables that may be written externally before first finalize()
for (Variable v : bed.localBufferedExternalWrite) {
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
// instance counting
if (s.connectionBindings == null)
result.append(" container->" + mangle(s.name) + ".n++;\n");
for (String alias : bed.accountableEndpoints) {
result.append(" " + mangle(alias) + "->" + prefix(s) + "_" + mangle(alias) + "_count++;\n");
}
// Request event monitors
for (EventTarget et : bed.eventTargets) {
for (EventSource es : et.sources) {
String part = "";
if (es.reference != null)
part = resolveContainer(es.reference, context, "");
result.append(" " + part + "eventMonitor_" + prefix(s) + ".push_back (this);\n");
}
}
// contained populations
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".init ();\n");
}
s.setInit(0);
context.hasEvent = false;
result.append("}\n");
result.append("\n");
}
// Unit integrate
if (bed.localIntegrated.size() > 0 || s.parts.size() > 0) {
result.append("void " + ns + "integrate ()\n");
result.append("{\n");
if (bed.localIntegrated.size() > 0) {
if (bed.lastT) {
result.append(" float dt = simulator.currentEvent->t - lastT;\n");
} else {
result.append(" EventStep * event = getEvent ();\n");
context.hasEvent = true;
result.append(" float dt = event->dt;\n");
}
// Note the resolve() call on the left-hand-side below has lvalue==false.
// Integration always takes place in the primary storage of a variable.
result.append(" if (preserve)\n");
result.append(" {\n");
for (Variable v : bed.localIntegrated) {
result.append(" " + resolve(v.reference, context, false) + " = preserve->" + mangle(v) + " + " + resolve(v.derivative.reference, context, false) + " * dt;\n");
}
result.append(" }\n");
result.append(" else\n");
result.append(" {\n");
for (Variable v : bed.localIntegrated) {
result.append(" " + resolve(v.reference, context, false) + " += " + resolve(v.derivative.reference, context, false) + " * dt;\n");
}
result.append(" }\n");
}
// contained populations
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".integrate ();\n");
}
context.hasEvent = false;
result.append("}\n");
result.append("\n");
}
// Unit update
if (bed.localUpdate.size() > 0 || s.parts.size() > 0) {
result.append("void " + ns + "update ()\n");
result.append("{\n");
for (Variable v : bed.localBufferedInternalUpdate) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
for (Variable v : bed.localUpdate) {
multiconditional(v, context, " ");
}
for (Variable v : bed.localBufferedInternalUpdate) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
// contained populations
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".update ();\n");
}
result.append("}\n");
result.append("\n");
}
// Unit finalize
if (bed.needLocalFinalize || s.parts.size() > 0) {
result.append("bool " + ns + "finalize ()\n");
result.append("{\n");
// contained populations
for (EquationSet e : s.parts) {
// ignore return value
result.append(" " + mangle(e.name) + ".finalize ();\n");
}
// Early-out if we are already dead
if (// $live is stored in this part
bed.liveFlag >= 0) {
// early-out if we are already dead, to avoid another call to die()
result.append(" if (! (flags & (" + bed.flagType + ") 0x1 << " + bed.liveFlag + ")) return false;\n");
}
// Preemptively fetch current event
boolean needT = bed.eventSources.size() > 0;
for (Variable v : bed.localBufferedExternal) {
if (v == bed.dt)
needT = true;
}
if (needT) {
result.append(" EventStep * event = getEvent ();\n");
context.hasEvent = true;
}
// Events
boolean declaredFire = false;
for (EventSource es : bed.eventSources) {
EventTarget et = es.target;
String eventMonitor = "eventMonitor_" + prefix(et.container);
if (es.testEach) {
result.append(" for (Part * p : " + eventMonitor + ")\n");
result.append(" {\n");
result.append(" if (! p || ! p->eventTest (" + et.valueIndex + ")) continue;\n");
eventGenerate(" ", et, context, false);
result.append(" }\n");
} else // All monitors share same condition, so only test one.
{
if (declaredFire) {
result.append(" fire = false;\n");
} else {
result.append(" bool fire = false;\n");
declaredFire = true;
}
// Find first non-null part.
result.append(" for (auto p : " + eventMonitor + ")\n");
result.append(" {\n");
result.append(" if (p)\n");
result.append(" {\n");
result.append(" fire = p->eventTest (" + et.valueIndex + ");\n");
result.append(" break;\n");
result.append(" }\n");
result.append(" }\n");
result.append(" if (fire)\n");
result.append(" {\n");
if (// Each target instance may require a different delay.
es.delayEach) {
result.append(" for (auto p : " + eventMonitor + ")\n");
result.append(" {\n");
result.append(" if (! p) continue;\n");
eventGenerate(" ", et, context, false);
result.append(" }\n");
} else // All delays are the same.
{
eventGenerate(" ", et, context, true);
}
result.append(" }\n");
}
}
int eventCount = bed.eventTargets.size();
if (eventCount > 0) {
result.append(" flags &= ~(" + bed.flagType + ") 0 << " + eventCount + ";\n");
}
// Finalize variables
if (bed.lastT) {
result.append(" lastT = simulator.currentEvent.t;\n");
}
for (Variable v : bed.localBufferedExternal) {
if (v == bed.dt) {
result.append(" if (" + mangle("next_", v) + " != event->dt) setPeriod (" + mangle("next_", v) + ");\n");
} else {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
}
for (Variable v : bed.localBufferedExternalWrite) {
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
if (bed.type != null) {
result.append(" switch (" + mangle("$type") + ")\n");
result.append(" {\n");
// Each "split" is one particular set of new parts to transform into.
// Each combination requires a separate piece of code. Thus, the outer
// structure here is a switch statement. Each case within the switch implements
// a particular combination of new parts. At this point, $type merely indicates
// which combination to process. Afterward, it will be set to an index within that
// combination, per the N2A language document.
int countSplits = s.splits.size();
for (int i = 0; i < countSplits; i++) {
ArrayList<EquationSet> split = s.splits.get(i);
// Check if $type = me. Ignore this particular case, since it is a null operation
if (split.size() == 1 && split.get(0) == s) {
continue;
}
result.append(" case " + i + ":\n");
result.append(" {\n");
// indicates that this instance is one of the resulting parts
boolean used = false;
int countParts = split.size();
for (int j = 0; j < countParts; j++) {
EquationSet to = split.get(j);
if (to == s && !used) {
used = true;
result.append(" " + mangle("$type") + " = " + (j + 1) + ";\n");
} else {
String container = "container->";
if (bed.pathToContainer != null)
container = mangle(bed.pathToContainer) + "->" + container;
result.append(" " + container + mangle(s.name) + "_2_" + mangle(to.name) + " (this, " + (j + 1) + ");\n");
}
}
if (used) {
result.append(" break;\n");
} else {
result.append(" die ();\n");
result.append(" return false;\n");
}
result.append(" }\n");
}
result.append(" }\n");
}
// TODO: recognize when $p is contingent on $connect (formerly !$live) and don't emit those equations
if (s.lethalP) {
// lethalP implies that $p exists, so no need to check for null
if (bed.p.hasAttribute("temporary")) {
multiconditional(bed.p, context, " ");
}
if (bed.p.hasAttribute("constant")) {
double pvalue = ((Scalar) ((Constant) bed.p.equations.first().expression).value).value;
if (pvalue != 0)
result.append(" if (" + resolve(bed.p.reference, context, false) + " < uniform ())\n");
} else {
result.append(" if (" + mangle("$p") + " == 0 || " + mangle("$p") + " < 1 && " + mangle("$p") + " < uniform ())\n");
}
result.append(" {\n");
result.append(" die ();\n");
result.append(" return false;\n");
result.append(" }\n");
}
if (s.lethalConnection) {
for (ConnectionBinding c : s.connectionBindings) {
VariableReference r = s.resolveReference(c.alias + ".$live");
if (!r.variable.hasAttribute("constant")) {
result.append(" if (" + resolve(r, context, false, "", true) + " == 0)\n");
result.append(" {\n");
result.append(" die ();\n");
result.append(" return false;\n");
result.append(" }\n");
}
}
}
if (s.lethalContainer) {
VariableReference r = s.resolveReference("$up.$live");
if (!r.variable.hasAttribute("constant")) {
result.append(" if (" + resolve(r, context, false, "", true) + " == 0)\n");
result.append(" {\n");
result.append(" die ();\n");
result.append(" return false;\n");
result.append(" }\n");
}
}
result.append(" return true;\n");
context.hasEvent = false;
result.append("}\n");
result.append("\n");
}
// Unit updateDerivative
if (bed.localDerivativeUpdate.size() > 0 || s.parts.size() > 0) {
result.append("void " + ns + "updateDerivative ()\n");
result.append("{\n");
for (Variable v : bed.localBufferedInternalDerivative) {
result.append(" " + type(v) + " " + mangle("next_", v) + ";\n");
}
for (Variable v : bed.localDerivativeUpdate) {
multiconditional(v, context, " ");
}
for (Variable v : bed.localBufferedInternalDerivative) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
// contained populations
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".updateDerivative ();\n");
}
result.append("}\n");
result.append("\n");
}
// Unit finalizeDerivative
if (bed.localBufferedExternalDerivative.size() > 0 || s.parts.size() > 0) {
result.append("void " + ns + "finalizeDerivative ()\n");
result.append("{\n");
for (Variable v : bed.localBufferedExternalDerivative) {
result.append(" " + mangle(v) + " = " + mangle("next_", v) + ";\n");
}
for (Variable v : bed.localBufferedExternalWriteDerivative) {
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
// contained populations
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".finalizeDerivative ();\n");
}
result.append("}\n");
result.append("\n");
}
if (bed.needLocalPreserve || s.parts.size() > 0) {
// Unit snapshot
result.append("void " + ns + "snapshot ()\n");
result.append("{\n");
if (bed.needLocalPreserve) {
result.append(" preserve = new Preserve;\n");
for (Variable v : bed.localIntegrated) {
result.append(" preserve->" + mangle(v) + " = " + mangle(v) + ";\n");
}
for (Variable v : bed.localDerivativePreserve) {
result.append(" preserve->" + mangle(v) + " = " + mangle(v) + ";\n");
}
for (Variable v : bed.localBufferedExternalWriteDerivative) {
result.append(" preserve->" + mangle("next_", v) + " = " + mangle("next_", v) + ";\n");
result.append(" " + mangle("next_", v) + clearAccumulator(v, context) + ";\n");
}
}
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".snapshot ();\n");
}
result.append("}\n");
result.append("\n");
// Unit restore
result.append("void " + ns + "restore ()\n");
result.append("{\n");
if (bed.needLocalPreserve) {
for (Variable v : bed.localDerivativePreserve) {
result.append(" " + mangle(v) + " = preserve->" + mangle(v) + ";\n");
}
for (Variable v : bed.localBufferedExternalWriteDerivative) {
result.append(" " + mangle("next_", v) + " = preserve->" + mangle("next_", v) + ";\n");
}
result.append(" delete preserve;\n");
result.append(" preserve = 0;\n");
}
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".restore ();\n");
}
result.append("}\n");
result.append("\n");
}
if (bed.localDerivative.size() > 0 || s.parts.size() > 0) {
// Unit pushDerivative
result.append("void " + ns + "pushDerivative ()\n");
result.append("{\n");
if (bed.localDerivative.size() > 0) {
result.append(" Derivative * temp = new Derivative;\n");
result.append(" temp->next = stackDerivative;\n");
result.append(" stackDerivative = temp;\n");
for (Variable v : bed.localDerivative) {
result.append(" temp->" + mangle(v) + " = " + mangle(v) + ";\n");
}
}
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".pushDerivative ();\n");
}
result.append("}\n");
result.append("\n");
// Unit multiplyAddToStack
result.append("void " + ns + "multiplyAddToStack (float scalar)\n");
result.append("{\n");
for (Variable v : bed.localDerivative) {
result.append(" stackDerivative->" + mangle(v) + " += " + mangle(v) + " * scalar;\n");
}
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".multiplyAddToStack (scalar);\n");
}
result.append("}\n");
result.append("\n");
// Unit multiply
result.append("void " + ns + "multiply (float scalar)\n");
result.append("{\n");
for (Variable v : bed.localDerivative) {
result.append(" " + mangle(v) + " *= scalar;\n");
}
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".multiply (scalar);\n");
}
result.append("}\n");
result.append("\n");
// Unit addToMembers
result.append("void " + ns + "addToMembers ()\n");
result.append("{\n");
if (bed.localDerivative.size() > 0) {
for (Variable v : bed.localDerivative) {
result.append(" " + mangle(v) + " += stackDerivative->" + mangle(v) + ";\n");
}
result.append(" Derivative * temp = stackDerivative;\n");
result.append(" stackDerivative = stackDerivative->next;\n");
result.append(" delete temp;\n");
}
for (EquationSet e : s.parts) {
result.append(" " + mangle(e.name) + ".addToMembers ();\n");
}
result.append("}\n");
result.append("\n");
}
// Unit getLive
if (bed.live != null && !bed.live.hasAttribute("constant")) {
result.append("float " + ns + "getLive ()\n");
result.append("{\n");
if (// "accessor" indicates whether or not $value is actually stored
!bed.live.hasAttribute("accessor")) {
result.append(" if (" + resolve(bed.live.reference, context, false, "", true) + " == 0) return 0;\n");
}
if (s.lethalConnection) {
for (ConnectionBinding c : s.connectionBindings) {
VariableReference r = s.resolveReference(c.alias + ".$live");
if (!r.variable.hasAttribute("constant")) {
result.append(" if (" + resolve(r, context, false, "", true) + " == 0) return 0;\n");
}
}
}
if (s.lethalContainer) {
VariableReference r = s.resolveReference("$up.$live");
if (!r.variable.hasAttribute("constant")) {
result.append(" if (" + resolve(r, context, false, "", true) + " == 0) return 0;\n");
}
}
result.append(" return 1;\n");
result.append("}\n");
result.append("\n");
}
// Unit getP
if (s.connectionBindings != null) {
if (bed.p != null) {
result.append("float " + ns + "getP ()\n");
result.append("{\n");
s.setInit(1);
// set $live to 0
Set<String> liveAttributes = bed.live.attributes;
bed.live.attributes = null;
bed.live.addAttribute("constant");
// this should always be an equation we create; the user cannot declare $live (or $init for that matter)
EquationEntry e = bed.live.equations.first();
Scalar liveValue = (Scalar) ((Constant) e.expression).value;
liveValue.value = 0;
if (!bed.p.hasAttribute("constant")) {
// Generate any temporaries needed by $p
for (Variable t : s.variables) {
if (t.hasAttribute("temporary") && bed.p.dependsOn(t) != null) {
multiconditional(t, context, " ");
}
}
// $p is always calculated, because we are in a pseudo-init phase
multiconditional(bed.p, context, " ");
}
result.append(" return " + resolve(bed.p.reference, context, false) + ";\n");
// restore $live
bed.live.attributes = liveAttributes;
liveValue.value = 1;
s.setInit(0);
result.append("}\n");
result.append("\n");
}
}
// Unit getXYZ
if (// Connections can also have $xyz, but only compartments need to provide an accessor.
s.connectionBindings == null) {
Variable xyz = s.find(new Variable("$xyz", 0));
if (xyz != null) {
result.append("void " + ns + "getXYZ (Vector3 & xyz)\n");
result.append("{\n");
// If stored, then simply copy into the return value.
if (xyz.hasAttribute("temporary")) {
// Generate any temporaries needed by $xyz
for (Variable t : s.variables) {
if (t.hasAttribute("temporary") && xyz.dependsOn(t) != null) {
multiconditional(t, context, " ");
}
}
multiconditional(xyz, context, " ");
}
result.append(" xyz = " + resolve(xyz.reference, context, false) + ";\n");
result.append("}\n");
result.append("\n");
}
}
// Unit events
if (bed.eventTargets.size() > 0) {
result.append("bool " + ns + "eventTest (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (EventTarget et : bed.eventTargets) {
result.append(" case " + et.valueIndex + ":\n");
result.append(" {\n");
for (Variable v : et.dependencies) {
multiconditional(v, context, " ");
}
if (et.edge != EventTarget.NONZERO) {
result.append(" float before = " + resolve(et.track.reference, context, false) + ";\n");
}
if (// This is a single variable, so check its value directly.
et.trackOne) {
result.append(" float after = " + resolve(et.track.reference, context, true) + ";\n");
} else // This is an expression, so use our private auxiliary variable.
{
result.append(" float after = ");
et.event.operands[0].render(context);
result.append(";\n");
if (et.edge != EventTarget.NONZERO) {
result.append(" " + mangle(et.track) + " = after;\n");
}
}
switch(et.edge) {
case EventTarget.NONZERO:
if (et.timeIndex >= 0) {
// Guard against multiple events in a given cycle.
// Note that other trigger types don't need this because they set the auxiliary variable,
// so the next test in the same cycle will no longer see change.
result.append(" if (after == 0) return false;\n");
// Wrap time at 1 second, to fit in float precision.
result.append(" float moduloTime = (float) fmod (simulator.currentEvent->t, 1);\n");
result.append(" if (eventTime" + et.timeIndex + " == moduloTime) return false;\n");
result.append(" eventTime" + et.timeIndex + " = moduloTime;\n");
result.append(" return true;\n");
} else {
result.append(" return after != 0;\n");
}
break;
case EventTarget.CHANGE:
result.append(" return before != after;\n");
break;
case EventTarget.FALL:
result.append(" return before != 0 && after == 0;\n");
break;
case EventTarget.RISE:
default:
result.append(" return before == 0 && after != 0;\n");
}
result.append(" }\n");
}
result.append(" }\n");
result.append("}\n");
result.append("\n");
if (bed.needLocalEventDelay) {
result.append("float " + ns + "eventDelay (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (EventTarget et : bed.eventTargets) {
if (et.delay >= -1)
continue;
// Need to evaluate expression
result.append(" case " + et.valueIndex + ":\n");
result.append(" {\n");
for (Variable v : et.dependencies) {
multiconditional(v, context, " ");
}
result.append(" float result = ");
et.event.operands[1].render(context);
result.append(";\n");
result.append(" if (result < 0) return -1;\n");
result.append(" return result;\n");
result.append(" }\n");
}
result.append(" }\n");
result.append("}\n");
result.append("\n");
}
result.append("void " + ns + "setLatch (int i)\n");
result.append("{\n");
result.append(" flags |= (" + bed.flagType + ") 0x1 << i;\n");
result.append("}\n");
result.append("\n");
if (bed.eventReferences.size() > 0) {
result.append("void " + ns + "finalizeEvent ()\n");
result.append("{\n");
for (Variable v : bed.eventReferences) {
String current = resolve(v.reference, context, false);
String buffered = resolve(v.reference, context, true);
result.append(" " + current);
switch(v.assignment) {
case Variable.ADD:
result.append(" += " + buffered + ";\n");
result.append(" " + buffered + zero(v) + ";\n");
break;
case Variable.MULTIPLY:
case Variable.DIVIDE:
result.append(" *= " + buffered + ";\n");
result.append(" " + buffered + clear(v, 1, context) + ";\n");
break;
case Variable.MIN:
// TODO: Write elementwise min() and max() for matrices.
result.append(" = min (" + current + ", " + buffered + ");\n");
result.append(" " + buffered + clear(v, Double.POSITIVE_INFINITY, context) + ";\n");
break;
case Variable.MAX:
result.append(" = max (" + current + ", " + buffered + ");\n");
result.append(" " + buffered + clear(v, Double.NEGATIVE_INFINITY, context) + ";\n");
break;
default:
// REPLACE
result.append(" = " + buffered + ";\n");
break;
}
}
result.append("}\n");
result.append("\n");
}
}
// Unit project
if (bed.hasProjectFrom || bed.hasProjectTo) {
Variable xyz = s.find(new Variable("$xyz", 0));
boolean xyzStored = false;
boolean xyzTemporary = false;
if (xyz != null) {
xyzTemporary = xyz.hasAttribute("temporary");
xyzStored = !xyzTemporary;
}
result.append("void " + ns + "project (int i, int j, Vector3 & xyz)\n");
result.append("{\n");
String localXYZ = "xyz";
if (bed.hasProjectTo) {
localXYZ = "__24xyz";
// local temporary storage
if (!xyzStored)
result.append(" Vector3 " + mangle(xyz) + ";\n");
}
// TODO: Handle the case where $xyz is explicitly specified with an equation.
// This should override all instances of $projectFrom.
// Or should it merely be the default when $projectFrom is missing?
result.append(" switch (i)\n");
result.append(" {\n");
boolean needDefault = false;
for (ConnectionBinding c : s.connectionBindings) {
Variable projectFrom = s.find(new Variable(c.alias + ".$projectFrom"));
if (projectFrom == null) {
VariableReference fromXYZ = s.resolveReference(c.alias + ".$xyz");
if (fromXYZ.variable == null) {
needDefault = true;
} else {
result.append(" case " + c.index + ": " + localXYZ + " = " + resolve(fromXYZ, context, false) + "; break;\n");
}
} else {
result.append(" case " + c.index + ":\n");
result.append(" {\n");
if (// it could also be "constant", but no other type
projectFrom.hasAttribute("temporary")) {
for (Variable t : s.variables) {
if (t.hasAttribute("temporary") && projectFrom.dependsOn(t) != null) {
multiconditional(t, context, " ");
}
}
multiconditional(projectFrom, context, " ");
}
result.append(" " + localXYZ + " = " + resolve(projectFrom.reference, context, false) + ";\n");
result.append(" break;\n");
result.append(" }\n");
}
}
if (needDefault) {
result.append(" default:\n");
result.append(" " + localXYZ + "[0] = 0;\n");
result.append(" " + localXYZ + "[1] = 0;\n");
result.append(" " + localXYZ + "[2] = 0;\n");
}
result.append(" }\n");
result.append("\n");
if (xyzStored && !localXYZ.equals("__24xyz")) {
result.append(" __24xyz = " + localXYZ + ";\n");
}
if (bed.hasProjectTo) {
if (xyzTemporary)
xyz.removeAttribute("temporary");
result.append(" switch (j)\n");
result.append(" {\n");
needDefault = false;
for (ConnectionBinding c : s.connectionBindings) {
Variable projectTo = s.find(new Variable(c.alias + ".$projectTo"));
if (projectTo == null) {
needDefault = true;
} else {
result.append(" case " + c.index + ":\n");
result.append(" {\n");
if (projectTo.hasAttribute("temporary")) {
for (Variable t : s.variables) {
if (t.hasAttribute("temporary") && projectTo.dependsOn(t) != null) {
multiconditional(t, context, " ");
}
}
multiconditional(projectTo, context, " ");
}
result.append(" xyz = " + resolve(projectTo.reference, context, false) + ";\n");
result.append(" break;\n");
result.append(" }\n");
}
}
if (needDefault) {
result.append(" default:\n");
result.append(" xyz = __24xyz;\n");
}
result.append(" }\n");
if (xyzTemporary)
xyz.addAttribute("temporary");
}
result.append("}\n");
result.append("\n");
}
// Unit getCount
if (bed.accountableEndpoints.size() > 0) {
result.append("int " + ns + "getCount (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
if (bed.accountableEndpoints.contains(c.alias)) {
result.append(" case " + c.index + ": return " + mangle(c.alias) + "->" + prefix(s) + "_" + mangle(c.alias) + "_count;\n");
}
}
result.append(" }\n");
result.append(" return 0;\n");
result.append("}\n");
result.append("\n");
}
// Unit setPart
if (s.connectionBindings != null) {
result.append("void " + ns + "setPart (int i, Part * part)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
// TODO: This assumes that all the parts are children of the same container as the connection. Need to generalize so connections can cross branches of the containment hierarchy.
result.append(" case " + c.index + ": " + mangle(c.alias) + " = (" + prefix(c.endpoint) + " *) part; return;\n");
}
result.append(" }\n");
result.append("}\n");
result.append("\n");
}
// Unit getPart
if (s.connectionBindings != null) {
result.append("Part * " + ns + "getPart (int i)\n");
result.append("{\n");
result.append(" switch (i)\n");
result.append(" {\n");
for (ConnectionBinding c : s.connectionBindings) {
result.append(" case " + c.index + ": return " + mangle(c.alias) + ";\n");
}
result.append(" }\n");
result.append(" return 0;\n");
result.append("}\n");
result.append("\n");
}
if (bed.needLocalPath) {
result.append("void " + ns + "path (String & result)\n");
result.append("{\n");
if (s.connectionBindings == null) {
// We assume that result is passed in as the empty string.
if (s.container != null) {
if (// Will our container provide a non-empty path?
((BackendDataC) s.container.backendData).needLocalPath) {
result.append(" container->path (result);\n");
result.append(" result += \"." + s.name + "\";\n");
} else {
result.append(" result = \"" + s.name + "\";\n");
}
}
result.append(" result += __24index;\n");
} else {
boolean first = true;
for (ConnectionBinding c : s.connectionBindings) {
if (first) {
result.append(" " + mangle(c.alias) + ".path (result);\n");
result.append(" String temp;\n");
first = false;
} else {
result.append(" " + mangle(c.alias) + ".path (temp);\n");
result.append(" result += \"-\" + temp;\n");
}
}
}
result.append("}\n");
result.append("\n");
}
// Unit conversions
Set<Conversion> conversions = s.getConversions();
for (Conversion pair : conversions) {
EquationSet source = pair.from;
EquationSet dest = pair.to;
boolean connectionSource = source.connectionBindings != null;
boolean connectionDest = dest.connectionBindings != null;
if (connectionSource != connectionDest) {
Backend.err.get().println("Can't change $type between connection and non-connection.");
throw new Backend.AbortRun();
// Why not? Because a connection *must* know the instances it connects, while
// a compartment cannot know those instances. Thus, one can never be converted
// to the other.
}
// The "2" functions only have local meaning, so they are never virtual.
// Must do everything init() normally does, including increment $n.
// Parameters:
// from -- the source part
// visitor -- the one managing the source part
// $type -- The integer index, in the $type expression, of the current target part. The target part's $type field will be initialized with this number (and zeroed after one cycle).
result.append("void " + ns + mangle(source.name) + "_2_" + mangle(dest.name) + " (" + mangle(source.name) + " * from, int " + mangle("$type") + ")\n");
result.append("{\n");
result.append(" " + mangle(dest.name) + " * to = " + mangle(dest.name) + ".allocate ();\n");
if (connectionDest) {
// Match connection bindings
for (ConnectionBinding c : dest.connectionBindings) {
ConnectionBinding d = source.findConnection(c.alias);
if (d == null) {
Backend.err.get().println("Unfulfilled connection binding during $type change.");
throw new Backend.AbortRun();
}
result.append(" to->" + mangle(c.alias) + " = from->" + mangle(c.alias) + ";\n");
}
}
result.append(" to->enterSimulation ();\n");
result.append(" getEvent ()->enqueue (to);\n");
// sets all variables, so partially redundant with the following code ...
result.append(" to->init ();\n");
// TODO: Convert contained populations from matching populations in the source part?
// Match variables between the two sets.
// TODO: a match between variables should be marked as a dependency. This might change some "dummy" variables into stored values.
String[] forbiddenAttributes = new String[] { "global", "constant", "accessor", "reference", "temporary", "dummy", "preexistent" };
for (Variable v : dest.variables) {
if (v.name.equals("$type")) {
// initialize new part with its position in the $type split
result.append(" to->" + mangle(v) + " = " + mangle("$type") + ";\n");
continue;
}
if (v.hasAny(forbiddenAttributes)) {
continue;
}
Variable v2 = source.find(v);
if (v2 != null && v2.equals(v)) {
result.append(" to->" + mangle(v) + " = " + resolve(v2.reference, context, false, "from->", false) + ";\n");
}
}
result.append("}\n");
result.append("\n");
}
}
Aggregations