Search in sources :

Example 41 with Scalar

use of gov.sandia.n2a.language.type.Scalar in project n2a by frothga.

the class ExportJob method input.

public String input(MPart source, List<Element> parentElements, Synapse synapse) {
    String type = source.get("$metadata", "backend.lems.part");
    List<Element> inputElements = new ArrayList<Element>();
    List<String> skip = new ArrayList<String>();
    for (MNode c : source) {
        MPart p = (MPart) c;
        if (p.isPart()) {
            skip.add(p.key());
            input(p, inputElements, null);
        } else if (p.key().equals("times")) {
            skip.add("times");
            String[] pieces = p.get().split("\\[", 2)[1].split("]", 2)[0].split(";");
            for (int i = 0; i < pieces.length; i++) {
                if (Scalar.convert(pieces[i]) == Double.POSITIVE_INFINITY)
                    continue;
                Element spike = addElement("spike", inputElements);
                spike.setAttribute("id", String.valueOf(i));
                spike.setAttribute("time", biophysicalUnits(pieces[i]));
            }
        }
    }
    if (// decide between them
    type.contains("ramp") && type.contains("pulse")) {
        NameMap nameMap = partMap.importMap("rampGenerator");
        EquationSet sourceEquations = getEquations(source);
        Variable high1 = sourceEquations.find(new Variable(nameMap.importName("startAmplitude"), 0));
        Variable high2 = sourceEquations.find(new Variable(nameMap.importName("finishAmplitude"), 0));
        try {
            double value1 = ((Scalar) high1.eval(context)).value;
            double value2 = ((Scalar) high2.eval(context)).value;
            if (value1 == value2)
                type = "pulseGenerator";
            else
                type = "rampGenerator";
        } catch (// eval can fail if eqset contains fatal errors
        Exception e) {
            if (source.get("high2").equals("high1"))
                type = "pulseGenerator";
            else
                type = "rampGenerator";
        }
    } else if (// more action is needed
    type.contains(",")) {
        if (synapse != null) {
            String[] types = type.split(",");
            for (String t : types) {
                if (// prefix of both "Synapse" and "Synaptic"
                t.contains("Synap")) {
                    type = t;
                    break;
                }
            }
        }
        // remove any remaining ambiguity
        type = type.split(",")[0];
    }
    Element input = addElement(type, parentElements);
    String id;
    if (synapse == null) {
        id = source.get("$metadata", "backend.lems.id");
        if (id.isEmpty())
            id = source.key();
    } else {
        id = synapse.id;
        input.setAttribute("synapse", synapse.chainID);
        input.setAttribute("spikeTarget", "./" + synapse.chainID);
    }
    input.setAttribute("id", id);
    standalone(source, input, inputElements);
    genericPart(source, input, skip.toArray(new String[] {}));
    sequencer.append(input, inputElements);
    return id;
}
Also used : EquationSet(gov.sandia.n2a.eqset.EquationSet) MPart(gov.sandia.n2a.eqset.MPart) AccessVariable(gov.sandia.n2a.language.AccessVariable) Variable(gov.sandia.n2a.eqset.Variable) Element(org.w3c.dom.Element) ArrayList(java.util.ArrayList) NameMap(gov.sandia.n2a.backend.neuroml.PartMap.NameMap) MNode(gov.sandia.n2a.db.MNode) IncommensurableException(javax.measure.IncommensurableException) UnconvertibleException(javax.measure.UnconvertibleException) ParseException(gov.sandia.n2a.language.ParseException) Scalar(gov.sandia.n2a.language.type.Scalar)

Example 42 with Scalar

use of gov.sandia.n2a.language.type.Scalar 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

Scalar (gov.sandia.n2a.language.type.Scalar)42 Type (gov.sandia.n2a.language.Type)17 AccessVariable (gov.sandia.n2a.language.AccessVariable)14 Variable (gov.sandia.n2a.eqset.Variable)13 Matrix (gov.sandia.n2a.language.type.Matrix)11 Constant (gov.sandia.n2a.language.Constant)10 Operator (gov.sandia.n2a.language.Operator)10 Text (gov.sandia.n2a.language.type.Text)8 EquationSet (gov.sandia.n2a.eqset.EquationSet)6 ArrayList (java.util.ArrayList)6 Simulator (gov.sandia.n2a.backend.internal.Simulator)5 Instance (gov.sandia.n2a.language.type.Instance)5 MatrixDense (gov.sandia.n2a.language.type.MatrixDense)5 TreeSet (java.util.TreeSet)5 EventSource (gov.sandia.n2a.backend.internal.InternalBackendData.EventSource)4 EventTarget (gov.sandia.n2a.backend.internal.InternalBackendData.EventTarget)4 EquationEntry (gov.sandia.n2a.eqset.EquationEntry)3 VariableReference (gov.sandia.n2a.eqset.VariableReference)3 ConnectionBinding (gov.sandia.n2a.eqset.EquationSet.ConnectionBinding)2 EvaluationException (gov.sandia.n2a.language.EvaluationException)2