Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow extra index subscripts in 2D const lists #110

Merged
merged 8 commits into from
Sep 27, 2021
36 changes: 36 additions & 0 deletions models/arrays_cname/arrays_cname.dat
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,39 @@ y[D4,A2]
0 42
y[D4,A3]
0 43
z[C1,A1,B1]
0 110
z[C1,A1,B2]
0 111
z[C1,A1,B3]
0 112
z[C1,A2,B1]
0 120
z[C1,A2,B2]
0 121
z[C1,A2,B3]
0 122
z[C1,A3,B1]
0 130
z[C1,A3,B2]
0 131
z[C1,A3,B3]
0 132
z[C2,A1,B1]
0 210
z[C2,A1,B2]
0 211
z[C2,A1,B3]
0 212
z[C2,A2,B1]
0 220
z[C2,A2,B2]
0 221
z[C2,A2,B3]
0 222
z[C2,A3,B1]
0 230
z[C2,A3,B2]
0 231
z[C2,A3,B3]
0 232
40 changes: 40 additions & 0 deletions models/arrays_cname/arrays_cname.mdl
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,47 @@ y[DimD,DimA]=
~ 2D constant array with dimensions not in normal order
|

z[C1, DimA, DimB]=
110, 111, 112;
120, 121, 122;
130, 131, 132;
~
~ 2D constant array with additional index subscript (1/2)
|

z[C2, DimA, DimB]=
210, 211, 212;
220, 221, 222;
230, 231, 232;
~
~ 2D constant array with additional index subscript (2/2)
|

INITIAL TIME = 0 ~~|
FINAL TIME = 1 ~~|
TIME STEP = 1 ~~|
SAVEPER = TIME STEP ~~|

\\\---/// Sketch information - do not modify anything except names
V300 Do not put anything below this section - it will be ignored
*View 1
$0-0-0,0,|0||0-0-0|0-0-0|0-0-0|0-0-0|0-0-0|0,0,100,0
///---\\\
:L<%^E!@
9:arrays_cname
15:0,0,0,0,0,0
19:100,0
27:2,
34:0,
5:FINAL TIME
35:Date
36:YYYY-MM-DD
37:2000
38:1
39:1
40:2
41:0
42:1
24:0
25:0
26:0
58 changes: 14 additions & 44 deletions src/EquationGen.js
Original file line number Diff line number Diff line change
Expand Up @@ -1029,51 +1029,21 @@ export default class EquationGen extends ModelReader {
// Emit a single constant into the expression code.
emitConstAtPos(0)
} else {
// Extract a single value from the const list by its index number.
// All const lists with > 1 value are separated on dimensions in the LHS.
// The LHS of a separated variable here will contain only index subscripts.
let numDims = this.var.separationDims.length
if (numDims === 1) {
// Find the index that is in the separation dimension.
let sepDim = sub(this.var.separationDims[0])
for (let ind of this.var.subscripts) {
let i = sepDim.value.indexOf(ind)
if (i >= 0) {
// Emit the constant at this position in the constant list.
emitConstAtPos(i)
break
}
}
} else if (numDims === 2) {
// Calculate an index into a flattened array by converting the indices to numeric form and looking them up
// in a C name array listed in the same Vensim order as the constant array in the model.
let cVarName
let modelLHSReader = new ModelLHSReader()
modelLHSReader.read(this.var.modelLHS)
let cNames = modelLHSReader.names().map(Model.cName)
// Visit dims in normal order. Find the ind in the dim. Compose the C array expression with numeric indices.
for (let i = 0; i < this.var.separationDims.length; i++) {
const dim = this.var.separationDims[i]
const sepDim = sub(dim)
const ind = this.var.subscripts[i]
const j = sepDim.value.indexOf(ind)
if (j >= 0) {
const indexNum = sub(ind).value
if (!cVarName) {
cVarName = `${this.var.varName}[${indexNum}]`
} else {
cVarName += `[${indexNum}]`
}
}
}
// Find the position of the constant in Vensim order from the expanded LHS var list.
let constPos = R.indexOf(cVarName, cNames)
if (constPos >= 0) {
emitConstAtPos(constPos)
// console.error(`${this.var.refId} position = ${constPos}`)
} else {
console.error(`${this.var.refId} → ${cVarName} not found in C names`)
}
// The LHS of a separated variable here will contain only index subscripts in normal order.
// Calculate an index into a flattened array by converting the indices to numeric form and looking them up
// in a C name array listed in the same Vensim order as the constant array in the model.
let modelLHSReader = new ModelLHSReader()
modelLHSReader.read(this.var.modelLHS)
let cNames = modelLHSReader.names().map(Model.cName)
let cVarName = this.var.varName + R.map(indName => `[${sub(indName).value}]`, this.var.subscripts).join('')
// Find the position of the constant in Vensim order from the expanded LHS var list.
let constPos = R.indexOf(cVarName, cNames)
if (constPos >= 0) {
emitConstAtPos(constPos)
// console.error(`${this.var.refId} position = ${constPos}`)
} else {
console.error(`ERROR: const list element ${this.var.refId} → ${cVarName} not found in C names`)
}
}
}
Expand Down
45 changes: 22 additions & 23 deletions src/VariableReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ModelReader from './ModelReader.js'
import Model from './Model.js'
import Variable from './Variable.js'
import { sub, isDimension, isIndex, normalizeSubscripts } from './Subscript.js'
import { canonicalName, vlog, replaceInArray, strlist } from './Helpers.js'
import { canonicalName, vlog, replaceInArray, strlist, cartesianProductOf } from './Helpers.js'

