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

feat: implement GET DIRECT SUBSCRIPT for CSV #79

Merged
merged 9 commits into from
Jul 23, 2021
4 changes: 4 additions & 0 deletions models/directsubs/b_subs.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DimB
B1
B2
B3
2 changes: 2 additions & 0 deletions models/directsubs/c_subs.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DimC,,
C1,C2,C3
30 changes: 30 additions & 0 deletions models/directsubs/directsubs.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
a[A1]
0 10
a[A2]
0 20
a[A3]
0 30
b[B1]
0 1
b[B2]
0 2
b[B3]
0 3
c[C1]
0 11
1 11
c[C2]
0 21
1 21
c[C3]
0 31
1 31
FINAL TIME
0 1
INITIAL TIME
0 0
SAVEPER
0 1
1 1
TIME STEP
0 1
61 changes: 61 additions & 0 deletions models/directsubs/directsubs.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{UTF-8}
DimA: A1, A2, A3 -> DimB, DimC ~~|
DimB:
GET DIRECT SUBSCRIPT(
'b_subs.csv',
',',
'A2',
'A',
''
)
~~|
DimC:
GET DIRECT SUBSCRIPT(
'c_subs.csv',
',',
'A2',
'2',
''
)
~~|
a[DimA] = 10, 20, 30
~~|
b[DimB] = 1, 2, 3
~~~:SUPPLEMENTARY|
c[DimC] = a[DimA] + 1
~~~:SUPPLEMENTARY|

********************************************************
.Control
********************************************************~
Simulation Control Parameters
|

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

\\\---/// 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:Current
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
63 changes: 63 additions & 0 deletions models/directsubs/directsubs_subs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
_dima:
{
modelName: 'DimA',
modelValue: [ 'A1', 'A2', 'A3' ],
modelMappings: [ { toDim: 'DimB', value: [] }, { toDim: 'DimC', value: [] } ],
name: '_dima',
value: [ '_a1', '_a2', '_a3' ],
size: 3,
family: '_dima',
mappings: { _dimb: [ '_a1', '_a2', '_a3' ], _dimc: [ '_a1', '_a2', '_a3' ] }
}

_dimb:
{
modelName: 'DimB',
modelValue: [ 'B1', 'B2', 'B3' ],
modelMappings: [],
name: '_dimb',
value: [ '_b1', '_b2', '_b3' ],
size: 3,
family: '_dimb',
mappings: {}
}

_dimc:
{
modelName: 'DimC',
modelValue: [ 'C1', 'C2', 'C3' ],
modelMappings: [],
name: '_dimc',
value: [ '_c1', '_c2', '_c3' ],
size: 3,
family: '_dimc',
mappings: {}
}

_a1:
{ name: '_a1', value: 0, size: 1, family: '_dima', mappings: {} }

_a2:
{ name: '_a2', value: 1, size: 1, family: '_dima', mappings: {} }

_a3:
{ name: '_a3', value: 2, size: 1, family: '_dima', mappings: {} }

_b1:
{ name: '_b1', value: 0, size: 1, family: '_dimb', mappings: {} }

_b2:
{ name: '_b2', value: 1, size: 1, family: '_dimb', mappings: {} }

_b3:
{ name: '_b3', value: 2, size: 1, family: '_dimb', mappings: {} }

_c1:
{ name: '_c1', value: 0, size: 1, family: '_dimc', mappings: {} }

_c2:
{ name: '_c2', value: 1, size: 1, family: '_dimc', mappings: {} }

_c3:
{ name: '_c3', value: 2, size: 1, family: '_dimc', mappings: {} }

82 changes: 82 additions & 0 deletions models/directsubs/directsubs_vars.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
a[DimA]: const (non-apply-to-all)
= 10,20,30
refId(_a[_a1])
families(_dima)
subscripts(_a1)
separationDims(_dima)
hasInitValue(false)

a[DimA]: const (non-apply-to-all)
= 10,20,30
refId(_a[_a2])
families(_dima)
subscripts(_a2)
separationDims(_dima)
hasInitValue(false)

a[DimA]: const (non-apply-to-all)
= 10,20,30
refId(_a[_a3])
families(_dima)
subscripts(_a3)
separationDims(_dima)
hasInitValue(false)

b[DimB]: const (non-apply-to-all)
= 1,2,3
refId(_b[_b1])
families(_dimb)
subscripts(_b1)
separationDims(_dimb)
hasInitValue(false)

b[DimB]: const (non-apply-to-all)
= 1,2,3
refId(_b[_b2])
families(_dimb)
subscripts(_b2)
separationDims(_dimb)
hasInitValue(false)

b[DimB]: const (non-apply-to-all)
= 1,2,3
refId(_b[_b3])
families(_dimb)
subscripts(_b3)
separationDims(_dimb)
hasInitValue(false)

c[DimC]: aux
= a[DimA]+1
refId(_c)
families(_dimc)
subscripts(_dimc)
hasInitValue(false)
refs(_a[_a1], _a[_a2], _a[_a3])

FINAL TIME: const
= 1
refId(_final_time)
hasInitValue(false)

INITIAL TIME: const
= 0
refId(_initial_time)
hasInitValue(false)

SAVEPER: aux
= TIME STEP
refId(_saveper)
hasInitValue(false)
refs(_time_step)

Time: const
=
refId(_time)
hasInitValue(false)

