@sdeverywhere/plugin-check 0.3.19 → 0.3.21

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.
@@ -4,25 +4,24 @@ import type {
4
4
  Bundle,
5
5
  BundleGraphData,
6
6
  BundleModel as CheckBundleModel,
7
- Dataset,
8
7
  DatasetKey,
9
- DatasetMap,
10
8
  DatasetsResult,
11
9
  LinkItem,
12
10
  ModelSpec,
13
11
  ScenarioSpec
14
12
  } from '@sdeverywhere/check-core'
15
13
 
16
- import type { InputValue, InputVarId, ModelRunner, Outputs, Point } from '@sdeverywhere/runtime'
14
+ import type { InputVarId } from '@sdeverywhere/runtime'
17
15
  import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async'
18
16
 
19
- import type { Input } from './inputs'
20
- import { getInputVars, setInputsForScenario } from './inputs'
17
+ import { getImplVars } from './impl-vars'
18
+ import { type Input, getInputVars } from './inputs'
21
19
  import { getOutputVars } from './outputs'
22
20
 
23
- import { inputSpecs, outputSpecs, modelSizeInBytes, dataSizeInBytes } from 'virtual:model-spec'
21
+ import { encodedImplVars, inputSpecs, outputSpecs, modelSizeInBytes, dataSizeInBytes } from 'virtual:model-spec'
24
22
 
25
23
  import modelWorkerJs from '@_model_worker_/worker.js?raw'
24
+ import { BundleModelRunner } from './bundle-model-runner'
26
25
 
27
26
  // The current version of the check bundle format. This should be
28
27
  // incremented when there is an incompatible change to the bundle format.
@@ -31,62 +30,18 @@ import modelWorkerJs from '@_model_worker_/worker.js?raw'
31
30
  const VERSION = 1
32
31
 
33
32
  export class BundleModel implements CheckBundleModel {
34
- private readonly inputs: InputValue[]
35
- private outputs: Outputs
36
-
37
33
  /**
38
34
  * @param modelSpec The spec for the bundled model.
39
- * @param inputMap The model inputs.
40
- * @param modelRunner The model runner.
35
+ * @param bundleModelRunner The bundle model runner.
41
36
  */
42
37
  constructor(
43
38
  public readonly modelSpec: ModelSpec,
44
- private readonly inputMap: Map<InputVarId, Input>,
45
- private readonly modelRunner: ModelRunner
46
- ) {
47
- // Derive an array of `InputValue` instances that can be passed to the runner
48
- this.inputs = [...inputMap.values()].map(input => input.value)
49
-
50
- // Create an `Outputs` instance that is initialized to hold output data
51
- // produced by the Wasm model
52
- this.outputs = modelRunner.createOutputs()
53
- }
39
+ private readonly bundleModelRunner: BundleModelRunner
40
+ ) {}
54
41
 
55
42
  // from CheckBundleModel interface
56
43
  async getDatasetsForScenario(scenario: ScenarioSpec, datasetKeys: DatasetKey[]): Promise<DatasetsResult> {
57
- const datasetMap: DatasetMap = new Map()
58
-
59
- // Set the input values according to the given scenario
60
- setInputsForScenario(this.inputMap, scenario)
61
-
62
- // Run the JS model
63
- this.outputs = await this.modelRunner.runModel(this.inputs, this.outputs)
64
- const modelRunTime = this.outputs.runTimeInMillis
65
-
66
- // Extract the data for each requested output variable and put it into a map
67
- for (const datasetKey of datasetKeys) {
68
- // Get the output variable for the given dataset key; if the variable doesn't
69
- // exist in this version of the model/bundle, just skip it
70
- const outputVar = this.modelSpec.outputVars.get(datasetKey)
71
- if (!outputVar) {
72
- continue
73
- }
74
-
75
- if (outputVar.sourceName === undefined) {
76
- // See if we have data for the requested model output variable
77
- const series = this.outputs.getSeriesForVar(outputVar.varId)
78
- if (series) {
79
- datasetMap.set(datasetKey, datasetFromPoints(series.points))
80
- }
81
- } else {
82
- console.error('Static data sources not yet handled in default model check bundle')
83
- }
84
- }
85
-
86
- return {
87
- datasetMap,
88
- modelRunTime
89
- }
44
+ return this.bundleModelRunner.runModelForScenario(scenario, datasetKeys)
90
45
  }
