Search in sources :

Example 6 with EventTarget

use of gov.sandia.n2a.backend.internal.InternalBackendData.EventTarget in project n2a by frothga.

the class BackendDataC method analyze.

public void analyze(final EquationSet s) {
    System.out.println(s.name);
    for (// we want the sub-lists to be ordered correctly
    Variable v : // we want the sub-lists to be ordered correctly
    s.ordered) {
        String className = "null";
        if (v.type != null)
            className = v.type.getClass().getSimpleName();
        System.out.println("  " + v.nameString() + " " + v.attributeString() + " " + className);
        if (v.name.equals("$n") && v.order == 0)
            n = v;
        else if (v.name.equals("$p") && v.order == 0)
            p = v;
        else if (v.name.equals("$type"))
            type = v;
        else if (v.name.equals("$xyz") && v.order == 0)
            xyz = v;
        else if (v.name.equals("$index")) {
            index = v;
            // Don't let $index enter into any variable lists. Instead, always give it special treatment. In effect, it is a list of one.
            continue;
        } else if (v.name.equals("$live")) {
            live = v;
            // $live can never function as a regular variable because it is stored as a bit flag
            continue;
        } else if (v.name.equals("$t")) {
            if (v.order == 0)
                t = v;
            else if (v.order == 1)
                dt = v;
        }
        if (v.hasAny(new String[] { "constant", "accessor" }))
            continue;
        boolean initOnly = v.hasAttribute("initOnly");
        boolean derivativeOrDependency = v.hasAttribute("derivativeOrDependency");
        boolean temporary = v.hasAttribute("temporary");
        boolean unusedTemporary = temporary && !v.hasUsers();
        boolean updates = !initOnly && v.equations.size() > 0 && (v.derivative == null || v.hasAttribute("updates"));
        if (v.hasAttribute("global")) {
            if (updates && !unusedTemporary)
                globalUpdate.add(v);
            if (derivativeOrDependency)
                globalDerivativeUpdate.add(v);
            if (!v.hasAttribute("reference")) {
                if (!unusedTemporary)
                    globalInit.add(v);
                if (!temporary && !v.hasAttribute("dummy")) {
                    if (!v.hasAttribute("preexistent")) {
                        globalMembers.add(v);
                        // check if v.usedBy contains any variable that is not v's integral
                        if (derivativeOrDependency && v.derivative == null && v.usedBy != null) {
                            for (Object o : v.usedBy) {
                                if (o instanceof Variable && ((Variable) o).derivative != v) {
                                    globalDerivativePreserve.add(v);
                                    break;
                                }
                            }
                        }
                    }
                    boolean external = false;
                    if (!initOnly) {
                        if (v.name.equals("$t")) {
                            if (v.order > 1)
                                globalDerivative.add(v);
                        } else // any other variable
                        {
                            if (v.order > 0)
                                globalDerivative.add(v);
                        }
                        // The integration step has roughly the same effect as an external write.
                        if (v.hasAttribute("externalWrite") || v.assignment != Variable.REPLACE) {
                            external = true;
                            globalBufferedExternalWrite.add(v);
                            if (derivativeOrDependency)
                                globalBufferedExternalWriteDerivative.add(v);
                        }
                        if (external || (v.hasAttribute("externalRead") && updates)) {
                            external = true;
                            globalBufferedExternal.add(v);
                            if (derivativeOrDependency)
                                globalBufferedExternalDerivative.add(v);
                        }
                    }
                    if (external || v.hasAttribute("cycle")) {
                        globalBuffered.add(v);
                        if (!external) {
                            globalBufferedInternal.add(v);
                            if (!initOnly) {
                                globalBufferedInternalUpdate.add(v);
                                if (derivativeOrDependency)
                                    globalBufferedInternalDerivative.add(v);
                            }
                        }
                    }
                }
            }
        } else // local
        {
            if (updates && !unusedTemporary)
                localUpdate.add(v);
            if (derivativeOrDependency)
                localDerivativeUpdate.add(v);
            if (v.hasAttribute("reference")) {
                if (v.reference.variable.container.canDie())
                    localReference.add(v.reference);
            } else {
                if (!unusedTemporary)
                    localInit.add(v);
                if (!temporary && !v.hasAttribute("dummy")) {
                    if (!v.hasAttribute("preexistent")) {
                        localMembers.add(v);
                        if (derivativeOrDependency && v.derivative == null && v.usedBy != null) {
                            for (Object o : v.usedBy) {
                                if (o instanceof Variable && ((Variable) o).derivative != v) {
                                    localDerivativePreserve.add(v);
                                    break;
                                }
                            }
                        }
                    }
                    boolean external = false;
                    if (!initOnly) {
                        if (v.name.equals("$t")) {
                            if (v.order > 1)
                                localDerivative.add(v);
                        } else {
                            if (v.order > 0)
                                localDerivative.add(v);
                        }
                        if (v.hasAttribute("externalWrite") || v.assignment != Variable.REPLACE) {
                            external = true;
                            localBufferedExternalWrite.add(v);
                            if (derivativeOrDependency)
                                localBufferedExternalWriteDerivative.add(v);
                        }
                        if (external || (v.hasAttribute("externalRead") && updates)) {
                            external = true;
                            localBufferedExternal.add(v);
                            if (derivativeOrDependency)
                                localBufferedExternalDerivative.add(v);
                        }
                    }
                    if (external || v.hasAttribute("cycle")) {
                        localBuffered.add(v);
                        if (!external) {
                            localBufferedInternal.add(v);
                            if (!initOnly) {
                                localBufferedInternalUpdate.add(v);
                                if (derivativeOrDependency)
                                    localBufferedInternalDerivative.add(v);
                            }
                        }
                    }
                }
            }
        }
    }
    for (// we need these to be in order by differential level, not by dependency
    Variable v : // we need these to be in order by differential level, not by dependency
    s.variables) {
        if (v.derivative != null && !v.hasAny("constant", "initOnly")) {
            if (v.hasAttribute("global"))
                globalIntegrated.add(v);
            else
                localIntegrated.add(v);
        }
    }
    // Purge any lists that consist solely of temporaries, as they accomplish nothing.
    for (List<Variable> list : Arrays.asList(globalUpdate, globalDerivativeUpdate, globalInit, globalIntegrated, localUpdate, localDerivativeUpdate, localInit, localIntegrated)) {
        boolean allTemporary = true;
        for (Variable v : list) if (!v.hasAttribute("temporary"))
            allTemporary = false;
        if (allTemporary)
            list.clear();
    }
    if (s.connectionBindings != null) {
        for (ConnectionBinding c : s.connectionBindings) {
            Variable v = s.find(new Variable(c.alias + ".$max"));
            if (v == null)
                v = s.find(new Variable(c.alias + ".$min"));
            if (v != null)
                accountableEndpoints.add(c.alias);
        }
    }
    refcount = s.referenced && s.canDie();
    needGlobalPreserve = globalIntegrated.size() > 0 || globalDerivativePreserve.size() > 0 || globalBufferedExternalWriteDerivative.size() > 0;
    needGlobalDtor = needGlobalPreserve || globalDerivative.size() > 0;
    needGlobalCtor = needGlobalDtor || index != null || n != null;
    // Works correctly even if n is null.
    canResize = globalMembers.contains(n);
    trackInstances = s.connected || s.needInstanceTracking || canResize;
    canGrowOrDie = s.lethalP || s.lethalType || s.canGrow();
    needGlobalFinalizeN = s.container == null && (canResize || canGrowOrDie);
    needGlobalFinalize = globalBufferedExternal.size() > 0 || needGlobalFinalizeN || (canResize && (canGrowOrDie || !n.hasAttribute("initOnly")));
    if (s.connectionBindings != null) {
        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)
                needK = true;
            v = s.find(new Variable(c.alias + ".$max"));
            e = null;
            if (v != null)
                e = v.equations.first();
            if (e != null)
                needMax = true;
            v = s.find(new Variable(c.alias + ".$min"));
            e = null;
            if (v != null)
                e = v.equations.first();
            if (e != null)
                needMin = true;
            v = s.find(new Variable(c.alias + ".$radius"));
            e = null;
            if (v != null)
                e = v.equations.first();
            if (e != null)
                needRadius = true;
            if (s.find(new Variable(c.alias + ".$projectFrom")) != null)
                hasProjectFrom = true;
            if (s.find(new Variable(c.alias + ".$projectTo")) != null)
                hasProjectTo = true;
        }
    }
    if (eventTargets.size() > 0) {
        for (EventTarget et : eventTargets) {
            if (et.delay < -1) {
                needLocalEventDelay = true;
                break;
            }
        }
    }
    int flagCount = eventTargets.size();
    if (live != null && !live.hasAny(new String[] { "constant", "accessor" }))
        liveFlag = flagCount++;
    if (flagCount == 0)
        flagType = "";
    else if (flagCount <= 8)
        flagType = "uint8_t";
    else if (flagCount <= 16)
        flagType = "uint16_t";
    else if (flagCount <= 32)
        flagType = "uint32_t";
    else if (flagCount <= 64)
        flagType = "uint64_t";
    else {
        Backend.err.get().println("ERROR: Too many flags to fit in basic integer type");
        throw new Backend.AbortRun();
    }
    needLocalPreserve = localIntegrated.size() > 0 || localDerivativePreserve.size() > 0 || localBufferedExternalWriteDerivative.size() > 0;
    needLocalDtor = needLocalPreserve || localDerivative.size() > 0;
    needLocalCtor = needLocalDtor || s.accountableConnections != null || refcount || index != null || localMembers.size() > 0;
    needLocalDie = s.canDie() && (liveFlag >= 0 || s.connectionBindings == null || accountableEndpoints.size() > 0 || eventTargets.size() > 0);
    needLocalInit = s.connectionBindings == null || localInit.size() > 0 || accountableEndpoints.size() > 0 || eventTargets.size() > 0;
    needLocalFinalize = localBufferedExternal.size() > 0 || type != null || s.canDie();
}
Also used : Variable(gov.sandia.n2a.eqset.Variable) ConnectionBinding(gov.sandia.n2a.eqset.EquationSet.ConnectionBinding) EquationEntry(gov.sandia.n2a.eqset.EquationEntry) EventTarget(gov.sandia.n2a.backend.internal.InternalBackendData.EventTarget)

