From 92b7105c356668825f28edf5b69ed6491705a6ad Mon Sep 17 00:00:00 2001 From: Todd Fincannon Date: Wed, 8 Sep 2021 22:46:47 -0700 Subject: [PATCH 1/7] add DELAY FIXED function with test model --- models/delayfixed/delayfixed.dat | 418 +++++++++++++++++++++++++++++++ models/delayfixed/delayfixed.mdl | 72 ++++++ src/c/vensim.c | 31 ++- src/c/vensim.h | 13 + 4 files changed, 533 insertions(+), 1 deletion(-) create mode 100644 models/delayfixed/delayfixed.dat create mode 100644 models/delayfixed/delayfixed.mdl diff --git a/models/delayfixed/delayfixed.dat b/models/delayfixed/delayfixed.dat new file mode 100644 index 00000000..3ea617a7 --- /dev/null +++ b/models/delayfixed/delayfixed.dat @@ -0,0 +1,418 @@ +FINAL TIME +0 100 +INITIAL TIME +0 0 +receiving +0 0 +1 0 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 0 +9 0 +10 0 +11 0 +12 0 +13 0 +14 0 +15 0 +16 0 +17 0 +18 0 +19 0 +20 0 +21 0 +22 0 +23 0 +24 0 +25 0 +26 0 +27 0 +28 0 +29 0 +30 1 +31 1 +32 1 +33 1 +34 1 +35 1 +36 1 +37 1 +38 1 +39 1 +40 0 +41 0 +42 0 +43 0 +44 0 +45 0 +46 0 +47 0 +48 0 +49 0 +50 0 +51 0 +52 0 +53 0 +54 0 +55 0 +56 0 +57 0 +58 0 +59 0 +60 0 +61 0 +62 0 +63 0 +64 0 +65 0 +66 0 +67 0 +68 0 +69 0 +70 0 +71 0 +72 0 +73 0 +74 0 +75 0 +76 0 +77 0 +78 0 +79 0 +80 0 +81 0 +82 0 +83 0 +84 0 +85 0 +86 0 +87 0 +88 0 +89 0 +90 0 +91 0 +92 0 +93 0 +94 0 +95 0 +96 0 +97 0 +98 0 +99 0 +100 0 +reference shipping rate +0 1 +SAVEPER +0 1 +1 1 +2 1 +3 1 +4 1 +5 1 +6 1 +7 1 +8 1 +9 1 +10 1 +11 1 +12 1 +13 1 +14 1 +15 1 +16 1 +17 1 +18 1 +19 1 +20 1 +21 1 +22 1 +23 1 +24 1 +25 1 +26 1 +27 1 +28 1 +29 1 +30 1 +31 1 +32 1 +33 1 +34 1 +35 1 +36 1 +37 1 +38 1 +39 1 +40 1 +41 1 +42 1 +43 1 +44 1 +45 1 +46 1 +47 1 +48 1 +49 1 +50 1 +51 1 +52 1 +53 1 +54 1 +55 1 +56 1 +57 1 +58 1 +59 1 +60 1 +61 1 +62 1 +63 1 +64 1 +65 1 +66 1 +67 1 +68 1 +69 1 +70 1 +71 1 +72 1 +73 1 +74 1 +75 1 +76 1 +77 1 +78 1 +79 1 +80 1 +81 1 +82 1 +83 1 +84 1 +85 1 +86 1 +87 1 +88 1 +89 1 +90 1 +91 1 +92 1 +93 1 +94 1 +95 1 +96 1 +97 1 +98 1 +99 1 +100 1 +shipments in transit +0 0 +1 0 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 0 +9 0 +10 0 +11 1 +12 2 +13 3 +14 4 +15 5 +16 6 +17 7 +18 8 +19 9 +20 10 +21 10 +22 10 +23 10 +24 10 +25 10 +26 10 +27 10 +28 10 +29 10 +30 10 +31 9 +32 8 +33 7 +34 6 +35 5 +36 4 +37 3 +38 2 +39 1 +40 0 +41 0 +42 0 +43 0 +44 0 +45 0 +46 0 +47 0 +48 0 +49 0 +50 0 +51 0 +52 0 +53 0 +54 0 +55 0 +56 0 +57 0 +58 0 +59 0 +60 0 +61 0 +62 0 +63 0 +64 0 +65 0 +66 0 +67 0 +68 0 +69 0 +70 0 +71 0 +72 0 +73 0 +74 0 +75 0 +76 0 +77 0 +78 0 +79 0 +80 0 +81 0 +82 0 +83 0 +84 0 +85 0 +86 0 +87 0 +88 0 +89 0 +90 0 +91 0 +92 0 +93 0 +94 0 +95 0 +96 0 +97 0 +98 0 +99 0 +100 0 +shipping +0 0 +1 0 +2 0 +3 0 +4 0 +5 0 +6 0 +7 0 +8 0 +9 0 +10 1 +11 1 +12 1 +13 1 +14 1 +15 1 +16 1 +17 1 +18 1 +19 1 +20 0 +21 0 +22 0 +23 0 +24 0 +25 0 +26 0 +27 0 +28 0 +29 0 +30 0 +31 0 +32 0 +33 0 +34 0 +35 0 +36 0 +37 0 +38 0 +39 0 +40 0 +41 0 +42 0 +43 0 +44 0 +45 0 +46 0 +47 0 +48 0 +49 0 +50 0 +51 0 +52 0 +53 0 +54 0 +55 0 +56 0 +57 0 +58 0 +59 0 +60 0 +61 0 +62 0 +63 0 +64 0 +65 0 +66 0 +67 0 +68 0 +69 0 +70 0 +71 0 +72 0 +73 0 +74 0 +75 0 +76 0 +77 0 +78 0 +79 0 +80 0 +81 0 +82 0 +83 0 +84 0 +85 0 +86 0 +87 0 +88 0 +89 0 +90 0 +91 0 +92 0 +93 0 +94 0 +95 0 +96 0 +97 0 +98 0 +99 0 +100 0 +shipping time +0 20 +TIME STEP +0 1 diff --git a/models/delayfixed/delayfixed.mdl b/models/delayfixed/delayfixed.mdl new file mode 100644 index 00000000..ee8c86e2 --- /dev/null +++ b/models/delayfixed/delayfixed.mdl @@ -0,0 +1,72 @@ +{UTF-8} +receiving = DELAY FIXED(shipping, shipping time, shipping) ~~| +shipments in transit = INTEG(shipping - receiving, shipping * shipping time) ~~| +reference shipping rate = 1 ~~| +shipping = STEP(reference shipping rate, 10) - STEP(reference shipping rate, 20) ~~| +shipping time = 20 ~~| + +INITIAL TIME = 0 ~~| +FINAL TIME = 100 ~~| +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 +$192-192-192,0,Times New Roman|12||0-0-0|0-0-0|0-0-255|-1--1--1|255-255-255|96,96,100,0 +///---\\\ +:L<%^E!@ +1:delayfixed.vdfx +4:Time +5:FINAL TIME +9:delayfixed +19:100,0 +24:0 +25:100 +26:100 +57:1 +54:0 +55:0 +82:0 +86:0 +59:0 +56:0 +58:0 +71:0 +44:65001 +46:0 +45:0 +49:0 +50:0 +51: +52: +53: +43:delayfixed +47:delayfixed +48: +15:0,0,0,0,0,0 +27:0, +34:0, +42:1 +72:0 +73:0 +35:Date +36:YYYY-MM-DD +37:2000 +38:1 +39:1 +40:2 +41:0 +95:0 +96:0 +97:0 +77:0 +78:0 +93:0 +94:0 +92:0 +91:0 +90:0 +87:0 +75: +43:delayfixed diff --git a/src/c/vensim.c b/src/c/vensim.c index a05f0d5b..2748f33b 100644 --- a/src/c/vensim.c +++ b/src/c/vensim.c @@ -10,7 +10,6 @@ double _epsilon = 1e-6; // See the Vensim Reference Manual for descriptions of the functions. // http://www.vensim.com/documentation/index.html?22300.htm // - double _PULSE(double start, double width) { double time_plus = _time + _time_step / 2.0; if (width == 0.0) { @@ -404,3 +403,33 @@ double* _ALLOCATE_AVAILABLE( // Return a pointer to the allocations array the caller passed with the results filled in. return allocations; } + +// +// DELAY FIXED +// +FixedDelay* __new_fixed_delay(double delay_time, double initial_value) { + // Make new fixed delay data with a ring buffer for the delay line. + // We don't know the size until runtime, so it must be dynamically allocated. + // Oniy initialize once, when the pointer to the structure is still null. + FixedDelay* fixed_delay = malloc(sizeof(FixedDelay)); + fixed_delay->n = (size_t)ceil(delay_time / _time_step); + fixed_delay->data = malloc(sizeof(double) * fixed_delay->n); + fixed_delay->data_index = 0; + fixed_delay->initial_value = initial_value; + return fixed_delay; +} +double _DELAY_FIXED(double input, FixedDelay* fixed_delay) { + // Cache input values in a ring buffer for the number of time steps equal to the delay time. + // Return the init value until the time reaches the delay time. + double result; + if (_time < fixed_delay->n * _time_step - 1e-6) { + result = fixed_delay->initial_value; + } else { + result = fixed_delay->data[fixed_delay->data_index + 1]; + } + fixed_delay->data[fixed_delay->data_index++] = input; + if (fixed_delay->data_index >= fixed_delay->n) { + fixed_delay->data_index = 0; + } + return result; +} diff --git a/src/c/vensim.h b/src/c/vensim.h index 2ded646e..a4616b13 100644 --- a/src/c/vensim.h +++ b/src/c/vensim.h @@ -71,6 +71,19 @@ double __get_data_between_times(double *data, size_t n, double input, LookupMode #define _GET_DATA_MODE_TO_LOOKUP_MODE(mode) ((mode) >= 1) ? Forward : (((mode) <= -1) ? Backward : Interpolate) #define _GET_DATA_BETWEEN_TIMES(lookup, x, mode) __get_data_between_times((lookup)->data, (lookup)->n, x, _GET_DATA_MODE_TO_LOOKUP_MODE(mode)) +// +// DELAY FIXED +// +typedef struct { + double* data; + size_t n; + size_t data_index; + double initial_value; +} FixedDelay; + +double _DELAY_FIXED(double input, FixedDelay* fixed_delay); +FixedDelay* __new_fixed_delay(double delay_time, double initial_value); + #ifdef __cplusplus } #endif From 994b172e35612c9edef0ef14fc2b27d9218cbf5f Mon Sep 17 00:00:00 2001 From: Todd Fincannon Date: Thu, 9 Sep 2021 21:20:42 -0700 Subject: [PATCH 2/7] set initial value in data buffer before delay time reached --- src/c/vensim.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/c/vensim.c b/src/c/vensim.c index 2748f33b..70d7825f 100644 --- a/src/c/vensim.c +++ b/src/c/vensim.c @@ -423,13 +423,26 @@ double _DELAY_FIXED(double input, FixedDelay* fixed_delay) { // Return the init value until the time reaches the delay time. double result; if (_time < fixed_delay->n * _time_step - 1e-6) { - result = fixed_delay->initial_value; + result = fixed_delay->initial_value; + fixed_delay->data[fixed_delay->data_index] = fixed_delay->initial_value; } else { - result = fixed_delay->data[fixed_delay->data_index + 1]; + result = fixed_delay->data[fixed_delay->data_index]; + fixed_delay->data[fixed_delay->data_index] = input; } - fixed_delay->data[fixed_delay->data_index++] = input; - if (fixed_delay->data_index >= fixed_delay->n) { + if (++fixed_delay->data_index >= fixed_delay->n) { fixed_delay->data_index = 0; } return result; } +// double _DELAY_FIXED(double input, FixedDelay* fixed_delay) { +// // Cache input values in a ring buffer for the number of time steps equal to the delay time. +// // Return the init value until the time reaches the delay time. +// double result; +// bool delayTimeReached = _time > fixed_delay->n * _time_step + 1e-6; +// result = delayTimeReached ? fixed_delay->data[fixed_delay->data_index] : fixed_delay->initial_value; +// fixed_delay->data[fixed_delay->data_index++] = delayTimeReached ? input : fixed_delay->initial_value; +// if (fixed_delay->data_index >= fixed_delay->n) { +// fixed_delay->data_index = 0; +// } +// return result; +// } From 1b6c44309e07e62d879aa80c5a05955a958e799a Mon Sep 17 00:00:00 2001 From: Todd Fincannon Date: Fri, 10 Sep 2021 17:16:53 -0700 Subject: [PATCH 3/7] emit FIXED DELAY as a level with special support data --- src/CodeGen.js | 7 ++++++- src/EquationGen.js | 22 +++++++++++++++++++--- src/EquationReader.js | 9 +++++++-- src/Helpers.js | 6 ++++++ src/ModelReader.js | 6 +++++- src/Variable.js | 7 +++++++ src/c/vensim.c | 36 ++++++++++++------------------------ 7 files changed, 62 insertions(+), 31 deletions(-) diff --git a/src/CodeGen.js b/src/CodeGen.js index d63c86be..33ee3efd 100644 --- a/src/CodeGen.js +++ b/src/CodeGen.js @@ -197,12 +197,17 @@ ${postStep} // function declSection() { // Emit a declaration for each variable in the model. + let fixedDelayDecls = '' let decl = v => { // Build a C array declaration for the variable v. // This uses the subscript family for each dimension, which may overallocate // if the subscript is a subdimension. let varType = v.isLookup() || v.isData() ? 'Lookup* ' : 'double ' let families = subscriptFamilies(v.subscripts) + if (v.isFixedDelay()) { + // Add the associated FixedDelay var decl. + fixedDelayDecls += `\nFixedDelay* ${v.fixedDelayVarName};` + } return varType + v.varName + R.map(family => `[${sub(family).size}]`, families).join('') } // Non-apply-to-all variables are declared multiple times, but coalesce using uniq. @@ -212,7 +217,7 @@ ${postStep} asort, lines ) - return decls(Model.allVars()) + return decls(Model.allVars()) + fixedDelayDecls } function internalVarsSection() { // Declare internal variables to run the model. diff --git a/src/EquationGen.js b/src/EquationGen.js index 455fd51c..64d1359f 100644 --- a/src/EquationGen.js +++ b/src/EquationGen.js @@ -754,23 +754,39 @@ export default class EquationGen extends ModelReader { let exprs = ctx.expr() let fn = this.currentFunctionName() // Split level functions into init and eval expressions. - if (fn === '_INTEG' || fn === '_SAMPLE_IF_TRUE' || fn === '_ACTIVE_INITIAL') { + if (fn === '_INTEG' || fn === '_SAMPLE_IF_TRUE' || fn === '_ACTIVE_INITIAL' || fn === '_DELAY_FIXED') { if (this.mode.startsWith('init')) { // Get the index of the argument holding the initial value. let i = 0 if (fn === '_INTEG' || fn === '_ACTIVE_INITIAL') { i = 1 - } else if (fn === '_SAMPLE_IF_TRUE') { + } else if (fn === '_SAMPLE_IF_TRUE' || fn === '_DELAY_FIXED') { i = 2 } this.setArgIndex(i) exprs[i].accept(this) + // For DELAY FIXED, also initialize the support struct out of band, as it is not a Vensim var. + if (fn === '_DELAY_FIXED') { + this.emit(`;\n ${this.var.fixedDelayVarName} = __new_fixed_delay(`) + this.setArgIndex(1) + exprs[1].accept(this) + this.emit(', ') + this.setArgIndex(2) + exprs[2].accept(this) + this.emit(')') + } } else { // We are in eval mode, not init mode. - // For ACTIVE INITIAL, emit the first arg without a function call. if (fn === '_ACTIVE_INITIAL') { + // For ACTIVE INITIAL, emit the first arg without a function call. this.setArgIndex(0) exprs[0].accept(this) + } else if (fn === '_DELAY_FIXED') { + // For DELAY FIXED, emit the first arg followed by the FixedDelay support var. + this.setArgIndex(0) + exprs[0].accept(this) + this.emit(', ') + this.emit(this.var.fixedDelayVarName) } else { // Emit the variable LHS as the first arg at eval time, giving the current value for the level. this.emit(this.lhs) diff --git a/src/EquationReader.js b/src/EquationReader.js index 9f37863d..d6220cd6 100644 --- a/src/EquationReader.js +++ b/src/EquationReader.js @@ -25,7 +25,8 @@ import { matchRegex, newAuxVarName, newLevelVarName, - newLookupVarName + newLookupVarName, + newFixedDelayVarName } from './Helpers.js' // Set this true to get a list of functions used in the model. This may include lookups. @@ -88,9 +89,13 @@ export default class EquationReader extends ModelReader { if (PRINT_FUNCTION_NAMES) { console.error(fn) } - if (fn === '_INTEG') { + if (fn === '_INTEG' || fn === '_DELAY_FIXED') { this.var.varType = 'level' this.var.hasInitValue = true + if (fn === '_DELAY_FIXED') { + this.var.varSubtype = 'fixedDelay' + this.var.fixedDelayVarName = canonicalName(newFixedDelayVarName()) + } } else if (fn === '_INITIAL') { this.var.varType = 'initial' this.var.hasInitValue = true diff --git a/src/Helpers.js b/src/Helpers.js index 12a6c826..5e586ee5 100644 --- a/src/Helpers.js +++ b/src/Helpers.js @@ -16,6 +16,8 @@ export const PRINT_VLOG_TRACE = false let nextTmpVarSeq = 1 // next sequence number for generated lookup variable names let nextLookupVarSeq = 1 +// next sequence number for generated fixed delay variable names +let nextFixedDelayVarSeq = 1 // next sequence number for generated level variable names let nextLevelVarSeq = 1 // next sequence number for generated aux variable names @@ -74,6 +76,10 @@ export let newLookupVarName = () => { // Return a unique lookup arg variable name return `_lookup${nextLookupVarSeq++}` } +export let newFixedDelayVarName = () => { + // Return a unique fixed delay variable name + return `_fixed_delay${nextFixedDelayVarSeq++}` +} export let newLevelVarName = (basename = null, levelNumber = 0) => { // Return a unique level variable name. let levelName = basename || nextLevelVarSeq++ diff --git a/src/ModelReader.js b/src/ModelReader.js index cc12f516..7b8970e6 100644 --- a/src/ModelReader.js +++ b/src/ModelReader.js @@ -3,20 +3,24 @@ import { ModelVisitor } from 'antlr4-vensim' export default class ModelReader extends ModelVisitor { constructor() { super() - // stack of function names and argument indices + // stack of function names and argument indices encountered on the RHS this.callStack = [] } currentFunctionName() { + // Return the name of the current function on top of the call stack. let n = this.callStack.length return n > 0 ? this.callStack[n - 1].fn : '' } setArgIndex(argIndex) { + // Set the argument index in the current function call on top of the call stack. + // This may be set in the exprList visitor and picked up in the var visitor to facilitate special argument handling. let n = this.callStack.length if (n > 0) { this.callStack[n - 1].argIndex = argIndex } } argIndexForFunctionName(name) { + // Search the call stack for the function name. Return the current argument index or undefined if not found. let argIndex for (let i = this.callStack.length - 1; i >= 0; i--) { if (this.callStack[i].fn === name) { diff --git a/src/Variable.js b/src/Variable.js index db7b35d2..879a719d 100644 --- a/src/Variable.js +++ b/src/Variable.js @@ -24,6 +24,8 @@ export default class Variable { this.refId = '' // The default varType is aux, but may be overridden later. this.varType = 'aux' + // The variable subtype accommodates special handling needed by some Vensim functions. + this.varSubtype = '' // A variable may reference other variable names at eval time. this.references = [] // Levels and certain other variables have an initial value that may reference other variable names. @@ -41,6 +43,8 @@ export default class Variable { // DELAY3* calls are expanded into new level vars and substituted during code generation. this.delayVarRefId = '' this.delayTimeVarName = '' + // DELAY FIXED calls generate a FixedDelay support var. + this.fixedDelayVarName = '' // Variables generated by special expansions are not included in output. this.includeInOutput = true } @@ -92,6 +96,9 @@ export default class Variable { isLevel() { return this.varType === 'level' } + isFixedDelay() { + return this.varSubtype === 'fixedDelay' + } isInitial() { return this.varType === 'initial' } diff --git a/src/c/vensim.c b/src/c/vensim.c index 70d7825f..daaa81e0 100644 --- a/src/c/vensim.c +++ b/src/c/vensim.c @@ -408,9 +408,10 @@ double* _ALLOCATE_AVAILABLE( // DELAY FIXED // FixedDelay* __new_fixed_delay(double delay_time, double initial_value) { - // Make new fixed delay data with a ring buffer for the delay line. + // Construct a FixedDelay struct with a ring buffer for the delay line. // We don't know the size until runtime, so it must be dynamically allocated. - // Oniy initialize once, when the pointer to the structure is still null. + // The delay time is quantized to an integral number of time steps. + // The FixedDelay should be constructed at init time to latch the delay time and initial value. FixedDelay* fixed_delay = malloc(sizeof(FixedDelay)); fixed_delay->n = (size_t)ceil(delay_time / _time_step); fixed_delay->data = malloc(sizeof(double) * fixed_delay->n); @@ -421,28 +422,15 @@ FixedDelay* __new_fixed_delay(double delay_time, double initial_value) { double _DELAY_FIXED(double input, FixedDelay* fixed_delay) { // Cache input values in a ring buffer for the number of time steps equal to the delay time. // Return the init value until the time reaches the delay time. - double result; - if (_time < fixed_delay->n * _time_step - 1e-6) { - result = fixed_delay->initial_value; - fixed_delay->data[fixed_delay->data_index] = fixed_delay->initial_value; - } else { - result = fixed_delay->data[fixed_delay->data_index]; - fixed_delay->data[fixed_delay->data_index] = input; - } - if (++fixed_delay->data_index >= fixed_delay->n) { - fixed_delay->data_index = 0; + double result = 0.0; + // Require the buffer size to be positive to protect from buffer overflows. + if (fixed_delay->n > 0) { + // Compare the current time to the delay time up to an epsilon difference. + bool delayTimeReached = _time > fixed_delay->n * _time_step - 1e-6; + fixed_delay->data[fixed_delay->data_index] = delayTimeReached ? input : fixed_delay->initial_value; + // Because DELAY FIXED is a level, get the value one time step ahead in the buffer. + fixed_delay->data_index = (fixed_delay->data_index + 1) % fixed_delay->n; + result = delayTimeReached ? fixed_delay->data[fixed_delay->data_index] : fixed_delay->initial_value; } return result; } -// double _DELAY_FIXED(double input, FixedDelay* fixed_delay) { -// // Cache input values in a ring buffer for the number of time steps equal to the delay time. -// // Return the init value until the time reaches the delay time. -// double result; -// bool delayTimeReached = _time > fixed_delay->n * _time_step + 1e-6; -// result = delayTimeReached ? fixed_delay->data[fixed_delay->data_index] : fixed_delay->initial_value; -// fixed_delay->data[fixed_delay->data_index++] = delayTimeReached ? input : fixed_delay->initial_value; -// if (fixed_delay->data_index >= fixed_delay->n) { -// fixed_delay->data_index = 0; -// } -// return result; -// } From 00011fc5ba613ce0ba4ef4ff4e83a5af2a951e29 Mon Sep 17 00:00:00 2001 From: Todd Fincannon Date: Fri, 10 Sep 2021 19:57:31 -0700 Subject: [PATCH 4/7] set init and eval references for the DELAY FIXED var --- src/EquationReader.js | 4 ++++ src/c/vensim.c | 11 +++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/EquationReader.js b/src/EquationReader.js index d6220cd6..d334c998 100644 --- a/src/EquationReader.js +++ b/src/EquationReader.js @@ -239,6 +239,10 @@ export default class EquationReader extends ModelReader { // with the generated level var. } else if (this.argIndexForFunctionName('_INTEG') === 1) { this.addReferencesToList(this.var.initReferences) + } else if (this.argIndexForFunctionName('_DELAY_FIXED') === 1) { + this.addReferencesToList(this.var.initReferences) + } else if (this.argIndexForFunctionName('_DELAY_FIXED') === 2) { + this.addReferencesToList(this.var.initReferences) } else if (this.argIndexForFunctionName('_ACTIVE_INITIAL') === 1) { this.addReferencesToList(this.var.initReferences) } else if (this.argIndexForFunctionName('_SAMPLE_IF_TRUE') === 2) { diff --git a/src/c/vensim.c b/src/c/vensim.c index daaa81e0..7672e2b6 100644 --- a/src/c/vensim.c +++ b/src/c/vensim.c @@ -425,12 +425,15 @@ double _DELAY_FIXED(double input, FixedDelay* fixed_delay) { double result = 0.0; // Require the buffer size to be positive to protect from buffer overflows. if (fixed_delay->n > 0) { - // Compare the current time to the delay time up to an epsilon difference. - bool delayTimeReached = _time > fixed_delay->n * _time_step - 1e-6; - fixed_delay->data[fixed_delay->data_index] = delayTimeReached ? input : fixed_delay->initial_value; + fixed_delay->data[fixed_delay->data_index] = input; // Because DELAY FIXED is a level, get the value one time step ahead in the buffer. fixed_delay->data_index = (fixed_delay->data_index + 1) % fixed_delay->n; - result = delayTimeReached ? fixed_delay->data[fixed_delay->data_index] : fixed_delay->initial_value; + // Compare the current time to the delay time up to an epsilon difference. + if (_time < fixed_delay->n * _time_step - 1e-6) { + result = fixed_delay->initial_value; + } else { + result = fixed_delay->data[fixed_delay->data_index]; + } } return result; } From 4eec5c017238a93220c834a9b686697b44a1f2d3 Mon Sep 17 00:00:00 2001 From: Todd Fincannon Date: Fri, 10 Sep 2021 21:07:27 -0700 Subject: [PATCH 5/7] add subscripts to FixedDelay support vars when LHS subscripts used --- models/delayfixed/delayfixed.dat | 514 +++++++++++++++++++------------ models/delayfixed/delayfixed.mdl | 65 +--- src/CodeGen.js | 5 +- src/EquationGen.js | 4 +- 4 files changed, 336 insertions(+), 252 deletions(-) diff --git a/models/delayfixed/delayfixed.dat b/models/delayfixed/delayfixed.dat index 3ea617a7..b96279af 100644 --- a/models/delayfixed/delayfixed.dat +++ b/models/delayfixed/delayfixed.dat @@ -1,7 +1,319 @@ FINAL TIME -0 100 +0 50 INITIAL TIME 0 0 +input[A1] +0 0 +1 10 +2 20 +3 30 +4 40 +5 50 +6 60 +7 70 +8 80 +9 90 +10 100 +11 110 +12 120 +13 130 +14 140 +15 150 +16 160 +17 170 +18 180 +19 190 +20 200 +21 210 +22 220 +23 230 +24 240 +25 250 +26 260 +27 270 +28 280 +29 290 +30 300 +31 310 +32 320 +33 330 +34 340 +35 350 +36 360 +37 370 +38 380 +39 390 +40 400 +41 410 +42 420 +43 430 +44 440 +45 450 +46 460 +47 470 +48 480 +49 490 +50 500 +input[A2] +0 0 +1 20 +2 40 +3 60 +4 80 +5 100 +6 120 +7 140 +8 160 +9 180 +10 200 +11 220 +12 240 +13 260 +14 280 +15 300 +16 320 +17 340 +18 360 +19 380 +20 400 +21 420 +22 440 +23 460 +24 480 +25 500 +26 520 +27 540 +28 560 +29 580 +30 600 +31 620 +32 640 +33 660 +34 680 +35 700 +36 720 +37 740 +38 760 +39 780 +40 800 +41 820 +42 840 +43 860 +44 880 +45 900 +46 920 +47 940 +48 960 +49 980 +50 1000 +input[A3] +0 0 +1 30 +2 60 +3 90 +4 120 +5 150 +6 180 +7 210 +8 240 +9 270 +10 300 +11 330 +12 360 +13 390 +14 420 +15 450 +16 480 +17 510 +18 540 +19 570 +20 600 +21 630 +22 660 +23 690 +24 720 +25 750 +26 780 +27 810 +28 840 +29 870 +30 900 +31 930 +32 960 +33 990 +34 1020 +35 1050 +36 1080 +37 1110 +38 1140 +39 1170 +40 1200 +41 1230 +42 1260 +43 1290 +44 1320 +45 1350 +46 1380 +47 1410 +48 1440 +49 1470 +50 1500 +output[A1] +0 0 +1 0 +2 10 +3 20 +4 30 +5 40 +6 50 +7 60 +8 70 +9 80 +10 90 +11 100 +12 110 +13 120 +14 130 +15 140 +16 150 +17 160 +18 170 +19 180 +20 190 +21 200 +22 210 +23 220 +24 230 +25 240 +26 250 +27 260 +28 270 +29 280 +30 290 +31 300 +32 310 +33 320 +34 330 +35 340 +36 350 +37 360 +38 370 +39 380 +40 390 +41 400 +42 410 +43 420 +44 430 +45 440 +46 450 +47 460 +48 470 +49 480 +50 490 +output[A2] +0 0 +1 0 +2 20 +3 40 +4 60 +5 80 +6 100 +7 120 +8 140 +9 160 +10 180 +11 200 +12 220 +13 240 +14 260 +15 280 +16 300 +17 320 +18 340 +19 360 +20 380 +21 400 +22 420 +23 440 +24 460 +25 480 +26 500 +27 520 +28 540 +29 560 +30 580 +31 600 +32 620 +33 640 +34 660 +35 680 +36 700 +37 720 +38 740 +39 760 +40 780 +41 800 +42 820 +43 840 +44 860 +45 880 +46 900 +47 920 +48 940 +49 960 +50 980 +output[A3] +0 0 +1 0 +2 30 +3 60 +4 90 +5 120 +6 150 +7 180 +8 210 +9 240 +10 270 +11 300 +12 330 +13 360 +14 390 +15 420 +16 450 +17 480 +18 510 +19 540 +20 570 +21 600 +22 630 +23 660 +24 690 +25 720 +26 750 +27 780 +28 810 +29 840 +30 870 +31 900 +32 930 +33 960 +34 990 +35 1020 +36 1050 +37 1080 +38 1110 +39 1140 +40 1170 +41 1200 +42 1230 +43 1260 +44 1290 +45 1320 +46 1350 +47 1380 +48 1410 +49 1440 +50 1470 receiving 0 0 1 0 @@ -54,56 +366,6 @@ receiving 48 0 49 0 50 0 -51 0 -52 0 -53 0 -54 0 -55 0 -56 0 -57 0 -58 0 -59 0 -60 0 -61 0 -62 0 -63 0 -64 0 -65 0 -66 0 -67 0 -68 0 -69 0 -70 0 -71 0 -72 0 -73 0 -74 0 -75 0 -76 0 -77 0 -78 0 -79 0 -80 0 -81 0 -82 0 -83 0 -84 0 -85 0 -86 0 -87 0 -88 0 -89 0 -90 0 -91 0 -92 0 -93 0 -94 0 -95 0 -96 0 -97 0 -98 0 -99 0 -100 0 reference shipping rate 0 1 SAVEPER @@ -158,56 +420,6 @@ SAVEPER 48 1 49 1 50 1 -51 1 -52 1 -53 1 -54 1 -55 1 -56 1 -57 1 -58 1 -59 1 -60 1 -61 1 -62 1 -63 1 -64 1 -65 1 -66 1 -67 1 -68 1 -69 1 -70 1 -71 1 -72 1 -73 1 -74 1 -75 1 -76 1 -77 1 -78 1 -79 1 -80 1 -81 1 -82 1 -83 1 -84 1 -85 1 -86 1 -87 1 -88 1 -89 1 -90 1 -91 1 -92 1 -93 1 -94 1 -95 1 -96 1 -97 1 -98 1 -99 1 -100 1 shipments in transit 0 0 1 0 @@ -260,56 +472,6 @@ shipments in transit 48 0 49 0 50 0 -51 0 -52 0 -53 0 -54 0 -55 0 -56 0 -57 0 -58 0 -59 0 -60 0 -61 0 -62 0 -63 0 -64 0 -65 0 -66 0 -67 0 -68 0 -69 0 -70 0 -71 0 -72 0 -73 0 -74 0 -75 0 -76 0 -77 0 -78 0 -79 0 -80 0 -81 0 -82 0 -83 0 -84 0 -85 0 -86 0 -87 0 -88 0 -89 0 -90 0 -91 0 -92 0 -93 0 -94 0 -95 0 -96 0 -97 0 -98 0 -99 0 -100 0 shipping 0 0 1 0 @@ -362,56 +524,6 @@ shipping 48 0 49 0 50 0 -51 0 -52 0 -53 0 -54 0 -55 0 -56 0 -57 0 -58 0 -59 0 -60 0 -61 0 -62 0 -63 0 -64 0 -65 0 -66 0 -67 0 -68 0 -69 0 -70 0 -71 0 -72 0 -73 0 -74 0 -75 0 -76 0 -77 0 -78 0 -79 0 -80 0 -81 0 -82 0 -83 0 -84 0 -85 0 -86 0 -87 0 -88 0 -89 0 -90 0 -91 0 -92 0 -93 0 -94 0 -95 0 -96 0 -97 0 -98 0 -99 0 -100 0 shipping time 0 20 TIME STEP diff --git a/models/delayfixed/delayfixed.mdl b/models/delayfixed/delayfixed.mdl index ee8c86e2..45d0702a 100644 --- a/models/delayfixed/delayfixed.mdl +++ b/models/delayfixed/delayfixed.mdl @@ -1,55 +1,33 @@ {UTF-8} receiving = DELAY FIXED(shipping, shipping time, shipping) ~~| -shipments in transit = INTEG(shipping - receiving, shipping * shipping time) ~~| -reference shipping rate = 1 ~~| shipping = STEP(reference shipping rate, 10) - STEP(reference shipping rate, 20) ~~| shipping time = 20 ~~| +reference shipping rate = 1 ~~| +shipments in transit = INTEG(shipping - receiving, shipping * shipping time) ~~| + +DimA: A1, A2, A3 ~~| +input[A1] = 10 * TIME ~~| +input[A2] = 20 * TIME ~~| +input[A3] = 30 * TIME ~~| +output[DimA] = DELAY FIXED(input[DimA], 1, 0) ~~| INITIAL TIME = 0 ~~| -FINAL TIME = 100 ~~| +FINAL TIME = 50 ~~| 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 -$192-192-192,0,Times New Roman|12||0-0-0|0-0-0|0-0-255|-1--1--1|255-255-255|96,96,100,0 +$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!@ -1:delayfixed.vdfx -4:Time -5:FINAL TIME 9:delayfixed -19:100,0 -24:0 -25:100 -26:100 -57:1 -54:0 -55:0 -82:0 -86:0 -59:0 -56:0 -58:0 -71:0 -44:65001 -46:0 -45:0 -49:0 -50:0 -51: -52: -53: -43:delayfixed -47:delayfixed -48: 15:0,0,0,0,0,0 -27:0, +19:100,0 +27:2, 34:0, -42:1 -72:0 -73:0 +5:FINAL TIME 35:Date 36:YYYY-MM-DD 37:2000 @@ -57,16 +35,7 @@ $192-192-192,0,Times New Roman|12||0-0-0|0-0-0|0-0-255|-1--1--1|255-255-255|96,9 39:1 40:2 41:0 -95:0 -96:0 -97:0 -77:0 -78:0 -93:0 -94:0 -92:0 -91:0 -90:0 -87:0 -75: -43:delayfixed +42:1 +24:0 +25:0 +26:0 diff --git a/src/CodeGen.js b/src/CodeGen.js index 33ee3efd..0c0a6a08 100644 --- a/src/CodeGen.js +++ b/src/CodeGen.js @@ -206,7 +206,10 @@ ${postStep} let families = subscriptFamilies(v.subscripts) if (v.isFixedDelay()) { // Add the associated FixedDelay var decl. - fixedDelayDecls += `\nFixedDelay* ${v.fixedDelayVarName};` + fixedDelayDecls += `\nFixedDelay* ${v.fixedDelayVarName}${R.map( + family => `[${sub(family).size}]`, + families + ).join('')};` } return varType + v.varName + R.map(family => `[${sub(family).size}]`, families).join('') } diff --git a/src/EquationGen.js b/src/EquationGen.js index 64d1359f..be58afdb 100644 --- a/src/EquationGen.js +++ b/src/EquationGen.js @@ -767,7 +767,7 @@ export default class EquationGen extends ModelReader { exprs[i].accept(this) // For DELAY FIXED, also initialize the support struct out of band, as it is not a Vensim var. if (fn === '_DELAY_FIXED') { - this.emit(`;\n ${this.var.fixedDelayVarName} = __new_fixed_delay(`) + this.emit(`;\n ${this.var.fixedDelayVarName}${this.lhsSubscriptGen(this.var.subscripts)} = __new_fixed_delay(`) this.setArgIndex(1) exprs[1].accept(this) this.emit(', ') @@ -786,7 +786,7 @@ export default class EquationGen extends ModelReader { this.setArgIndex(0) exprs[0].accept(this) this.emit(', ') - this.emit(this.var.fixedDelayVarName) + this.emit(`${this.var.fixedDelayVarName}${this.lhsSubscriptGen(this.var.subscripts)}`) } else { // Emit the variable LHS as the first arg at eval time, giving the current value for the level. this.emit(this.lhs) From 1ea5ed1e68793c6f29615f8275207e339ac743b2 Mon Sep 17 00:00:00 2001 From: Todd Fincannon Date: Fri, 10 Sep 2021 21:10:10 -0700 Subject: [PATCH 6/7] run prettier on source files --- .prettierignore | 1 + src/EquationGen.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index e90d48e4..32d13ceb 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ src/web +.vscode diff --git a/src/EquationGen.js b/src/EquationGen.js index be58afdb..9f0453ab 100644 --- a/src/EquationGen.js +++ b/src/EquationGen.js @@ -767,7 +767,9 @@ export default class EquationGen extends ModelReader { exprs[i].accept(this) // For DELAY FIXED, also initialize the support struct out of band, as it is not a Vensim var. if (fn === '_DELAY_FIXED') { - this.emit(`;\n ${this.var.fixedDelayVarName}${this.lhsSubscriptGen(this.var.subscripts)} = __new_fixed_delay(`) + this.emit( + `;\n ${this.var.fixedDelayVarName}${this.lhsSubscriptGen(this.var.subscripts)} = __new_fixed_delay(` + ) this.setArgIndex(1) exprs[1].accept(this) this.emit(', ') From 4d9fe70cf8884e3735e8349828566ecd2eb11eca Mon Sep 17 00:00:00 2001 From: Todd Fincannon Date: Mon, 27 Sep 2021 13:04:38 -0700 Subject: [PATCH 7/7] allow for INITIAL TIME > 0 in DELAY FIXED --- models/delayfixed2/delayfixed2.dat | 66 ++++++++++++++++++++++++++++++ models/delayfixed2/delayfixed2.mdl | 35 ++++++++++++++++ src/c/vensim.c | 4 +- 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 models/delayfixed2/delayfixed2.dat create mode 100644 models/delayfixed2/delayfixed2.mdl diff --git a/models/delayfixed2/delayfixed2.dat b/models/delayfixed2/delayfixed2.dat new file mode 100644 index 00000000..a0f75bdc --- /dev/null +++ b/models/delayfixed2/delayfixed2.dat @@ -0,0 +1,66 @@ +FINAL TIME +10 20 +INITIAL TIME +10 10 +input1 +10 110 +11 120 +12 130 +13 140 +14 150 +15 160 +16 170 +17 180 +18 190 +19 200 +20 210 +input2 +10 110 +11 120 +12 130 +13 140 +14 150 +15 160 +16 170 +17 180 +18 190 +19 200 +20 210 +output1 +10 0 +11 110 +12 120 +13 130 +14 140 +15 150 +16 160 +17 170 +18 180 +19 190 +20 200 +output2 +10 0 +11 0 +12 0 +13 0 +14 0 +15 110 +16 120 +17 130 +18 140 +19 150 +20 160 +SAVEPER +10 1 +11 1 +12 1 +13 1 +14 1 +15 1 +16 1 +17 1 +18 1 +19 1 +20 1 +TIME STEP +10 1 diff --git a/models/delayfixed2/delayfixed2.mdl b/models/delayfixed2/delayfixed2.mdl new file mode 100644 index 00000000..2a85c9e9 --- /dev/null +++ b/models/delayfixed2/delayfixed2.mdl @@ -0,0 +1,35 @@ +{UTF-8} +input1 = 10 * TIME + 10 ~~| +output1 = DELAY FIXED(input1, 1, 0) ~~| + +input2 = 10 * TIME + 10 ~~| +output2 = DELAY FIXED(input2, 5, 0) ~~| + +INITIAL TIME = 10 ~~| +FINAL TIME = 20 ~~| +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:delayfixed2 +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 diff --git a/src/c/vensim.c b/src/c/vensim.c index 7672e2b6..b8cee120 100644 --- a/src/c/vensim.c +++ b/src/c/vensim.c @@ -428,8 +428,8 @@ double _DELAY_FIXED(double input, FixedDelay* fixed_delay) { fixed_delay->data[fixed_delay->data_index] = input; // Because DELAY FIXED is a level, get the value one time step ahead in the buffer. fixed_delay->data_index = (fixed_delay->data_index + 1) % fixed_delay->n; - // Compare the current time to the delay time up to an epsilon difference. - if (_time < fixed_delay->n * _time_step - 1e-6) { + // Start pulling from the ring buffer when the next time step will reach the delay time. + if (_time < _initial_time + (fixed_delay->n - 1) * _time_step - 1e-6) { result = fixed_delay->initial_value; } else { result = fixed_delay->data[fixed_delay->data_index];