91
46
 
92
47
  // from CheckBundleModel interface
@@ -107,28 +62,16 @@ export class BundleModel implements CheckBundleModel {
107
62
  * under different scenarios.
108
63
  */
109
64
  async function initBundleModel(modelSpec: ModelSpec, inputMap: Map<InputVarId, Input>): Promise<BundleModel> {
110
- // Initialize the Wasm model asynchronously. We inline the worker code in the
65
+ // Initialize the JS/Wasm model asynchronously. We inline the worker code in the
111
66
  // rolled-up bundle so that we don't have to fetch a separate `worker.js` file
112
67
  const modelRunner = await spawnAsyncModelRunner({ source: modelWorkerJs })
113
68
 
114
- // Return a `BundleModel` that wraps the underlying config and Wasm model
115
- return new BundleModel(modelSpec, inputMap, modelRunner)
116
- }
69
+ // Create a `BundleModelRunner` that wraps the `ModelRunner` instance and takes
70
+ // care of translating `runtime` types to the types expected by `check-core`
71
+ const bundleModelRunner = new BundleModelRunner(modelSpec, inputMap, modelRunner)
117
72
 
118
- /**
119
- * Create a `Dataset` containing the given data points from the model.
120
- */
121
- export function datasetFromPoints(points: Point[]): Dataset {
122
- const dataMap = new Map()
123
- for (const point of points) {
124
- // We omit points that have an undefined value. SDE represents `:NA:` values as
125
- // a special value (`-DBL_MAX`); the `runtime` package detects these and converts
126
- // them to undefined instead. We don't need them to appear in graphs, so omit them.
127
- if (point.y !== undefined) {
128
- dataMap.set(point.x, point.y)
129
- }
130
- }
131
- return dataMap
73
+ // Return a `BundleModel` that wraps the underlying config and Wasm model
74
+ return new BundleModel(modelSpec, bundleModelRunner)
132
75
  }
133
76
 
134
77
  /**
@@ -140,12 +83,16 @@ export function createBundle(): Bundle {
140
83
  const inputVars = getInputVars(inputSpecs)
141
84
  const outputVars = getOutputVars(outputSpecs)
142
85
 
86
+ // Gather information about internal/implementation variables
87
+ const { implVars, implVarGroups } = getImplVars(encodedImplVars)
88
+
143
89
  const modelSpec: ModelSpec = {
144
90
  modelSizeInBytes,
145
91
  dataSizeInBytes,
146
92
  inputVars,
147
93
  outputVars,
148
- implVars: new Map()
94
+ implVars,
95
+ implVarGroups
149
96
  // TODO: startTime and endTime are optional; the comparison graphs work OK if
150
97
  // they are undefined. The main benefit of using these is to set a specific
151
98
  // range for the x-axis on the comparison graphs, so maybe we should find
@@ -6,10 +6,14 @@
6
6
  * up using aliases in the Vite config file.)
7
7
  */
8
8
 
9
+ import type { EncodedImplVars } from '@sdeverywhere/check-core'
10
+
9
11
  import type { InputSpec } from './inputs'
10
12
  import type { OutputSpec } from './outputs'
11
13
 
12
14
  export const inputSpecs: InputSpec[] = []
13
15
  export const outputSpecs: OutputSpec[] = []
16
+ export const encodedImplVars: EncodedImplVars = undefined
17
+
14
18
  export const modelSizeInBytes = 0
15
19
  export const dataSizeInBytes = 0
@@ -0,0 +1,59 @@
1
+ // Copyright (c) 2022 Climate Interactive / New Venture Fund
2
+
3
+ import type { DatasetKey, EncodedImplVars, ImplVar, ImplVarGroup } from '@sdeverywhere/check-core'
4
+ import { decodeImplVars } from '@sdeverywhere/check-core'
5
+
6
+ /** The properties related to impl variables that will be included in the bundle model spec. */
7
+ export interface ImplSpec {
8
+ /** The map of all variables (both internal and exported) in this version of the model. */
9
+ implVars: Map<DatasetKey, ImplVar>
10
+ /** The groupings of internal/implementation variables in this version of the model. */
11
+ implVarGroups?: ImplVarGroup[]
12
+ }
13
+
14
+ /**
15
+ * Gather the set of internal/implementation variables used in this version of the model.
16
+ */
17
+ export function getImplVars(encodedImplVars: EncodedImplVars): ImplSpec {
18
+ // Decode the encoded impl variables into the original `ImplVar` objects
19
+ const decodedImplVars = decodeImplVars(encodedImplVars)
20
+
21
+ const implVars: Map<DatasetKey, ImplVar> = new Map()
22
+ const implVarGroups: ImplVarGroup[] = []
23
+
24
+ function addGroup(fn: string, implVarInstances: ImplVar[]): void {
25
+ // Add the var instances for this group to the map
26
+ const datasetKeysForGroup: DatasetKey[] = []
27
+ for (const implVarInstance of implVarInstances) {
28
+ // TODO: For now, skip lookup and data variables because we have no way to
29
+ // access them in the model
30
+ if (implVarInstance.varType === 'lookup' || implVarInstance.varType === 'data') {
31
+ continue
32
+ }
33
+ const varId = implVarInstance.varId
34
+ const datasetKey = `ModelImpl_${varId}`
35
+ implVars.set(datasetKey, implVarInstance)
36
+ datasetKeysForGroup.push(datasetKey)
37
+ }
38
+
39
+ // Add the group info
40
+ implVarGroups.push({
41
+ title: fn,
42
+ fn,
43
+ datasetKeys: datasetKeysForGroup
44
+ })
45
+ }
46
+
47
+ addGroup('initConstants', decodedImplVars.constants || [])
48
+ // TODO: Include lookups and data variables
49
+ // addGroup('initLookups', decodedImplVars.lookupVars || [])
50
+ // addGroup('initData', decodedImplVars.dataVars || [])
51
+ addGroup('initLevels', decodedImplVars.initVars || [])
52
+ addGroup('evalLevels', decodedImplVars.levelVars || [])
53
+ addGroup('evalAux', decodedImplVars.auxVars || [])
54
+
55
+ return {
56
+ implVars,
57
+ implVarGroups
58
+ }
59
+ }
@@ -23,6 +23,28 @@
23
23
  }