TIME STEP: const
= 1
refId(_time_step)
hasInitValue(false)

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"dependencies": {
"antlr4": "4.9.2",
"antlr4-vensim": "https://github.com/climateinteractive/antlr4-vensim#a1d8e0b",
"antlr4-vensim": "https://github.com/climateinteractive/antlr4-vensim#d008777",
"bufx": "^1.0.5",
"byline": "^5.0.0",
"chart.js": "^2.9.4",
Expand Down
4 changes: 2 additions & 2 deletions src/CodeGen.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { sub, allDimensions, allMappings, subscriptFamilies } from './Subscript.
import { asort, lines, strlist, abend, mapIndexed } from './Helpers.js'

export let codeGenerator = (parseTree, opts) => {
const { spec, operation, extData, directData } = opts
const { spec, operation, extData, directData, modelDirname } = opts
// Set to 'decl', 'init-lookups', 'eval', etc depending on the section being generated.
let mode = ''
// Set to true to output all variables when there is no model run spec.
Expand All @@ -25,7 +25,7 @@ export let codeGenerator = (parseTree, opts) => {
// Read variables and subscript ranges from the model parse tree.
// This is the main entry point for code generation and is called just once.
try {
Model.read(parseTree, spec, extData, directData)
Model.read(parseTree, spec, extData, directData, modelDirname)
// In list mode, print variables to the console instead of generating code.
if (operation === 'printRefIdTest') {
Model.printRefIdTest()
Expand Down
58 changes: 42 additions & 16 deletions src/Helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import sh from 'shelljs'
import split from 'split-string'
import byline from 'byline'
import XLSX from 'xlsx'
import parseCsv from 'csv-parse/lib/sync.js'
import B from 'bufx'

// Set true to print a stack trace in vlog
Expand Down Expand Up @@ -237,7 +238,7 @@ export let modelPathProps = model => {
return {
modelDirname: p.dir,
modelName: p.name,
modelPathname: path.format(p),
modelPathname: path.format(p)
}
}
export let execCmd = cmd => {
Expand Down Expand Up @@ -293,7 +294,9 @@ export let readDat = async (pathname, prefix = '') => {
if (Number.isNaN(t)) {
console.error(`DAT file ${pathname}:${lineNum} time value is NaN`)
} else if (Number.isNaN(value)) {
console.error(`DAT file ${pathname}:${lineNum} var "${varName}" value is NaN at time=${t}`)
console.error(
`DAT file ${pathname}:${lineNum} var "${varName}" value is NaN at time=${t}`
)
} else {
varValues.set(t, value)
}
Expand All @@ -310,6 +313,26 @@ export let readDat = async (pathname, prefix = '') => {
export let readXlsx = pathname => {
return XLSX.readFile(pathname, { cellDates: true })
}
export let readCsv = (pathname, delimiter = ',') => {
// Read the CSV file at the pathname and parse it with the given delimiter.
// Return an array of rows that are each an array of columns.
// If there is a header row, it is returned as the first row.
let result = null
const CSV_PARSE_OPTS = {
delimiter,
columns: false,
trim: true,
skip_empty_lines: true,
skip_lines_with_empty_values: true
}
try {
let data = B.read(pathname)
result = parseCsv(data, CSV_PARSE_OPTS)
} catch (error) {
console.error(`ERROR: CSV file ${pathname} not found`)
}
return result
}
// Convert the var name and subscript names to canonical form separately.
export let canonicalVensimName = vname => {
let result = vname
Expand All @@ -334,7 +357,9 @@ export let mapIndexed = R.addIndex(R.map)
// Function to sort an array of strings
export let asort = R.sort((a, b) => (a > b ? 1 : a < b ? -1 : 0))
// Function to alpha sort an array of variables on the model LHS
export let vsort = R.sort((a, b) => (a.modelLHS > b.modelLHS ? 1 : a.modelLHS < b.modelLHS ? -1 : 0))
export let vsort = R.sort((a, b) =>
a.modelLHS > b.modelLHS ? 1 : a.modelLHS < b.modelLHS ? -1 : 0
)
// Function to list an array to stderr
export let printArray = R.forEach(x => console.error(x))
// Function to expand an array of strings into a comma-delimited list of strings
Expand Down Expand Up @@ -407,13 +432,14 @@ export let replaceDelimitedStrings = (str, open, close, newStr) => {
* This can be used in place of nested for loops and has the benefit of working
* for multi-dimensional inputs.
*/
export const cartesianProductOf = arr => {
export const cartesianProductOf = arr => {
// Implementation based on: https://stackoverflow.com/a/36234242
return arr.reduce((a, b) => {
return a
.map(x => b.map(y => x.concat([y])))
.reduce((v, w) => v.concat(w), [])
}, [[]])
return arr.reduce(
(a, b) => {
return a.map(x => b.map(y => x.concat([y]))).reduce((v, w) => v.concat(w), [])
},
[[]]
)
}

/**
Expand All @@ -424,14 +450,14 @@ export let replaceDelimitedStrings = (str, open, close, newStr) => {
* this function will return all the permutations, e.g.:
* [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
*/
export const permutationsOf = (elems, subperms = [[]]) => {
export const permutationsOf = (elems, subperms = [[]]) => {
// Implementation based on: https://gist.github.com/CrossEye/f7c2f77f7db7a94af209
return R.isEmpty(elems) ?
subperms :
R.addIndex(R.chain)((elem, idx) => permutationsOf(
R.remove(idx, 1, elems),
R.map(R.append(elem), subperms)
), elems)
return R.isEmpty(elems)
? subperms
: R.addIndex(R.chain)(
(elem, idx) => permutationsOf(R.remove(idx, 1, elems), R.map(R.append(elem), subperms)),
elems
)
}

//
Expand Down
Loading