@sdeverywhere/check-core 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +12 -0
- package/dist/index.cjs +2988 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +907 -0
- package/dist/index.js +2938 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
- package/schema/check.schema.json +756 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/_shared/scenario.ts","../src/_shared/task-queue.ts","../src/check/check-data-coordinator.ts","../src/check/check-report.ts","../src/check/check-predicate.ts","../src/check/check-summary.ts","../src/check/check-parser.ts","../src/check/check.schema.js","../src/check/check-planner.ts","../src/check/check-func.ts","../src/check/check-action.ts","../src/_shared/combo.ts","../src/check/check-dataset.ts","../src/check/check-scenario.ts","../src/compare/compare-data-coordinator.ts","../src/compare/compare-datasets.ts","../src/compare/compare-graphs.ts","../src/compare/compare-summary.ts","../src/config/synchronized-model.ts","../src/config/config.ts","../src/config/dataset-manager.ts","../src/config/scenario-manager.ts","../src/perf/perf-runner.ts","../src/perf/perf-stats.ts","../src/suite/suite-runner.ts","../src/data/data-planner.ts","../src/check/check-runner.ts","../src/compare/compare-runner.ts","../src/suite/suite-summary.ts"],"sourcesContent":["// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\nimport type { ScenarioGroupKey, ScenarioKey, VarId } from './types'\n\nexport type InputPosition = 'at-default' | 'at-minimum' | 'at-maximum'\n\nexport interface PositionSetting {\n kind: 'position'\n inputVarId: VarId\n position: InputPosition\n}\n\nexport interface ValueSetting {\n kind: 'value'\n inputVarId: VarId\n value: number\n}\n\nexport type InputSetting = PositionSetting | ValueSetting\n\nexport interface SettingsScenario {\n kind: 'settings'\n key: ScenarioKey\n groupKey: ScenarioGroupKey\n settings: InputSetting[]\n}\n\nexport interface AllInputsScenario {\n kind: 'all-inputs'\n key: ScenarioKey\n groupKey: ScenarioGroupKey\n position: InputPosition\n}\n\nexport type Scenario = SettingsScenario | AllInputsScenario\n\nexport function positionSetting(inputVarId: VarId, position: InputPosition): InputSetting {\n return {\n kind: 'position',\n inputVarId,\n position\n }\n}\n\nexport function valueSetting(inputVarId: VarId, value: number): InputSetting {\n return {\n kind: 'value',\n inputVarId,\n value\n }\n}\n\nexport function settingsScenario(key: ScenarioKey, groupKey: ScenarioGroupKey, settings: InputSetting[]): Scenario {\n return {\n kind: 'settings',\n key,\n groupKey,\n settings\n }\n}\n\nexport function inputAtPositionScenario(\n inputVarId: VarId,\n groupKey: ScenarioGroupKey,\n position: InputPosition\n): Scenario {\n const key = keyForInputAtPosition(`input${inputVarId}`, position)\n return settingsScenario(key, groupKey, [positionSetting(inputVarId, position)])\n}\n\nexport function inputAtValueScenario(inputVarId: VarId, groupKey: ScenarioGroupKey, value: number): Scenario {\n const key = keyForInputAtValue(`input${inputVarId}`, value)\n return settingsScenario(key, groupKey, [valueSetting(inputVarId, value)])\n}\n\nexport function allInputsAtPositionScenario(position: InputPosition): Scenario {\n return {\n kind: 'all-inputs',\n key: keyForInputAtPosition('all_inputs', position),\n groupKey: 'all_inputs',\n position\n }\n}\n\n/**\n * Return an array of scenarios that can be used to run the model\n * with a matrix of output/input scenarios.\n *\n * For each output variable, run the model:\n * - once with all inputs at their default\n * - once with all inputs at their minimum\n * - once with all inputs at their maximum\n * - twice for each input\n * - once with single input at its minimum\n * - once with single input at its maximum\n */\nexport function matrixScenarios(inputVarIds: VarId[]): Scenario[] {\n const scenarios: Scenario[] = []\n scenarios.push(allInputsAtPositionScenario('at-default'))\n scenarios.push(allInputsAtPositionScenario('at-minimum'))\n scenarios.push(allInputsAtPositionScenario('at-maximum'))\n for (const inputVarId of inputVarIds) {\n scenarios.push(inputAtPositionScenario(inputVarId, inputVarId, 'at-minimum'))\n scenarios.push(inputAtPositionScenario(inputVarId, inputVarId, 'at-maximum'))\n }\n return scenarios\n}\n\nfunction keyForInputPosition(position: InputPosition): string {\n switch (position) {\n case 'at-default':\n return 'default'\n case 'at-minimum':\n return 'min'\n case 'at-maximum':\n return 'max'\n default:\n assertNever(position)\n }\n}\n\nexport function keyForInputAtPosition(inputKey: string, position: InputPosition): string {\n return `${inputKey}_at_${keyForInputPosition(position)}`\n}\n\nexport function keyForInputAtValue(inputKey: string, value: number): string {\n return `${inputKey}_at_${value}`\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nexport type TaskKey = string\n\nexport interface TaskProcessor<I, O> {\n process(input: I): Promise<O>\n}\n\ninterface Task<I, O> {\n input: I\n onComplete: (output: O) => void\n}\n\nexport class TaskQueue<I, O> {\n /** The queue of task keys, most recent at front. */\n private readonly taskKeyQueue: TaskKey[] = []\n\n /** The map of tasks. */\n private readonly taskMap: Map<TaskKey, Task<I, O>> = new Map()\n\n /** Whether tasks are being processed. */\n private processing = false\n\n /** Whether `shutdown` has been called. */\n private stopped = false\n\n public onIdle?: (error?: Error) => void\n\n constructor(private readonly processor: TaskProcessor<I, O>) {}\n\n addTask(key: TaskKey, input: I, onComplete: (output: O) => void): void {\n if (this.stopped) {\n return\n }\n\n if (this.taskMap.has(key)) {\n throw new Error(`Task already added for key ${key}`)\n }\n\n // Add the latest request at the end of the queue\n this.taskKeyQueue.push(key)\n this.taskMap.set(key, {\n input,\n onComplete\n })\n\n // Start the process if it's not already in motion\n this.processTasksIfNeeded()\n }\n\n cancelTask(taskKey: TaskKey): void {\n const index = this.taskKeyQueue.indexOf(taskKey)\n if (index >= 0) {\n this.taskKeyQueue.splice(index, 1)\n }\n this.taskMap.delete(taskKey)\n }\n\n shutdown(): void {\n this.stopped = true\n this.processing = false\n this.taskKeyQueue.length = 0\n this.taskMap.clear()\n }\n\n private processTasksIfNeeded(): void {\n if (!this.stopped && !this.processing) {\n // No tasks are already in being processed, so schedule them now\n this.processing = true\n\n // Process the next task asynchronously\n setTimeout(() => {\n this.processNextTask()\n })\n }\n }\n\n private async processNextTask(): Promise<void> {\n // Pop the latest request off the front of the queue\n const taskKey = this.taskKeyQueue.shift()\n if (!taskKey) {\n return\n }\n const task = this.taskMap.get(taskKey)\n if (task) {\n this.taskMap.delete(taskKey)\n } else {\n return\n }\n\n // Run the task asynchronously\n let output: O\n try {\n output = await this.processor.process(task.input)\n } catch (e) {\n if (!this.stopped) {\n // TODO: For now, if we encounter an error, stop processing tasks\n // and notify the onIdle callback with the error. Maybe we should\n // change this to continue processing other tasks.\n this.shutdown()\n this.onIdle?.(e)\n }\n return\n }\n\n // Notify the callback\n task.onComplete(output)\n\n // See if another run is needed\n if (this.taskKeyQueue.length > 0) {\n // Keep `processing` set and process the next task\n setTimeout(() => {\n this.processNextTask()\n })\n } else {\n // No more tasks, so clear the flag\n this.processing = false\n if (!this.stopped) {\n this.onIdle?.()\n }\n }\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { TaskQueue } from '../_shared/task-queue'\nimport type { Scenario } from '../_shared/scenario'\nimport type { Dataset, DatasetKey } from '../_shared/types'\nimport type { BundleModel } from '../bundle/bundle-types'\n\nexport type CheckDataRequestKey = string\n\ninterface DataRequest {\n scenario: Scenario\n datasetKey: DatasetKey\n}\n\ninterface DataResponse {\n dataset: Dataset\n}\n\n/**\n * Coordinates on-demand loading of data used to display a graph representation\n * of a check/predicate.\n */\nexport class CheckDataCoordinator {\n private readonly taskQueue: TaskQueue<DataRequest, DataResponse>\n\n constructor(public readonly bundleModel: BundleModel) {\n this.taskQueue = new TaskQueue({\n process: async request => {\n // Run the model for this scenario\n const result = await this.bundleModel.getDatasetsForScenario(request.scenario, [request.datasetKey])\n const dataset = result.datasetMap.get(request.datasetKey)\n return {\n dataset\n }\n }\n })\n }\n\n requestDataset(\n requestKey: CheckDataRequestKey,\n scenario: Scenario,\n datasetKey: DatasetKey,\n onResponse: (dataset: Dataset) => void\n ): void {\n const request: DataRequest = {\n scenario,\n datasetKey\n }\n this.taskQueue.addTask(requestKey, request, response => {\n onResponse(response.dataset)\n })\n }\n\n cancelRequest(key: CheckDataRequestKey): void {\n this.taskQueue.cancelTask(key)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport assertNever from 'assert-never'\nimport type { InputPosition } from '../_shared/scenario'\nimport type { CheckDataRef } from './check-data-ref'\nimport type { CheckDataset } from './check-dataset'\nimport type { CheckResult } from './check-func'\nimport type { CheckKey, CheckPlan, CheckPlanPredicate } from './check-planner'\nimport type { CheckPredicateOp } from './check-predicate'\nimport { symbolForPredicateOp } from './check-predicate'\nimport type { CheckScenario, CheckScenarioInputDesc } from './check-scenario'\nimport type { CheckPredicateTimeOptions, CheckPredicateTimeRange, CheckPredicateTimeSpec } from './check-spec'\n\nexport type CheckStatus = 'passed' | 'failed' | 'error'\n\nexport interface CheckPredicateOpConstantRef {\n kind: 'constant'\n value: number\n}\n\nexport interface CheckPredicateOpDataRef {\n kind: 'data'\n dataRef: CheckDataRef\n}\n\nexport type CheckPredicateOpRef = CheckPredicateOpConstantRef | CheckPredicateOpDataRef\n\nexport interface CheckPredicateReport {\n checkKey: CheckKey\n result: CheckResult\n opRefs: Map<CheckPredicateOp, CheckPredicateOpRef>\n opValues: string[]\n time?: CheckPredicateTimeSpec\n tolerance?: number\n}\n\nexport interface CheckDatasetReport {\n checkDataset: CheckDataset\n status: CheckStatus\n predicates: CheckPredicateReport[]\n}\n\nexport interface CheckScenarioReport {\n checkScenario: CheckScenario\n status: CheckStatus\n datasets: CheckDatasetReport[]\n}\n\nexport interface CheckTestReport {\n name: string\n status: CheckStatus\n scenarios: CheckScenarioReport[]\n}\n\nexport interface CheckGroupReport {\n name: string\n tests: CheckTestReport[]\n}\n\nexport interface CheckReport {\n groups: CheckGroupReport[]\n}\n\nexport type StyleFunc = (s: string) => string\n\nexport function buildCheckReport(checkPlan: CheckPlan, checkResults: Map<CheckKey, CheckResult>): CheckReport {\n const groupReports: CheckGroupReport[] = []\n\n for (const groupPlan of checkPlan.groups) {\n const testReports: CheckTestReport[] = []\n\n for (const testPlan of groupPlan.tests) {\n let testStatus: CheckStatus = 'passed'\n const scenarioReports: CheckScenarioReport[] = []\n\n for (const scenarioPlan of testPlan.scenarios) {\n let scenarioStatus: CheckStatus = 'passed'\n if (scenarioPlan.checkScenario.scenario === undefined) {\n // The scenario spec didn't match known inputs; treat as an error\n testStatus = 'error'\n scenarioStatus = 'error'\n }\n const datasetReports: CheckDatasetReport[] = []\n\n for (const datasetPlan of scenarioPlan.datasets) {\n let datasetStatus: CheckStatus = 'passed'\n if (datasetPlan.checkDataset.datasetKey === undefined) {\n // The dataset spec didn't match known outputs; treat as an error\n testStatus = 'error'\n scenarioStatus = 'error'\n datasetStatus = 'error'\n }\n const predicateReports: CheckPredicateReport[] = []\n\n for (const predicatePlan of datasetPlan.predicates) {\n const checkKey = predicatePlan.checkKey\n const checkResult = checkResults.get(checkKey)\n if (checkResult) {\n if (checkResult.status !== 'passed') {\n // Set the status for parent groupings; 'error' status has higher\n // precendence than 'failed' status\n if (checkResult.status === 'error') {\n testStatus = 'error'\n scenarioStatus = 'error'\n datasetStatus = 'error'\n } else if (checkResult.status === 'failed' && testStatus !== 'error') {\n testStatus = 'failed'\n scenarioStatus = 'failed'\n datasetStatus = 'failed'\n }\n }\n predicateReports.push(predicateReport(predicatePlan, checkKey, checkResult))\n } else {\n // When there is no check result in the map (as may be the case when\n // restoring from a simplified `CheckSummary`, which only includes\n // failed/errored checks), assume that it passed\n // TODO: There may be other cases where no result means the test wasn't\n // run for some reason, so maybe we should not assume \"passed\" here always\n predicateReports.push(predicateReport(predicatePlan, checkKey, { status: 'passed' }))\n }\n }\n\n datasetReports.push({\n checkDataset: datasetPlan.checkDataset,\n status: datasetStatus,\n predicates: predicateReports\n })\n }\n\n scenarioReports.push({\n checkScenario: scenarioPlan.checkScenario,\n status: scenarioStatus,\n datasets: datasetReports\n })\n }\n\n testReports.push({\n name: testPlan.name,\n status: testStatus,\n scenarios: scenarioReports\n })\n }\n\n groupReports.push({\n name: groupPlan.name,\n tests: testReports\n })\n }\n\n return {\n groups: groupReports\n }\n}\n\nfunction predicateReport(\n predicatePlan: CheckPlanPredicate,\n checkKey: CheckKey,\n result: CheckResult\n): CheckPredicateReport {\n if (result.status === 'error') {\n // For error cases, return a report that only includes the check result\n // (and don't process the ops)\n return {\n checkKey,\n result,\n opRefs: new Map(),\n opValues: []\n }\n }\n\n const predicateSpec = predicatePlan.action.predicateSpec\n const opRefs: Map<CheckPredicateOp, CheckPredicateOpRef> = new Map()\n const opValues: string[] = []\n\n function addOp(op: CheckPredicateOp): void {\n const sym = symbolForPredicateOp(op)\n const predOp = predicateSpec[op]\n\n if (predOp !== undefined) {\n let opRef: CheckPredicateOpRef\n let opValue: string\n if (typeof predOp === 'number') {\n const opConstantRef: CheckPredicateOpConstantRef = {\n kind: 'constant',\n value: predOp\n }\n opRef = opConstantRef\n opValue = `${sym} ${predOp}`\n } else {\n const dataRef = predicatePlan.dataRefs?.get(op)\n if (!dataRef) {\n return\n }\n const opDataRef: CheckPredicateOpDataRef = {\n kind: 'data',\n dataRef\n }\n opRef = opDataRef\n opValue = `${sym} '${dataRef.dataset.name}'`\n\n const refScenario = dataRef.scenario?.scenario\n if (!refScenario) {\n return\n }\n if (predOp.scenario === 'inherit') {\n opValue += ` (w/ same scenario)`\n } else {\n if (refScenario.kind === 'all-inputs' && refScenario.position === 'at-default') {\n opValue += ` (w/ default scenario)`\n } else {\n // TODO: We could include the scenario/input details here, but it might\n // be too verbose, so for now use a generic string\n opValue += ` (w/ configured scenario)`\n }\n }\n }\n\n if (op === 'approx') {\n const tolerance = predicateSpec.tolerance || 0.1\n opValue += ` ±${tolerance}`\n }\n opRefs.set(op, opRef)\n opValues.push(opValue)\n }\n }\n\n addOp('gt')\n addOp('gte')\n addOp('lt')\n addOp('lte')\n addOp('eq')\n addOp('approx')\n if (opValues.length === 0) {\n opValues.push('INVALID PREDICATE')\n }\n\n return {\n checkKey,\n result,\n opRefs,\n opValues,\n time: predicateSpec.time,\n tolerance: predicateSpec.tolerance\n }\n}\n\n/**\n * Return a string representation of the given scenario.\n *\n * @param scenario The scenario report.\n * @param bold A function that applies bold styling to a string.\n */\nexport function scenarioMessage(scenario: CheckScenarioReport, bold: StyleFunc): string {\n const checkScenario = scenario.checkScenario\n if (checkScenario.scenario === undefined) {\n if (checkScenario.error) {\n switch (checkScenario.error.kind) {\n case 'unknown-input-group':\n return `error: input group ${bold(checkScenario.error.name)} is unknown`\n case 'empty-input-group':\n return `error: input group ${bold(checkScenario.error.name)} is empty`\n default:\n assertNever(checkScenario.error.kind)\n }\n } else {\n const badInputNames = checkScenario.inputDescs.filter(d => d.inputVar === undefined).map(d => bold(d.name))\n const label = badInputNames.length === 1 ? 'input' : 'inputs'\n return `error: unknown ${label} ${badInputNames.join(', ')}`\n }\n }\n\n function positionName(position: InputPosition): string {\n switch (position) {\n case 'at-default':\n return 'default'\n case 'at-minimum':\n return 'minimum'\n case 'at-maximum':\n return 'maximum'\n default:\n assertNever(position)\n }\n }\n\n function inputMessage(inputDesc: CheckScenarioInputDesc): string {\n let msg = bold(inputDesc.name)\n if (inputDesc.position) {\n msg += ` is at ${bold(positionName(inputDesc.position))}`\n if (inputDesc.value !== undefined) {\n msg += ` (${inputDesc.value})`\n }\n } else if (inputDesc.value !== undefined) {\n msg += ` is ${bold(inputDesc.value.toString())}`\n }\n return msg\n }\n\n if (checkScenario.scenario.kind === 'all-inputs') {\n // This is an \"all inputs\" scenario\n const position = checkScenario.scenario.position\n return `when ${bold('all inputs')} are at ${bold(positionName(position))}...`\n } else if (checkScenario.inputGroupName) {\n // This is an \"all inputs in group\" scenario\n // TODO: Currently we don't have a special `Scenario` kind for the \"all inputs\n // in group\" case, so we use a multi-setting `Scenario`; therefore we have to\n // dig out the position from the first setting\n let position: InputPosition = 'at-default'\n if (checkScenario.scenario.settings[0].kind === 'position') {\n position = checkScenario.scenario.settings[0].position\n }\n const groupName = checkScenario.inputGroupName\n return `when all inputs in ${bold(groupName)} are at ${bold(positionName(position))}...`\n } else {\n // This scenario includes one or more inputs\n // TODO: This will get hard to read when there are many inputs; consider displaying\n // as a bulleted list or something\n const inputMessages = checkScenario.inputDescs.map(inputMessage).join(' and ')\n return `when ${inputMessages}...`\n }\n}\n\n/**\n * Return a string representation of the given dataset.\n *\n * @param dataset The dataset report.\n * @param bold A function that applies bold styling to a string.\n */\nexport function datasetMessage(dataset: CheckDatasetReport, bold: StyleFunc): string {\n const checkDataset = dataset.checkDataset\n if (checkDataset.datasetKey === undefined) {\n return `error: ${bold(checkDataset.name)} did not match any datasets`\n } else {\n return `then ${bold(checkDataset.name)}...`\n }\n}\n\n/**\n * Return a string representation of the given predicate.\n *\n * @param predicate The predicate report.\n * @param bold A function that applies bold styling to a string.\n */\nexport function predicateMessage(predicate: CheckPredicateReport, bold: StyleFunc): string {\n const result = predicate.result\n if (result.status === 'error') {\n if (result.message) {\n return `error: ${predicate.result.message}`\n } else if (result.errorInfo) {\n switch (result.errorInfo.kind) {\n case 'unknown-dataset':\n return `error: referenced dataset ${bold(result.errorInfo.name)} is unknown`\n case 'unknown-input':\n return `error: referenced input ${bold(result.errorInfo.name)} is unknown`\n case 'unknown-input-group':\n return `error: referenced input group ${bold(result.errorInfo.name)} is unknown`\n case 'empty-input-group':\n return `error: referenced input group ${bold(result.errorInfo.name)} is empty`\n default:\n assertNever(result.errorInfo.kind)\n }\n } else {\n return `unknown error`\n }\n }\n\n const predicateParts = predicate.opValues.map(bold).join(' and ')\n let msg = `should be ${predicateParts}`\n\n if (predicate.time !== undefined) {\n if (typeof predicate.time === 'number') {\n msg += ` in ${bold(predicate.time.toString())}`\n } else {\n let minTime: number\n let maxTime: number\n let minIncl: boolean\n let maxIncl: boolean\n if (Array.isArray(predicate.time)) {\n // This is an inclusive range shorthand (e.g. `time: [0, 1]`)\n const timeSpec = predicate.time as CheckPredicateTimeRange\n minTime = timeSpec[0]\n maxTime = timeSpec[1]\n minIncl = true\n maxIncl = true\n } else {\n // This is a full time spec with `after` and/or `before`\n const timeSpec = predicate.time as CheckPredicateTimeOptions\n if (timeSpec.after_excl !== undefined) {\n minTime = timeSpec.after_excl\n minIncl = false\n } else if (timeSpec.after_incl !== undefined) {\n minTime = timeSpec.after_incl\n minIncl = true\n }\n if (timeSpec.before_excl !== undefined) {\n maxTime = timeSpec.before_excl\n maxIncl = false\n } else if (timeSpec.before_incl !== undefined) {\n maxTime = timeSpec.before_incl\n maxIncl = true\n }\n }\n if (minTime !== undefined && maxTime !== undefined) {\n const prefix = minIncl ? '[' : '('\n const suffix = maxIncl ? ']' : ')'\n const range = `${prefix}${minTime}, ${maxTime}${suffix}`\n msg += ` in ${bold(range)}`\n } else if (minTime !== undefined) {\n const prefix = minIncl ? 'in/after' : 'after'\n msg += ` ${prefix} ${bold(minTime.toString())}`\n } else if (maxTime !== undefined) {\n const prefix = maxIncl ? 'in/before' : 'before'\n msg += ` ${prefix} ${bold(maxTime.toString())}`\n }\n }\n }\n\n if (predicate.result.status === 'failed') {\n if (predicate.result.failValue !== undefined) {\n msg += ` but got ${bold(predicate.result.failValue.toString())}`\n if (predicate.result.failRefValue !== undefined) {\n // TODO: Include tolerance value here (e.g. \"expected ≈ 6.5 ±0.3\")\n const failSym = symbolForPredicateOp(predicate.result.failOp)\n const refValue = `${failSym} ${predicate.result.failRefValue.toString()}`\n msg += ` (expected ${bold(refValue)})`\n }\n } else if (predicate.result.message) {\n msg += ` but got ${bold(predicate.result.message)}`\n }\n if (predicate.result.failTime !== undefined) {\n msg += ` in ${bold(predicate.result.failTime.toString())}`\n }\n } else if (predicate.result.status === 'error' && predicate.result.message) {\n msg += ` but got error: ${bold(predicate.result.message)}`\n }\n\n return msg\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport assertNever from 'assert-never'\n\nexport type CheckPredicateOp = 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'approx'\n\n/**\n * Return the symbol that describes the given predicate op.\n *\n * @param op The predicate operation.\n */\nexport function symbolForPredicateOp(op: CheckPredicateOp): string {\n switch (op) {\n case 'gt':\n return '>'\n case 'gte':\n return '>='\n case 'lt':\n return '<'\n case 'lte':\n return '<='\n case 'eq':\n return '=='\n case 'approx':\n return '≈'\n default:\n assertNever(op)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\nimport type { CheckConfig } from './check-config'\nimport type { CheckResult } from './check-func'\nimport { parseTestYaml } from './check-parser'\nimport type { CheckKey } from './check-planner'\nimport { CheckPlanner } from './check-planner'\nimport type { CheckReport } from './check-report'\nimport { buildCheckReport } from './check-report'\n\n/**\n * A simplified/terse version of `CheckPredicateReport` that matches the\n * format of the JSON objects emitted by the CLI in terse mode.\n */\nexport interface CheckPredicateSummary {\n checkKey: CheckKey\n result: CheckResult\n}\n\n/**\n * A simplified/terse version of `CheckReport` that matches the\n * format of the JSON objects emitted by the CLI in terse mode.\n * This only contains predicate summaries for checks that have a status\n * of 'failed' or 'error'.\n */\nexport interface CheckSummary {\n predicateSummaries: CheckPredicateSummary[]\n}\n\n/**\n * Convert a full `CheckReport` to a simplified `CheckSummary` that only includes\n * failed/errored checks.\n *\n * @param checkReport The full check report.\n * @return The converted check summary.\n */\nexport function checkSummaryFromReport(checkReport: CheckReport): CheckSummary {\n const predicateSummaries: CheckPredicateSummary[] = []\n\n for (const group of checkReport.groups) {\n for (const test of group.tests) {\n for (const scenario of test.scenarios) {\n for (const dataset of scenario.datasets) {\n for (const predicate of dataset.predicates) {\n switch (predicate.result.status) {\n case 'passed':\n break\n case 'failed':\n case 'error':\n predicateSummaries.push({\n checkKey: predicate.checkKey,\n result: predicate.result\n })\n break\n default:\n assertNever(predicate.result.status)\n }\n }\n }\n }\n }\n }\n\n return {\n predicateSummaries\n }\n}\n\n/**\n * Convert a simplified `CheckSummary` to a full `CheckReport` that restores the\n * structure of the tests from the given configuration.\n *\n * @param checkConfig The config used to reconstruct the check test structure.\n * @param checkSummary The simplified check summary.\n * @param simplifyScenarios If true, reduce the number of scenarios generated for a `matrix`.\n * @return The converted check report.\n */\nexport function checkReportFromSummary(\n checkConfig: CheckConfig,\n checkSummary: CheckSummary,\n simplifyScenarios: boolean\n): CheckReport | undefined {\n // Parse the tests\n const checkSpecResult = parseTestYaml(checkConfig.tests)\n if (checkSpecResult.isErr()) {\n // TODO: Use Result type here instead\n return undefined\n }\n const checkSpec = checkSpecResult.value\n\n // Build the check plan\n const checkPlanner = new CheckPlanner(checkConfig.bundle.model.modelSpec)\n checkPlanner.addAllChecks(checkSpec, simplifyScenarios)\n const checkPlan = checkPlanner.buildPlan()\n\n // Put the check results into a map\n const checkResults: Map<CheckKey, CheckResult> = new Map()\n for (const predicateSummary of checkSummary.predicateSummaries) {\n checkResults.set(predicateSummary.checkKey, predicateSummary.result)\n }\n\n // Build the full report\n return buildCheckReport(checkPlan, checkResults)\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport Ajv from 'ajv'\nimport type { Result } from 'neverthrow'\nimport { err, ok } from 'neverthrow'\nimport yaml from 'yaml'\n\nimport type { CheckGroupSpec, CheckSpec } from './check-spec'\n\nimport jsonSchema from './check.schema'\n\nexport function parseTestYaml(yamlStrings: string[]): Result<CheckSpec, Error> {\n const groups: CheckGroupSpec[] = []\n\n // Prepare the yaml parser/validator\n const ajv = new Ajv()\n // TODO: Ideally we would use JSONSchemaType here, but it doesn't\n // seem to work if we import the schema.json file directly\n // const schema: JSONSchemaType<GroupSpec[]> = jsonSchema\n const validate = ajv.compile<CheckGroupSpec[]>(jsonSchema)\n\n // Parse the yaml strings\n for (const yamlString of yamlStrings) {\n const parsed = yaml.parse(yamlString)\n\n if (validate(parsed)) {\n for (const group of parsed) {\n groups.push(group)\n }\n } else {\n let msg = 'Failed to parse YAML tests'\n for (const error of validate.errors || []) {\n if (error.message) {\n msg += `\\n${error.message}`\n }\n }\n return err(new Error(msg))\n }\n }\n\n const checkSpec: CheckSpec = {\n groups\n }\n\n return ok(checkSpec)\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nexport default {\n $schema: 'http://json-schema.org/draft-07/schema#',\n title: 'Model Check Test',\n type: 'array',\n description: 'A group of tests.',\n items: {\n $ref: '#/$defs/group'\n },\n\n $defs: {\n group: {\n type: 'object',\n additionalProperties: false,\n properties: {\n describe: {\n type: 'string'\n },\n tests: {\n type: 'array',\n items: {\n $ref: '#/$defs/test'\n }\n }\n },\n required: ['describe', 'tests']\n },\n\n test: {\n type: 'object',\n additionalProperties: false,\n properties: {\n it: {\n type: 'string'\n },\n scenarios: {\n type: 'array',\n items: {\n $ref: '#/$defs/scenario'\n },\n minItems: 1\n },\n datasets: {\n type: 'array',\n items: {\n $ref: '#/$defs/dataset'\n },\n minItems: 1\n },\n predicates: {\n type: 'array',\n items: {\n $ref: '#/$defs/predicate'\n },\n minItems: 1\n }\n },\n required: ['it', 'datasets', 'predicates']\n },\n\n scenario: {\n oneOf: [\n { $ref: '#/$defs/scenario_with_input_at_position' },\n { $ref: '#/$defs/scenario_with_input_at_value' },\n { $ref: '#/$defs/scenario_with_multiple_input_settings' },\n { $ref: '#/$defs/scenario_with_inputs_in_preset_at_position' },\n { $ref: '#/$defs/scenario_with_inputs_in_group_at_position' },\n { $ref: '#/$defs/scenario_preset' },\n { $ref: '#/$defs/scenario_expand_for_each_input_in_group' }\n ]\n },\n\n scenario_position: {\n type: 'string',\n enum: ['min', 'max', 'default']\n },\n\n scenario_with_input_at_position: {\n type: 'object',\n additionalProperties: false,\n properties: {\n with: {\n type: 'string'\n },\n at: {\n $ref: '#/$defs/scenario_position'\n }\n },\n required: ['with', 'at']\n },\n\n scenario_with_input_at_value: {\n type: 'object',\n additionalProperties: false,\n properties: {\n with: {\n type: 'string'\n },\n at: {\n type: 'number'\n }\n },\n required: ['with', 'at']\n },\n\n scenario_input_at_position: {\n type: 'object',\n additionalProperties: false,\n properties: {\n input: {\n type: 'string'\n },\n at: {\n $ref: '#/$defs/scenario_position'\n }\n },\n required: ['input', 'at']\n },\n\n scenario_input_at_value: {\n type: 'object',\n additionalProperties: false,\n properties: {\n input: {\n type: 'string'\n },\n at: {\n type: 'number'\n }\n },\n required: ['input', 'at']\n },\n\n scenario_input_setting: {\n oneOf: [{ $ref: '#/$defs/scenario_input_at_position' }, { $ref: '#/$defs/scenario_input_at_value' }]\n },\n\n scenario_input_setting_array: {\n type: 'array',\n items: {\n $ref: '#/$defs/scenario_input_setting'\n },\n minItems: 1\n },\n\n scenario_with_multiple_input_settings: {\n type: 'object',\n additionalProperties: false,\n properties: {\n with: {\n $ref: '#/$defs/scenario_input_setting_array'\n }\n },\n required: ['with']\n },\n\n scenario_with_inputs_in_preset_at_position: {\n type: 'object',\n additionalProperties: false,\n properties: {\n with_inputs: {\n type: 'string',\n enum: ['all']\n },\n at: {\n $ref: '#/$defs/scenario_position'\n }\n },\n required: ['with_inputs', 'at']\n },\n\n scenario_with_inputs_in_group_at_position: {\n type: 'object',\n additionalProperties: false,\n properties: {\n with_inputs_in: {\n type: 'string'\n },\n at: {\n $ref: '#/$defs/scenario_position'\n }\n },\n required: ['with_inputs_in', 'at']\n },\n\n scenario_preset: {\n type: 'object',\n additionalProperties: false,\n properties: {\n preset: {\n type: 'string',\n enum: ['matrix']\n }\n },\n required: ['preset']\n },\n\n scenario_expand_for_each_input_in_group: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenarios_for_each_input_in: {\n type: 'string'\n },\n at: {\n $ref: '#/$defs/scenario_position'\n }\n },\n required: ['scenarios_for_each_input_in', 'at']\n },\n\n dataset: {\n oneOf: [{ $ref: '#/$defs/dataset_name' }, { $ref: '#/$defs/dataset_group' }, { $ref: '#/$defs/dataset_matching' }]\n },\n\n dataset_name: {\n type: 'object',\n additionalProperties: false,\n properties: {\n name: {\n type: 'string'\n },\n source: {\n type: 'string'\n }\n },\n required: ['name']\n },\n\n dataset_group: {\n type: 'object',\n additionalProperties: false,\n properties: {\n group: {\n type: 'string'\n }\n },\n required: ['group']\n },\n\n dataset_matching: {\n type: 'object',\n additionalProperties: false,\n properties: {\n matching: {\n type: 'object',\n additionalProperties: false,\n properties: {\n type: {\n type: 'string'\n }\n },\n required: ['type']\n }\n },\n required: ['matching']\n },\n\n predicate: {\n type: 'object',\n oneOf: [\n { $ref: '#/$defs/predicate_gt' },\n { $ref: '#/$defs/predicate_gte' },\n { $ref: '#/$defs/predicate_lt' },\n { $ref: '#/$defs/predicate_lte' },\n { $ref: '#/$defs/predicate_gt_lt' },\n { $ref: '#/$defs/predicate_gt_lte' },\n { $ref: '#/$defs/predicate_gte_lt' },\n { $ref: '#/$defs/predicate_gte_lte' },\n { $ref: '#/$defs/predicate_eq' },\n { $ref: '#/$defs/predicate_approx' }\n ]\n },\n\n predicate_gt: {\n type: 'object',\n additionalProperties: false,\n properties: {\n gt: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['gt']\n },\n predicate_gte: {\n type: 'object',\n additionalProperties: false,\n properties: {\n gte: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['gte']\n },\n predicate_lt: {\n type: 'object',\n additionalProperties: false,\n properties: {\n lt: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['lt']\n },\n predicate_lte: {\n type: 'object',\n additionalProperties: false,\n properties: {\n lte: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['lte']\n },\n predicate_gt_lt: {\n type: 'object',\n additionalProperties: false,\n properties: {\n gt: { $ref: '#/$defs/predicate_ref' },\n lt: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['gt', 'lt']\n },\n predicate_gt_lte: {\n type: 'object',\n additionalProperties: false,\n properties: {\n gt: { $ref: '#/$defs/predicate_ref' },\n lte: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['gt', 'lte']\n },\n predicate_gte_lt: {\n type: 'object',\n additionalProperties: false,\n properties: {\n gte: { $ref: '#/$defs/predicate_ref' },\n lt: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['gte', 'lt']\n },\n predicate_gte_lte: {\n type: 'object',\n additionalProperties: false,\n properties: {\n gte: { $ref: '#/$defs/predicate_ref' },\n lte: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['gte', 'lte']\n },\n predicate_eq: {\n type: 'object',\n additionalProperties: false,\n properties: {\n eq: { $ref: '#/$defs/predicate_ref' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['eq']\n },\n predicate_approx: {\n type: 'object',\n additionalProperties: false,\n properties: {\n approx: { $ref: '#/$defs/predicate_ref' },\n tolerance: { type: 'number' },\n time: { $ref: '#/$defs/predicate_time' }\n },\n required: ['approx']\n },\n\n predicate_ref: {\n oneOf: [{ $ref: '#/$defs/predicate_ref_constant' }, { $ref: '#/$defs/predicate_ref_data' }]\n },\n\n predicate_ref_constant: {\n type: 'number'\n },\n\n predicate_ref_data: {\n type: 'object',\n additionalProperties: false,\n properties: {\n dataset: { $ref: '#/$defs/predicate_ref_data_dataset' },\n scenario: { $ref: '#/$defs/predicate_ref_data_scenario' }\n },\n required: ['dataset']\n },\n\n predicate_ref_data_dataset: {\n oneOf: [{ $ref: '#/$defs/dataset_name' }, { $ref: '#/$defs/predicate_ref_data_dataset_special' }]\n },\n predicate_ref_data_dataset_special: {\n type: 'string',\n enum: ['inherit']\n },\n\n predicate_ref_data_scenario: {\n oneOf: [\n { $ref: '#/$defs/scenario_with_input_at_position' },\n { $ref: '#/$defs/scenario_with_input_at_value' },\n { $ref: '#/$defs/scenario_with_multiple_input_settings' },\n { $ref: '#/$defs/scenario_with_inputs_in_preset_at_position' },\n { $ref: '#/$defs/scenario_with_inputs_in_group_at_position' },\n { $ref: '#/$defs/predicate_ref_data_scenario_special' }\n ]\n },\n predicate_ref_data_scenario_special: {\n type: 'string',\n enum: ['inherit']\n },\n\n predicate_time: {\n oneOf: [\n { $ref: '#/$defs/predicate_time_single' },\n { $ref: '#/$defs/predicate_time_pair' },\n { $ref: '#/$defs/predicate_time_gt' },\n { $ref: '#/$defs/predicate_time_gte' },\n { $ref: '#/$defs/predicate_time_lt' },\n { $ref: '#/$defs/predicate_time_lte' },\n { $ref: '#/$defs/predicate_time_gt_lt' },\n { $ref: '#/$defs/predicate_time_gt_lte' },\n { $ref: '#/$defs/predicate_time_gte_lt' },\n { $ref: '#/$defs/predicate_time_gte_lte' }\n ]\n },\n\n predicate_time_single: {\n type: 'number'\n },\n predicate_time_pair: {\n type: 'array',\n items: [{ type: 'number' }, { type: 'number' }],\n minItems: 2,\n maxItems: 2\n },\n predicate_time_gt: {\n type: 'object',\n additionalProperties: false,\n properties: {\n after_excl: { type: 'number' }\n },\n required: ['after_excl']\n },\n predicate_time_gte: {\n type: 'object',\n additionalProperties: false,\n properties: {\n after_incl: { type: 'number' }\n },\n required: ['after_incl']\n },\n predicate_time_lt: {\n type: 'object',\n additionalProperties: false,\n properties: {\n before_excl: { type: 'number' }\n },\n required: ['before_excl']\n },\n predicate_time_lte: {\n type: 'object',\n additionalProperties: false,\n properties: {\n before_incl: { type: 'number' }\n },\n required: ['before_incl']\n },\n predicate_time_gt_lt: {\n type: 'object',\n additionalProperties: false,\n properties: {\n after_excl: { type: 'number' },\n before_excl: { type: 'number' }\n },\n required: ['after_excl', 'before_excl']\n },\n predicate_time_gt_lte: {\n type: 'object',\n additionalProperties: false,\n properties: {\n after_excl: { type: 'number' },\n before_incl: { type: 'number' }\n },\n required: ['after_excl', 'before_incl']\n },\n predicate_time_gte_lt: {\n type: 'object',\n additionalProperties: false,\n properties: {\n after_incl: { type: 'number' },\n before_excl: { type: 'number' }\n },\n required: ['after_incl', 'before_excl']\n },\n predicate_time_gte_lte: {\n type: 'object',\n additionalProperties: false,\n properties: {\n after_incl: { type: 'number' },\n before_incl: { type: 'number' }\n },\n required: ['after_incl', 'before_incl']\n }\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport assertNever from 'assert-never'\nimport type { ModelSpec } from '../bundle/bundle-types'\nimport type { CheckAction } from './check-action'\nimport { actionForPredicate } from './check-action'\nimport type { CheckDataRef, CheckDataRefKey } from './check-data-ref'\nimport type { CheckDataset } from './check-dataset'\nimport { expandDatasets } from './check-dataset'\nimport type { CheckPredicateOp } from './check-predicate'\nimport type { CheckScenario } from './check-scenario'\nimport { expandScenarios } from './check-scenario'\nimport type { CheckDatasetSpec, CheckPredicateSpec, CheckScenarioSpec, CheckSpec } from './check-spec'\n\nexport type CheckKey = number\n\nexport interface CheckPlanPredicate {\n /** The key that associates this predicate with a check task. */\n checkKey: CheckKey\n /** The action that performs the check for a scenario/dataset/predicate. */\n action: CheckAction\n /** The op->ref pairs for any reference data needed for performing the check. */\n dataRefs?: Map<CheckPredicateOp, CheckDataRef>\n}\n\nexport interface CheckPlanDataset {\n checkDataset: CheckDataset\n predicates: CheckPlanPredicate[]\n}\n\nexport interface CheckPlanScenario {\n checkScenario: CheckScenario\n datasets: CheckPlanDataset[]\n}\n\nexport interface CheckPlanTest {\n name: string\n scenarios: CheckPlanScenario[]\n}\n\nexport interface CheckPlanGroup {\n name: string\n tests: CheckPlanTest[]\n}\n\n/**\n * Contains the metadata needed to perform a check for a scenario/dataset/predicate\n * combination.\n */\nexport interface CheckTask {\n /** The scenario that will be configured. */\n scenario: CheckScenario\n /** The dataset to be checked. */\n dataset: CheckDataset\n /** The action that performs the check for a scenario/dataset/predicate. */\n action: CheckAction\n /** The op->ref pairs for any reference data needed for performing the check. */\n dataRefs?: Map<CheckPredicateOp, CheckDataRef>\n}\n\nexport interface CheckPlan {\n /**\n * The top-level plan groups (one plan for each `describe` group).\n */\n groups: CheckPlanGroup[]\n /**\n * The map of all check tasks to be performed.\n */\n tasks: Map<CheckKey, CheckTask>\n /**\n * All data references for the checks. These are kept separate so that the\n * reference data can be fetched in advance (and kept in memory) before\n * the actual checks are performed.\n *\n * TODO: Ideally we would have a more sophisticated system for managing\n * data references so that we don't need to keep all reference data in\n * memory, but for now it's easier to just load all the reference data\n * as a preliminary step.\n */\n dataRefs: Map<CheckDataRefKey, CheckDataRef>\n}\n\nexport class CheckPlanner {\n private readonly groups: CheckPlanGroup[] = []\n private readonly tasks: Map<CheckKey, CheckTask> = new Map()\n private readonly dataRefs: Map<CheckDataRefKey, CheckDataRef> = new Map()\n private checkKey = 1\n\n constructor(private readonly modelSpec: ModelSpec) {}\n\n addAllChecks(checkSpec: CheckSpec, simplifyScenarios: boolean): void {\n // Iterate over all groups\n for (const groupSpec of checkSpec.groups) {\n const groupName = groupSpec.describe\n\n // Iterate over the tests in this group\n const planTests: CheckPlanTest[] = []\n for (const testSpec of groupSpec.tests) {\n const testName = testSpec.it\n\n // Expand the set of scenarios for this test\n const checkScenarios = expandScenarios(this.modelSpec, testSpec.scenarios || [], simplifyScenarios)\n\n // Expand the set of datasets for this test\n const checkDatasets: CheckDataset[] = []\n for (const datasetSpec of testSpec.datasets) {\n checkDatasets.push(...expandDatasets(this.modelSpec, datasetSpec))\n }\n\n // Build a check function for each predicate in this test\n const checkActions: CheckAction[] = []\n for (const predicateSpec of testSpec.predicates) {\n // Add the action that runs the check\n checkActions.push(actionForPredicate(predicateSpec))\n }\n\n // Build the scenario/dataset/action combinations\n const planScenarios: CheckPlanScenario[] = []\n for (const checkScenario of checkScenarios) {\n if (checkScenario.scenario === undefined) {\n // The scenario spec didn't match known inputs; add it to the plan\n // so that it can be reported as an error later\n planScenarios.push({\n checkScenario,\n datasets: []\n })\n continue\n }\n\n const planDatasets: CheckPlanDataset[] = []\n\n // Add the datasets for the current scenario\n for (const checkDataset of checkDatasets) {\n if (checkDataset.datasetKey === undefined) {\n // The dataset spec didn't match known outputs; add it to the plan\n // so that it can be reported as an error later\n planDatasets.push({\n checkDataset,\n predicates: []\n })\n continue\n }\n\n const planPredicates: CheckPlanPredicate[] = []\n\n // Add the predicate tasks for the current scenario and dataset\n for (const checkAction of checkActions) {\n // If the predicate references other datasets (for cases where\n // the check is against a dataset rather than a constant value),\n // then keep track of those references so that we can load the\n // data in advance of performing the actual checks\n const dataRefs = this.addDataRefs(checkAction.predicateSpec, checkScenario, checkDataset)\n\n // Add the predicate to the plan\n const key = this.checkKey++\n planPredicates.push({\n checkKey: key,\n action: checkAction,\n dataRefs\n })\n\n // Add a task that runs the check for the scenario and dataset\n this.tasks.set(key, {\n scenario: checkScenario,\n dataset: checkDataset,\n action: checkAction,\n dataRefs\n })\n }\n\n planDatasets.push({\n checkDataset,\n predicates: planPredicates\n })\n }\n\n planScenarios.push({\n checkScenario,\n datasets: planDatasets\n })\n }\n\n planTests.push({\n name: testName,\n scenarios: planScenarios\n })\n }\n\n this.groups.push({\n name: groupName,\n tests: planTests\n })\n }\n }\n\n buildPlan(): CheckPlan {\n return {\n groups: this.groups,\n tasks: this.tasks,\n dataRefs: this.dataRefs\n }\n }\n\n /**\n * Record any references to additional datasets contained in the given predicate.\n * For example, if the predicate is:\n * ```\n * gt:\n * dataset:\n * name: 'XYZ'\n * scenario:\n * inputs: all\n * at: default\n * ```\n * this will add a reference to the scenario/dataset pair so that the data can\n * be fetched in a later stage.\n *\n * @param predicateSpec The predicate spec.\n * @param checkScenario The scenario in which the dataset is being checked.\n * @param checkDataset The dataset that is being checked.\n */\n private addDataRefs(\n predicateSpec: CheckPredicateSpec,\n checkScenario: CheckScenario,\n checkDataset: CheckDataset\n ): Map<CheckPredicateOp, CheckDataRef> | undefined {\n // This map will be created lazily (only when there are data refs for one\n // or more ops)\n let dataRefs: Map<CheckPredicateOp, CheckDataRef>\n\n const addDataRef = (op: CheckPredicateOp) => {\n const predOp = predicateSpec[op]\n if (predOp === undefined || typeof predOp === 'number') {\n return\n }\n\n // Resolve the dataset\n let refDataset: CheckDataset\n if (typeof predOp.dataset === 'string') {\n switch (predOp.dataset) {\n case 'inherit':\n // Use the same dataset as the one being checked (this is typically used\n // when checking one dataset in one scenario against the same dataset in\n // a different scenario)\n refDataset = checkDataset\n break\n default:\n assertNever(predOp.dataset)\n }\n } else {\n // Resolve the dataset for the given name; if it does not expand\n // to a single valid dataset, treat it as an error case\n const refDatasetSpec: CheckDatasetSpec = { name: predOp.dataset.name }\n const matchedRefDatasets = expandDatasets(this.modelSpec, refDatasetSpec)\n if (matchedRefDatasets.length === 1) {\n refDataset = matchedRefDatasets[0]\n } else {\n // We failed to match a dataset (or the match expanded to multiple datasets);\n // use an empty CheckDataset so that we can report the error later\n refDataset = {\n name: predOp.dataset.name\n }\n }\n }\n\n // Resolve the scenario\n let refScenario: CheckScenario\n if (typeof predOp.scenario === 'string') {\n switch (predOp.scenario) {\n case 'inherit':\n // Use the same scenario as the one being checked (this is typically used\n // when checking one dataset in one scenario against a different dataset in\n // the same scenario)\n refScenario = checkScenario\n break\n default:\n assertNever(predOp.scenario)\n }\n } else {\n // The following will convert the `CheckPredicateRefDataScenarioSpec` to a\n // `CheckScenario`. If no scenario was included for the predicate/op, we use an\n // empty array, which will resolve to the \"all inputs at default\" scenario.\n const refScenarioSpecs: CheckScenarioSpec[] = predOp.scenario ? [predOp.scenario] : []\n const matchedRefScenarios = expandScenarios(this.modelSpec, refScenarioSpecs, true)\n if (matchedRefScenarios.length === 1) {\n refScenario = matchedRefScenarios[0]\n }\n if (refScenario === undefined) {\n // We failed to match a scenario/input; use an empty CheckScenario so that\n // we can report the error later\n refScenario = {\n inputDescs: []\n }\n }\n }\n\n // Create the data ref that will be used for this predicate/op\n let dataRefKey: CheckDataRefKey\n if (refScenario.scenario && refDataset.datasetKey) {\n dataRefKey = `${refScenario.scenario.key}::${refDataset.datasetKey}`\n }\n const dataRef: CheckDataRef = {\n key: dataRefKey,\n dataset: refDataset,\n scenario: refScenario\n }\n\n // Add the data ref to the plan only if the key is defined\n if (dataRefKey) {\n this.dataRefs.set(dataRefKey, dataRef)\n }\n\n // Add an entry to the map so that the op is associated with a particular\n // reference dataset\n if (dataRefs === undefined) {\n dataRefs = new Map()\n }\n dataRefs.set(op, dataRef)\n }\n\n addDataRef('gt')\n addDataRef('gte')\n addDataRef('lt')\n addDataRef('lte')\n addDataRef('eq')\n addDataRef('approx')\n\n return dataRefs\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { Dataset } from '../_shared/types'\nimport type { CheckPredicateOp } from './check-predicate'\nimport type { CheckPredicateSpec, CheckPredicateTimeOptions, CheckPredicateTimeRange } from './check-spec'\n\nexport interface CheckResultErrorInfo {\n kind: 'unknown-dataset' | 'unknown-input' | 'unknown-input-group' | 'empty-input-group'\n name: string\n}\n\nexport interface CheckResult {\n status: 'passed' | 'failed' | 'error'\n message?: string\n failValue?: number\n failOp?: CheckPredicateOp\n failRefValue?: number\n failTime?: number\n errorInfo?: CheckResultErrorInfo\n}\n\nexport type CheckFunc = (dataset: Dataset, refDatasets?: Map<CheckPredicateOp, Dataset>) => CheckResult\n\nconst passed: CheckResult = {\n status: 'passed'\n}\n\ntype CheckValueCompareFunc = (a: number, b: number) => boolean\n\nconst gt: CheckValueCompareFunc = (a, b) => a > b\nconst gte: CheckValueCompareFunc = (a, b) => a >= b\nconst lt: CheckValueCompareFunc = (a, b) => a < b\nconst lte: CheckValueCompareFunc = (a, b) => a <= b\nconst eq: CheckValueCompareFunc = (a, b) => a === b\nconst approx = (tolerance: number) => {\n const f: CheckValueCompareFunc = (a, b) => {\n return a >= b - tolerance && a <= b + tolerance\n }\n return f\n}\n\n/**\n * Return a function that can check a given dataset to see if it meets\n * the criteria defined in the predicate spec.\n *\n * @param spec The dataset spec from a check test.\n */\nexport function checkFunc(spec: CheckPredicateSpec): CheckFunc | undefined {\n // Allow multiple value predicates in the same check (these are essentially\n // combined with boolean AND operations)\n type CheckValueFunc = (value: number, time: number, refDatasets?: Map<CheckPredicateOp, Dataset>) => CheckResult\n\n function addCheckValueFunc(op: CheckPredicateOp, compareFunc: CheckValueCompareFunc): void {\n const refSpec = spec[op]\n if (refSpec === undefined) {\n // No check defined for this op, so don't add a check func\n return\n }\n\n if (typeof refSpec === 'number') {\n checkValueFuncs.push((value, time) => {\n if (compareFunc(value, refSpec)) {\n return passed\n } else {\n return {\n status: 'failed',\n failValue: value,\n failTime: time\n }\n }\n })\n } else {\n checkValueFuncs.push((value, time, refDatasets) => {\n const refDataset = refDatasets?.get(op)\n if (refDataset === undefined) {\n // This should not happen in practice; treat it as an internal error\n return {\n status: 'error',\n message: 'unhandled data reference'\n }\n }\n const refValue = refDataset.get(time)\n if (refValue !== undefined) {\n if (compareFunc(value, refValue)) {\n return passed\n } else {\n return {\n status: 'failed',\n failValue: value,\n failOp: op,\n failRefValue: refValue,\n failTime: time\n }\n }\n } else {\n return {\n status: 'failed',\n message: 'no reference value',\n failTime: time\n }\n }\n })\n }\n }\n\n // Include a check for each op that is defined in the predicate\n const checkValueFuncs: CheckValueFunc[] = []\n addCheckValueFunc('gt', gt)\n addCheckValueFunc('gte', gte)\n addCheckValueFunc('lt', lt)\n addCheckValueFunc('lte', lte)\n addCheckValueFunc('eq', eq)\n if (spec.approx !== undefined) {\n const tolerance = spec.tolerance || 0.1\n addCheckValueFunc('approx', approx(tolerance))\n }\n\n // The check returns a 'passed' result only if all predicates passed\n const checkValue: CheckValueFunc = (value, time, refDatasets) => {\n for (const f of checkValueFuncs) {\n const result = f(value, time, refDatasets)\n if (result.status !== 'passed') {\n return result\n }\n }\n return passed\n }\n\n if (spec.time !== undefined && typeof spec.time === 'number') {\n // Only sample the value at the requested time\n const time: number = spec.time\n return (dataset, refDatasets) => {\n const value = dataset.get(time)\n if (value !== undefined) {\n return checkValue(value, time, refDatasets)\n } else {\n return {\n status: 'failed',\n message: 'no value',\n failTime: time\n }\n }\n }\n } else {\n // Sample the values that pass the included time predicate(s)\n type CheckTimeFunc = (time: number) => boolean\n let checkTime: CheckTimeFunc\n if (spec.time !== undefined) {\n if (Array.isArray(spec.time)) {\n // This is an inclusive range shorthand (e.g. `time: [0, 1]`)\n const timeSpec = spec.time as CheckPredicateTimeRange\n checkTime = time => time >= timeSpec[0] && time <= timeSpec[1]\n } else {\n // This is a full time spec with `after` and/or `before`. Allow up\n // to two time predicates in the same check; this allows for range\n // comparisons (for example, after t0 AND before t1).\n const checkTimeFuncs: CheckTimeFunc[] = []\n const timeSpec = spec.time as CheckPredicateTimeOptions\n if (timeSpec.after_excl !== undefined) {\n checkTimeFuncs.push(time => time > timeSpec.after_excl)\n }\n if (timeSpec.after_incl !== undefined) {\n checkTimeFuncs.push(time => time >= timeSpec.after_incl)\n }\n if (timeSpec.before_excl !== undefined) {\n checkTimeFuncs.push(time => time < timeSpec.before_excl)\n }\n if (timeSpec.before_incl !== undefined) {\n checkTimeFuncs.push(time => time <= timeSpec.before_incl)\n }\n checkTime = time => {\n for (const f of checkTimeFuncs) {\n if (!f(time)) {\n return false\n }\n }\n return true\n }\n }\n } else {\n // No time predicate; sample all values\n checkTime = () => true\n }\n\n return (dataset, refDatasets) => {\n for (const [time, value] of dataset) {\n if (checkTime(time)) {\n const result = checkValue(value, time, refDatasets)\n if (result.status !== 'passed') {\n return result\n }\n }\n }\n return passed\n }\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { CheckFunc } from './check-func'\nimport { checkFunc } from './check-func'\nimport type { CheckPredicateSpec } from './check-spec'\n\n/**\n * Associates a check function instance with the metadata that describes the predicate.\n */\nexport interface CheckAction {\n predicateSpec: CheckPredicateSpec\n run: CheckFunc\n}\n\n/**\n * Return a `CheckAction` that runs a check according to the given predicate.\n *\n * @param predicateSpec The predicate spec.\n */\nexport function actionForPredicate(predicateSpec: CheckPredicateSpec): CheckAction {\n return {\n predicateSpec,\n run: checkFunc(predicateSpec)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\n/**\n * Return the cartesian product of the given array of arrays.\n *\n * For example, if we have an array that lists out two dimensions:\n * [ ['a1','a2'], ['b1','b2','b3'] ]\n * this function will return all the combinations, e.g.:\n * [ ['a1', 'b1'], ['a1', 'b2'], ['a1', 'b3'], ['a2', 'b1'], ... ]\n *\n * This can be used in place of nested for loops and has the benefit of working\n * for multi-dimensional inputs.\n */\nexport function cartesianProductOf<T>(arr: T[][]): T[][] {\n // Implementation based on: https://stackoverflow.com/a/36234242\n return arr.reduce(\n (a, b) => {\n return a.map(x => b.map(y => x.concat([y]))).reduce((v, w) => v.concat(w), [])\n },\n [[]]\n )\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { DatasetKey } from '../_shared/types'\nimport type { ModelSpec } from '../bundle/bundle-types'\nimport type { CheckDatasetSpec } from './check-spec'\nimport type { ImplVar, OutputVar } from '../bundle/var-types'\nimport { cartesianProductOf } from '../_shared/combo'\n\nexport type CheckDatasetError = 'no-matches-for-dataset' | 'no-matches-for-group' | 'no-matches-for-type'\n\nexport interface CheckDataset {\n /** The key for the matched dataset; can be undefined if no dataset matched. */\n datasetKey?: DatasetKey\n /** The name of the matched dataset, or the name associated with the error, if defined. */\n name: string\n /** The error info if the dataset query failed to match. */\n error?: CheckDatasetError\n}\n\ninterface Match {\n datasetKey: DatasetKey\n outputVar?: OutputVar\n implVar?: ImplVar\n}\n\ninterface ExpandResult {\n /** The resolved dataset matches. */\n matches: Match[]\n error?: {\n /** The error kind if the dataset query failed to match. */\n kind: CheckDatasetError\n /** The name associated with the error, if any. */\n name: string\n }\n}\n\n/**\n * Return the list of datasets that match the given spec. If a matched dataset\n * is subscripted (i.e., contains dimensions), those dimensions will be expanded\n * so that one dataset is included for each subscript combination.\n *\n * @param modelSpec The model spec that provides output and impl var information.\n * @param datasetSpec The dataset spec from a check test.\n */\nexport function expandDatasets(modelSpec: ModelSpec, datasetSpec: CheckDatasetSpec): CheckDataset[] {\n // Find datasets that match the given query\n let result: ExpandResult\n if (datasetSpec.name) {\n result = matchByName(modelSpec, datasetSpec.name, datasetSpec.source)\n } else if (datasetSpec.group) {\n result = matchByGroup(modelSpec, datasetSpec.group)\n } else if (datasetSpec.matching?.type) {\n result = matchByType(modelSpec, datasetSpec.matching.type)\n }\n if (result.error) {\n // We didn't match any datasets; add a check dataset with undefined\n // `datasetKey` so that we can report the error later\n return [\n {\n name: result.error.name,\n error: result.error.kind\n }\n ]\n }\n\n // Expand dimensions for each match\n const matches: Match[] = result.matches\n const checkDatasets: CheckDataset[] = []\n for (const match of matches) {\n if (match.outputVar) {\n // Output vars are already expanded\n checkDatasets.push({\n datasetKey: match.datasetKey,\n name: match.outputVar.varName\n })\n } else if (match.implVar) {\n // Impl vars with dimensions need to be expanded so that we have\n // one dataset for each subscript combination\n const implVar = match.implVar\n if (implVar.dimensions.length > 0) {\n // The variable is subscripted, so expand all combinations\n const baseDatasetKey = match.datasetKey\n const subscripts = [...implVar.dimensions.map(dim => dim.subscripts)]\n const subscriptCombos = cartesianProductOf(subscripts)\n for (const subscriptCombo of subscriptCombos) {\n const subIdParts = subscriptCombo.map(sub => `[${sub.id}]`).join('')\n const subNameParts = subscriptCombo.map(sub => sub.name).join(',')\n checkDatasets.push({\n datasetKey: `${baseDatasetKey}${subIdParts}`,\n name: `${implVar.varName}[${subNameParts}]`\n })\n }\n } else {\n // The variable is not subscripted\n checkDatasets.push({\n datasetKey: match.datasetKey,\n name: implVar.varName\n })\n }\n }\n }\n\n return checkDatasets\n}\n\nfunction matchByName(modelSpec: ModelSpec, datasetName: string, datasetSource: string | undefined): ExpandResult {\n // Ignore case when matching by name\n const varNameToMatch = datasetName.toLowerCase()\n const sourceToMatch = datasetSource?.toLowerCase()\n\n // When matching by name, first consult output vars, and failing that,\n // try impl vars\n for (const [datasetKey, outputVar] of modelSpec.outputVars) {\n if (outputVar.sourceName?.toLowerCase() === sourceToMatch && outputVar.varName.toLowerCase() === varNameToMatch) {\n return {\n matches: [\n {\n datasetKey,\n outputVar\n }\n ]\n }\n }\n }\n\n // We didn't match an output var, so try impl vars\n for (const [datasetKey, implVar] of modelSpec.implVars) {\n if (implVar.varName.toLowerCase() === varNameToMatch) {\n return {\n matches: [\n {\n datasetKey,\n implVar\n }\n ]\n }\n }\n }\n\n // We didn't match anything; return the name so that we can report the\n // error later\n return {\n matches: [],\n error: {\n kind: 'no-matches-for-dataset',\n name: datasetName\n }\n }\n}\n\nfunction matchByGroup(modelSpec: ModelSpec, groupName: string): ExpandResult {\n // Ignore case when matching by group\n const groupToMatch = groupName.toLowerCase()\n\n // Find the group that matches the given name\n let matchedGroupName: string\n let matchedGroupDatasetKeys: DatasetKey[]\n for (const [group, datasetKeys] of modelSpec.datasetGroups) {\n if (group.toLowerCase() === groupToMatch) {\n matchedGroupName = group\n matchedGroupDatasetKeys = datasetKeys\n break\n }\n }\n if (matchedGroupName === undefined) {\n // We didn't match a group; return the name so that we can report the\n // error later\n return {\n matches: [],\n error: {\n kind: 'no-matches-for-group',\n name: groupName\n }\n }\n }\n\n // Find datasets for the given group; first consult output vars, and failing that,\n // try impl vars\n const matches: Match[] = []\n for (const datasetKey of matchedGroupDatasetKeys) {\n // First consult output vars\n const outputVar = modelSpec.outputVars.get(datasetKey)\n if (outputVar) {\n matches.push({\n datasetKey,\n outputVar\n })\n continue\n }\n\n // We didn't match an output var, so try impl vars\n const implVar = modelSpec.implVars.get(datasetKey)\n if (implVar) {\n matches.push({\n datasetKey,\n implVar\n })\n continue\n }\n\n // We didn't find a match; return the key so that we can report the\n // error later\n return {\n matches: [],\n error: {\n kind: 'no-matches-for-dataset',\n name: datasetKey\n }\n }\n }\n\n if (matches.length === 0) {\n // We didn't match anything; return the group name so that we can report the\n // error later\n return {\n matches: [],\n error: {\n kind: 'no-matches-for-group',\n name: matchedGroupName\n }\n }\n }\n\n return {\n matches\n }\n}\n\nfunction matchByType(modelSpec: ModelSpec, varTypeToMatch: string): ExpandResult {\n // When matching by type, we need to consult impl vars since those\n // have an associated type\n // TODO: If we match an impl var, see if there's a corresponding model\n // output and if so, use that instead (to avoid special model runs\n // to fetch impl var data)\n const matches: Match[] = []\n for (const [datasetKey, implVar] of modelSpec.implVars) {\n if (implVar.varType === varTypeToMatch) {\n matches.push({\n datasetKey,\n implVar\n })\n }\n }\n\n if (matches.length === 0) {\n // We didn't match anything; return the query so that we can report\n // the error later\n return {\n matches: [],\n error: {\n kind: 'no-matches-for-type',\n name: varTypeToMatch\n }\n }\n }\n\n return {\n matches\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport assertNever from 'assert-never'\n\nimport type { InputPosition, InputSetting, Scenario } from '../_shared/scenario'\nimport {\n allInputsAtPositionScenario,\n inputAtPositionScenario,\n keyForInputAtPosition,\n keyForInputAtValue,\n positionSetting,\n settingsScenario,\n valueSetting\n} from '../_shared/scenario'\n\nimport type { ModelSpec } from '../bundle/bundle-types'\nimport type { InputVar } from '../bundle/var-types'\n\nimport type { CheckScenarioInputSpec, CheckScenarioPosition, CheckScenarioSpec } from './check-spec'\n\nexport interface CheckScenarioError {\n kind: 'unknown-input-group' | 'empty-input-group'\n /** The name of the input group that failed to match. */\n name: string\n}\n\nexport interface CheckScenarioInputDesc {\n /** The name of the input. */\n name: string\n /** The matched input variable; can be undefined if no input matched. */\n inputVar?: InputVar\n /** The position of the input, if this is a position scenario. */\n position?: InputPosition\n /** The value of the input, for the given position or explicit value. */\n value?: number\n}\n\nexport interface CheckScenario {\n /** The scenario for the matched input(s); can be undefined if input(s) failed to match. */\n scenario?: Scenario\n /** The name of the associated input group, if any. */\n inputGroupName?: string\n /** The descriptions of the inputs; if empty, it is an \"all inputs\" scenario. */\n inputDescs: CheckScenarioInputDesc[]\n /** The error info if the scenario/input query failed to match. */\n error?: CheckScenarioError\n}\n\n/**\n * Return the list of scenarios that can be expanded from the given specs.\n *\n * @param modelSpec The model spec that provides input var information.\n * @param scenarioSpecs The scenario specs from a check test.\n * @param simplify If true, reduce the number of scenarios generated for a `matrix`\n * to make the tests run faster, otherwise expand the full set of scenarios.\n */\nexport function expandScenarios(\n modelSpec: ModelSpec,\n scenarioSpecs: CheckScenarioSpec[],\n simplify: boolean\n): CheckScenario[] {\n // When no scenarios are provided, default to \"all inputs at default\"\n if (scenarioSpecs.length === 0) {\n const scenarioSpec: CheckScenarioSpec = {\n with_inputs: 'all',\n at: 'default'\n }\n return checkScenariosFromSpec(modelSpec, scenarioSpec, simplify)\n }\n\n // Otherwise, convert the specs to actual `CheckScenario` instances\n const checkScenarios: CheckScenario[] = []\n for (const scenarioSpec of scenarioSpecs) {\n checkScenarios.push(...checkScenariosFromSpec(modelSpec, scenarioSpec, simplify))\n }\n return checkScenarios\n}\n\n/**\n * Convert a `CheckScenarioPosition` (from the parser) to an `InputPosition` (used by `Scenario`).\n */\nfunction inputPosition(position: CheckScenarioPosition): InputPosition | undefined {\n switch (position) {\n case 'default':\n return 'at-default'\n case 'min':\n return 'at-minimum'\n case 'max':\n return 'at-maximum'\n default:\n // Return undefined instead of using `assertNever` in the unlikely case that\n // the parser allowed an invalid position to sneak through\n return undefined\n }\n}\n\n/**\n * Get the value of the input at the given position.\n */\nfunction inputValueAtPosition(inputVar: InputVar, position: InputPosition): number {\n switch (position) {\n case 'at-default':\n return inputVar.defaultValue\n case 'at-minimum':\n return inputVar.minValue\n case 'at-maximum':\n return inputVar.maxValue\n default:\n assertNever(position)\n }\n}\n\n/**\n * Return an input description for the input at the given position.\n */\nfunction inputDescAtPosition(inputVar: InputVar, position: InputPosition): CheckScenarioInputDesc {\n return {\n name: inputVar.varName,\n inputVar,\n position,\n value: inputValueAtPosition(inputVar, position)\n }\n}\n\n/**\n * Return an input description for the input at the given value.\n */\nfunction inputDescAtValue(inputVar: InputVar, value: number): CheckScenarioInputDesc {\n return {\n name: inputVar.varName,\n inputVar,\n value\n }\n}\n\n/**\n * Return an input description for the given input variable.\n */\nfunction inputDescForVar(inputVar: InputVar, at: CheckScenarioPosition | number): CheckScenarioInputDesc {\n if (typeof at === 'number') {\n const value = at as number\n return inputDescAtValue(inputVar, value)\n } else {\n const position = inputPosition(at as CheckScenarioPosition)\n return inputDescAtPosition(inputVar, position)\n }\n}\n\n/**\n * Return an input description for the given input name and position/value.\n */\nfunction inputDescForName(\n modelSpec: ModelSpec,\n inputName: string,\n at: CheckScenarioPosition | number\n): CheckScenarioInputDesc {\n // Find an input variable that matches the given name\n const inputNameToMatch = inputName.toLowerCase()\n const inputVar = [...modelSpec.inputVars.values()].find(inputVar => {\n return inputVar.varName.toLowerCase() === inputNameToMatch\n })\n\n if (inputVar) {\n // Get a description of the input at the given value or position\n return inputDescForVar(inputVar, at)\n } else {\n // No input variable found that matches the given name; return with `inputVar`\n // left undefined so that we can report the error later\n return {\n name: inputName\n }\n }\n}\n\n/**\n * Return the input group that matches the given name.\n */\nfunction groupForName(modelSpec: ModelSpec, groupName: string): [string, InputVar[]] | undefined {\n // Ignore case when matching by group\n const groupToMatch = groupName.toLowerCase()\n\n // Find the group that matches the given name\n for (const [group, inputVars] of modelSpec.inputGroups) {\n if (group.toLowerCase() === groupToMatch) {\n return [group, inputVars]\n }\n }\n\n // No match\n return undefined\n}\n\n/**\n * Return a `CheckScenario` that includes error info for an unresolved input group.\n */\nfunction errorScenarioForInputGroup(\n kind: 'unknown-input-group' | 'empty-input-group',\n groupName: string\n): CheckScenario {\n return {\n inputDescs: [],\n error: {\n kind,\n name: groupName\n }\n }\n}\n\n/**\n * Return a `CheckScenario` with all inputs at the given position.\n */\nfunction checkScenarioWithAllInputsAtPosition(position: InputPosition): CheckScenario {\n const scenario = allInputsAtPositionScenario(position)\n return {\n scenario,\n inputDescs: []\n }\n}\n\n/**\n * Return a `CheckScenario` with the input at the given position.\n */\nfunction checkScenarioWithInputAtPosition(inputVar: InputVar, position: InputPosition): CheckScenario {\n const varId = inputVar.varId\n const scenario = inputAtPositionScenario(varId, varId, position)\n return {\n scenario,\n inputDescs: [inputDescAtPosition(inputVar, position)]\n }\n}\n\n/**\n * Return a `CheckScenario` for the given input descriptions.\n */\nfunction checkScenarioForInputDescs(\n groupName: string | undefined,\n inputDescs: CheckScenarioInputDesc[]\n): CheckScenario {\n let scenario: Scenario\n if (inputDescs.every(desc => desc.inputVar !== undefined)) {\n // All inputs were resolved, so create a `Scenario` that includes them all\n const settings: InputSetting[] = []\n const keyParts: string[] = []\n for (const inputDesc of inputDescs) {\n const varId = inputDesc.inputVar.varId\n if (inputDesc.position) {\n settings.push(positionSetting(varId, inputDesc.position))\n keyParts.push(keyForInputAtPosition(varId, inputDesc.position))\n } else {\n settings.push(valueSetting(varId, inputDesc.value))\n keyParts.push(keyForInputAtValue(varId, inputDesc.value))\n }\n }\n if (settings.length === 1) {\n // Use a simple key when there's only one input\n const scenarioKey = `input${keyParts[0]}`\n const groupKey = settings[0].inputVarId\n scenario = settingsScenario(scenarioKey, groupKey, settings)\n } else if (settings.length > 1) {\n // Derive or build a key when there are multiple inputs\n let scenarioKey: string\n let groupKey: string\n if (groupName) {\n // Build a scenario/group key using the provided group name\n // TODO: Allow for specifying the groupKey in YAML?\n scenarioKey = `group_${groupName.toLowerCase().replace(/ /g, '_')}`\n groupKey = scenarioKey\n } else {\n // No group name was provided, so build a scenario key by joining\n // all the key parts together\n // TODO: This could create very long and unwieldy keys if there are\n // many inputs; consider using a hash instead?\n scenarioKey = 'multi' + keyParts.join('_')\n groupKey = scenarioKey\n }\n scenario = settingsScenario(scenarioKey, groupKey, settings)\n }\n } else {\n // One or more inputs could not be resolved; leave `scenario` undefined\n // so that we can report the error later\n scenario = undefined\n }\n\n return {\n scenario,\n inputGroupName: groupName,\n inputDescs\n }\n}\n\n/**\n * Return a `CheckScenario` for the given inputs and positions/values.\n */\nfunction checkScenarioForInputSpecs(modelSpec: ModelSpec, inputSpecs: CheckScenarioInputSpec[]): CheckScenario {\n // Convert the input specs to `CheckScenarioInputDesc` instances\n const inputDescs = inputSpecs.map(inputSpec => {\n return inputDescForName(modelSpec, inputSpec.input, inputSpec.at)\n })\n\n // Create a `CheckScenario` with the input descriptions\n return checkScenarioForInputDescs(undefined, inputDescs)\n}\n\n/**\n * Return a matrix of scenarios that covers all inputs for the given model.\n */\nfunction checkScenarioMatrix(modelSpec: ModelSpec, simplify: boolean): CheckScenario[] {\n const checkScenarios: CheckScenario[] = []\n checkScenarios.push(checkScenarioWithAllInputsAtPosition('at-default'))\n if (!simplify) {\n checkScenarios.push(checkScenarioWithAllInputsAtPosition('at-minimum'))\n checkScenarios.push(checkScenarioWithAllInputsAtPosition('at-maximum'))\n for (const inputVar of modelSpec.inputVars.values()) {\n checkScenarios.push(checkScenarioWithInputAtPosition(inputVar, 'at-minimum'))\n checkScenarios.push(checkScenarioWithInputAtPosition(inputVar, 'at-maximum'))\n }\n }\n return checkScenarios\n}\n\n/**\n * Return a `CheckScenario` with all inputs in the given group at a position.\n */\nfunction checkScenarioWithAllInputsInGroupAtPosition(\n modelSpec: ModelSpec,\n groupName: string,\n position: CheckScenarioPosition\n): CheckScenario {\n // Find the group that matches the given name\n const result = groupForName(modelSpec, groupName)\n if (result === undefined) {\n // We didn't match a group; return an empty scenario with the group name so that\n // we can report the error later\n return errorScenarioForInputGroup('unknown-input-group', groupName)\n }\n const [matchedGroupName, inputVars] = result\n if (inputVars.length === 0) {\n return errorScenarioForInputGroup('empty-input-group', matchedGroupName)\n }\n\n // Get a description of each input in the group\n const inputDescs: CheckScenarioInputDesc[] = []\n for (const inputVar of inputVars) {\n inputDescs.push(inputDescForVar(inputVar, position))\n }\n\n // Create a `CheckScenario` with the input descriptions\n return checkScenarioForInputDescs(matchedGroupName, inputDescs)\n}\n\n/**\n * Return a set of scenarios, with one scenario for each input in the given group.\n */\nfunction checkScenariosForEachInputInGroup(\n modelSpec: ModelSpec,\n groupName: string,\n position: CheckScenarioPosition\n): CheckScenario[] {\n // Find the group that matches the given name\n const result = groupForName(modelSpec, groupName)\n if (result === undefined) {\n // We didn't match a group; return an empty scenario with the group name so that\n // we can report the error later\n return [errorScenarioForInputGroup('unknown-input-group', groupName)]\n }\n const [matchedGroupName, inputVars] = result\n if (inputVars.length === 0) {\n return [errorScenarioForInputGroup('empty-input-group', matchedGroupName)]\n }\n\n // Create one scenario for each input in the group\n const checkScenarios: CheckScenario[] = []\n for (const inputVar of inputVars) {\n // Get a description of the input at the given value or position,\n // then create a scenario with it\n const inputDesc = inputDescForVar(inputVar, position)\n // TODO: It might be more appropriate to use the group name as the group\n // key here (instead of deriving it from the input name)\n checkScenarios.push(checkScenarioForInputDescs(undefined, [inputDesc]))\n }\n\n return checkScenarios\n}\n\n/**\n * Return one or more `CheckScenario` instances for the given scenario spec.\n */\nfunction checkScenariosFromSpec(\n modelSpec: ModelSpec,\n scenarioSpec: CheckScenarioSpec,\n simplify: boolean\n): CheckScenario[] {\n if (scenarioSpec.preset === 'matrix') {\n // Create a matrix of scenarios\n return checkScenarioMatrix(modelSpec, simplify)\n }\n\n if (scenarioSpec.scenarios_for_each_input_in !== undefined) {\n // Create multiple scenarios (one scenario for each input in the given group)\n const groupName = scenarioSpec.scenarios_for_each_input_in\n const position = scenarioSpec.at as CheckScenarioPosition\n return checkScenariosForEachInputInGroup(modelSpec, groupName, position)\n }\n\n if (scenarioSpec.with !== undefined) {\n if (Array.isArray(scenarioSpec.with)) {\n // Create one scenario that contains the given input settings\n const inputSpecs = scenarioSpec.with as CheckScenarioInputSpec[]\n return [checkScenarioForInputSpecs(modelSpec, inputSpecs)]\n } else {\n // Create a single \"input at <position|value>\" scenario\n const inputSpec: CheckScenarioInputSpec = {\n input: scenarioSpec.with,\n at: scenarioSpec.at\n }\n return [checkScenarioForInputSpecs(modelSpec, [inputSpec])]\n }\n }\n\n if (scenarioSpec.with_inputs === 'all') {\n // Create an \"all inputs at <position>\" scenario\n const position = inputPosition(scenarioSpec.at as CheckScenarioPosition)\n return [checkScenarioWithAllInputsAtPosition(position)]\n }\n\n if (scenarioSpec.with_inputs_in !== undefined) {\n // Create one scenario that sets all inputs in the given group to a position\n const groupName = scenarioSpec.with_inputs_in\n const position = scenarioSpec.at as CheckScenarioPosition\n return [checkScenarioWithAllInputsInGroupAtPosition(modelSpec, groupName, position)]\n }\n\n // Internal error\n throw new Error(`Unhandled scenario spec: ${JSON.stringify(scenarioSpec)}`)\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\nimport { TaskQueue } from '../_shared/task-queue'\nimport type { Scenario } from '../_shared/scenario'\nimport type { DatasetKey, DatasetMap } from '../_shared/types'\nimport type { BundleModel, BundleGraphData, BundleGraphId } from '../bundle/bundle-types'\n\nexport type CompareDataRequestKey = string\n\ninterface DatasetRequest {\n kind: 'dataset'\n scenario: Scenario\n datasetKeys: DatasetKey[]\n}\n\ninterface GraphDataRequest {\n kind: 'graph-data'\n bundle: 'left' | 'right'\n scenario: Scenario\n graphId: BundleGraphId\n}\n\ntype DataRequest = DatasetRequest | GraphDataRequest\n\ninterface DatasetResponse {\n kind: 'dataset'\n datasetMapL: DatasetMap\n datasetMapR: DatasetMap\n}\n\ninterface GraphDataResponse {\n kind: 'graph-data'\n graphData?: BundleGraphData\n}\n\ntype DataResponse = DatasetResponse | GraphDataResponse\n\n/**\n * Coordinates loading of data in parallel from two models.\n */\nexport class CompareDataCoordinator {\n private readonly taskQueue: TaskQueue<DataRequest, DataResponse>\n\n constructor(public readonly bundleModelL: BundleModel, public readonly bundleModelR: BundleModel) {\n this.taskQueue = new TaskQueue({\n process: async request => {\n switch (request.kind) {\n case 'dataset': {\n // Run the models for this scenario (in parallel)\n const [resultL, resultR] = await Promise.all([\n this.bundleModelL.getDatasetsForScenario(request.scenario, request.datasetKeys),\n this.bundleModelR.getDatasetsForScenario(request.scenario, request.datasetKeys)\n ])\n return {\n kind: 'dataset',\n datasetMapL: resultL.datasetMap,\n datasetMapR: resultR.datasetMap\n }\n }\n case 'graph-data': {\n // Run the selected model for this scenario\n const bundleModel = request.bundle === 'right' ? this.bundleModelR : this.bundleModelL\n const graphData = await bundleModel.getGraphDataForScenario(request.scenario, request.graphId)\n return {\n kind: 'graph-data',\n graphData\n }\n }\n default:\n assertNever(request)\n }\n }\n })\n }\n\n requestDatasetMaps(\n requestKey: CompareDataRequestKey,\n scenario: Scenario,\n datasetKeys: DatasetKey[],\n onResponse: (datasetMapL: DatasetMap, datasetMapR: DatasetMap) => void\n ): void {\n const request: DatasetRequest = {\n kind: 'dataset',\n scenario,\n datasetKeys\n }\n this.taskQueue.addTask(requestKey, request, response => {\n if (response.kind === 'dataset') {\n onResponse(response.datasetMapL, response.datasetMapR)\n }\n })\n }\n\n requestGraphData(\n requestKey: CompareDataRequestKey,\n bundle: 'left' | 'right',\n scenario: Scenario,\n graphId: BundleGraphId,\n onResponse: (graphData: BundleGraphData) => void\n ): void {\n const request: GraphDataRequest = {\n kind: 'graph-data',\n bundle,\n scenario,\n graphId\n }\n this.taskQueue.addTask(requestKey, request, response => {\n if (response.kind === 'graph-data') {\n onResponse(response.graphData)\n }\n })\n }\n\n cancelRequest(key: CompareDataRequestKey): void {\n this.taskQueue.cancelTask(key)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { Dataset, DatasetKey, DatasetMap, ScenarioKey } from '../_shared/types'\nimport type { CompareDatasetReport } from './compare-report'\n\nexport interface DiffPoint {\n time: number\n valueL: number\n valueR: number\n}\n\nexport type DiffValidity = 'neither' | 'left-only' | 'right-only' | 'both'\n\nexport interface DiffReport {\n validity: DiffValidity\n minValue: number\n maxValue: number\n avgDiff: number\n minDiff: number\n maxDiff: number\n maxDiffPoint: DiffPoint\n}\n\nexport function diffDatasets(datasetL: Dataset | undefined, datasetR: Dataset | undefined): DiffReport {\n let minValueL = Number.MAX_VALUE\n let maxValueL = Number.MIN_VALUE\n let minValueR = Number.MAX_VALUE\n let maxValueR = Number.MIN_VALUE\n let minValue = Number.MAX_VALUE\n let maxValue = Number.MIN_VALUE\n let minRawDiff = Number.MAX_VALUE\n let maxRawDiff = -1\n let maxDiffPoint: DiffPoint\n let diffCount = 0\n let totalRawDiff = 0\n\n if (datasetL && datasetR) {\n const times = new Set([...datasetL.keys(), ...datasetR.keys()])\n\n for (const t of times) {\n const valueL = datasetL.get(t)\n if (valueL !== undefined) {\n if (valueL < minValueL) minValueL = valueL\n if (valueL > maxValueL) maxValueL = valueL\n if (valueL < minValue) minValue = valueL\n if (valueL > maxValue) maxValue = valueL\n }\n\n const valueR = datasetR.get(t)\n if (valueR !== undefined) {\n if (valueR < minValueR) minValueR = valueR\n if (valueR > maxValueR) maxValueR = valueR\n if (valueR < minValue) minValue = valueR\n if (valueR > maxValue) maxValue = valueR\n }\n\n if (valueL === undefined || valueR === undefined) {\n // Only include diffs if we have a value from both datasets at this time\n continue\n }\n\n const rawDiff = Math.abs(valueR - valueL)\n if (rawDiff < minRawDiff) {\n minRawDiff = rawDiff\n }\n if (rawDiff > maxRawDiff) {\n maxRawDiff = rawDiff\n maxDiffPoint = {\n time: t,\n valueL,\n valueR\n }\n }\n diffCount++\n // TODO: This might overflow if the numbers are very large\n totalRawDiff += rawDiff\n }\n }\n\n function pct(x: number): number {\n return x * 100\n }\n\n let minDiff: number\n let maxDiff: number\n let avgDiff: number\n if (minValueL === maxValueL && minValueR === maxValueR) {\n // When both values hold constant, it doesn't make sense to diff\n // against the spread, so use relative change instead (where the\n // left dataset is assumed to be the baseline/reference)\n const diff = pct(maxValueL !== 0 ? Math.abs((maxValueR - maxValueL) / maxValueL) : 1)\n minDiff = diff\n maxDiff = diff\n avgDiff = diff\n } else {\n // Otherwise, calculate the differences relative to the spread\n // (i.e., the distance between the extremes) of the two datasets\n const spread = maxValue - minValue\n minDiff = pct(spread > 0 ? minRawDiff / spread : 0)\n maxDiff = pct(spread > 0 ? maxRawDiff / spread : 0)\n const avgRawDiff = totalRawDiff / diffCount\n avgDiff = pct(spread > 0 ? avgRawDiff / spread : 0)\n }\n\n let validity: DiffValidity\n if (datasetL && datasetR) {\n validity = 'both'\n } else if (datasetL) {\n validity = 'left-only'\n } else if (datasetR) {\n validity = 'right-only'\n } else {\n validity = 'neither'\n }\n\n return {\n validity,\n minValue,\n maxValue,\n avgDiff,\n minDiff,\n maxDiff,\n maxDiffPoint\n }\n}\n\nexport function compareDatasets(\n scenarioKey: ScenarioKey,\n datasetKey: DatasetKey,\n datasetMapL: DatasetMap,\n datasetMapR: DatasetMap\n): CompareDatasetReport {\n const datasetL = datasetMapL.get(datasetKey)\n const datasetR = datasetMapR.get(datasetKey)\n const diffReport = diffDatasets(datasetL, datasetR)\n return {\n scenarioKey,\n datasetKey,\n diffReport\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { DatasetKey, ScenarioKey } from '../_shared/types'\nimport type { BundleGraphSpec } from '../bundle/bundle-types'\nimport type { CompareDatasetSummary } from './compare-summary'\n\nexport type GraphInclusion = 'neither' | 'left-only' | 'right-only' | 'both'\n\nexport interface GraphMetadataReport {\n /** The key for the metadata field. */\n key: string\n /** The value of the metadata field in the left bundle. */\n valueL?: string\n /** The value of the metadata field in the right bundle. */\n valueR?: string\n}\n\nexport interface GraphDatasetReport {\n /** The dataset key. */\n datasetKey: DatasetKey\n /** The max diff for this dataset. */\n maxDiff?: number\n}\n\nexport interface GraphReport {\n /** Indicates which bundles the graph is defined in. */\n inclusion: GraphInclusion\n /** The metadata fields with differences. */\n metadataReports: GraphMetadataReport[]\n /** The datasets with differences. */\n datasetReports: GraphDatasetReport[]\n}\n\n/**\n * Compare the metadata and datasets for the given graphs.\n *\n * @param graphL The graph defined in the left bundle.\n * @param graphR The graph defined in the right bundle.\n * @param scenarioKey The scenario used for comparing datasets.\n * @param datasetSummaries The set of summaries from a previous comparison run.\n */\nexport function diffGraphs(\n graphL: BundleGraphSpec | undefined,\n graphR: BundleGraphSpec | undefined,\n scenarioKey: ScenarioKey,\n datasetSummaries: CompareDatasetSummary[]\n): GraphReport {\n // Check in which bundles the graph is defined\n let inclusion: GraphInclusion\n if (graphL && graphR) {\n inclusion = 'both'\n } else if (graphL) {\n inclusion = 'left-only'\n } else if (graphR) {\n inclusion = 'right-only'\n } else {\n inclusion = 'neither'\n }\n\n // Compare the metadata for the two graphs\n const metadataReports: GraphMetadataReport[] = []\n if (graphL?.metadata && graphR?.metadata) {\n const metaKeys: Set<string> = new Set()\n for (const key of graphL.metadata.keys()) {\n metaKeys.add(key)\n }\n for (const key of graphR.metadata.keys()) {\n metaKeys.add(key)\n }\n for (const key of metaKeys) {\n const valueL = graphL.metadata.get(key)\n const valueR = graphR.metadata.get(key)\n if (valueL !== valueR) {\n // Add a report only if the values are different for this key\n metadataReports.push({\n key,\n valueL,\n valueR\n })\n }\n }\n }\n\n // Compare the datasets for the two graphs\n const datasetReports: GraphDatasetReport[] = []\n if (graphL && graphR) {\n const datasetKeys: Set<DatasetKey> = new Set()\n for (const dataset of graphL.datasets) {\n datasetKeys.add(dataset.datasetKey)\n }\n for (const dataset of graphR.datasets) {\n datasetKeys.add(dataset.datasetKey)\n }\n for (const datasetKey of datasetKeys) {\n const summary = datasetSummaries.find(summary => summary.d === datasetKey && summary.s === scenarioKey)\n // TODO: Flag as an error if we don't have a CompareDatasetSummary\n // for the datasets?\n const maxDiff = summary !== undefined ? summary.md : undefined\n datasetReports.push({\n datasetKey,\n maxDiff\n })\n }\n }\n\n return {\n inclusion,\n metadataReports,\n datasetReports\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { PerfReport } from '../perf/perf-stats'\nimport type { DatasetKey, ScenarioKey } from '../_shared/types'\nimport type { CompareReport } from './compare-report'\n\n/**\n * A simplified/terse version of `CompareDatasetReport` that matches the\n * format of the JSON objects emitted by the CLI in terse mode.\n * The object keys are terse and it only includes the minimum set of fields\n * to keep the file smaller when there are many reported differences.\n */\nexport interface CompareDatasetSummary {\n /** Short for `scenarioKey`. */\n s: ScenarioKey\n /** Short for `datasetKey`. */\n d: DatasetKey\n /** Short for `maxDiff`. */\n md: number\n}\n\n/**\n * A simplified/terse version of `CompareReport` that matches the\n * format of the JSON objects emitted by the CLI in terse mode.\n */\nexport interface CompareSummary {\n datasetSummaries: CompareDatasetSummary[]\n perfReportL: PerfReport\n perfReportR: PerfReport\n}\n\n/**\n * Convert a full `CompareReport` to a simplified `CompareSummary` that includes\n * the minimum set of fields needed to keep the file smaller when there are many\n * reported differences.\n *\n * @param compareReport The full compare report.\n * @return The converted compare summary.\n */\nexport function compareSummaryFromReport(compareReport: CompareReport): CompareSummary {\n const datasetSummaries: CompareDatasetSummary[] = []\n\n for (const r of compareReport.datasetReports) {\n if (r.diffReport.validity === 'both' && r.diffReport.maxDiff > 0) {\n datasetSummaries.push({\n s: r.scenarioKey,\n d: r.datasetKey,\n md: r.diffReport.maxDiff\n })\n }\n }\n\n return {\n datasetSummaries,\n perfReportL: compareReport.perfReportL,\n perfReportR: compareReport.perfReportR\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { BundleGraphId, BundleModel } from '../bundle/bundle-types'\nimport type { Scenario } from '../_shared/scenario'\nimport type { DatasetKey } from '../_shared/types'\n\n/**\n * Wrap the given `BundleModel` in a new `BundleModel` that synchronizes\n * (i.e., single-tracks) the wrapped model so that only one call to\n * `getDatasetsForScenario` can be made at a time.\n *\n * This is a convenience for models that use an asynchronous model runner\n * but only allow for one model run at a time. In most cases, we use\n * a `TaskQueue` to serialize requests, but due to the fact that we allow\n * for cancellation of e.g. `runSuite`, it's possible that we may try to\n * start a new `runSuite` before the previous model runs had a chance\n * to complete.\n *\n * TODO: This is a heavy-handed approach and may not be appropriate for\n * all model types (it is mainly designed for SDEverywhere-generated models\n * that use the `sde-model-async` package). It might be better to instead\n * revisit the design of the `SuiteRunner`, `DataCoordinator`, etc classes.\n *\n * @param sourceModel The underlying bundle model.\n */\nexport function synchronizedBundleModel(sourceModel: BundleModel): BundleModel {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const promiseQueue: PromiseQueue<any> = new PromiseQueue()\n\n return {\n modelSpec: sourceModel.modelSpec,\n getDatasetsForScenario: (scenario: Scenario, datasetKeys: DatasetKey[]) => {\n return promiseQueue.add(() => sourceModel.getDatasetsForScenario(scenario, datasetKeys))\n },\n getGraphsForDataset: sourceModel.getGraphsForDataset?.bind(sourceModel),\n getGraphDataForScenario: (scenario: Scenario, graphId: BundleGraphId) => {\n return promiseQueue.add(() => sourceModel.getGraphDataForScenario(scenario, graphId))\n },\n getGraphLinksForScenario: sourceModel.getGraphLinksForScenario.bind(sourceModel)\n }\n}\n\ntype PromiseFunc<T> = () => Promise<T>\n\n/**\n * Quick and dirty promise queue that supports running at most one operation\n * at a time.\n */\nclass PromiseQueue<T> {\n private readonly tasks: PromiseFunc<void>[] = []\n private runningCount = 0\n\n add(f: PromiseFunc<T>): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const run = async (): Promise<void> => {\n this.runningCount++\n\n const promise = f()\n try {\n const result = await promise\n resolve(result)\n } catch (e) {\n reject(e)\n } finally {\n this.runningCount--\n this.runNext()\n }\n }\n\n if (this.runningCount < 1) {\n run()\n } else {\n this.tasks.push(run)\n }\n })\n }\n\n private runNext(): void {\n if (this.tasks.length > 0) {\n const task = this.tasks.shift()\n if (task) {\n task()\n }\n }\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { Scenario } from '../_shared/scenario'\nimport type { DatasetKey, DatasetMap } from '../_shared/types'\nimport type { BundleModel, LoadedBundle, NamedBundle } from '../bundle/bundle-types'\nimport type { CompareConfig } from '../compare/compare-config'\nimport type { CheckConfig } from '../check/check-config'\nimport type { Config, ConfigOptions } from './config-types'\nimport { synchronizedBundleModel } from './synchronized-model'\n\nexport async function createConfig(options: ConfigOptions): Promise<Config> {\n // Initialize the \"current\" bundle model (the one being checked)\n const origCurrentBundle = await loadSynchronized(options.current)\n\n // Create the comparison configuration, if defined\n let currentBundle: LoadedBundle\n let compareConfig: CompareConfig\n if (options.compare === undefined) {\n // When there is no comparison configuration, there are no renames to handle,\n // so use the unmodified \"current\" bundle\n currentBundle = origCurrentBundle\n } else {\n // Initialize the \"baseline\" bundle model (the one that \"current\" will be\n // compared against)\n const baselineBundle = await loadSynchronized(options.compare.baseline)\n\n // Invert the map of renamed keys so that new names are on the left (map\n // keys) old names are on the right (map values)\n const renamedDatasetKeys = options.compare.datasets.renamedDatasetKeys\n const invertedRenamedKeys: Map<DatasetKey, DatasetKey> = new Map()\n renamedDatasetKeys?.forEach((newKey, oldKey) => {\n invertedRenamedKeys.set(newKey, oldKey)\n })\n\n const rightKeyForLeftKey = (leftKey: DatasetKey) => {\n return renamedDatasetKeys?.get(leftKey) || leftKey\n }\n\n const leftKeyForRightKey = (rightKey: DatasetKey) => {\n return invertedRenamedKeys.get(rightKey) || rightKey\n }\n\n // Wrap the right bundle model with one that maps \"old\" dataset keys\n // to \"new\" dataset keys\n const origBundleModelR = origCurrentBundle.model\n const adjBundleModelR: BundleModel = {\n modelSpec: origBundleModelR.modelSpec,\n getDatasetsForScenario: async (scenario: Scenario, datasetKeys: DatasetKey[]) => {\n // The given dataset keys are for the \"left\" bundle, so convert to the \"right\" keys\n const rightKeys = datasetKeys.map(rightKeyForLeftKey)\n\n // The returned dataset map has the \"right\" keys, so convert back to the \"left\" keys\n const result = await origBundleModelR.getDatasetsForScenario(scenario, rightKeys)\n const mapWithRightKeys = result.datasetMap\n const mapWithLeftKeys: DatasetMap = new Map()\n for (const [rightKey, dataset] of mapWithRightKeys.entries()) {\n const leftKey = leftKeyForRightKey(rightKey)\n mapWithLeftKeys.set(leftKey, dataset)\n }\n\n return {\n datasetMap: mapWithLeftKeys,\n modelRunTime: result.modelRunTime\n }\n },\n getGraphsForDataset: origBundleModelR.getGraphsForDataset?.bind(origBundleModelR),\n getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),\n getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)\n }\n\n // Initialize the configuration for comparisons\n currentBundle = {\n ...origCurrentBundle,\n model: adjBundleModelR\n }\n compareConfig = {\n bundleL: baselineBundle,\n bundleR: currentBundle,\n thresholds: options.compare.thresholds,\n scenarios: options.compare.scenarios,\n datasets: options.compare.datasets\n }\n }\n\n // Create the check configuration\n const checkConfig: CheckConfig = {\n bundle: currentBundle,\n tests: options.check.tests\n }\n\n return {\n check: checkConfig,\n compare: compareConfig\n }\n}\n\n/**\n * Loads the given model and wraps the underlying `BundleModel` in a new\n * `BundleModel` that synchronizes (i.e., single-tracks) the wrapped model\n * so that only one call to `getDatasetsForScenario` can be made at a time.\n *\n * @param sourceBundle The bundle to be loaded.\n */\nasync function loadSynchronized(sourceBundle: NamedBundle): Promise<LoadedBundle> {\n const sourceModel = await sourceBundle.bundle.initModel()\n const synchronizedModel = synchronizedBundleModel(sourceModel)\n return {\n name: sourceBundle.name,\n version: sourceBundle.bundle.version,\n model: synchronizedModel\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { Scenario } from '../_shared/scenario'\nimport type { DatasetKey } from '../_shared/types'\nimport type { Bundle } from '../bundle/bundle-types'\nimport type { OutputVar } from '../bundle/var-types'\nimport type { CompareDatasets } from '../compare/compare-config'\nimport type { DatasetInfo } from '../compare/compare-info'\n\n/**\n * Manages a set of dataset keys (corresponding to the available model outputs\n * in the given bundles) that can be used to compare two versions of the model.\n *\n * This class computes the union of the available dataset keys and handles\n * renames so that if any variables were renamed in the \"right\" bundle, the\n * old key will be used so that the variable can still be compared.\n *\n * This is intended to be a simple, general purpose way to create a set of\n * dataset keys, but every model is different, so you can replace this with\n * a different set of dataset keys that is better suited for the model you\n * are testing.\n */\nexport class DatasetManager implements CompareDatasets {\n public readonly allOutputVarKeys: DatasetKey[]\n public readonly modelOutputVarKeys: DatasetKey[]\n\n /**\n * @param bundleL The \"left\" bundle being compared.\n * @param bundleR The \"right\" bundle being compared.\n * @param renamedDatasetKeys The mapping of renamed dataset keys.\n */\n constructor(\n private readonly bundleL: Bundle,\n private readonly bundleR: Bundle,\n public readonly renamedDatasetKeys?: Map<DatasetKey, DatasetKey>\n ) {\n // Invert the map of renamed keys so that new names are on the left (map\n // keys) old names are on the right (map values)\n const invertedRenamedKeys: Map<DatasetKey, DatasetKey> = new Map()\n renamedDatasetKeys?.forEach((newKey, oldKey) => {\n invertedRenamedKeys.set(newKey, oldKey)\n })\n\n function leftKeyForRightKey(rightKey: DatasetKey): DatasetKey {\n return invertedRenamedKeys.get(rightKey) || rightKey\n }\n\n // Get the union of all output variables appearing in left and/or right\n const allOutputVarKeysSet: Set<DatasetKey> = new Set()\n const modelOutputVarKeysSet: Set<DatasetKey> = new Set()\n function addOutputVars(outputVars: Map<DatasetKey, OutputVar>, handleRenames: boolean): void {\n outputVars.forEach((outputVar, key) => {\n // When there are renamed output variables, only include the old dataset\n // key in the set of all keys\n const remappedKey = handleRenames ? leftKeyForRightKey(key) : key\n allOutputVarKeysSet.add(remappedKey)\n if (outputVar.sourceName === undefined) {\n modelOutputVarKeysSet.add(remappedKey)\n }\n })\n }\n addOutputVars(bundleL.modelSpec.outputVars, false)\n addOutputVars(bundleR.modelSpec.outputVars, true)\n this.allOutputVarKeys = Array.from(allOutputVarKeysSet)\n this.modelOutputVarKeys = Array.from(modelOutputVarKeysSet)\n }\n\n // from CompareDatasets interface\n getDatasetKeysForScenario(scenario: Scenario): DatasetKey[] {\n if (scenario.kind === 'all-inputs' && scenario.position === 'at-default') {\n // Include both model and static variables for the \"all at default\" scenario\n return this.allOutputVarKeys\n } else {\n // For all other scenarios, only include model variables (since only model\n // outputs are affected by different input scenarios)\n return this.modelOutputVarKeys\n }\n }\n\n // from CompareDatasets interface\n getDatasetInfo(datasetKey: DatasetKey): DatasetInfo | undefined {\n const modelSpecL = this.bundleL.modelSpec\n const modelSpecR = this.bundleR.modelSpec\n\n // Get the dataset keys accounting for renames\n const datasetKeyL = datasetKey\n const datasetKeyR = this.renamedDatasetKeys?.get(datasetKeyL) || datasetKeyL\n\n // Get the output variable name\n const outputVarL = modelSpecL.outputVars.get(datasetKeyL)\n const outputVarR = modelSpecR.outputVars.get(datasetKeyR)\n let varName: string\n let newVarName: string\n let sourceName: string\n let newSourceName: string\n if (outputVarL && outputVarR && outputVarL.varName != outputVarR.varName) {\n varName = outputVarL.varName\n newVarName = outputVarR.varName\n } else {\n const outputVar = outputVarR || outputVarL\n varName = outputVar?.varName || 'Unknown'\n }\n if (outputVarL && outputVarR && outputVarL.sourceName != outputVarR.sourceName) {\n sourceName = outputVarL.sourceName\n newSourceName = outputVarR.sourceName\n } else {\n const outputVar = outputVarR || outputVarL\n sourceName = outputVar?.sourceName\n }\n\n return {\n varName,\n newVarName,\n sourceName,\n newSourceName,\n relatedItems: outputVarR?.relatedItems || []\n }\n }\n}\n","// Copyright (c) 2022 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { InputPosition, InputSetting, Scenario } from '../_shared/scenario'\nimport { allInputsAtPositionScenario, inputAtPositionScenario } from '../_shared/scenario'\nimport type { ScenarioGroupKey, ScenarioKey, VarId } from '../_shared/types'\n\nimport type { Bundle } from '../bundle/bundle-types'\nimport type { InputVar, RelatedItem } from '../bundle/var-types'\n\nimport type { CompareGroupInfo } from '../compare/compare-group'\nimport type { ScenarioInfo } from '../compare/compare-info'\nimport type { CompareScenarios } from '../compare/compare-config'\n\n/**\n * Manages a set of scenarios (corresponding to the available model inputs\n * in the given bundles) that can be used to compare two versions of the model.\n */\nexport class ScenarioManager implements CompareScenarios {\n private readonly scenarios: Map<ScenarioKey, Scenario> = new Map()\n private readonly scenarioInfo: Map<ScenarioKey, ScenarioInfo> = new Map()\n private readonly defaultInfoForGroup: Map<ScenarioGroupKey, ScenarioInfo> = new Map()\n private readonly groupInfo: Map<ScenarioGroupKey, CompareGroupInfo> = new Map()\n\n /**\n * @param bundleL The \"left\" bundle being compared.\n * @param bundleR The \"right\" bundle being compared.\n */\n constructor(private readonly bundleL: Bundle, private readonly bundleR: Bundle) {}\n\n // from CompareScenarios interface\n getScenarios(): Scenario[] {\n return [...this.scenarios.values()]\n }\n\n // from CompareScenarios interface\n getScenario(scenarioKey: ScenarioKey): Scenario | undefined {\n return this.scenarios.get(scenarioKey)\n }\n\n // from CompareScenarios interface\n getScenarioGroupInfo(groupKey: ScenarioGroupKey): CompareGroupInfo | undefined {\n return this.groupInfo.get(groupKey)\n }\n\n // from CompareScenarios interface\n getScenarioInfo(scenario: Scenario, groupKey: ScenarioGroupKey): ScenarioInfo | undefined {\n // If this is the \"all inputs at default\" scenario, see if we have custom\n // info for the given group\n if (scenario.key === 'all_inputs_at_default') {\n const defaultInfo = this.defaultInfoForGroup.get(groupKey)\n if (defaultInfo) {\n return defaultInfo\n }\n }\n\n // Otherwise, return the info that was provided or computed for the scenario\n return this.scenarioInfo.get(scenario.key)\n }\n\n /**\n * Override the title and subtitle that are displayed when the \"all inputs at default\"\n * scenario is included for a particular group. This can be used to customize the\n * text instead of showing the default message (\"...at default\").\n *\n * @param groupKey The scenario group key.\n * @param scenarioInfo The custom info to be displayed.\n */\n setDefaultScenarioInfoForGroup(groupKey: ScenarioGroupKey, scenarioInfo: ScenarioInfo): void {\n this.defaultInfoForGroup.set(groupKey, scenarioInfo)\n }\n\n /**\n * Add a scenario to the set.\n *\n * @param scenario The scenario to be added.\n * @param scenarioInfo The custom title/subtitle for the scenario. If left undefined, the\n * default (possibly generic) info will be used.\n * @param groupInfo The custom title/subtitle for the group. If left undefined, the\n * default (possibly generic) info will be used.\n */\n addScenario(scenario: Scenario, scenarioInfo?: ScenarioInfo, groupInfo?: CompareGroupInfo): void {\n // Add the scenario\n this.scenarios.set(scenario.key, scenario)\n\n // Add the scenario info, if provided\n if (!scenarioInfo) {\n scenarioInfo = this.getInfoForScenario(scenario)\n }\n this.scenarioInfo.set(scenario.key, scenarioInfo)\n\n // Add the provided group info or use defaults\n if (!groupInfo) {\n groupInfo = this.getGroupInfoForScenario(scenario)\n }\n this.groupInfo.set(scenario.groupKey, groupInfo)\n }\n\n /**\n * Adds a set of scenarios that can be used to compare the two versions of\n * the given model.\n *\n * This function computes a matrix of input scenarios using the input variables\n * advertised by the given bundles. It will generate scenarios such that for\n * any output variable, the model will be run:\n * - once with all inputs at their default\n * - once with all inputs at their minimum\n * - once with all inputs at their maximum\n * - twice for each input\n * - once with single input at its minimum\n * - once with single input at its maximum\n *\n * This is intended to be a simple, general purpose way to create a set of\n * scenarios, but every model is different, so you can replace this with a\n * function to generate a different set of scenarios that is better suited\n * for the model you are testing.\n */\n addScenarioMatrix(): void {\n // Get the set of input variable IDs for each model\n const inputVarIdsL = Array.from(this.bundleL.modelSpec.inputVars.keys())\n const inputVarIdsR = Array.from(this.bundleR.modelSpec.inputVars.keys())\n\n // Get the union of all input variables appearing in left and/or right\n // TODO: Omit the inputs that are in L but not in R\n const inputVarIds = new Set([...inputVarIdsL, ...inputVarIdsR])\n\n // Compute the matrix of scenarios based on the available input variables\n this.addScenario(allInputsAtPositionScenario('at-default'))\n this.addScenario(allInputsAtPositionScenario('at-minimum'))\n this.addScenario(allInputsAtPositionScenario('at-maximum'))\n for (const inputVarId of inputVarIds) {\n this.addScenario(inputAtPositionScenario(inputVarId, inputVarId, 'at-minimum'))\n this.addScenario(inputAtPositionScenario(inputVarId, inputVarId, 'at-maximum'))\n }\n }\n\n private getGroupInfoForScenario(scenario: Scenario): CompareGroupInfo {\n switch (scenario.kind) {\n case 'all-inputs':\n return {\n title: 'All Inputs',\n relatedItems: []\n }\n case 'settings':\n if (scenario.settings.length === 1) {\n const inputVar = this.getInputVarForSetting(scenario.settings[0])\n let relatedItems: RelatedItem[]\n let subtitle: string\n if (inputVar?.relatedItem) {\n relatedItems = [inputVar.relatedItem]\n subtitle = inputVar.relatedItem.locationPath.join(' <span class=\"related-sep\">></span> ')\n } else {\n relatedItems = []\n subtitle = undefined\n }\n return {\n title: inputVar?.varName || 'Unknown Input',\n subtitle,\n relatedItems\n }\n } else {\n return {\n title: 'Multiple Inputs',\n relatedItems: this.getRelatedItemsForSettings(scenario.settings)\n }\n }\n default:\n assertNever(scenario)\n }\n }\n\n private getInfoForScenario(scenario: Scenario): ScenarioInfo {\n switch (scenario.kind) {\n case 'all-inputs':\n return this.getInfoForPositionSetting(undefined, scenario.position)\n case 'settings':\n // For now we only attempt to build info for single-input-at-position scenarios;\n // for all others, use a placeholder message, which should encourage the user to\n // provide custom info\n if (scenario.settings.length === 1 && scenario.settings[0].kind === 'position') {\n const setting = scenario.settings[0]\n return this.getInfoForPositionSetting(setting.inputVarId, setting.position)\n } else {\n return {\n title: `PLACEHOLDER (scenario info not provided)`,\n position: 1\n }\n }\n default:\n assertNever(scenario)\n }\n }\n\n private getInfoForPositionSetting(inputVarId: VarId | undefined, inputPosition: InputPosition): ScenarioInfo {\n const title = inputPosition.replace('-', ' ')\n\n let subtitle: string\n if (inputVarId) {\n const inputVarL = this.bundleL.modelSpec.inputVars.get(inputVarId)\n const inputVarR = this.bundleR.modelSpec.inputVars.get(inputVarId)\n const valueL = inputValue(inputVarL, inputPosition)\n const valueR = inputValue(inputVarR, inputPosition)\n if (valueL !== valueR) {\n // The values are different, so show both in different colors\n let values = ''\n values += '('\n values += `<span class='dataset-color-0'>${valueL}</span>`\n values += ' | '\n values += `<span class='dataset-color-1'>${valueR}</span>`\n values += ')'\n subtitle = values\n } else {\n // The values are the same, so just show a single value in gray\n subtitle = `(${valueL})`\n }\n } else {\n subtitle = undefined\n }\n\n // TODO: For now the positioning is fixed; should make it customizable since\n // sometimes it makes more sense to have the default scenario in the middle\n let position: number\n switch (inputPosition) {\n case 'at-default':\n position = 0\n break\n case 'at-minimum':\n position = 1\n break\n case 'at-maximum':\n position = 2\n break\n default:\n assertNever(inputPosition)\n }\n\n return {\n title,\n subtitle,\n position\n }\n }\n\n private getRelatedItemsForSettings(settings: InputSetting[]): RelatedItem[] {\n const relatedItems: RelatedItem[] = []\n for (const setting of settings) {\n const inputVar = this.getInputVarForSetting(setting)\n if (inputVar?.relatedItem) {\n relatedItems.push(inputVar.relatedItem)\n }\n }\n return relatedItems\n }\n\n private getInputVarForSetting(setting: InputSetting): InputVar | undefined {\n const inputVarId = setting.inputVarId\n const inputVarL = this.bundleL.modelSpec.inputVars.get(inputVarId)\n const inputVarR = this.bundleR.modelSpec.inputVars.get(inputVarId)\n return inputVarR || inputVarL\n }\n}\n\nfunction inputValue(inputVar: InputVar | undefined, position: InputPosition): string {\n if (inputVar) {\n switch (position) {\n case 'at-default':\n return inputVar.defaultValue.toString()\n case 'at-minimum':\n return inputVar.minValue.toString()\n case 'at-maximum':\n return inputVar.maxValue.toString()\n default:\n assertNever(position)\n }\n } else {\n return 'n/a'\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport { TaskQueue } from '../_shared/task-queue'\nimport { allInputsAtPositionScenario } from '../_shared/scenario'\n\nimport type { BundleModel } from '../bundle/bundle-types'\n\nimport type { PerfReport } from './perf-stats'\nimport { PerfStats } from './perf-stats'\n\n// The number of warmups for each perf run\nconst warmupCount = 5\n\n// The number of times to run the model for each perf run\nconst runCount = 100\n\ntype PerfRequestKind = 'left' | 'right' | 'both'\n\ninterface PerfRequest {\n kind: PerfRequestKind\n}\n\ninterface PerfResponse {\n runTimeL?: number\n runTimeR?: number\n}\n\nexport class PerfRunner {\n private readonly taskQueue: TaskQueue<PerfRequest, PerfResponse>\n public onComplete?: (reportL: PerfReport, reportR: PerfReport) => void\n public onError?: (error: Error) => void\n\n constructor(\n public readonly bundleModelL: BundleModel,\n public readonly bundleModelR: BundleModel,\n private readonly mode: 'serial' | 'parallel' = 'serial'\n ) {\n const scenario = allInputsAtPositionScenario('at-default')\n\n this.taskQueue = new TaskQueue({\n process: async request => {\n switch (request.kind) {\n case 'left': {\n const result = await bundleModelL.getDatasetsForScenario(scenario, [])\n return {\n runTimeL: result.modelRunTime\n }\n }\n case 'right': {\n const result = await bundleModelR.getDatasetsForScenario(scenario, [])\n return {\n runTimeR: result.modelRunTime\n }\n }\n case 'both': {\n const [resultL, resultR] = await Promise.all([\n bundleModelL.getDatasetsForScenario(scenario, []),\n bundleModelR.getDatasetsForScenario(scenario, [])\n ])\n return {\n runTimeL: resultL.modelRunTime,\n runTimeR: resultR.modelRunTime\n }\n }\n default:\n assertNever(request.kind)\n }\n }\n })\n }\n\n start(): void {\n const statsL = new PerfStats()\n const statsR = new PerfStats()\n this.taskQueue.onIdle = error => {\n if (error) {\n this.onError(error)\n } else {\n this.onComplete?.(statsL.toReport(), statsR.toReport())\n }\n }\n\n const taskQueue = this.taskQueue\n function addTask(index: number, warmup: boolean, kind: PerfRequestKind) {\n const key = `${warmup ? 'warmup-' : ''}${kind}-${index}`\n const request: PerfRequest = {\n kind\n }\n taskQueue.addTask(key, request, response => {\n if (!warmup && response.runTimeL !== undefined) {\n statsL.addRun(response.runTimeL)\n }\n if (!warmup && response.runTimeR !== undefined) {\n statsR.addRun(response.runTimeR)\n }\n })\n }\n function addTasks(kind: PerfRequestKind) {\n for (let i = 0; i < warmupCount; i++) {\n addTask(i, true, kind)\n }\n for (let i = 0; i < runCount; i++) {\n addTask(i, false, kind)\n }\n }\n\n if (this.mode === 'parallel') {\n addTasks('both')\n } else {\n addTasks('left')\n addTasks('right')\n }\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nexport interface PerfReport {\n readonly minTime: number\n readonly maxTime: number\n readonly avgTime: number\n readonly allTimes: number[]\n}\n\nexport class PerfStats {\n private readonly times: number[] = []\n\n addRun(timeInMillis: number): void {\n this.times.push(timeInMillis)\n }\n\n toReport(): PerfReport {\n if (this.times.length === 0) {\n return {\n minTime: 0,\n maxTime: 0,\n avgTime: 0,\n allTimes: []\n }\n }\n\n // Get the absolute min and max times, just for informational\n // purposes (these will be thrown out before computing the average)\n const minTime = Math.min(...this.times)\n const maxTime = Math.max(...this.times)\n\n // Sort the run times, then keep only the middle 50% so that we\n // ignore outliers for computing the average time\n const sortedTimes = this.times.sort()\n const minIndex = Math.floor(sortedTimes.length / 4)\n const maxIndex = minIndex + Math.ceil(sortedTimes.length / 2)\n const middleTimes = sortedTimes.slice(minIndex, maxIndex)\n const totalTime = middleTimes.reduce((a, b) => a + b, 0)\n const avgTime = totalTime / middleTimes.length\n\n return {\n minTime,\n maxTime,\n avgTime,\n allTimes: sortedTimes\n }\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport assertNever from 'assert-never'\n\nimport { TaskQueue } from '../_shared/task-queue'\nimport type { DatasetKey } from '../_shared/types'\n\nimport type { DataRequest } from '../data/data-planner'\nimport { DataPlanner } from '../data/data-planner'\n\nimport { parseTestYaml } from '../check/check-parser'\nimport { runChecks } from '../check/check-runner'\n\nimport type { CompareDatasetReport, CompareReport } from '../compare/compare-report'\nimport { runCompare } from '../compare/compare-runner'\n\nimport type { Config } from '../config/config-types'\n\nimport { PerfStats } from '../perf/perf-stats'\n\nimport type { SuiteReport } from './suite-report'\nimport type { DatasetsResult } from '../_shared/data-source'\n\nexport type CancelRunSuite = () => void\n\nexport interface RunSuiteCallbacks {\n onProgress?: (pct: number) => void\n onComplete?: (suiteReport: SuiteReport) => void\n onError?: (error: Error) => void\n}\n\nexport interface RunSuiteOptions {\n /** Set to true to reduce the number of scenarios generated for a `matrix`. */\n simplifyScenarios?: boolean\n}\n\n/**\n * Coordinates running the full suite of checks and comparisons defined in the\n * configuration. This plans out the data fetches in advance so that the minimal\n * set of model runs are performed. For example, if the same scenario is needed\n * for both a check and a comparison, we only need to run the model(s) once for\n * that scenario.\n */\nclass SuiteRunner {\n private readonly taskQueue: TaskQueue<DataRequest, void>\n private readonly perfStatsL: PerfStats = new PerfStats()\n private readonly perfStatsR: PerfStats = new PerfStats()\n private stopped = false\n\n constructor(private readonly config: Config, private readonly callbacks: RunSuiteCallbacks) {\n this.taskQueue = new TaskQueue({\n process: request => {\n return this.processRequest(request)\n }\n })\n }\n\n cancel(): void {\n if (!this.stopped) {\n this.stopped = true\n this.taskQueue.shutdown()\n }\n }\n\n start(options?: RunSuiteOptions): void {\n // Send the initial progress update\n this.callbacks.onProgress?.(0)\n\n // Create a data planner to map out the model runs that are needed to\n // efficiently fetch the data to perform both the checks and comparisons\n const modelSpec = this.config.check.bundle.model.modelSpec\n const dataPlanner = new DataPlanner(modelSpec.outputVars.size)\n\n // Create a separate data planner for ref data; these datasets will be\n // fetched first and kept in memory so that they can be accessed by any\n // checks/predicates that reference them\n const refDataPlanner = new DataPlanner(modelSpec.outputVars.size)\n\n // Parse the check tests\n const checkSpecResult = parseTestYaml(this.config.check.tests)\n if (checkSpecResult.isErr()) {\n this.callbacks.onError?.(checkSpecResult.error)\n return\n }\n const checkSpec = checkSpecResult.value\n\n // Plan the checks\n const simplifyScenarios = options?.simplifyScenarios === true\n const buildCheckReport = runChecks(this.config.check, checkSpec, dataPlanner, refDataPlanner, simplifyScenarios)\n\n // Plan the comparisons, if configured\n let buildCompareDatasetReports: () => CompareDatasetReport[]\n if (this.config.compare) {\n buildCompareDatasetReports = runCompare(this.config.compare, dataPlanner, simplifyScenarios)\n }\n\n // When all tasks have been processed, build the report\n this.taskQueue.onIdle = error => {\n if (this.stopped) {\n return\n }\n\n if (error) {\n this.callbacks.onError?.(error)\n } else {\n const checkReport = buildCheckReport()\n let compareReport: CompareReport\n if (this.config.compare) {\n compareReport = {\n datasetReports: buildCompareDatasetReports(),\n perfReportL: this.perfStatsL.toReport(),\n perfReportR: this.perfStatsR.toReport()\n }\n }\n this.callbacks.onComplete?.({\n checkReport,\n compareReport\n })\n }\n }\n\n // Plan the data tasks. The ref data tasks must be processed first so that\n // the reference data is available in memory when checks are performed.\n const refDataPlan = refDataPlanner.buildPlan()\n const dataPlan = dataPlanner.buildPlan()\n const dataRequests = [...refDataPlan.requests, ...dataPlan.requests]\n const taskCount = dataRequests.length\n if (taskCount === 0) {\n // There are no checks or comparison tests; notify completion callback\n // with empty reports\n let compareReport: CompareReport\n if (this.config.compare) {\n compareReport = {\n datasetReports: [],\n perfReportL: this.perfStatsL.toReport(),\n perfReportR: this.perfStatsR.toReport()\n }\n }\n this.cancel()\n this.callbacks.onProgress?.(1)\n this.callbacks.onComplete?.({\n checkReport: {\n groups: []\n },\n compareReport\n })\n return\n }\n\n // Schedule a task for each data request\n let tasksCompleted = 0\n let dataTaskId = 1\n for (const dataRequest of dataRequests) {\n this.taskQueue.addTask(`data${dataTaskId++}`, dataRequest, () => {\n // Notify the progress callback after each task is processed\n tasksCompleted++\n this.callbacks.onProgress?.(tasksCompleted / taskCount)\n })\n }\n }\n\n private async processRequest(request: DataRequest): Promise<void> {\n // Get the set of dataset keys requested for this run\n const datasetKeySet: Set<DatasetKey> = new Set()\n for (const dataTask of request.dataTasks) {\n datasetKeySet.add(dataTask.datasetKey)\n }\n const datasetKeys = [...datasetKeySet]\n\n // Run the model(s) and extract the requested datasets\n const scenario = request.scenario\n let datasetsResultL: DatasetsResult\n let datasetsResultR: DatasetsResult\n switch (request.kind) {\n case 'check': {\n // Run the \"current\" model only\n const bundleModel = this.config.check.bundle.model\n datasetsResultR = await bundleModel.getDatasetsForScenario(scenario, datasetKeys)\n break\n }\n case 'compare': {\n // Run both the \"baseline\" and \"current\" models\n const bundleModelL = this.config.compare.bundleL.model\n const bundleModelR = this.config.compare.bundleR.model\n const [resultL, resultR] = await Promise.all([\n bundleModelL.getDatasetsForScenario(scenario, datasetKeys),\n bundleModelR.getDatasetsForScenario(scenario, datasetKeys)\n ])\n datasetsResultL = resultL\n datasetsResultR = resultR\n break\n }\n default:\n assertNever(request.kind)\n }\n\n // Update the performance stats (only for 'compare' requests)\n if (datasetsResultL?.modelRunTime) {\n this.perfStatsL.addRun(datasetsResultL?.modelRunTime)\n }\n if (datasetsResultR?.modelRunTime) {\n this.perfStatsR.addRun(datasetsResultR?.modelRunTime)\n }\n\n // Perform the requested action on the dataset(s)\n const datasetMapL = datasetsResultL?.datasetMap\n const datasetMapR = datasetsResultR?.datasetMap\n for (const dataTask of request.dataTasks) {\n const datasetL = datasetMapL?.get(dataTask.datasetKey)\n const datasetR = datasetMapR?.get(dataTask.datasetKey)\n dataTask.dataFunc({\n datasetL,\n datasetR\n })\n }\n }\n}\n\n/**\n * Run the full suite of checks and comparisons defined in the given configuration.\n *\n * @param config The test suite configuration.\n * @param callbacks The callbacks that will be notified.\n * @param options Options to control how the tests are run.\n * @return A function that will cancel the process when invoked.\n */\nexport function runSuite(config: Config, callbacks: RunSuiteCallbacks, options?: RunSuiteOptions): CancelRunSuite {\n const suiteRunner = new SuiteRunner(config, callbacks)\n suiteRunner.start(options)\n return () => {\n suiteRunner.cancel()\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { Scenario } from '../_shared/scenario'\nimport type { Dataset, DatasetKey, ScenarioKey } from '../_shared/types'\n\nexport interface DatasetPair {\n datasetL?: Dataset\n datasetR?: Dataset\n}\n\nexport type DataRequestKind = 'check' | 'compare'\n\nexport type DataFunc = (datasets: DatasetPair) => void\n\nexport interface DataTask {\n datasetKey: DatasetKey\n dataFunc: DataFunc\n}\n\nexport interface DataRequest {\n kind: DataRequestKind\n scenario: Scenario\n dataTasks: DataTask[]\n}\n\nexport interface DataPlan {\n requests: DataRequest[]\n}\n\nclass ScenarioTaskSet {\n private readonly modelTasks: Map<DatasetKey, DataTask[]> = new Map()\n private readonly modelImplTasks: Map<DatasetKey, DataTask[]> = new Map()\n private requestKind: DataRequestKind = 'check'\n\n constructor(private readonly scenario: Scenario) {}\n\n /**\n * Add a data request for the given dataset along with the function to be\n * called with the fetched dataset.\n *\n * @param kind The request kind, which determines whether to fetch 1 dataset or 2.\n * @param datasetKey The dataset to be fetched for this scenario.\n * @param dataFunc The function to be called with the fetched dataset.\n */\n addTask(kind: DataRequestKind, datasetKey: DatasetKey, dataFunc: DataFunc): void {\n // If a 'compare' request comes in, we need to fetch both datasets; otherwise\n // leave it set to 'check' so that we only need to fetch the dataset from the\n // \"current\" source\n if (kind === 'compare') {\n this.requestKind = 'compare'\n }\n\n // Create a task for the requested dataset\n const dataTask: DataTask = {\n datasetKey,\n dataFunc\n }\n\n // Separate \"Model\" keys from \"ModelImpl\" keys; the former are pulled\n // from normal model runs, but the latter need special model runs\n // that extract specific datasets\n // TODO: For now, treat any non-ModelImpl data as \"Model\"; we may\n // want to group external datasets separately\n let taskMap: Map<DatasetKey, DataTask[]>\n if (datasetKey.startsWith('ModelImpl')) {\n taskMap = this.modelImplTasks\n } else {\n taskMap = this.modelTasks\n }\n\n // Add the task to the map\n let tasks = taskMap.get(datasetKey)\n if (!tasks) {\n tasks = []\n taskMap.set(datasetKey, tasks)\n }\n tasks.push(dataTask)\n }\n\n /**\n * Create one or more data requests that can be used to fetch data\n * for this scenario.\n *\n * @param batchSize The maximum number of impl vars that can be fetched\n * with a single request; this is usually the same as the number of\n * normal model outputs.\n */\n buildRequests(batchSize: number): DataRequest[] {\n const dataRequests: DataRequest[] = []\n\n if (this.modelTasks.size > 0) {\n // Schedule a normal model run\n const dataTasks: DataTask[] = []\n this.modelTasks.forEach(tasks => dataTasks.push(...tasks))\n dataRequests.push({\n kind: this.requestKind,\n scenario: this.scenario,\n dataTasks\n })\n }\n\n if (this.modelImplTasks.size > 0) {\n // Create batches of datasets. The model can only accept a\n // limited number of keys per run, up to N keys, where N is the\n // number of normal outputs specified for the model. If there\n // are more than N datasets to be accessed, we break those up\n // into batches of N datasets.\n const allKeys = [...this.modelImplTasks.keys()]\n for (let i = 0; i < allKeys.length; i += batchSize) {\n const batchKeys = allKeys.slice(i, i + batchSize)\n const dataTasks: DataTask[] = []\n for (const datasetKey of batchKeys) {\n dataTasks.push(...this.modelImplTasks.get(datasetKey))\n }\n dataRequests.push({\n kind: this.requestKind,\n scenario: this.scenario,\n dataTasks\n })\n }\n }\n\n return dataRequests\n }\n}\n\nexport class DataPlanner {\n private readonly scenarioTaskSets: Map<ScenarioKey, ScenarioTaskSet> = new Map()\n\n /**\n * @param batchSize The maximum number of impl vars that can be fetched\n * with a single request; this is usually the same as the number of\n * normal model outputs.\n */\n constructor(private readonly batchSize: number) {}\n\n /**\n * Add a scenario and a requested dataset to the plan.\n *\n * @param kind The request kind, which determines whether to fetch 1 dataset or 2.\n * @param scenario The input scenario.\n * @param datasetKey The dataset to be fetched for this scenario.\n * @param dataFunc The function to be called with the fetched dataset.\n */\n addRequest(kind: DataRequestKind, scenario: Scenario, datasetKey: DatasetKey, dataFunc: DataFunc): void {\n let scenarioTaskSet = this.scenarioTaskSets.get(scenario.key)\n if (!scenarioTaskSet) {\n scenarioTaskSet = new ScenarioTaskSet(scenario)\n this.scenarioTaskSets.set(scenario.key, scenarioTaskSet)\n }\n scenarioTaskSet.addTask(kind, datasetKey, dataFunc)\n }\n\n /**\n * Build a plan that minimizes the number of data fetches needed.\n */\n buildPlan(): DataPlan {\n const requests: DataRequest[] = []\n for (const taskSet of this.scenarioTaskSets.values()) {\n requests.push(...taskSet.buildRequests(this.batchSize))\n }\n return {\n requests\n }\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { Dataset } from '../_shared/types'\nimport type { DataPlanner } from '../data/data-planner'\nimport type { CheckConfig } from './check-config'\nimport type { CheckResult } from './check-func'\nimport type { CheckKey, CheckTask } from './check-planner'\nimport { CheckPlanner } from './check-planner'\nimport type { CheckPredicateOp } from './check-predicate'\nimport type { CheckReport } from './check-report'\nimport { buildCheckReport } from './check-report'\nimport type { CheckSpec } from './check-spec'\nimport type { CheckDataRefKey } from './check-data-ref'\n\n/**\n * Process all checks from the given spec and add them to the given data planner.\n *\n * @param checkConfig The check configuration.\n * @param checkSpec The check spec that resulted from parsing the tests.\n * @param dataPlanner The planner that will plan out data fetches for the check tests.\n * @param refDataPlanner The planner that will plan out reference data fetches.\n * @param simplifyScenarios If true, reduce the number of scenarios generated for a `matrix`.\n * @return A function that will build the check report after the data requests are all processed.\n */\nexport function runChecks(\n checkConfig: CheckConfig,\n checkSpec: CheckSpec,\n dataPlanner: DataPlanner,\n refDataPlanner: DataPlanner,\n simplifyScenarios: boolean\n): () => CheckReport {\n // Visit all the check test specs and plan the checks that need\n // to be performed\n const modelSpec = checkConfig.bundle.model.modelSpec\n const checkPlanner = new CheckPlanner(modelSpec)\n checkPlanner.addAllChecks(checkSpec, simplifyScenarios)\n const checkPlan = checkPlanner.buildPlan()\n\n // Create a map to hold reference datasets; these will be fetched before\n // performing any checks that rely on reference data\n const refDatasets: Map<CheckDataRefKey, Dataset> = new Map()\n\n // Plan the reference data fetches\n for (const [dataRefKey, dataRef] of checkPlan.dataRefs.entries()) {\n // Add a request to the ref data planner for each dataset that is referenced\n // by one or more predicates. These requests will be processed before all\n // other checks so that the reference data is available in memory when the\n // check action is performed.\n refDataPlanner.addRequest('check', dataRef.scenario.scenario, dataRef.dataset.datasetKey, datasets => {\n const dataset = datasets.datasetR\n if (dataset) {\n refDatasets.set(dataRefKey, dataset)\n }\n })\n }\n\n // Create a map that will hold the result of each check\n const checkResults: Map<CheckKey, CheckResult> = new Map()\n\n // Plan the checks\n for (const [checkKey, checkTask] of checkPlan.tasks.entries()) {\n // For each check, add a request to the data planner so that the check\n // runs when the dataset is fetched\n dataPlanner.addRequest('check', checkTask.scenario.scenario, checkTask.dataset.datasetKey, datasets => {\n // Run the check action on the dataset, then save the result\n const dataset = datasets.datasetR\n const checkResult = runCheck(checkTask, dataset, refDatasets)\n checkResults.set(checkKey, checkResult)\n })\n }\n\n // Return a function that will build the report with the check results; this\n // should be called only after all data tasks have been processed\n // TODO: This is an unusual approach; should refactor\n return () => {\n return buildCheckReport(checkPlan, checkResults)\n }\n}\n\n/**\n * Run a single check on the given dataset.\n *\n * @param checkTask The check action.\n * @param dataset The primary dataset to be checked.\n * @param refDatasets The other datasets referenced by the predicate.\n */\nexport function runCheck(\n checkTask: CheckTask,\n dataset: Dataset | undefined,\n refDatasets: Map<CheckDataRefKey, Dataset> | undefined\n): CheckResult {\n if (dataset === undefined) {\n // Set an error status when the primary dataset is not available;\n // this should not happen in practice because the dataset should have\n // already been resolved in an earlier stage\n return {\n status: 'error',\n message: 'no data available'\n }\n }\n\n // Associate each op with a ref dataset (if the op references one)\n let opRefDatasets: Map<CheckPredicateOp, Dataset>\n if (checkTask.dataRefs) {\n opRefDatasets = new Map()\n for (const [op, dataRef] of checkTask.dataRefs.entries()) {\n const refDataset = refDatasets?.get(dataRef.key)\n if (refDataset === undefined) {\n // Set an error status when the reference data could not be resolved\n if (dataRef.dataset.datasetKey === undefined) {\n // The dataset could not be resolved\n return {\n status: 'error',\n errorInfo: {\n kind: 'unknown-dataset',\n name: dataRef.dataset.name\n }\n }\n } else if (dataRef.scenario.scenario === undefined) {\n // One or more inputs could not be resolved\n if (dataRef.scenario.error) {\n return {\n status: 'error',\n errorInfo: {\n kind: dataRef.scenario.error.kind,\n name: dataRef.scenario.error.name\n }\n }\n } else {\n let inputName: string\n if (dataRef.scenario.inputDescs.length > 0) {\n // TODO: Include all unresolved input names here\n inputName = dataRef.scenario.inputDescs[0].name\n } else {\n inputName = 'unknown'\n }\n return {\n status: 'error',\n errorInfo: {\n kind: 'unknown-input',\n name: inputName\n }\n }\n }\n } else {\n // Something else went wrong; treat this as an internal error\n return {\n status: 'error',\n message: 'unresolved data reference'\n }\n }\n }\n\n // Associate the dataset with the op\n opRefDatasets.set(op, refDataset)\n }\n }\n\n // All data was resolved; run the check action on the dataset\n return checkTask.action.run(dataset, opRefDatasets)\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { allInputsAtPositionScenario } from '../_shared/scenario'\nimport type { DataPlanner } from '../data/data-planner'\nimport type { CompareConfig } from './compare-config'\nimport { diffDatasets } from './compare-datasets'\nimport type { CompareDatasetReport } from './compare-report'\n\n/**\n * Prepare all comparison tests and add them to the given data planner.\n *\n * @param compareConfig The compare configuration.\n * @param dataPlanner The planner that will plan out data fetches for the compare tests.\n * @param simplifyScenarios If true, only run the \"all inputs at default\" scenario.\n * @return A function that will build the compare reports after the data requests are all processed.\n */\nexport function runCompare(\n compareConfig: CompareConfig,\n dataPlanner: DataPlanner,\n simplifyScenarios: boolean\n): () => CompareDatasetReport[] {\n // Get the configured set of scenarios\n const scenarios = simplifyScenarios\n ? [allInputsAtPositionScenario('at-default')]\n : compareConfig.scenarios.getScenarios()\n\n // TODO: The following leads to an explosion of scenario/dataset combinations;\n // if memory usage becomes a concern, we can change this to add a wildcard\n // placeholder in the data request and then expand the dataset keys at the\n // time that the request is processed instead of adding them all in advance\n const datasetReports: CompareDatasetReport[] = []\n for (const scenario of scenarios) {\n // Get the keys of the datasets of interest for this scenario\n const datasetKeys = compareConfig.datasets.getDatasetKeysForScenario(scenario)\n\n // For each dataset key, add a request so that the datasets are fetched\n // from the data sources (i.e., run the models with the given scenario\n // and compare the datasets)\n for (const datasetKey of datasetKeys) {\n dataPlanner.addRequest('compare', scenario, datasetKey, datasets => {\n // Diff the two datasets\n const diffReport = diffDatasets(datasets.datasetL, datasets.datasetR)\n datasetReports.push({\n scenarioKey: scenario.key,\n datasetKey,\n diffReport\n })\n })\n }\n }\n\n // Return a function that will build the report with the check results; this\n // should be called only after all data tasks have been processed\n // TODO: This is an unusual approach; should refactor\n return () => {\n return datasetReports\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { CheckSummary } from '../check/check-summary'\nimport { checkSummaryFromReport } from '../check/check-summary'\nimport type { CompareSummary } from '../compare/compare-summary'\nimport { compareSummaryFromReport } from '../compare/compare-summary'\nimport type { SuiteReport } from './suite-report'\n\n/**\n * A simplified/terse version of `SuiteReport` that matches the\n * format of the JSON objects emitted by the CLI in terse mode.\n */\nexport interface SuiteSummary {\n checkSummary: CheckSummary\n compareSummary?: CompareSummary\n}\n\n/**\n * Convert a full `SuiteReport` to a simplified `SuiteSummary` that only includes\n * failed/errored checks or comparisons with differences.\n *\n * @param suiteReport The full suite report.\n * @return The converted suite summary.\n */\nexport function suiteSummaryFromReport(suiteReport: SuiteReport): SuiteSummary {\n // Convert check report to terse form that only includes failed/errored checks\n const checkSummary = checkSummaryFromReport(suiteReport.checkReport)\n\n // Convert compare report to terse summaries\n // TODO: For now we output \"terse\" JSON that contains just the ScenarioKey,\n // DatasetKey, and score (maxDiff) for each scenario, since this is all the\n // app needs. Later we should add different reporting modes.\n let compareSummary: CompareSummary\n if (suiteReport.compareReport) {\n compareSummary = compareSummaryFromReport(suiteReport.compareReport)\n }\n\n return {\n checkSummary,\n compareSummary\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sdeverywhere/check-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist/**",
|
|
6
|
+
"schema/**"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "dist/index.cjs",
|
|
10
|
+
"module": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"ajv": "^8.6.3",
|
|
21
|
+
"assert-never": "^1.2.1",
|
|
22
|
+
"neverthrow": "^4.2.2",
|
|
23
|
+
"yaml": "^2.1.0"
|
|
24
|
+
},
|
|
25
|
+
"author": "Climate Interactive",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"homepage": "https://sdeverywhere.org",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/climateinteractive/SDEverywhere.git",
|
|
31
|
+
"directory": "packages/check-core"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/climateinteractive/SDEverywhere/issues"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"clean": "rm -rf dist",
|
|
38
|
+
"lint": "eslint src --ext .ts --max-warnings 0",
|
|
39
|
+
"prettier:check": "prettier --check .",
|
|
40
|
+
"prettier:fix": "prettier --write .",
|
|
41
|
+
"precommit": "../../scripts/precommit",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"test:ci": "vitest run",
|
|
45
|
+
"compile": "tsup",
|
|
46
|
+
"copy-schema": "./scripts/copy-schema.js",
|
|
47
|
+
"build": "run-s compile copy-schema",
|
|
48
|
+
"docs": "../../scripts/gen-docs.js",
|
|
49
|
+
"ci:build": "run-s clean lint prettier:check test:ci build docs"
|
|
50
|
+
}
|
|
51
|
+
}
|