Example 7 with EventTarget

use of gov.sandia.n2a.backend.internal.InternalBackendData.EventTarget 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");
    }
}
Also used : EquationSet(gov.sandia.n2a.eqset.EquationSet) Variable(gov.sandia.n2a.eqset.Variable) AccessVariable(gov.sandia.n2a.language.AccessVariable) VariableReference(gov.sandia.n2a.eqset.VariableReference) ConnectionBinding(gov.sandia.n2a.eqset.EquationSet.ConnectionBinding) Conversion(gov.sandia.n2a.eqset.EquationSet.Conversion) AbortRun(gov.sandia.n2a.plugins.extpoints.Backend.AbortRun) Scalar(gov.sandia.n2a.language.type.Scalar) EventSource(gov.sandia.n2a.backend.internal.InternalBackendData.EventSource) TreeSet(java.util.TreeSet) EquationEntry(gov.sandia.n2a.eqset.EquationEntry) EventTarget(gov.sandia.n2a.backend.internal.InternalBackendData.EventTarget)

Aggregations

EventTarget (gov.sandia.n2a.backend.internal.InternalBackendData.EventTarget)7 EventSource (gov.sandia.n2a.backend.internal.InternalBackendData.EventSource)6 Variable (gov.sandia.n2a.eqset.Variable)5 Scalar (gov.sandia.n2a.language.type.Scalar)4 EquationSet (gov.sandia.n2a.eqset.EquationSet)3 ConnectionBinding (gov.sandia.n2a.eqset.EquationSet.ConnectionBinding)3 Instance (gov.sandia.n2a.language.type.Instance)3 ArrayList (java.util.ArrayList)3 EquationEntry (gov.sandia.n2a.eqset.EquationEntry)2 Conversion (gov.sandia.n2a.eqset.EquationSet.Conversion)2 AccessVariable (gov.sandia.n2a.language.AccessVariable)2 Type (gov.sandia.n2a.language.Type)2 Conversion (gov.sandia.n2a.backend.internal.InternalBackendData.Conversion)1 VariableReference (gov.sandia.n2a.eqset.VariableReference)1 Matrix (gov.sandia.n2a.language.type.Matrix)1 AbortRun (gov.sandia.n2a.plugins.extpoints.Backend.AbortRun)1 TreeSet (java.util.TreeSet)1