diff --git a/N2A/src/gov/sandia/n2a/backend/c/RendererC.java b/N2A/src/gov/sandia/n2a/backend/c/RendererC.java index 0cade8f2..05200b64 100644 --- a/N2A/src/gov/sandia/n2a/backend/c/RendererC.java +++ b/N2A/src/gov/sandia/n2a/backend/c/RendererC.java @@ -225,13 +225,6 @@ else if (useExponent) if (op instanceof Delay) { Delay d = (Delay) op; - if (d.operands.length == 1) - { - result.append ("("); - d.operands[0].render (this); - result.append (")"); - return true; - } result.append ("delay" + d.index + ".step (" + job.SIMULATOR + "currentEvent->t, "); d.operands[1].render (this); result.append (", "); diff --git a/N2A/src/gov/sandia/n2a/backend/internal/InstanceTemporaries.java b/N2A/src/gov/sandia/n2a/backend/internal/InstanceTemporaries.java index 9ae18c89..60a107cd 100644 --- a/N2A/src/gov/sandia/n2a/backend/internal/InstanceTemporaries.java +++ b/N2A/src/gov/sandia/n2a/backend/internal/InstanceTemporaries.java @@ -16,7 +16,7 @@ /** Provides a set of temporary values for use within a single function. - This handles formal temporaries (those with =: as an assignment), as well as buffering for cyclic dependencies. + This handles formal temporaries (those with =; as an assignment), as well as buffering for cyclic dependencies. Note: buffered variables that are accessed by external equation sets ("externalRead" and "externalWrite") have two entries in the main table of values, since many different function invocations may access them before they are finalized. **/ diff --git a/N2A/src/gov/sandia/n2a/eqset/EquationSet.java b/N2A/src/gov/sandia/n2a/eqset/EquationSet.java index 6f9b1bb6..b791f615 100644 --- a/N2A/src/gov/sandia/n2a/eqset/EquationSet.java +++ b/N2A/src/gov/sandia/n2a/eqset/EquationSet.java @@ -3839,7 +3839,7 @@ public void findConstants () protected boolean findConstantsEval () { boolean changed = false; - for (Variable v : variables) + for (Variable v : new ArrayList (variables)) // Buffer so that Variable.simplify() can add new variables. In that case, Variable.simplify() should return true so that another cycle can include the new variable in optimizations. { if (v.simplify ()) changed = true; diff --git a/N2A/src/gov/sandia/n2a/language/AccessVariable.java b/N2A/src/gov/sandia/n2a/language/AccessVariable.java index 85d2beff..93813654 100644 --- a/N2A/src/gov/sandia/n2a/language/AccessVariable.java +++ b/N2A/src/gov/sandia/n2a/language/AccessVariable.java @@ -1,5 +1,5 @@ /* -Copyright 2013-2023 National Technology & Engineering Solutions of Sandia, LLC (NTESS). +Copyright 2013-2024 National Technology & Engineering Solutions of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain rights in this software. */ @@ -117,6 +117,12 @@ public Operator simplify (Variable from, boolean evalOnly) // temporary that is made externally visible via a second variable. if (! v2.hasAny ("externalRead", "externalWrite")) return this; } + else // Referencing variable in same equation set + { + // If the intermediate variable is explicitly marked state, then is has been deliberately inserted + // to create a 1-cycle delay. Don't optimize this away. + if (v.hasAttribute ("state")) return this; + } // Fold aliased variable from.changed = true; diff --git a/N2A/src/gov/sandia/n2a/language/function/Delay.java b/N2A/src/gov/sandia/n2a/language/function/Delay.java index f0766a49..bd18363b 100644 --- a/N2A/src/gov/sandia/n2a/language/function/Delay.java +++ b/N2A/src/gov/sandia/n2a/language/function/Delay.java @@ -1,5 +1,5 @@ /* -Copyright 2020 National Technology & Engineering Solutions of Sandia, LLC (NTESS). +Copyright 2020-2024 National Technology & Engineering Solutions of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain rights in this software. */ @@ -9,13 +9,20 @@ import java.util.Map.Entry; import java.util.NavigableMap; import java.util.TreeMap; +import java.util.TreeSet; import gov.sandia.n2a.backend.internal.InstanceTemporaries; import gov.sandia.n2a.backend.internal.Simulator; +import gov.sandia.n2a.eqset.EquationEntry; +import gov.sandia.n2a.eqset.Variable; +import gov.sandia.n2a.eqset.VariableReference; import gov.sandia.n2a.eqset.EquationSet.ExponentContext; +import gov.sandia.n2a.language.AccessVariable; import gov.sandia.n2a.language.Function; import gov.sandia.n2a.language.Operator; import gov.sandia.n2a.language.Type; +import gov.sandia.n2a.language.operator.Multiply; +import gov.sandia.n2a.language.operator.NOT; import gov.sandia.n2a.language.type.Instance; import gov.sandia.n2a.language.type.Scalar; @@ -44,6 +51,65 @@ public boolean canBeConstant () return false; } + public Operator simplify (Variable from, boolean evalOnly) + { + for (int i = 0; i < operands.length; i++) operands[i] = operands[i].simplify (from, evalOnly); + if (evalOnly) return this; // Everything below involves structural changes. + + // Ensure that every Delay call is by itself as the sole expression for some variable. + + if (from.equations.size () == 1 && from.equations.first ().expression == this) // Already on private variable. + { + if (operands.length == 1) // But delay() itself can be removed in favor of state buffering. + { + from.addAttribute ("state"); + from.removeAttribute ("temporary"); + + // Construct the expression: !$init * operand[0] + // This should return 0 in the init cycle, and a delayed value of operand[0] in all subsequent cycles. + + Multiply mult = new Multiply (); + mult.parent = parent; + + Variable init = from.container.find (new Variable ("$init")); + VariableReference avref = new VariableReference (); + avref.variable = init; + AccessVariable avinit = new AccessVariable (avref); + + NOT not = new NOT (); + not.operand = avinit; + mult.operand0 = not; + mult.operand0.parent = mult; + + mult.operand1 = operands[0]; + mult.operand1.parent = mult; + + return mult; + } + return this; // Already moved to our own variable. + } + + // Add a new variable for this Delay. + // TODO: check if there is already a delay variable that matches Delay. + Variable d = new Variable ("delay1"); + int index = 2; + while (from.container.find (d) != null) d.name = "delay" + index++; + from.container.add (d); + d.reference = new VariableReference (); + d.reference.variable = d; + from.addDependencyOn (d); + + d.equations = new TreeSet (); + EquationEntry e = new EquationEntry (d, ""); + d.equations.add (e); + e.expression = this; + e.expression.parent = null; // This will later be changed to "from". + + VariableReference r = new VariableReference (); + r.variable = d; + return new AccessVariable (r); + } + public void determineExponent (ExponentContext context) { for (int i = 0; i < operands.length; i++) operands[i].determineExponent (context); @@ -108,7 +174,6 @@ public Type eval (Instance context) Type tempValue = operands[0].eval (context); Simulator simulator = Simulator.instance.get (); if (simulator == null) return tempValue; - if (operands.length < 2) return tempValue; // Zero delay, which generally introduces one cycle of delay in an expression like: A = delay(B) double value = ((Scalar) tempValue).value; double delay = ((Scalar) operands[1].eval (context)).value;