@sdeverywhere/compile 0.7.18 → 0.7.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/model/model.js +80 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdeverywhere/compile",
3
- "version": "0.7.18",
3
+ "version": "0.7.19",
4
4
  "description": "The core Vensim to C compiler for the SDEverywhere tool suite.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -25,6 +25,7 @@ import Variable from './variable.js'
25
25
  let variables = []
26
26
  let inputVars = []
27
27
  let constantExprs = new Map()
28
+ let cachedSortedVarsByType = new Map()
28
29
  let cachedVarIndexInfo
29
30
  let cachedJsonList
30
31
 
@@ -46,6 +47,7 @@ function resetModelState() {
46
47
  variablesByName.clear()
47
48
  constantExprs.clear()
48
49
  nonAtoANames = Object.create(null)
50
+ cachedSortedVarsByType.clear()
49
51
  cachedVarIndexInfo = undefined
50
52
  cachedJsonList = undefined
51
53
  }
@@ -439,8 +441,8 @@ function resolveDuplicateDeclarations() {
439
441
  // Least and greatest safe double values in C rounded to convenient consts
440
442
  const MIN_SAFE_DBL = -1e308
441
443
  const MAX_SAFE_DBL = 1e308
442
- let data = dataVars()
443
- for (let constVar of constVars()) {
444
+ let data = varsOfType('data')
445
+ for (let constVar of varsOfType('const')) {
444
446
  if (data.find(d => d.varName === constVar.varName)) {
445
447
  // Change the var type from const to data and add lookup data points.
446
448
  // For a constant, the equivalent lookup has the same value over the entire x axis.
@@ -524,26 +526,53 @@ function allVars() {
524
526
  }
525
527
  return R.filter(isNotPlaceholderVar, variables)
526
528
  }
529
+ function cachedSortedVars(varType, generate) {
530
+ // Return the cached array of sorted variables for the given type if
531
+ // available, otherwise call the `generate` function to generate the
532
+ // array and cache it in the map.
533
+ let vars = cachedSortedVarsByType.get(varType)
534
+ if (!vars) {
535
+ vars = generate()
536
+ cachedSortedVarsByType.set(varType, vars)
537
+ }
538
+ return vars
539
+ }
527
540
  function constVars() {
528
- return vsort(varsOfType('const'))
541
+ // Return an array of vars of type `const`, sorted by LHS variable name.
542
+ // Note that this caches the result, so should only be called after the
543
+ // model has been fully read and analyzed.
544
+ return cachedSortedVars('const', () => vsort(varsOfType('const')))
529
545
  }
530
546
  function lookupVars() {
531
- return vsort(varsOfType('lookup'))
547
+ // Return an array of vars of type `lookup`, sorted by LHS variable name.
548
+ // Note that this caches the result, so should only be called after the
549
+ // model has been fully read and analyzed.
550
+ return cachedSortedVars('lookup', () => vsort(varsOfType('lookup')))
532
551
  }
533
552
  function dataVars() {
534
- return vsort(varsOfType('data'))
553
+ // Return an array of vars of type `data`, sorted by LHS variable name.
554
+ // Note that this caches the result, so should only be called after the
555
+ // model has been fully read and analyzed.
556
+ return cachedSortedVars('data', () => vsort(varsOfType('data')))
535
557
  }
536
558
  function auxVars() {
537
- // console.error('AUX VARS');
538
- return sortVarsOfType('aux')
559
+ // Return an array of vars of type `aux`, sorted according to the dependency graph.
560
+ // Note that this caches the result, so should only be called after the
561
+ // model has been fully read and analyzed.
562
+ return cachedSortedVars('aux', () => sortVarsOfType('aux'))
539
563
  }
540
564
  function levelVars() {
541
- // console.error('LEVEL VARS');
542
- return sortVarsOfType('level')
565
+ // Return an array of vars of type `level`, sorted according to the dependency graph.
566
+ // Note that this caches the result, so should only be called after the
567
+ // model has been fully read and analyzed.
568
+ return cachedSortedVars('level', () => sortVarsOfType('level'))
543
569
  }
544
570
  function initVars() {
545
- // console.error('INIT VARS');
546
- return sortInitVars()
571
+ // Return an array of all vars that have the `hasInitValue` flag set to true,
572
+ // sorted according to the dependency graph.
573
+ // Note that this caches the result, so should only be called after the
574
+ // model has been fully read and analyzed.
575
+ return cachedSortedVars('init', () => sortInitVars())
547
576
  }
548
577
  function varWithRefId(refId) {
549
578
  const findVarWithRefId = rid => {
@@ -1055,26 +1084,52 @@ function printDepsGraph(graph, varType) {
1055
1084
 
1056
1085
  function allListedVars() {
1057
1086
  // Put variables into the order that they are evaluated by SDE in the generated model
1058
- let vars = []
1059
- vars.push(...constVars())
1060
- vars.push(...lookupVars())
1061
- vars.push(...dataVars())
1087
+ const listedVars = []
1088
+ const visitedRefIds = new Set()
1089
+ function addUnique(vars) {
1090
+ for (const v of vars) {
1091
+ // Skip variables that have already been visited
1092
+ if (visitedRefIds.has(v.refId)) {
1093
+ continue
1094
+ }
1095
+ visitedRefIds.add(v.refId)
1096
+
1097
+ // Filter out variables that are generated/used internally
1098
+ if (v.includeInOutput === false) {
1099
+ continue
1100
+ }
1101
+
1102
+ // Include the variable
1103
+ listedVars.push(v)
1104
+ }
1105
+ }
1106
+
1107
+ // The order of execution/evaluation in the generated model is:
1108
+ // initConstants (vars of type `const` only)
1109
+ // initLookups (vars of type `lookup` only)
1110
+ // initData (vars of type `data` only)
1111
+ // initLevels (vars returned by `initVars`, a mix of initial, aux, and level vars)
1112
+ // evalAux (vars of type `aux` only)
1113
+ // evalLevels (vars of type `level` only)
1114
+ // So to make the ordering in the listing better match the order of evaluation,
1115
+ // we emit variables in the above order, but filter to avoid having duplicates.
1116
+ addUnique(constVars())
1117
+ addUnique(lookupVars())
1118
+ addUnique(dataVars())
1062
1119
  // The special exogenous `Time` variable may have already been removed by
1063
1120
  // `removeUnusedVariables` if it is not referenced explicitly in the model,
1064
- // so we will only include it in the listing if it is defined here
1121
+ // so we will only include it in the listing if it is defined here. Note
1122
+ // that `_time` is set to `_initial_time` as the first step in the
1123
+ // `initLevels` function, which is why it is included here.
1065
1124
  const timeVar = varWithName('_time')
1066
1125
  if (timeVar) {
1067
- vars.push(timeVar)
1068
- }
1069
- vars.push(...auxVars())
1070
- vars.push(...levelVars())
1071
-
1072
- // Filter out variables that are generated/used internally
1073
- const isInternal = v => {
1074
- return v.includeInOutput === false
1126
+ addUnique([timeVar])
1075
1127
  }
1128
+ addUnique(initVars())
1129
+ addUnique(auxVars())
1130
+ addUnique(levelVars())
1076
1131
 
1077
- return R.filter(v => !isInternal(v), vars)
1132
+ return listedVars
1078
1133
  }
1079
1134
 
1080
1135
  function filteredListedVars() {