// Set true to print extra debugging information to stderr.
const DEBUG_LOG = false
Expand Down Expand Up @@ -116,12 +116,10 @@ export default class VariableReader extends ModelReader {
}
return skip
}
let skipExpansion2 = (indName0, indName1) => {
let skipExpansion2 = indNames => {
let skip = false
for (const exceptSubs of this.var.exceptSubscripts) {
let exceptSub0 = exceptSubs[0]
let exceptSub1 = exceptSubs[1]
if (isException(indName0, exceptSub0) && isException(indName1, exceptSub1)) {
for (let exceptSubs of this.var.exceptSubscripts) {
if (isException(indNames[0], exceptSubs[0]) && isException(indNames[1], exceptSubs[1])) {
skip = true
break
}
Expand Down Expand Up @@ -150,24 +148,25 @@ export default class VariableReader extends ModelReader {
`expanding ${this.var.varName}[${strlist(this.var.subscripts)}] subscripts`,
strlist(this.var.subscripts)
)
let expansionSubscript0 = this.var.subscripts[0]
let expansionSubscript1 = this.var.subscripts[1]
let expansionSubs0 = isIndex(expansionSubscript0)
? [sub(expansionSubscript0).name]
: sub(expansionSubscript0).value
let expansionSubs1 = isIndex(expansionSubscript1)
? [sub(expansionSubscript1).name]
: sub(expansionSubscript1).value
for (let indName0 of expansionSubs0) {
for (let indName1 of expansionSubs1) {
if (!skipExpansion2(indName0, indName1)) {
let v = new Variable(this.var.eqnCtx)
v.varName = this.var.varName
v.subscripts = [indName0, indName1]
v.separationDims = [expansionSubscript0, expansionSubscript1]
debugLog(` ${strlist(v.subscripts)}`)
this.expandedVars.push(v)
// Find the subscripts we need to expand.
let separationDims = []
for (let i = 0; i < expanding.length; i++) {
if (expanding[i]) {
separationDims.push(this.var.subscripts[i])
}
}
let expansionSubs = separationDims.map(s => (isIndex(s) ? [sub(s).name] : sub(s).value))
for (let indNames of cartesianProductOf(expansionSubs)) {
if (!skipExpansion2(indNames)) {
let v = new Variable(this.var.eqnCtx)
v.varName = this.var.varName
v.subscripts = []
for (let i = 0; i < expanding.length; i++) {
v.subscripts.push(expanding[i] ? indNames.shift() : this.var.subscripts[i])
}
v.separationDims = separationDims
debugLog(` ${strlist(v.subscripts)}`)
this.expandedVars.push(v)
}
}
}
Expand Down