use of gov.sandia.n2a.language.Transformer in project n2a by frothga.
the class EquationSet method resolveRHS.
public void resolveRHS(LinkedList<String> unresolved) {
for (EquationSet s : parts) {
s.resolveRHS(unresolved);
}
class Resolver extends Transformer {
public Variable from;
public LinkedList<String> unresolved;
public String fromName() {
String result = from.container.prefix();
if (!result.isEmpty())
result += ".";
return result + from.nameString();
}
public Operator transform(Operator op) {
if (op instanceof AccessVariable) {
AccessVariable av = (AccessVariable) op;
Variable query = new Variable(av.getName(), av.getOrder());
query.reference = new VariableReference();
EquationSet dest = resolveEquationSet(query, false);
if (dest == null) {
unresolved.add(av.name + "\t" + fromName());
} else {
query.reference.variable = dest.find(query);
if (query.reference.variable == null) {
if (query.name.equals("(connection)")) {
// create a phantom variable. TODO: should this be an attribute instead?
query.reference.variable = new Variable("(connection)");
// the container itself is really the target
query.reference.variable.container = dest;
// because instance variables are bound before the part is put into service, and remain constant for its entire life
query.reference.variable.addAttribute("initOnly");
// TODO: when $connect() is implemented, instances should become first class variables in the equation set, and this circular reference will be created by resolveLHS()
query.reference.variable.reference = query.reference;
} else if (// accountable endpoint
query.name.equals("$count")) {
if (dest.accountableConnections == null)
dest.accountableConnections = new TreeSet<AccountableConnection>();
String alias = av.name.split("\\.", 2)[0];
AccountableConnection ac = new AccountableConnection(EquationSet.this, alias);
if (!dest.accountableConnections.add(ac))
ac = dest.accountableConnections.floor(ac);
if (ac.count == null) {
// Create a fully-functional variable.
// However, it never gets formally added to dest, because dest should never evaluate it.
// Rather, it is maintained by the backend's connection system.
ac.count = new Variable(prefix() + ".$count");
ac.count.type = new Scalar(0);
ac.count.container = dest;
ac.count.equations = new TreeSet<EquationEntry>();
query.reference.variable = ac.count;
query.reference.variable.reference = query.reference;
} else {
query.reference.variable = ac.count;
}
} else {
unresolved.add(av.name + "\t" + fromName());
}
} else {
Variable target = query.reference.variable;
from.addDependencyOn(target);
if (from.container != target.container) {
target.addAttribute("externalRead");
if (target.hasAttribute("temporary")) {
Backend.err.get().println("WARNING: Variable " + target.container.prefix() + "." + target.nameString() + " has an external read, so cannot be temporary.");
target.removeAttribute("temporary");
}
}
}
}
av.reference = query.reference;
return av;
}
if (op instanceof Split) {
Split split = (Split) op;
int count = split.names.length;
split.parts = new ArrayList<EquationSet>(count);
for (int i = 0; i < count; i++) {
String temp = split.names[i];
EquationSet part = container.parts.floor(new EquationSet(temp));
if (part.name.equals(temp))
split.parts.add(part);
else
unresolved.add(temp + "\t" + fromName());
}
}
return null;
}
}
Resolver resolver = new Resolver();
for (Variable v : variables) {
resolver.from = v;
resolver.unresolved = unresolved;
v.transform(resolver);
}
}
use of gov.sandia.n2a.language.Transformer in project n2a by frothga.
the class EquationSet method flatten.
/**
* Convert this equation set into an equivalent object where each included part with $n==1
* (and satisfying a few other conditions) is merged into its containing part.
* Equations with combiners (=+, =*, and so on) are joined together into one long equation
* with the appropriate operator.
* @param backend Prefix for metadata keys specific to the engine selected to execute this model.
* Where such keys exists, the parts should not be flattened.
*/
public void flatten(String backend) {
TreeSet<EquationSet> temp = new TreeSet<EquationSet>(parts);
for (final EquationSet s : temp) {
s.flatten(backend);
// Check if connection or endpoint. They must remain separate equation sets for code-generation purposes.
if (s.connectionBindings != null)
continue;
if (s.connected)
continue;
// For similar reasons, if the part contains backend-related metadata, it should remain separate.
boolean hasBackendMetadata = false;
for (Entry<String, String> m : s.metadata.entrySet()) {
if (m.getKey().startsWith(backend)) {
hasBackendMetadata = true;
break;
}
}
if (hasBackendMetadata)
continue;
// Check if $n==1
if (!s.isSingleton())
continue;
// We don't want $n in the merged set.
s.variables.remove(new Variable("$n", 0));
// Don't merge if there are any conflicting $variables.
boolean conflict = false;
for (Variable v : s.variables) {
if (!v.name.startsWith("$") || v.name.startsWith("$up"))
continue;
Variable d = find(v);
if (// for this match we don't care about order; that is, any differential order on either side causes a conflict
d != null && d.name.equals(v.name)) {
conflict = true;
break;
}
}
if (conflict)
continue;
// Merge
final String prefix = s.name;
parts.remove(s);
// Variables
final TreeSet<String> names = new TreeSet<String>();
for (Variable v : s.variables) names.add(v.name);
class Prefixer extends Transformer {
public Operator transform(Operator op) {
if (!(op instanceof AccessVariable))
return null;
AccessVariable av = (AccessVariable) op;
if (av.name.startsWith("$")) {
if (av.name.startsWith("$up.")) {
av.name = av.name.substring(4);
}
// otherwise, don't modify references to $variables
} else if (names.contains(av.name)) {
av.name = prefix + "." + av.name;
}
return av;
}
}
Prefixer prefixer = new Prefixer();
for (Variable v : s.variables) {
if (v.name.startsWith("$")) {
if (v.name.startsWith("$up.")) {
v.name = v.name.substring(4);
}
// otherwise merge all $variables with containing set
} else {
v.name = prefix + "." + v.name;
}
v.transform(prefixer);
Variable v2 = find(v);
if (v2 == null) {
add(v);
} else {
v2.flattenExpressions(v);
}
}
// Parts
for (EquationSet sp : s.parts) {
// s was the former container for sp, but s is going away
sp.container = this;
sp.name = prefix + "." + sp.name;
parts.add(sp);
}
// Metadata
for (Entry<String, String> e : s.metadata.entrySet()) {
metadata.put(prefix + "." + e.getKey(), e.getValue());
}
// Dependent connections (paths that pass through s)
if (s.dependentConnections != null) {
if (dependentConnections == null)
dependentConnections = new ArrayList<ConnectionBinding>();
for (ConnectionBinding c : s.dependentConnections) {
// By construction, this element must exist.
int i = c.resolution.indexOf(s);
// replace s with this
c.resolution.set(i, this);
if (i + 1 < c.resolution.size() && c.resolution.get(i + 1) == this)
c.resolution.remove(i + 1);
if (i > 0 && c.resolution.get(i - 1) == this)
c.resolution.remove(i - 1);
if (!dependentConnections.contains(c))
dependentConnections.add(c);
}
}
}
}
Aggregations