24
24
  </style>
25
25
 
26
+ <!--
27
+ XXX: Include no-op polyfills for these modules that are used in the Node-specific
28
+ implementation of threads.js. This allows us to use one model-check bundle that
29
+ works in both Node and browser environments. Without this importmap, loading a
30
+ remote bundle would fail with the following error when trying to import Node
31
+ modules like `worker_threads` that aren't available in the browser:
32
+ TypeError: The specifier “worker_threads” was a bare specifier, but was not
33
+ remapped to anything.
34
+ -->
35
+ <script type="importmap">
36
+ {
37
+ "imports": {
38
+ "events": "./polyfills/noop-polyfills.js",
39
+ "fs": "./polyfills/noop-polyfills.js",
40
+ "os": "./polyfills/noop-polyfills.js",
41
+ "path": "./polyfills/noop-polyfills.js",
42
+ "url": "./polyfills/noop-polyfills.js",
43
+ "worker_threads": "./polyfills/noop-polyfills.js"
44
+ }
45
+ }
46
+ </script>
47
+
26
48
  <script type="module" src="./src/index.ts"></script>
27
49
  </head>
28
50
 
@@ -1,2 +1,2 @@
1
- This file is not used but must remain present to ensure that the `template-report/src/baselines`
1
+ This file is not used but must remain present to ensure that the `template-report/src/bundles`
2
2
  directory exists. See `vite-config-for-report.ts` for details.
@@ -2,6 +2,8 @@
2
2
 
3
3
  // These values are injected by Vite at build time, so we need to
4
4
  // declare types for them here
5
+ declare const __SUITE_SUMMARY_JSON__: string
5
6
  declare const __BASELINE_NAME__: string
6
7
  declare const __CURRENT_NAME__: string
7
- declare const __SUITE_SUMMARY_JSON__: string
8
+ declare const __CURRENT_BUNDLE_LAST_MODIFIED__: string
9
+ declare const __REMOTE_BUNDLES_URL__: string