@sdeverywhere/check-core 0.1.3 → 0.1.5

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.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/_shared/scenario-specs.ts","../src/comparison/run/comparison-data-coordinator.ts","../src/comparison/diff-datasets/diff-datasets.ts","../src/comparison/diff-graphs/diff-graphs.ts","../src/comparison/report/comparison-reporting.ts","../src/comparison/report/buckets.ts","../src/comparison/report/comparison-group-scores.ts","../src/comparison/report/comparison-grouping.ts","../src/comparison/config/parse/comparison-parser.ts","../src/comparison/config/parse/comparison.schema.js","../src/comparison/config/resolve/comparison-resolver.ts","../src/bundle/model-inputs.ts","../src/comparison/config/resolve/comparison-scenario-specs.ts","../src/comparison/config/comparison-config.ts","../src/comparison/config/comparison-datasets.ts","../src/comparison/config/comparison-scenarios.ts","../src/config/synchronized-model.ts","../src/config/config.ts","../src/perf/perf-runner.ts","../src/perf/perf-stats.ts","../src/data/data-planner.ts","../src/check/check-runner.ts","../src/comparison/run/comparison-runner.ts","../src/suite/suite-runner.ts","../src/suite/suite-reporting.ts"],"sourcesContent":["// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\n//\n// _shared\n//\n\nexport * from './_shared/data-source'\nexport * from './_shared/scenario-spec-types'\nexport * from './_shared/types'\n\n//\n// bundle\n//\n\nexport type {\n Bundle,\n BundleGraphData,\n BundleGraphDatasetSpec,\n BundleGraphId,\n BundleGraphSpec,\n BundleGraphView,\n BundleModel,\n DatasetGroupName,\n InputAliasName,\n InputGroupName,\n LegendItem,\n LinkItem,\n LoadedBundle,\n ModelSpec,\n NamedBundle\n} from './bundle/bundle-types'\n\nexport type { Dimension, ImplVar, InputId, InputVar, OutputVar, RelatedItem, Subscript } from './bundle/var-types'\n\n//\n// check\n//\n\nexport type { CheckDataRequestKey } from './check/check-data-coordinator'\nexport { CheckDataCoordinator } from './check/check-data-coordinator'\n\nexport type { CheckResult, CheckResultErrorInfo } from './check/check-func'\n\nexport type { CheckKey } from './check/check-planner'\n\nexport type { CheckPredicateOp } from './check/check-predicate'\n\nexport type {\n CheckDatasetReport,\n CheckGroupReport,\n CheckPredicateOpConstantRef,\n CheckPredicateOpDataRef,\n CheckPredicateOpRef,\n CheckPredicateReport,\n CheckReport,\n CheckScenarioReport,\n CheckStatus,\n CheckTestReport\n} from './check/check-report'\nexport { scenarioMessage, datasetMessage, predicateMessage } from './check/check-report'\n\nexport type { CheckScenario, CheckScenarioError, CheckScenarioInputDesc } from './check/check-scenario'\n\nexport type {\n CheckPredicateTimeOptions,\n CheckPredicateTimeRange,\n CheckPredicateTimeSingle,\n CheckPredicateTimeSpec\n} from './check/check-spec'\n\nexport type { CheckPredicateSummary, CheckSummary } from './check/check-summary'\nexport { checkSummaryFromReport, checkReportFromSummary } from './check/check-summary'\n\n//\n// comparison\n//\n\nexport * from './comparison/_shared/comparison-resolved-types'\n\nexport * from './comparison/config/comparison-spec-types'\nexport type {\n ComparisonConfig,\n ComparisonDatasetOptions,\n ComparisonOptions,\n ComparisonPlot\n} from './comparison/config/comparison-config'\nexport type { ComparisonScenarios } from './comparison/config/comparison-scenarios'\nexport type { ComparisonDatasets } from './comparison/config/comparison-datasets'\n\nexport type { ComparisonDataRequestKey } from './comparison/run/comparison-data-coordinator'\nexport { ComparisonDataCoordinator } from './comparison/run/comparison-data-coordinator'\n\nexport * from './comparison/diff-datasets/diff-datasets'\n\nexport * from './comparison/diff-graphs/diff-graphs'\n\nexport * from './comparison/report/comparison-report-types'\nexport { comparisonSummaryFromReport } from './comparison/report/comparison-reporting'\n\nexport * from './comparison/report/comparison-group-types'\nexport * from './comparison/report/comparison-group-scores'\nexport { categorizeComparisonTestSummaries } from './comparison/report/comparison-grouping'\n\n//\n// config\n//\n\nexport type { Config, ConfigInitOptions, ConfigOptions } from './config/config-types'\n\nexport { createConfig } from './config/config'\n\n//\n// perf\n//\n\nexport { PerfRunner } from './perf/perf-runner'\n\nexport type { PerfReport } from './perf/perf-stats'\nexport { PerfStats } from './perf/perf-stats'\n\n//\n// suite\n//\n\nexport type { RunSuiteCallbacks, RunSuiteOptions } from './suite/suite-runner'\nexport { runSuite } from './suite/suite-runner'\n\nexport * from './suite/suite-report-types'\n\nexport { suiteSummaryFromReport } from './suite/suite-reporting'\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 { ScenarioSpec } from '../_shared/scenario-spec-types'\nimport type { Dataset, DatasetKey } from '../_shared/types'\nimport type { BundleModel } from '../bundle/bundle-types'\n\nexport type CheckDataRequestKey = string\n\ninterface DataRequest {\n scenarioSpec: ScenarioSpec\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.scenarioSpec, [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 scenarioSpec: ScenarioSpec,\n datasetKey: DatasetKey,\n onResponse: (dataset: Dataset) => void\n ): void {\n const request: DataRequest = {\n scenarioSpec,\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'\n\nimport type { InputPosition } from '../_shared/scenario-spec-types'\n\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.spec === 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 refScenarioSpec = dataRef.scenario?.spec\n if (!refScenarioSpec) {\n return\n }\n if (predOp.scenario === 'inherit') {\n opValue += ` (w/ same scenario)`\n } else {\n if (refScenarioSpec.kind === 'all-inputs' && refScenarioSpec.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.spec === 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.spec.kind === 'all-inputs') {\n // This is an \"all inputs\" scenario\n const position = checkScenario.spec.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 `ScenarioSpec` kind for the \"all inputs\n // in group\" case, so we use a multi-setting `ScenarioSpec`; therefore we have to\n // dig out the position from the first setting\n let position: InputPosition = 'at-default'\n if (checkScenario.spec.settings[0].kind === 'position') {\n position = checkScenario.spec.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 * @return The converted check report.\n */\nexport function checkReportFromSummary(checkConfig: CheckConfig, checkSummary: CheckSummary): 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, false)\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 check definitions'\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.spec === 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.spec && refDataset.datasetKey) {\n dataRefKey = `${refScenario.spec.uid}::${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 let matchedGroupName: string\n let matchedGroupDatasetKeys: DatasetKey[]\n if (modelSpec.datasetGroups) {\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, datasetKeys] of modelSpec.datasetGroups) {\n if (group.toLowerCase() === groupToMatch) {\n matchedGroupName = group\n matchedGroupDatasetKeys = datasetKeys\n break\n }\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, ScenarioSpec } from '../_shared/scenario-spec-types'\nimport {\n allInputsAtPositionSpec,\n inputAtPositionSpec,\n inputSettingsSpec,\n positionSetting,\n valueSetting\n} from '../_shared/scenario-specs'\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 spec used to configure the model with the matched input(s); can be undefined if input(s) failed to match. */\n spec?: ScenarioSpec\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 // TODO: Use ModelInputs here\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 if (modelSpec.inputGroups) {\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\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 return {\n spec: allInputsAtPositionSpec(position),\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 return {\n spec: inputAtPositionSpec(varId, position),\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 spec: ScenarioSpec\n if (inputDescs.every(desc => desc.inputVar !== undefined)) {\n // All inputs were resolved, so create a `ScenarioSpec` that includes them all\n const settings = inputDescs.map(inputDesc => {\n const varId = inputDesc.inputVar.varId\n if (inputDesc.position) {\n return positionSetting(varId, inputDesc.position)\n } else {\n return valueSetting(varId, inputDesc.value)\n }\n })\n spec = inputSettingsSpec(settings)\n } else {\n // One or more inputs could not be resolved; leave `spec` undefined\n // so that we can report the error later\n spec = undefined\n }\n\n return {\n spec,\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 type { InputPosition, InputSetting, ScenarioSpec } from './scenario-spec-types'\nimport type { VarId } from './types'\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 inputSettingsSpec(settings: InputSetting[]): ScenarioSpec {\n const uidParts = settings.map(setting => {\n switch (setting.kind) {\n case 'position':\n return `${setting.inputVarId}_at_${keyForInputPosition(setting.position)}`\n case 'value':\n return `${setting.inputVarId}_at_${setting.value}`\n default:\n assertNever(setting)\n }\n })\n // TODO: Ideally we would reduce the long UID to a short hash here when there are many settings\n const uid = `inputs_${uidParts.sort().join('_')}`\n\n return {\n kind: 'input-settings',\n uid,\n settings\n }\n}\n\nexport function inputAtPositionSpec(inputVarId: VarId, position: InputPosition): ScenarioSpec {\n return inputSettingsSpec([positionSetting(inputVarId, position)])\n}\n\nexport function inputAtValueSpec(inputVarId: VarId, value: number): ScenarioSpec {\n return inputSettingsSpec([valueSetting(inputVarId, value)])\n}\n\nexport function allInputsAtPositionSpec(position: InputPosition): ScenarioSpec {\n return {\n kind: 'all-inputs',\n uid: `all_inputs_at_${keyForInputPosition(position)}`,\n position\n }\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","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { ScenarioSpec } from '../../_shared/scenario-spec-types'\nimport { TaskQueue } from '../../_shared/task-queue'\nimport type { DatasetKey, DatasetMap } from '../../_shared/types'\n\nimport type { BundleModel, BundleGraphData, BundleGraphId } from '../../bundle/bundle-types'\nimport type { DatasetsResult } from '../../_shared/data-source'\n\nexport type ComparisonDataRequestKey = string\n\ninterface DatasetRequest {\n kind: 'dataset'\n scenarioSpecL?: ScenarioSpec\n scenarioSpecR?: ScenarioSpec\n datasetKeys: DatasetKey[]\n}\n\ninterface GraphDataRequest {\n kind: 'graph-data'\n scenarioSpecL?: ScenarioSpec\n scenarioSpecR?: ScenarioSpec\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 graphDataL?: BundleGraphData\n graphDataR?: BundleGraphData\n}\n\ntype DataResponse = DatasetResponse | GraphDataResponse\n\n/**\n * Coordinates loading of data in parallel from two models.\n */\nexport class ComparisonDataCoordinator {\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 // Run the models in parallel\n switch (request.kind) {\n case 'dataset':\n return this.processDatasetRequest(request)\n case 'graph-data':\n return this.processGraphDataRequest(request)\n default:\n assertNever(request)\n }\n }\n })\n }\n\n private async processDatasetRequest(request: DatasetRequest): Promise<DatasetResponse> {\n // Helper function that fetches data from a particular model\n async function fetchDatasets(\n bundleModel: BundleModel,\n scenarioSpec: ScenarioSpec | undefined\n ): Promise<DatasetsResult> {\n if (scenarioSpec) {\n return bundleModel.getDatasetsForScenario(scenarioSpec, request.datasetKeys)\n } else {\n return undefined\n }\n }\n\n // Run the model(s) in parallel and extract the requested datasets\n const [resultL, resultR] = await Promise.all([\n fetchDatasets(this.bundleModelL, request.scenarioSpecL),\n fetchDatasets(this.bundleModelR, request.scenarioSpecR)\n ])\n\n return {\n kind: 'dataset',\n datasetMapL: resultL?.datasetMap,\n datasetMapR: resultR?.datasetMap\n }\n }\n\n private async processGraphDataRequest(request: GraphDataRequest): Promise<GraphDataResponse> {\n // Helper function that fetches data from a particular model\n async function fetchGraphData(\n bundleModel: BundleModel,\n scenarioSpec: ScenarioSpec | undefined\n ): Promise<BundleGraphData> {\n if (scenarioSpec) {\n return bundleModel.getGraphDataForScenario(scenarioSpec, request.graphId)\n } else {\n return undefined\n }\n }\n\n // Run the model(s) in parallel and extract the requested graph data\n const [graphDataL, graphDataR] = await Promise.all([\n fetchGraphData(this.bundleModelL, request.scenarioSpecL),\n fetchGraphData(this.bundleModelR, request.scenarioSpecR)\n ])\n\n return {\n kind: 'graph-data',\n graphDataL,\n graphDataR\n }\n }\n\n requestDatasetMaps(\n requestKey: ComparisonDataRequestKey,\n scenarioSpecL: ScenarioSpec,\n scenarioSpecR: ScenarioSpec,\n datasetKeys: DatasetKey[],\n onResponse: (datasetMapL?: DatasetMap, datasetMapR?: DatasetMap) => void\n ): void {\n const request: DatasetRequest = {\n kind: 'dataset',\n scenarioSpecL,\n scenarioSpecR,\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: ComparisonDataRequestKey,\n scenarioSpecL: ScenarioSpec,\n scenarioSpecR: ScenarioSpec,\n graphId: BundleGraphId,\n onResponse: (graphDataL?: BundleGraphData, graphDataR?: BundleGraphData) => void\n ): void {\n const request: GraphDataRequest = {\n kind: 'graph-data',\n scenarioSpecL,\n scenarioSpecR,\n graphId\n }\n this.taskQueue.addTask(requestKey, request, response => {\n if (response.kind === 'graph-data') {\n onResponse(response.graphDataL, response.graphDataR)\n }\n })\n }\n\n cancelRequest(key: ComparisonDataRequestKey): void {\n this.taskQueue.cancelTask(key)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { Dataset } from '../../_shared/types'\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","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { DatasetKey } from '../../_shared/types'\nimport type { BundleGraphSpec } from '../../bundle/bundle-types'\nimport type { ComparisonScenarioKey } from '../_shared/comparison-resolved-types'\nimport type { ComparisonTestSummary } from '../report/comparison-report-types'\n\nexport type GraphInclusion = 'neither' | 'left-only' | 'right-only' | 'both'\n\nexport interface GraphComparisonMetadataReport {\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 GraphComparisonDatasetReport {\n /** The dataset key. */\n datasetKey: DatasetKey\n /** The max diff for this dataset. */\n maxDiff?: number\n}\n\nexport interface GraphComparisonReport {\n /** Indicates which bundles the graph is defined in. */\n inclusion: GraphInclusion\n /** The metadata fields with differences. */\n metadataReports: GraphComparisonMetadataReport[]\n /** The datasets with differences. */\n datasetReports: GraphComparisonDatasetReport[]\n}\n\n/**\n * Comparison 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 key of the scenario used for comparing datasets.\n * @param testSummaries The set of test summaries from a previous comparison run.\n */\nexport function diffGraphs(\n graphL: BundleGraphSpec | undefined,\n graphR: BundleGraphSpec | undefined,\n scenarioKey: ComparisonScenarioKey,\n testSummaries: ComparisonTestSummary[]\n): GraphComparisonReport {\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 // Comparison the metadata for the two graphs\n const metadataReports: GraphComparisonMetadataReport[] = []\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 // Comparison the datasets for the two graphs\n const datasetReports: GraphComparisonDatasetReport[] = []\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 testSummary = testSummaries.find(summary => summary.d === datasetKey && summary.s === scenarioKey)\n // TODO: Flag as an error if we don't have a ComparisonTestSummary\n // for the datasets?\n const maxDiff = testSummary?.md\n datasetReports.push({\n datasetKey,\n maxDiff\n })\n }\n }\n\n return {\n inclusion,\n metadataReports,\n datasetReports\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { ComparisonConfig } from '../config/comparison-config'\nimport type { ComparisonReport, ComparisonSummary, ComparisonTestSummary } from './comparison-report-types'\n\n/**\n * Convert a full `ComparisonReport` to a simplified `ComparisonSummary` that includes\n * the minimum set of fields needed to keep the file smaller when there are many\n * reported differences. This only includes comparison results for which there\n * is a non-zero `maxDiff` value.\n *\n * @param comparisonReport The full comparison report.\n * @return The terse summary.\n */\nexport function comparisonSummaryFromReport(comparisonReport: ComparisonReport): ComparisonSummary {\n const terseSummaries: ComparisonTestSummary[] = []\n\n for (const r of comparisonReport.testReports) {\n if (r.diffReport.validity === 'both' && r.diffReport.maxDiff > 0) {\n terseSummaries.push({\n s: r.scenarioKey,\n d: r.datasetKey,\n md: r.diffReport.maxDiff\n })\n }\n }\n\n return {\n testSummaries: terseSummaries,\n perfReportL: comparisonReport.perfReportL,\n perfReportR: comparisonReport.perfReportR\n }\n}\n\n/**\n * Restore the full set of summaries that was generated by `runComparison` (one summary\n * for each scenario/dataset pairing based on the `ComparisonConfig`).\n *\n * @param comparisonConfig The config that contains the scenarios used to reconstruct the\n * full set of summaries.\n * @param terseSummaries The set of terse summaries for comparisons that produced a\n * non-zero `maxDiff` result.\n */\nexport function restoreFromTerseSummaries(\n comparisonConfig: ComparisonConfig,\n terseSummaries: ComparisonTestSummary[]\n): ComparisonTestSummary[] {\n // Put the provided summaries in a map for faster lookup\n const existingSummaries: Map<string, ComparisonTestSummary> = new Map()\n for (const summary of terseSummaries) {\n const key = `${summary.s}::${summary.d}`\n existingSummaries.set(key, summary)\n }\n\n // Get the full set of scenario/dataset pairs\n const allTestSummaries: ComparisonTestSummary[] = []\n for (const scenario of comparisonConfig.scenarios.getAllScenarios()) {\n const datasetKeys = comparisonConfig.datasets.getDatasetKeysForScenario(scenario)\n for (const datasetKey of datasetKeys) {\n // If we have a summary in the array that was passed in, it means\n // the `maxDiff` was non-zero, so include that value, otherwise\n // assume zero\n const key = `${scenario.key}::${datasetKey}`\n const existingSummary = existingSummaries.get(key)\n const maxDiff = existingSummary?.md || 0\n allTestSummaries.push({\n s: scenario.key,\n d: datasetKey,\n md: maxDiff\n })\n }\n }\n\n return allTestSummaries\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nexport function getBucketIndex(diffPct: number, thresholds: number[]): number {\n // When there are no differences, put it in the first (green) bucket\n if (diffPct === 0) {\n return 0\n }\n\n // Otherwise, test the given diff value against each threshold (using\n // strict \"less than\" comparison)\n for (let i = 0; i < thresholds.length; i++) {\n if (diffPct < thresholds[i]) {\n return i + 1\n }\n }\n\n // When the difference is greater than or equal to the last threshold,\n // put it in the final bucket\n return thresholds.length + 1\n}\n","// Copyright (c) 2024 Climate Interactive / New Venture Fund\n\nimport { getBucketIndex } from './buckets'\nimport type { ComparisonGroupScores } from './comparison-group-types'\nimport type { ComparisonTestSummary } from './comparison-report-types'\n\n/**\n * Compute the overall scores for the given group of comparison test summaries.\n *\n * @param testSummaries The comparison test summaries to consider.\n * @param thresholds The array of thresholds that determine the buckets into which\n * the scores will be summarized.\n */\nexport function getScoresForTestSummaries(\n testSummaries: ComparisonTestSummary[],\n thresholds: number[]\n): ComparisonGroupScores {\n // Add up scores and group them into buckets\n const diffCountByBucket = Array(thresholds.length + 2).fill(0)\n const totalMaxDiffByBucket = Array(thresholds.length + 2).fill(0)\n let totalDiffCount = 0\n for (const testSummary of testSummaries) {\n const bucketIndex = getBucketIndex(testSummary.md, thresholds)\n diffCountByBucket[bucketIndex]++\n totalMaxDiffByBucket[bucketIndex] += testSummary.md\n totalDiffCount++\n }\n\n // Get the percentage of diffs for each bucket relative to the total number\n // of comparisons in the given set\n let diffPercentByBucket: number[]\n if (totalDiffCount > 0) {\n diffPercentByBucket = diffCountByBucket.map(count => (count / totalDiffCount) * 100)\n } else {\n diffPercentByBucket = []\n }\n\n return {\n totalDiffCount,\n totalMaxDiffByBucket,\n diffCountByBucket,\n diffPercentByBucket\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { ComparisonConfig } from '../config/comparison-config'\nimport type { ComparisonDataset, ComparisonScenario } from '../_shared/comparison-resolved-types'\n\nimport type {\n ComparisonCategorizedResults,\n ComparisonGroup,\n ComparisonGroupKey,\n ComparisonGroupKind,\n ComparisonGroupScores,\n ComparisonGroupSummariesByCategory,\n ComparisonGroupSummary\n} from './comparison-group-types'\nimport type { ComparisonTestSummary } from './comparison-report-types'\nimport { restoreFromTerseSummaries } from './comparison-reporting'\nimport { getScoresForTestSummaries } from './comparison-group-scores'\n\n/**\n * Given a set of terse test summaries (which only includes summaries for tests with non-zero `maxDiff`\n * scores), restore the full set of summaries and then categorize them.\n *\n * @param comparisonConfig The comparison configuration.\n * @param terseSummaries The set of terse test summaries.\n */\nexport function categorizeComparisonTestSummaries(\n comparisonConfig: ComparisonConfig,\n terseSummaries: ComparisonTestSummary[]\n): ComparisonCategorizedResults {\n // Restore the full set of test results\n const allTestSummaries = restoreFromTerseSummaries(comparisonConfig, terseSummaries)\n\n // Categorize the results by scenario\n const groupsByScenario = groupComparisonTestSummaries(allTestSummaries, 'by-scenario')\n const byScenario = categorizeComparisonGroups(comparisonConfig, [...groupsByScenario.values()])\n\n // Categorize the results by dataset\n const groupsByDataset = groupComparisonTestSummaries(allTestSummaries, 'by-dataset')\n const byDataset = categorizeComparisonGroups(comparisonConfig, [...groupsByDataset.values()])\n\n return {\n allTestSummaries,\n byScenario,\n byDataset\n }\n}\n\n/**\n * Group the given comparison test summaries, returning one `ComparisonGroup` for each group.\n */\nexport function groupComparisonTestSummaries(\n testSummaries: ComparisonTestSummary[],\n groupKind: ComparisonGroupKind\n): Map<ComparisonGroupKey, ComparisonGroup> {\n const groups: Map<ComparisonGroupKey, ComparisonGroup> = new Map()\n\n for (const testSummary of testSummaries) {\n let groupKey: ComparisonGroupKey\n switch (groupKind) {\n case 'by-dataset':\n groupKey = testSummary.d\n break\n case 'by-scenario':\n groupKey = testSummary.s\n break\n default:\n assertNever(groupKind)\n }\n\n const group = groups.get(groupKey)\n if (group) {\n group.testSummaries.push(testSummary)\n } else {\n groups.set(groupKey, {\n kind: groupKind,\n key: groupKey,\n testSummaries: [testSummary]\n })\n }\n }\n\n return groups\n}\n\n/**\n * Given a set of `ComparisonGroup` instances, organize and sort the groups into the\n * following categories:\n *\n * withErrors\n * groups with items that have errors (are not valid) for both \"left\" and \"right\" models\n *\n * onlyInLeft\n * groups with items that are only valid for the \"left\" model (for example, datasets that\n * were removed and no longer available in the \"right\" model)\n *\n * onlyInRight\n * groups with items that are only valid for the \"right\" model (for example, scenarios\n * for inputs that were added in the \"right\" model)\n *\n * withDiffs\n * groups with one or more comparisons that have non-zero `maxDiff` scores; the groups\n * will be sorted by `maxDiff`, with higher scores at the front of the array\n *\n * withoutDiffs\n * groups where all comparisons have `maxDiff` scores of zero (no differences between\n * \"left\" and \"right\")\n */\nexport function categorizeComparisonGroups(\n comparisonConfig: ComparisonConfig,\n allGroups: ComparisonGroup[]\n): ComparisonGroupSummariesByCategory {\n const allGroupSummaries: Map<ComparisonGroupKey, ComparisonGroupSummary> = new Map()\n const withErrors: ComparisonGroupSummary[] = []\n const onlyInLeft: ComparisonGroupSummary[] = []\n const onlyInRight: ComparisonGroupSummary[] = []\n let withDiffs: ComparisonGroupSummary[] = []\n const withoutDiffs: ComparisonGroupSummary[] = []\n\n function addSummaryForGroup(\n group: ComparisonGroup,\n root: ComparisonDataset | ComparisonScenario,\n validInL: boolean,\n validInR: boolean\n ): void {\n // Compute the scores if the dataset/scenario is valid in both\n let scores: ComparisonGroupScores\n if (validInL && validInR) {\n scores = getScoresForTestSummaries(group.testSummaries, comparisonConfig.thresholds)\n }\n\n // Create the group summary\n const groupSummary: ComparisonGroupSummary = {\n root,\n group,\n scores\n }\n\n // Add to the map of all summaries\n allGroupSummaries.set(group.key, groupSummary)\n\n // Categorize the group\n if (validInL && validInR) {\n // The dataset/scenario is valid in both; see if there were any diffs\n if (scores.totalDiffCount !== scores.diffCountByBucket[0]) {\n withDiffs.push(groupSummary)\n } else {\n withoutDiffs.push(groupSummary)\n }\n } else if (validInL) {\n // The dataset/scenario is valid in \"left\" only\n onlyInLeft.push(groupSummary)\n } else if (validInR) {\n // The dataset/scenario is valid in \"right\" only\n onlyInRight.push(groupSummary)\n } else {\n // The dataset/scenario is not valid in either \"left\" or \"right\"\n withErrors.push(groupSummary)\n }\n }\n\n // Add a summary for each group\n for (const group of allGroups.values()) {\n switch (group.kind) {\n case 'by-dataset': {\n // Get the `ComparisonDataset` instance for this dataset key\n const dataset = comparisonConfig.datasets.getDataset(group.key)\n const validInL = dataset?.outputVarL !== undefined\n const validInR = dataset?.outputVarR !== undefined\n addSummaryForGroup(group, dataset, validInL, validInR)\n break\n }\n case 'by-scenario': {\n // Get the `ComparisonScenario` instance for this scenario key\n const scenario = comparisonConfig.scenarios.getScenario(group.key)\n const validInL = scenario?.specL !== undefined\n const validInR = scenario?.specR !== undefined\n addSummaryForGroup(group, scenario, validInL, validInR)\n break\n }\n default:\n assertNever(group.kind)\n }\n }\n\n if (withDiffs.length > 1) {\n // Sort the `withDiffs` summaries by score and name/title\n if (withDiffs[0].group.kind === 'by-dataset') {\n withDiffs = sortDatasetGroupSummaries(withDiffs)\n } else if (withDiffs[0].group.kind === 'by-scenario') {\n withDiffs = sortScenarioGroupSummaries(withDiffs)\n }\n }\n\n // TODO: Sort the `withoutDiffs` summaries according to the order that they were defined\n // in the config\n\n return {\n allGroupSummaries,\n withErrors,\n onlyInLeft,\n onlyInRight,\n withDiffs,\n withoutDiffs\n }\n}\n\n/**\n * NOTE: This currently can only be used in cases where the dataset/variable is\n * defined in both \"left\" and \"right\".\n */\nfunction sortDatasetGroupSummaries(summaries: ComparisonGroupSummary[]): ComparisonGroupSummary[] {\n return summaries.sort((a, b) => {\n const scoreResult = compareScores(a.scores, b.scores)\n if (scoreResult !== 0) {\n // Sort by score first (higher scores followed by lower scores)\n return -scoreResult\n } else {\n // Sort by variable/source name alphabetically if scores match. Note\n // that we use the \"left\" name for sorting purposes for now, in the case\n // where a variable was renamed\n const aVar = (a.root as ComparisonDataset).outputVarL\n const bVar = (b.root as ComparisonDataset).outputVarR\n const aSource = aVar.sourceName?.toLowerCase() || ''\n const bSource = bVar.sourceName?.toLowerCase() || ''\n if (aSource !== bSource) {\n // Sort by source name (non-model variables follow model variables)\n return aSource.localeCompare(bSource)\n } else {\n // Sort by dataset name alphabetically\n const aName = aVar.varName.toLowerCase()\n const bName = bVar.varName.toLowerCase()\n return aName.localeCompare(bName)\n }\n }\n })\n}\n\n/**\n * NOTE: This currently can only be used in cases where the scenario is\n * defined in both \"left\" and \"right\".\n */\nfunction sortScenarioGroupSummaries(summaries: ComparisonGroupSummary[]): ComparisonGroupSummary[] {\n return summaries.sort((a, b) => {\n const scoreResult = compareScores(a.scores, b.scores)\n if (scoreResult !== 0) {\n // Sort by score first (higher scores followed by lower scores)\n return -scoreResult\n } else {\n // Sort by scenario title alphabetically if scores match\n const aScenario = a.root as ComparisonScenario\n const bScenario = b.root as ComparisonScenario\n const aTitle = aScenario.title.toLowerCase()\n const bTitle = bScenario.title.toLowerCase()\n if (aTitle !== bTitle) {\n // Sort by scenario name\n return aTitle.localeCompare(bTitle)\n } else {\n // Sort by scenario subtitle alphabetically\n const aSubtitle = aScenario.subtitle?.toLowerCase() || ''\n const bSubtitle = bScenario.subtitle?.toLowerCase() || ''\n return aSubtitle.localeCompare(bSubtitle)\n }\n }\n })\n}\n\n/**\n * Return 1 if the most significant score in `a` is higher than that of `b`, -1 if `b` is higher than `a`,\n * or 0 if they are the same.\n */\nfunction compareScores(a: ComparisonGroupScores, b: ComparisonGroupScores): 1 | 0 | -1 {\n if (a.totalMaxDiffByBucket.length !== b.totalMaxDiffByBucket.length) {\n return 0\n }\n\n // Start with the highest threshold bucket (i.e., comparisons with the biggest differences),\n // and then work backwards\n const len = a.totalMaxDiffByBucket.length\n for (let i = len - 1; i >= 0; i--) {\n const aTotal = a.totalMaxDiffByBucket[i]\n const bTotal = b.totalMaxDiffByBucket[i]\n if (aTotal > bTotal) {\n return 1\n } else if (aTotal < bTotal) {\n return -1\n }\n }\n\n // No differences\n return 0\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport Ajv from 'ajv'\nimport assertNever from 'assert-never'\nimport type { Result } from 'neverthrow'\nimport { err, ok } from 'neverthrow'\nimport yaml from 'yaml'\n\nimport type {\n ComparisonDatasetSpec,\n ComparisonGraphGroupSpec,\n ComparisonScenarioGroupRefSpec,\n ComparisonScenarioGroupSpec,\n ComparisonScenarioId,\n ComparisonScenarioInputPosition,\n ComparisonScenarioInputSpec,\n ComparisonScenarioRefSpec,\n ComparisonScenarioSpec,\n ComparisonSpecs,\n ComparisonSpecsSource,\n ComparisonViewBoxSpec,\n ComparisonViewGraphOrder,\n ComparisonViewGraphsSpec,\n ComparisonViewGroupSpec,\n ComparisonViewRowSpec,\n ComparisonViewSpec\n} from '../comparison-spec-types'\n\nimport jsonSchema from './comparison.schema'\n\n/*\n * Note that the following types match the JSON/YAML schema, so they use snake case (underscores)\n * instead of the usual camel case. They also are in an unusual (not user friendly) format, with\n * different optional fields in the same interface instead of using discriminated union types.\n * These \"Parsed\" types are internal to this file; the parsed types are converted to the public\n * \"Spec\" types defined in `comparison-spec.ts`, which use camel case names and union types. The\n * goal of this split is to allow a user to define comparisons using either JSON/YAML format, or\n * programmatically using the \"Spec\" types. The rest of the code deals only with the \"Spec\" types,\n * so the \"Parsed\" types are just an implementation detail of the parser.\n *\n * TODO: Share the parsing types and code with the `check` module.\n */\n\n//\n// DATASETS\n//\n\ntype ParsedDatasetName = string\ntype ParsedDatasetSource = string\n\ninterface ParsedDataset {\n name?: ParsedDatasetName\n source?: ParsedDatasetSource\n}\n\n//\n// SCENARIOS\n//\n\ntype ParsedScenarioId = string\n\ntype ParsedScenarioTitle = string\ntype ParsedScenarioSubtitle = string\n\ntype ParsedScenarioInputName = string\n\ntype ParsedScenarioInputPosition = 'default' | 'min' | 'max'\n\n/**\n * A single input setting for a scenario. An input can be set to a specific number value,\n * or it can be set to a \"position\" (default / min / max), which depends on how a particular\n * model input is configured.\n */\ninterface ParsedScenarioInput {\n input: ParsedScenarioInputName\n at: ParsedScenarioInputPosition | number\n}\n\n/**\n * A definition of an input scenario. A scenario can set one input to a value/position, or it\n * can set multiple inputs to particular values/positions.\n */\ninterface ParsedScenario {\n id?: ParsedScenarioId\n title?: ParsedScenarioTitle\n subtitle?: ParsedScenarioSubtitle\n preset?: 'matrix'\n with?: ParsedScenarioInputName | ParsedScenarioInput[]\n with_inputs?: 'all'\n // with_inputs_in?: string\n at?: ParsedScenarioInputPosition | number\n}\n\n/** A single item in an array of scenario definitions. */\ninterface ParsedScenarioArrayItem {\n scenario: ParsedScenario\n}\n\n/** A reference to a scenario definition with optional title/subtitle overrides. */\ninterface ParsedScenarioRefObject {\n id: ParsedScenarioId\n title?: ParsedScenarioTitle\n subtitle?: ParsedScenarioSubtitle\n}\n\n/** A reference to a scenario definition. */\ninterface ParsedScenarioRef {\n scenario_ref: ParsedScenarioId | ParsedScenarioRefObject\n}\n\n//\n// SCENARIO GROUPS\n//\n\ntype ParsedScenarioGroupId = string\n\ntype ParsedScenarioGroupTitle = string\n\ntype ParsedScenarioGroupScenariosItem = ParsedScenarioArrayItem | ParsedScenarioRef\n\n/**\n * A definition of a group of input scenarios. Multiple scenarios can be grouped together under a single name, and\n * can later be referenced by group ID in a view definition.\n */\ninterface ParsedScenarioGroup {\n id?: ParsedScenarioGroupId\n title: ParsedScenarioGroupTitle\n scenarios: ParsedScenarioGroupScenariosItem[]\n}\n\n/** A single item in an array of scenario group definitions. */\ninterface ParsedScenarioGroupArrayItem {\n scenario_group: ParsedScenarioGroup\n}\n\n/** A reference to a scenario group definition. */\ninterface ParsedScenarioGroupRef {\n scenario_group_ref: ParsedScenarioGroupId\n}\n\n//\n// GRAPHS\n//\n\ntype ParsedGraphsPreset = 'all'\n\ntype ParsedGraphId = string\n\n//\n// GRAPH GROUPS\n//\n\ntype ParsedGraphGroupId = string\n\n/**\n * A definition of a group of graphs. Multiple graphs can be grouped together and can later be\n * referenced by group ID in a view definition.\n */\ninterface ParsedGraphGroup {\n id: ParsedGraphGroupId\n graphs: ParsedGraphId[]\n}\n\n/** A single item in an array of graph group definitions. */\ninterface ParsedGraphGroupArrayItem {\n graph_group: ParsedGraphGroup\n}\n\n/** A reference to a graph group definition. */\ninterface ParsedGraphGroupRef {\n graph_group_ref: ParsedGraphGroupId\n}\n\n//\n// VIEWS\n//\n\ntype ParsedViewTitle = string\ntype ParsedViewSubtitle = string\n\ntype ParsedViewRowTitle = string\ntype ParsedViewRowSubtitle = string\n\ntype ParsedViewBoxTitle = string\ntype ParsedViewBoxSubtitle = string\n\ntype ParsedViewGraphs = ParsedGraphsPreset | ParsedGraphId[] | ParsedGraphGroupRef\n\n/** A definition of a comparison (dataset + scenario) box. */\ninterface ParsedViewBox {\n title: ParsedViewBoxTitle\n subtitle?: ParsedViewBoxSubtitle\n dataset: ParsedDataset\n scenario_ref: ParsedScenarioId\n}\n\n/** A single comparison box in an array of view comparison box definitions. */\ninterface ParsedViewRowBoxesItem {\n box: ParsedViewBox\n}\n\n/** A definition of a row of comparison boxes. */\ninterface ParsedViewRow {\n title: ParsedViewRowTitle\n subtitle?: ParsedViewRowSubtitle\n boxes: ParsedViewRowBoxesItem[]\n}\n\n/** A single row item in an array of view row definitions. */\ninterface ParsedViewRowsItem {\n row: ParsedViewRow\n}\n\n/**\n * A definition of a view. A view presents a set of graphs, either for a single input scenario\n * or for a mix of different dataset/scenario combinations.\n */\ninterface ParsedView {\n title?: ParsedViewTitle\n subtitle?: ParsedViewSubtitle\n scenario_ref?: ParsedScenarioId\n rows?: ParsedViewRowsItem[]\n graphs?: ParsedViewGraphs\n graph_order?: ComparisonViewGraphOrder\n}\n\n//\n// VIEW GROUPS\n//\n\ntype ParsedViewGroupTitle = string\n\ninterface ParsedViewGroupViewsItem {\n view: ParsedView\n}\n\ntype ParsedViewGroupScenariosItem = ParsedScenarioRef | ParsedScenarioGroupRef\n\n/** A definition of a group of views. */\ninterface ParsedViewGroup {\n title: ParsedViewGroupTitle\n views?: ParsedViewGroupViewsItem[]\n scenarios?: ParsedViewGroupScenariosItem[]\n graphs?: ParsedViewGraphs\n graph_order?: ComparisonViewGraphOrder\n}\n\n/** A single item in an array of view definitions. */\ninterface ParsedViewGroupArrayItem {\n view_group: ParsedViewGroup\n}\n\n//\n// TOP-LEVEL DEFS\n//\n\ntype ParsedTopLevelDefItem =\n | ParsedScenarioArrayItem\n | ParsedScenarioGroupArrayItem\n | ParsedGraphGroupArrayItem\n | ParsedViewGroupArrayItem\n\n/**\n * Parse the comparison test definitions in the given JSON or YAML strings.\n *\n * @param specSource The JSON or YAML formatted string to parse.\n */\nexport function parseComparisonSpecs(specSource: ComparisonSpecsSource): Result<ComparisonSpecs, Error> {\n const scenarios: ComparisonScenarioSpec[] = []\n const scenarioGroups: ComparisonScenarioGroupSpec[] = []\n const graphGroups: ComparisonGraphGroupSpec[] = []\n const viewGroups: ComparisonViewGroupSpec[] = []\n\n // Prepare the JSON 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<ComparisonSpec> = jsonSchema\n const validate = ajv.compile<ParsedTopLevelDefItem[]>(jsonSchema)\n\n // Parse the JSON or YAML string\n let parsed: unknown\n switch (specSource.kind) {\n case 'json':\n parsed = JSON.parse(specSource.content)\n break\n case 'yaml':\n parsed = yaml.parse(specSource.content)\n break\n default:\n assertNever(specSource.kind)\n }\n\n // Validate and convert the parsed objects to specs\n if (validate(parsed)) {\n for (const specItem of parsed) {\n if ('scenario' in specItem) {\n scenarios.push(scenarioSpecFromParsed(specItem.scenario))\n } else if ('scenario_group' in specItem) {\n scenarioGroups.push(scenarioGroupSpecFromParsed(specItem.scenario_group))\n } else if ('graph_group' in specItem) {\n graphGroups.push(graphGroupSpecFromParsed(specItem.graph_group))\n } else if ('view_group' in specItem) {\n viewGroups.push(viewGroupSpecFromParsed(specItem.view_group))\n }\n }\n } else {\n let msg = 'Failed to parse YAML comparison definitions'\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 return ok({\n scenarios,\n scenarioGroups,\n graphGroups,\n viewGroups\n })\n}\n\n//\n// DATASETS\n//\n\nfunction datasetSpecFromParsed(parsedDataset: ParsedDataset): ComparisonDatasetSpec {\n return {\n kind: 'dataset',\n name: parsedDataset.name,\n source: parsedDataset.source\n }\n}\n\n//\n// SCENARIOS\n//\n\nfunction scenarioSpecFromParsed(parsedScenario: ParsedScenario): ComparisonScenarioSpec {\n if (parsedScenario.preset === 'matrix') {\n // Create matrix spec\n return {\n kind: 'scenario-matrix'\n }\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 ParsedScenarioInputPosition\n // TODO...\n // }\n\n if (parsedScenario.with !== undefined) {\n let inputSpecs: ComparisonScenarioInputSpec[]\n if (Array.isArray(parsedScenario.with)) {\n // Create one scenario that contains the given input settings\n const parsedInputs = parsedScenario.with as ParsedScenarioInput[]\n inputSpecs = parsedInputs.map(inputSpecFromParsed)\n } else {\n // Create a single \"input at <position|value>\" scenario\n inputSpecs = [\n inputSpecFromParsed({\n input: parsedScenario.with,\n at: parsedScenario.at\n })\n ]\n }\n return {\n kind: 'scenario-with-inputs',\n id: parsedScenario.id,\n title: parsedScenario.title,\n subtitle: parsedScenario.subtitle,\n inputs: inputSpecs\n }\n }\n\n if (parsedScenario.with_inputs === 'all') {\n // Create an \"all inputs at <position>\" scenario\n return {\n kind: 'scenario-with-all-inputs',\n id: parsedScenario.id,\n title: parsedScenario.title,\n subtitle: parsedScenario.subtitle,\n position: parsedScenario.at as ComparisonScenarioInputPosition\n }\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 ParsedScenarioInputPosition\n // TODO...\n // }\n\n // Internal error\n throw new Error(`Unable to convert parsed scenario: ${JSON.stringify(parsedScenario)}`)\n}\n\nfunction inputSpecFromParsed(parsedInput: ParsedScenarioInput): ComparisonScenarioInputSpec {\n if (typeof parsedInput.at === 'number') {\n const value = parsedInput.at as number\n return {\n kind: 'input-at-value',\n inputName: parsedInput.input,\n value\n }\n } else {\n return {\n kind: 'input-at-position',\n inputName: parsedInput.input,\n position: parsedInput.at as ComparisonScenarioInputPosition\n }\n }\n}\n\n//\n// SCENARIO GROUPS\n//\n\nfunction scenarioGroupSpecFromParsed(parsedScenarioGroup: ParsedScenarioGroup): ComparisonScenarioGroupSpec {\n const scenarioSpecs: (ComparisonScenarioSpec | ComparisonScenarioRefSpec)[] = parsedScenarioGroup.scenarios.map(\n parsedScenarioOrRef => {\n if ('scenario_ref' in parsedScenarioOrRef) {\n return scenarioRefSpecFromParsed(parsedScenarioOrRef)\n } else {\n return scenarioSpecFromParsed(parsedScenarioOrRef.scenario)\n }\n }\n )\n return {\n kind: 'scenario-group',\n id: parsedScenarioGroup.id,\n title: parsedScenarioGroup.title,\n scenarios: scenarioSpecs\n }\n}\n\nfunction scenarioRefSpecFromParsed(parsedScenarioRef: ParsedScenarioRef): ComparisonScenarioRefSpec {\n if (typeof parsedScenarioRef.scenario_ref === 'string') {\n return {\n kind: 'scenario-ref',\n scenarioId: parsedScenarioRef.scenario_ref\n }\n } else {\n return {\n kind: 'scenario-ref',\n scenarioId: parsedScenarioRef.scenario_ref.id,\n title: parsedScenarioRef.scenario_ref.title,\n subtitle: parsedScenarioRef.scenario_ref.subtitle\n }\n }\n}\n\nfunction scenarioGroupRefSpecFromParsed(parsedGroupRef: ParsedScenarioGroupRef): ComparisonScenarioGroupRefSpec {\n return {\n kind: 'scenario-group-ref',\n groupId: parsedGroupRef.scenario_group_ref\n }\n}\n\n//\n// GRAPH GROUPS\n//\n\nfunction graphGroupSpecFromParsed(parsedGraphGroup: ParsedGraphGroup): ComparisonGraphGroupSpec {\n return {\n kind: 'graph-group',\n id: parsedGraphGroup.id,\n graphIds: parsedGraphGroup.graphs\n }\n}\n\n//\n// VIEWS\n//\n\nfunction viewSpecFromParsed(parsedView: ParsedView): ComparisonViewSpec {\n let scenarioId: ComparisonScenarioId\n let rows: ComparisonViewRowSpec[]\n if (parsedView.scenario_ref !== undefined) {\n scenarioId = parsedView.scenario_ref\n } else if (parsedView.rows !== undefined) {\n rows = parsedView.rows.map(item => viewRowSpecFromParsed(item.row))\n }\n\n let graphs: ComparisonViewGraphsSpec\n if (parsedView.graphs !== undefined) {\n graphs = viewGraphsSpecFromParsed(parsedView.graphs)\n }\n\n return {\n kind: 'view',\n title: parsedView.title,\n subtitle: parsedView.subtitle,\n scenarioId,\n rows,\n graphs,\n graphOrder: parsedView.graph_order\n }\n}\n\nfunction viewRowSpecFromParsed(parsedRow: ParsedViewRow): ComparisonViewRowSpec {\n return {\n kind: 'view-row',\n title: parsedRow.title,\n subtitle: parsedRow.subtitle,\n boxes: parsedRow.boxes.map(item => viewBoxSpecFromParsed(item.box))\n }\n}\n\nfunction viewBoxSpecFromParsed(parsedBox: ParsedViewBox): ComparisonViewBoxSpec {\n return {\n kind: 'view-box',\n title: parsedBox.title,\n subtitle: parsedBox.subtitle,\n dataset: datasetSpecFromParsed(parsedBox.dataset),\n scenarioId: parsedBox.scenario_ref\n }\n}\n\nfunction viewGraphsSpecFromParsed(parsedGraphs: ParsedViewGraphs): ComparisonViewGraphsSpec {\n if (parsedGraphs === 'all') {\n return {\n kind: 'graphs-preset',\n preset: 'all'\n }\n } else if (Array.isArray(parsedGraphs)) {\n return {\n kind: 'graphs-array',\n graphIds: parsedGraphs\n }\n } else if ('graph_group_ref' in parsedGraphs) {\n return {\n kind: 'graph-group-ref',\n groupId: parsedGraphs.graph_group_ref\n }\n } else {\n // Internal error (this should have already been rejected by the validator)\n throw new Error('Invalid graphs spec in comparison view')\n }\n}\n\n//\n// VIEW GROUPS\n//\n\nfunction viewGroupSpecFromParsed(parsedViewGroup: ParsedViewGroup): ComparisonViewGroupSpec {\n if (parsedViewGroup.views !== undefined) {\n // This is a view group with an array of views\n return {\n kind: 'view-group-with-views',\n title: parsedViewGroup.title,\n views: parsedViewGroup.views.map(item => viewSpecFromParsed(item.view))\n }\n } else if (parsedViewGroup.scenarios !== undefined) {\n // This is a view group with an array of scenario (or scenario group) references\n const scenarios: (ComparisonScenarioRefSpec | ComparisonScenarioGroupRefSpec)[] = parsedViewGroup.scenarios.map(\n parsedScenarioOrGroupRef => {\n if ('scenario_ref' in parsedScenarioOrGroupRef) {\n return scenarioRefSpecFromParsed(parsedScenarioOrGroupRef)\n } else if ('scenario_group_ref' in parsedScenarioOrGroupRef) {\n return scenarioGroupRefSpecFromParsed(parsedScenarioOrGroupRef)\n } else {\n // Internal error (this should have already been rejected by the validator)\n throw new Error('Invalid view group')\n }\n }\n )\n return {\n kind: 'view-group-with-scenarios',\n title: parsedViewGroup.title,\n scenarios,\n graphs: viewGraphsSpecFromParsed(parsedViewGroup.graphs)\n }\n } else {\n // Internal error (this should have already been rejected by the validator)\n throw new Error('Invalid view group')\n }\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 Comparison Test',\n type: 'array',\n description: 'A group of model comparison scenarios and views.',\n items: {\n $ref: '#/$defs/top_level_array_item'\n },\n\n $defs: {\n //\n // TOP-LEVEL\n //\n\n top_level_array_item: {\n oneOf: [\n { $ref: '#/$defs/scenario_array_item' },\n { $ref: '#/$defs/scenario_group_array_item' },\n { $ref: '#/$defs/graph_group_array_item' },\n { $ref: '#/$defs/view_group_array_item' }\n ]\n },\n\n //\n // DATASETS\n //\n\n dataset: {\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 //\n // SCENARIOS\n //\n\n scenario_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenario: {\n $ref: '#/$defs/scenario'\n }\n },\n required: ['scenario']\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 id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\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 id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\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 id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\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 id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\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_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 //\n // SCENARIO GROUPS\n //\n\n scenario_group_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenario_group: {\n $ref: '#/$defs/scenario_group'\n }\n },\n required: ['scenario_group']\n },\n\n scenario_group: {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n scenarios: {\n type: 'array',\n items: {\n $ref: '#/$defs/scenario_group_scenarios_array_item'\n },\n minItems: 1\n }\n },\n required: ['title', 'scenarios']\n },\n\n scenario_group_scenarios_array_item: {\n oneOf: [{ $ref: '#/$defs/scenario_array_item' }, { $ref: '#/$defs/scenario_ref_array_item' }]\n },\n\n scenario_ref_id: {\n type: 'string'\n },\n\n scenario_ref_object: {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n }\n },\n required: ['id']\n },\n\n scenario_ref: {\n oneOf: [{ $ref: '#/$defs/scenario_ref_id' }, { $ref: '#/$defs/scenario_ref_object' }]\n },\n\n scenario_ref_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenario_ref: {\n $ref: '#/$defs/scenario_ref'\n }\n },\n required: ['scenario_ref']\n },\n\n scenario_group_ref: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenario_group_ref: {\n type: 'string'\n }\n },\n required: ['scenario_group_ref']\n },\n\n //\n // GRAPHS\n //\n\n graphs_preset: {\n type: 'string',\n enum: ['all']\n },\n\n graphs_array: {\n type: 'array',\n items: {\n type: 'string'\n },\n minItems: 1\n },\n\n graph_group_ref: {\n type: 'object',\n additionalProperties: false,\n properties: {\n graph_group_ref: {\n type: 'string'\n }\n },\n required: ['graph_group_ref']\n },\n\n //\n // GRAPH GROUPS\n //\n\n graph_group_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n graph_group: {\n $ref: '#/$defs/graph_group'\n }\n },\n required: ['graph_group']\n },\n\n graph_group: {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string'\n },\n graphs: {\n $ref: '#/$defs/graphs_array'\n }\n },\n required: ['id', 'graphs']\n },\n\n //\n // VIEWS\n //\n\n view: {\n oneOf: [{ $ref: '#/$defs/view_with_scenario' }, { $ref: '#/$defs/view_with_rows' }]\n },\n\n view_with_scenario: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\n scenario_ref: {\n type: 'string'\n },\n graphs: {\n $ref: '#/$defs/view_graphs'\n },\n graph_order: {\n $ref: '#/$defs/view_graph_order'\n }\n },\n required: ['scenario_ref']\n },\n\n view_with_rows: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\n rows: {\n $ref: '#/$defs/view_rows_array'\n }\n },\n required: ['title', 'rows']\n },\n\n view_rows_array: {\n type: 'array',\n items: {\n $ref: '#/$defs/view_rows_array_item'\n },\n minItems: 1\n },\n\n view_rows_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n row: {\n $ref: '#/$defs/view_row'\n }\n }\n },\n\n view_row: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\n boxes: {\n $ref: '#/$defs/view_boxes_array'\n }\n },\n required: ['title', 'boxes']\n },\n\n view_boxes_array: {\n type: 'array',\n items: {\n $ref: '#/$defs/view_boxes_array_item'\n },\n minItems: 1\n },\n\n view_boxes_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n box: {\n $ref: '#/$defs/view_box'\n }\n }\n },\n\n view_box: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\n dataset: {\n $ref: '#/$defs/dataset'\n },\n scenario_ref: {\n $ref: '#/$defs/scenario_ref_id'\n }\n },\n required: ['title', 'dataset', 'scenario_ref']\n },\n\n view_graphs: {\n oneOf: [{ $ref: '#/$defs/graphs_preset' }, { $ref: '#/$defs/graphs_array' }, { $ref: '#/$defs/graph_group_ref' }]\n },\n\n view_graph_order: {\n type: 'string',\n enum: ['default', 'grouped-by-diffs']\n },\n\n //\n // VIEW GROUPS\n //\n\n view_group_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n view_group: {\n $ref: '#/$defs/view_group'\n }\n },\n required: ['view_group']\n },\n\n view_group: {\n oneOf: [\n { $ref: '#/$defs/view_group_with_views_array' },\n { $ref: '#/$defs/view_group_shorthand_with_scenarios_array' }\n ]\n },\n\n view_group_with_views_array: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n views: {\n type: 'array',\n items: {\n $ref: '#/$defs/view_group_views_array_item'\n },\n minItems: 1\n }\n },\n required: ['title', 'views']\n },\n\n view_group_views_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n view: {\n $ref: '#/$defs/view'\n }\n }\n },\n\n view_group_shorthand_with_scenarios_array: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n scenarios: {\n type: 'array',\n items: {\n $ref: '#/$defs/view_group_scenarios_array_item'\n },\n minItems: 1\n },\n graphs: {\n $ref: '#/$defs/view_graphs'\n }\n },\n required: ['title', 'scenarios', 'graphs']\n },\n\n view_group_scenarios_array_item: {\n oneOf: [{ $ref: '#/$defs/scenario_ref_array_item' }, { $ref: '#/$defs/scenario_group_ref' }]\n }\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { InputPosition, InputSetting, ScenarioSpec } from '../../../_shared/scenario-spec-types'\nimport { inputSettingsSpec } from '../../../_shared/scenario-specs'\n\nimport type { BundleGraphId, ModelSpec } from '../../../bundle/bundle-types'\nimport { ModelInputs } from '../../../bundle/model-inputs'\nimport type { InputId, InputVar, OutputVar } from '../../../bundle/var-types'\n\nimport type {\n ComparisonDataset,\n ComparisonScenario,\n ComparisonScenarioAllInputsSettings,\n ComparisonScenarioGroup,\n ComparisonScenarioInput,\n ComparisonScenarioInputSettings,\n ComparisonScenarioInputState,\n ComparisonScenarioKey,\n ComparisonUnresolvedScenarioRef,\n ComparisonUnresolvedView,\n ComparisonView,\n ComparisonViewBox,\n ComparisonViewGroup,\n ComparisonViewRow\n} from '../../_shared/comparison-resolved-types'\n\nimport type {\n ComparisonDatasetName,\n ComparisonDatasetSource,\n ComparisonGraphGroupId,\n ComparisonGraphGroupSpec,\n ComparisonGraphId,\n ComparisonScenarioGroupId,\n ComparisonScenarioGroupSpec,\n ComparisonScenarioId,\n ComparisonScenarioInputPosition,\n ComparisonScenarioInputSpec,\n ComparisonScenarioRefSpec,\n ComparisonScenarioSpec,\n ComparisonScenarioSubtitle,\n ComparisonScenarioTitle,\n ComparisonSpecs,\n ComparisonViewBoxSpec,\n ComparisonViewGraphOrder,\n ComparisonViewGraphsSpec,\n ComparisonViewGroupSpec,\n ComparisonViewRowSpec,\n ComparisonViewSubtitle,\n ComparisonViewTitle\n} from '../comparison-spec-types'\nimport { inputSettingFromResolvedInputState, scenarioSpecsFromSettings } from './comparison-scenario-specs'\n\nexport interface ComparisonResolvedDefs {\n /** The set of resolved scenarios. */\n scenarios: ComparisonScenario[]\n /** The set of resolved scenario groups. */\n scenarioGroups: ComparisonScenarioGroup[]\n /** The set of resolved view groups. */\n viewGroups: ComparisonViewGroup[]\n}\n\ntype GenKey = () => ComparisonScenarioKey\n\n/**\n * Expand and resolve all the provided scenario and view specs. This will inspect all the\n * requested specs, resolve references to input variables and scenarios, and then return\n * the definitions for the fully resolved scenarios and views.\n *\n * @param modelSpecL The model spec for the \"left\" bundle being compared.\n * @param modelSpecR The model spec for the \"right\" bundle being compared.\n * @param specs The scenario and view specs that were parsed from YAML/JSON definitions.\n */\nexport function resolveComparisonSpecs(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n specs: ComparisonSpecs\n): ComparisonResolvedDefs {\n let key = 1\n const genKey: GenKey = () => {\n return `${key++}` as ComparisonScenarioKey\n }\n\n // Create `ModelInputs` instances to make lookups easier\n const modelInputsL = new ModelInputs(modelSpecL)\n const modelInputsR = new ModelInputs(modelSpecR)\n\n // Create a `ModelOutputs` instance to make lookups easier\n const modelOutputs = new ModelOutputs(modelSpecL, modelSpecR)\n\n // Resolve the top-level scenario specs and convert to `ComparisonScenario` instances\n const resolvedScenarios = new ResolvedScenarios()\n for (const scenarioSpec of specs.scenarios || []) {\n resolvedScenarios.add(resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioSpec, genKey))\n }\n\n // Resolve scenarios that are defined inside scenario groups and add them to the set of scenarios.\n // Note that we track the key for each scenario in the group so that we can preserve the key when\n // creating a copy/reference in the next step.\n interface PartiallyResolvedScenarioGroup {\n spec: ComparisonScenarioGroupSpec\n scenarios: (ComparisonScenario | ComparisonScenarioRefSpec)[]\n }\n const partiallyResolvedScenarioGroups: PartiallyResolvedScenarioGroup[] = []\n for (const scenarioGroupSpec of specs.scenarioGroups || []) {\n const scenariosForGroup: (ComparisonScenario | ComparisonScenarioRefSpec)[] = []\n for (const scenarioItem of scenarioGroupSpec.scenarios) {\n if (scenarioItem.kind === 'scenario-ref') {\n scenariosForGroup.push(scenarioItem)\n } else {\n const scenarios = resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioItem, genKey)\n resolvedScenarios.add(scenarios)\n scenariosForGroup.push(...scenarios)\n }\n }\n partiallyResolvedScenarioGroups.push({\n spec: scenarioGroupSpec,\n scenarios: scenariosForGroup\n })\n }\n\n // Now that all scenarios have been resolved, resolve the groups themselves. Note that we do this as a\n // secondary pass after resolving all top-level and nested scenario definitions so that groups can refer\n // to scenarios that are defined elsewhere (order does not matter).\n const resolvedScenarioGroups = new ResolvedScenarioGroups()\n for (const partiallyResolvedGroup of partiallyResolvedScenarioGroups) {\n const scenariosForGroup: (ComparisonScenario | ComparisonUnresolvedScenarioRef)[] = []\n for (const scenarioItem of partiallyResolvedGroup.scenarios) {\n if (scenarioItem.kind === 'scenario-ref') {\n // See if we have a scenario defined for this ID\n const referencedScenario = resolvedScenarios.getScenarioForId(scenarioItem.scenarioId)\n if (referencedScenario) {\n // Found it; create a copy of it to allow for adding the title/subtitle overrides if provided.\n // Note that we use the same key as the original so that report references work correctly.\n const resolvedScenario = { ...referencedScenario }\n if (scenarioItem.title) {\n resolvedScenario.title = scenarioItem.title\n }\n if (scenarioItem.subtitle) {\n resolvedScenario.subtitle = scenarioItem.subtitle\n }\n scenariosForGroup.push(resolvedScenario)\n } else {\n // Not found, add an unresolved ref item to the group\n scenariosForGroup.push({\n kind: 'unresolved-scenario-ref',\n scenarioId: scenarioItem.scenarioId\n })\n }\n } else {\n // Add the fully resolved scenario to this group (using the same key as the original)\n scenariosForGroup.push(scenarioItem)\n }\n }\n resolvedScenarioGroups.add({\n kind: 'scenario-group',\n id: partiallyResolvedGroup.spec.id,\n title: partiallyResolvedGroup.spec.title,\n scenarios: scenariosForGroup\n })\n }\n\n // Resolve the top-level graph group specs\n const resolvedGraphGroups = new ResolvedGraphGroups()\n for (const graphGroupSpec of specs.graphGroups || []) {\n resolvedGraphGroups.add(graphGroupSpec)\n }\n\n // Resolve the view groups\n const resolvedViewGroups: ComparisonViewGroup[] = []\n for (const viewGroupSpec of specs.viewGroups || []) {\n const resolvedViewGroup = resolveViewGroupFromSpec(\n modelSpecL,\n modelSpecR,\n modelOutputs,\n resolvedScenarios,\n resolvedScenarioGroups,\n resolvedGraphGroups,\n viewGroupSpec\n )\n resolvedViewGroups.push(resolvedViewGroup)\n }\n\n return {\n scenarios: resolvedScenarios.getAll(),\n scenarioGroups: resolvedScenarioGroups.getAll(),\n viewGroups: resolvedViewGroups\n }\n}\n\n//\n// DATASETS\n//\n\n// TODO: This isn't very efficient because it iterates over all datasets to match by\n// variable and source name, but it likely is not used as much as scenario lookup; it\n// is probably OK for now but should be improved eventually\nclass ModelOutputs {\n constructor(private readonly modelSpecL: ModelSpec, private readonly modelSpecR: ModelSpec) {}\n\n getDatasetForName(name: ComparisonDatasetName, source?: ComparisonDatasetSource): ComparisonDataset | undefined {\n // TODO: This doesn't currently handle renames; ideally this would delegate to the\n // existing `ComparisonDatasets` type, which does account for renamed variables.\n // For now, only look in the \"right\" model spec, and get the variable from the \"left\"\n // model spec if the keys match.\n function findOutputVar(modelSpec: ModelSpec): OutputVar | undefined {\n for (const outputVar of modelSpec.outputVars.values()) {\n if (outputVar.varName === name && outputVar.sourceName === source) {\n return outputVar\n }\n }\n return undefined\n }\n\n const outputVarR = findOutputVar(this.modelSpecR)\n if (outputVarR) {\n // XXX: See if there is a matching item in the \"left\" model spec\n const datasetKey = outputVarR.datasetKey\n const outputVarL = this.modelSpecL.outputVars.get(datasetKey)\n return {\n kind: 'dataset',\n key: datasetKey,\n outputVarL,\n outputVarR\n }\n } else {\n return undefined\n }\n }\n}\n\n//\n// SCENARIOS\n//\n\nclass ResolvedScenarios {\n /** The array of all resolved scenarios. */\n private readonly resolvedScenarios: ComparisonScenario[] = []\n\n /** The set of resolved scenarios that include an ID, keyed by ID. */\n private readonly resolvedScenariosById: Map<ComparisonScenarioId, ComparisonScenario> = new Map()\n\n add(scenarios: ComparisonScenario[]): void {\n for (const resolvedScenario of scenarios) {\n // Add the scenario to the general set\n this.resolvedScenarios.push(resolvedScenario)\n\n // Also add to the map of scenarios with an ID, if one is defined\n if (resolvedScenario.id !== undefined) {\n // See if ID is already used\n // TODO: Mark this as an error in the interface rather than throwing\n if (this.resolvedScenariosById.has(resolvedScenario.id)) {\n throw new Error(`Multiple scenarios defined with the same id (${resolvedScenario.id})`)\n }\n this.resolvedScenariosById.set(resolvedScenario.id, resolvedScenario)\n }\n }\n }\n\n getAll(): ComparisonScenario[] {\n return this.resolvedScenarios\n }\n\n getScenarioForId(id: ComparisonScenarioId): ComparisonScenario | undefined {\n return this.resolvedScenariosById.get(id)\n }\n}\n\n/**\n * Return one or more resolved `ComparisonScenario` instances for the given scenario spec.\n */\nfunction resolveScenariosFromSpec(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n scenarioSpec: ComparisonScenarioSpec,\n genKey: GenKey\n): ComparisonScenario[] {\n switch (scenarioSpec.kind) {\n case 'scenario-matrix':\n // Create a matrix of scenarios\n return resolveScenarioMatrix(modelInputsL, modelInputsR, genKey)\n\n case 'scenario-with-all-inputs': {\n // Create an \"all inputs at <position>\" scenario\n const position = inputPosition(scenarioSpec.position)\n return [\n resolveScenarioWithAllInputsAtPosition(\n genKey(),\n scenarioSpec.id,\n scenarioSpec.title,\n scenarioSpec.subtitle,\n position\n )\n ]\n }\n\n case 'scenario-with-inputs': {\n // Create one scenario that contains the given input setting(s)\n return [\n resolveScenarioForInputSpecs(\n modelInputsL,\n modelInputsR,\n genKey(),\n scenarioSpec.id,\n scenarioSpec.title,\n scenarioSpec.subtitle,\n scenarioSpec.inputs\n )\n ]\n }\n\n case 'scenario-with-distinct-inputs': {\n // Create one scenario in which the inputs are configured differently\n // for the two models\n return [\n resolveScenarioForDistinctInputSpecs(\n modelInputsL,\n modelInputsR,\n genKey(),\n scenarioSpec.id,\n scenarioSpec.title,\n scenarioSpec.subtitle,\n scenarioSpec.inputsL,\n scenarioSpec.inputsR\n )\n ]\n }\n\n default:\n assertNever(scenarioSpec)\n }\n}\n\n/**\n * Return a matrix of scenarios that covers all inputs for the given model.\n */\nfunction resolveScenarioMatrix(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n genKey: GenKey\n): ComparisonScenario[] {\n const resolvedScenarios: ComparisonScenario[] = []\n\n // Add an \"all inputs at default\" scenario\n resolvedScenarios.push(\n resolveScenarioWithAllInputsAtPosition(genKey(), undefined, undefined, undefined, 'at-default')\n )\n\n // Get the union of all input IDs appearing on either side\n const inputIdAliases: Set<InputId> = new Set()\n modelInputsL.getAllInputIdAliases().forEach(alias => inputIdAliases.add(alias))\n modelInputsR.getAllInputIdAliases().forEach(alias => inputIdAliases.add(alias))\n\n // Create two scenarios for each input, one with the input at its minimum, and one\n // with the input at its maximum. If the input only exists on one side, we still\n // create a scenario for it, but it will be flagged in the UI to make it clear\n // that the input configuration has changed.\n for (const inputIdAlias of inputIdAliases) {\n const inputAtMin: ComparisonScenarioInputSpec = {\n kind: 'input-at-position',\n inputName: inputIdAlias,\n position: 'min'\n }\n const inputAtMax: ComparisonScenarioInputSpec = {\n kind: 'input-at-position',\n inputName: inputIdAlias,\n position: 'max'\n }\n resolvedScenarios.push(\n resolveScenarioForInputSpecs(modelInputsL, modelInputsR, genKey(), undefined, undefined, undefined, [inputAtMin])\n )\n resolvedScenarios.push(\n resolveScenarioForInputSpecs(modelInputsL, modelInputsR, genKey(), undefined, undefined, undefined, [inputAtMax])\n )\n }\n\n return resolvedScenarios\n}\n\n/**\n * Return a resolved `ComparisonScenario` with all inputs set to the given position.\n */\nfunction resolveScenarioWithAllInputsAtPosition(\n key: ComparisonScenarioKey,\n id: ComparisonScenarioId | undefined,\n title: ComparisonScenarioTitle | undefined,\n subtitle: ComparisonScenarioSubtitle | undefined,\n position: InputPosition\n): ComparisonScenario {\n // Create the settings and specs\n const settings: ComparisonScenarioAllInputsSettings = {\n kind: 'all-inputs-settings',\n position\n }\n const [specL, specR] = scenarioSpecsFromSettings(settings)\n\n // Create a `ComparisonScenario` with the settings\n return {\n kind: 'scenario',\n key,\n id,\n title,\n subtitle,\n settings,\n specL,\n specR\n }\n}\n\n/**\n * Return a resolved `ComparisonScenario` for the given inputs and positions/values.\n */\nfunction resolveScenarioForInputSpecs(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n key: ComparisonScenarioKey,\n id: ComparisonScenarioId | undefined,\n title: ComparisonScenarioTitle | undefined,\n subtitle: ComparisonScenarioSubtitle | undefined,\n inputSpecs: ComparisonScenarioInputSpec[]\n): ComparisonScenario {\n // Convert the input specs to `ComparisonScenarioInput` instances\n const resolvedInputs = inputSpecs.map(inputSpec => {\n switch (inputSpec.kind) {\n case 'input-at-position':\n return resolveInputForName(modelInputsL, modelInputsR, inputSpec.inputName, inputSpec.position)\n case 'input-at-value':\n return resolveInputForName(modelInputsL, modelInputsR, inputSpec.inputName, inputSpec.value)\n default:\n assertNever(inputSpec)\n }\n })\n\n // Create the settings and specs\n const settings: ComparisonScenarioInputSettings = {\n kind: 'input-settings',\n inputs: resolvedInputs\n }\n const [specL, specR] = scenarioSpecsFromSettings(settings)\n\n // Create a `ComparisonScenario` with the resolved inputs\n return {\n kind: 'scenario',\n key,\n id,\n title,\n subtitle,\n settings,\n specL,\n specR\n }\n}\n\n/**\n * Return a resolved `ComparisonScenario` for the given settings that are different\n * for the two models.\n */\nfunction resolveScenarioForDistinctInputSpecs(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n key: ComparisonScenarioKey,\n id: ComparisonScenarioId | undefined,\n title: ComparisonScenarioTitle | undefined,\n subtitle: ComparisonScenarioSubtitle | undefined,\n inputSpecsL: ComparisonScenarioInputSpec[],\n inputSpecsR: ComparisonScenarioInputSpec[]\n): ComparisonScenario {\n // TODO: Unlike the more typical \"scenario with inputs\" case, when we have \"distinct\"\n // inputs (separate sets of inputs for the two models) we only include the `settings`\n // array in the resulting `ComparisonScenario` if there are errors in resolving the\n // inputs. If all inputs were resolved successfully, then the `settings` array will\n // be empty. This is probably fine for now but it could stand to be redesigned.\n const inputsWithErrors: ComparisonScenarioInput[] = []\n\n // Resolve the input settings for the left and right sides separately\n const settingsL: InputSetting[] = []\n const settingsR: InputSetting[] = []\n\n // Helper function that resolves an input for the given model/side. If the input is\n // resolved successfully, an `InputSetting` will be saved for that side. Otherwise,\n // a `ComparisonScenarioInput` describing the error will be saved.\n function resolveInputSpec(\n side: 'left' | 'right',\n modelInputs: ModelInputs,\n inputSpec: ComparisonScenarioInputSpec\n ): void {\n let inputState: ComparisonScenarioInputState\n switch (inputSpec.kind) {\n case 'input-at-position':\n inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.position)\n break\n case 'input-at-value':\n inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.value)\n break\n default:\n assertNever(inputSpec)\n }\n\n if (inputState.error !== undefined) {\n // The input could not be resolved, so add it to the set of error inputs\n // TODO: For now we include an empty object (with undefined properties) for the\n // \"other\" side. Maybe we can make the state properties optional, or maybe we\n // just need a less awkward way of handling these input states in the \"distinct\"\n // inputs case.\n inputsWithErrors.push({\n requestedName: inputSpec.inputName,\n stateL: side === 'left' ? inputState : {},\n stateR: side === 'right' ? inputState : {}\n })\n } else {\n // The input was resolved, so create a scenario that works for this side\n const inputSetting = inputSettingFromResolvedInputState(inputState)\n if (side === 'left') {\n settingsL.push(inputSetting)\n } else {\n settingsR.push(inputSetting)\n }\n }\n }\n\n // Resolve the input settings for the left and right sides separately\n inputSpecsL.forEach(inputSpec => resolveInputSpec('left', modelInputsL, inputSpec))\n inputSpecsR.forEach(inputSpec => resolveInputSpec('right', modelInputsR, inputSpec))\n\n // Create a `ScenarioSpec` for each side if there were no errors\n let specL: ScenarioSpec\n let specR: ScenarioSpec\n if (inputsWithErrors.length === 0) {\n specL = inputSettingsSpec(settingsL)\n specR = inputSettingsSpec(settingsR)\n }\n\n // Create a `ComparisonScenario` with the resolved inputs\n return {\n kind: 'scenario',\n key,\n id,\n title,\n subtitle,\n settings: {\n kind: 'input-settings',\n inputs: inputsWithErrors\n },\n specL,\n specR\n }\n}\n\n/**\n * Return a resolved `ComparisonScenarioInput` for the given input name and position/value.\n */\nfunction resolveInputForName(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n inputName: string,\n at: ComparisonScenarioInputPosition | number\n): ComparisonScenarioInput {\n // Resolve the input in the context of each model\n return {\n requestedName: inputName,\n stateL: resolveInputForNameInModel(modelInputsL, inputName, at),\n stateR: resolveInputForNameInModel(modelInputsR, inputName, at)\n }\n}\n\n/**\n * Return a resolved `ComparisonScenarioInputState` for the given input name and position/value\n * in the context of a specific model.\n */\nfunction resolveInputForNameInModel(\n modelInputs: ModelInputs,\n inputName: string,\n at: ComparisonScenarioInputPosition | number\n): ComparisonScenarioInputState {\n // Find an input variable that matches the given name (either the actual variable name\n // or an alias)\n const inputVar = modelInputs.getInputVarForName(inputName)\n if (inputVar) {\n // Resolve the input at the given value or position\n return resolveInputVar(inputVar, at)\n } else {\n // No input variable found that matches the given name\n return {\n error: {\n kind: 'unknown-input'\n }\n }\n }\n}\n\n/**\n * Return a `ComparisonScenarioInputState` for the given input variable and position/value.\n */\nfunction resolveInputVar(\n inputVar: InputVar,\n at: ComparisonScenarioInputPosition | number\n): ComparisonScenarioInputState {\n if (typeof at === 'number') {\n const value = at as number\n return resolveInputVarAtValue(inputVar, value)\n } else {\n const position = inputPosition(at as ComparisonScenarioInputPosition)\n return resolveInputVarAtPosition(inputVar, position)\n }\n}\n\n/**\n * Return a `ComparisonScenarioInputState` for the input at the given position.\n */\nfunction resolveInputVarAtPosition(inputVar: InputVar, position: InputPosition): ComparisonScenarioInputState {\n return {\n inputVar,\n position,\n value: inputValueAtPosition(inputVar, position)\n }\n}\n\n/**\n * Return a `ComparisonScenarioInputState` for the input at the given value.\n */\nfunction resolveInputVarAtValue(inputVar: InputVar, value: number): ComparisonScenarioInputState {\n // Check that the value is in the valid range\n // TODO: This may not be valid for switch inputs\n if (value >= inputVar.minValue && value <= inputVar.maxValue) {\n return {\n inputVar,\n value\n }\n } else {\n return {\n error: {\n kind: 'invalid-value'\n }\n }\n }\n}\n\n/**\n * Convert a `ComparisonScenarioInputPosition` (from the parser) to an `InputPosition` (used by `Scenario`).\n */\nfunction inputPosition(position: ComparisonScenarioInputPosition): 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// SCENARIO GROUPS\n//\n\nclass ResolvedScenarioGroups {\n /** The array of all resolved scenario groups. */\n private readonly resolvedGroups: ComparisonScenarioGroup[] = []\n\n /** The set of resolved scenario groups that include an ID, keyed by ID. */\n private readonly resolvedGroupsById: Map<ComparisonScenarioGroupId, ComparisonScenarioGroup> = new Map()\n\n add(group: ComparisonScenarioGroup): void {\n // Add the group to the general set\n this.resolvedGroups.push(group)\n\n // Also add to the map of groups with an ID, if one is defined\n if (group.id !== undefined) {\n // See if ID is already used\n // TODO: Mark this as an error in the interface rather than throwing\n if (this.resolvedGroupsById.has(group.id)) {\n throw new Error(`Multiple scenario groups defined with the same id (${group.id})`)\n }\n this.resolvedGroupsById.set(group.id, group)\n }\n }\n\n getAll(): ComparisonScenarioGroup[] {\n return this.resolvedGroups\n }\n\n getGroupForId(id: ComparisonScenarioGroupId): ComparisonScenarioGroup | undefined {\n return this.resolvedGroupsById.get(id)\n }\n}\n\n//\n// GRAPH GROUPS\n//\n\n// TODO: This doesn't currently check that the referenced graph IDs are available in one or both\n// model specs\nclass ResolvedGraphGroups {\n /** The set of resolved graph groups, keyed by ID. */\n private readonly resolvedGroupsById: Map<ComparisonGraphGroupId, ComparisonGraphGroupSpec> = new Map()\n\n add(group: ComparisonGraphGroupSpec): void {\n // Add to the map of groups\n if (this.resolvedGroupsById.has(group.id)) {\n // TODO: Mark this as an error in the interface rather than throwing\n throw new Error(`Multiple graph groups defined with the same id (${group.id})`)\n }\n this.resolvedGroupsById.set(group.id, group)\n }\n\n getGroupForId(id: ComparisonGraphGroupId): ComparisonGraphGroupSpec | undefined {\n return this.resolvedGroupsById.get(id)\n }\n}\n\n//\n// VIEWS\n//\n\n/**\n * Return the graphs \"all\" preset or the graph IDs from a view graphs spec.\n */\nfunction resolveGraphsFromSpec(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n resolvedGraphGroups: ResolvedGraphGroups,\n graphsSpec: ComparisonViewGraphsSpec\n): ComparisonGraphId[] {\n switch (graphsSpec.kind) {\n case 'graphs-preset': {\n switch (graphsSpec.preset) {\n case 'all': {\n // Get the union of all graph IDs appearing in either left or right\n const graphIds: Set<BundleGraphId> = new Set()\n const addGraphIds = (modelSpec: ModelSpec) => {\n if (modelSpec.graphSpecs) {\n for (const graphSpec of modelSpec.graphSpecs) {\n graphIds.add(graphSpec.id)\n }\n }\n }\n addGraphIds(modelSpecL)\n addGraphIds(modelSpecR)\n return [...graphIds]\n }\n default:\n assertNever(graphsSpec.preset)\n }\n }\n // eslint-disable-next-line no-fallthrough\n case 'graphs-array':\n return graphsSpec.graphIds\n case 'graph-group-ref': {\n const groupSpec = resolvedGraphGroups.getGroupForId(graphsSpec.groupId)\n if (groupSpec === undefined) {\n // TODO: Mark this as an error in the interface rather than throwing\n throw new Error(`No graph group found for id ${graphsSpec.groupId}`)\n }\n return groupSpec.graphIds\n }\n default:\n assertNever(graphsSpec)\n }\n}\n\nfunction resolveViewForScenarioId(\n resolvedScenarios: ResolvedScenarios,\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n scenarioId: ComparisonScenarioId,\n graphIds: ComparisonGraphId[],\n graphOrder: ComparisonViewGraphOrder\n): ComparisonView | ComparisonUnresolvedView {\n const resolvedScenario = resolvedScenarios.getScenarioForId(scenarioId)\n if (resolvedScenario) {\n // Add the resolved view\n return resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graphIds, graphOrder)\n } else {\n // Add the unresolved view\n return unresolvedViewForScenarioId(viewTitle, viewSubtitle, scenarioId)\n }\n}\n\nfunction resolveViewForScenarioRefSpec(\n resolvedScenarios: ResolvedScenarios,\n refSpec: ComparisonScenarioRefSpec,\n graphIds: ComparisonGraphId[],\n graphOrder: ComparisonViewGraphOrder\n): ComparisonView | ComparisonUnresolvedView {\n const resolvedScenario = resolvedScenarios.getScenarioForId(refSpec.scenarioId)\n if (resolvedScenario) {\n // Add the resolved view\n return resolveViewForScenario(refSpec.title, refSpec.subtitle, resolvedScenario, graphIds, graphOrder)\n } else {\n // Add the unresolved view\n return unresolvedViewForScenarioId(undefined, undefined, refSpec.scenarioId)\n }\n}\n\nfunction resolveViewForScenario(\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n resolvedScenario: ComparisonScenario,\n graphIds: ComparisonGraphId[],\n graphOrder: ComparisonViewGraphOrder\n): ComparisonView {\n // If explicit title/subtitle were not provided for the view, infer them from the scenario\n // TODO: For now we only infer the subtitle if an explicit title was also not provided; this might\n // be surprising if the user wants the title inferred but wants to provide an explicit subtitle\n if (viewTitle === undefined) {\n viewTitle = resolvedScenario.title\n if (viewTitle === undefined) {\n viewTitle = 'Untitled view'\n }\n if (viewSubtitle === undefined) {\n viewSubtitle = resolvedScenario.subtitle\n }\n }\n\n return {\n kind: 'view',\n title: viewTitle,\n subtitle: viewSubtitle,\n scenario: resolvedScenario,\n graphIds,\n graphOrder\n }\n}\n\ninterface ComparisonUnresolvedViewBox {\n kind: 'unresolved-view-box'\n datasetName?: ComparisonDatasetName\n datasetSource?: ComparisonDatasetSource\n scenarioId?: ComparisonScenarioId\n}\n\nfunction resolveViewBoxForSpec(\n modelOutputs: ModelOutputs,\n resolvedScenarios: ResolvedScenarios,\n boxSpec: ComparisonViewBoxSpec\n): ComparisonViewBox | ComparisonUnresolvedViewBox {\n const resolvedDataset = modelOutputs.getDatasetForName(boxSpec.dataset.name, boxSpec.dataset.source)\n if (resolvedDataset === undefined) {\n return {\n kind: 'unresolved-view-box',\n datasetName: boxSpec.dataset.name,\n datasetSource: boxSpec.dataset.source\n }\n }\n\n const resolvedScenario = resolvedScenarios.getScenarioForId(boxSpec.scenarioId)\n if (resolvedScenario === undefined) {\n return {\n kind: 'unresolved-view-box',\n scenarioId: boxSpec.scenarioId\n }\n }\n\n return {\n kind: 'view-box',\n title: boxSpec.title,\n subtitle: boxSpec.subtitle,\n dataset: resolvedDataset,\n scenario: resolvedScenario\n }\n}\n\nfunction resolveViewRowForSpec(\n modelOutputs: ModelOutputs,\n resolvedScenarios: ResolvedScenarios,\n rowSpec: ComparisonViewRowSpec\n): ComparisonViewRow | ComparisonUnresolvedViewBox {\n const resolvedBoxes: ComparisonViewBox[] = []\n for (const boxSpec of rowSpec.boxes) {\n const box = resolveViewBoxForSpec(modelOutputs, resolvedScenarios, boxSpec)\n if (box.kind === 'view-box') {\n resolvedBoxes.push(box)\n } else {\n return box\n }\n }\n\n return {\n kind: 'view-row',\n title: rowSpec.title,\n subtitle: rowSpec.subtitle,\n boxes: resolvedBoxes\n }\n}\n\nfunction resolveViewWithRowSpecs(\n modelOutputs: ModelOutputs,\n resolvedScenarios: ResolvedScenarios,\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n rowSpecs: ComparisonViewRowSpec[]\n): ComparisonView | ComparisonUnresolvedView {\n const resolvedRows: ComparisonViewRow[] = []\n for (const rowSpec of rowSpecs) {\n const row = resolveViewRowForSpec(modelOutputs, resolvedScenarios, rowSpec)\n if (row.kind === 'view-row') {\n resolvedRows.push(row)\n } else {\n return unresolvedViewForScenarioId(viewTitle, viewSubtitle, row.scenarioId, row.datasetName, row.datasetSource)\n }\n }\n\n if (viewTitle === undefined) {\n viewTitle = 'Untitled view'\n }\n\n return {\n kind: 'view',\n title: viewTitle,\n subtitle: viewSubtitle,\n rows: resolvedRows,\n // TODO: The schema doesn't currently allow for graphs in a view that is defined\n // with rows. Probably we should have different view types to make this more clear.\n graphIds: [],\n graphOrder: 'default'\n }\n}\n\nfunction unresolvedViewForScenarioId(\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n scenarioId?: ComparisonScenarioId,\n datasetName?: ComparisonDatasetName,\n datasetSource?: ComparisonDatasetSource\n): ComparisonUnresolvedView {\n return {\n kind: 'unresolved-view',\n title: viewTitle,\n subtitle: viewSubtitle,\n scenarioId,\n datasetName,\n datasetSource\n }\n}\n\nfunction unresolvedViewForScenarioGroupId(\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n scenarioGroupId: ComparisonScenarioGroupId\n): ComparisonUnresolvedView {\n return {\n kind: 'unresolved-view',\n title: viewTitle,\n subtitle: viewSubtitle,\n scenarioGroupId\n }\n}\n\n//\n// VIEW GROUPS\n//\n\n/**\n * Return a resolved `ComparisonViewGroup` instance for the given view group spec.\n */\nfunction resolveViewGroupFromSpec(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n modelOutputs: ModelOutputs,\n resolvedScenarios: ResolvedScenarios,\n resolvedScenarioGroups: ResolvedScenarioGroups,\n resolvedGraphGroups: ResolvedGraphGroups,\n viewGroupSpec: ComparisonViewGroupSpec\n): ComparisonViewGroup {\n let views: (ComparisonView | ComparisonUnresolvedView)[]\n\n switch (viewGroupSpec.kind) {\n case 'view-group-with-views': {\n // Resolve each view\n views = viewGroupSpec.views.map(viewSpec => {\n if (viewSpec.scenarioId) {\n let graphIds: ComparisonGraphId[]\n if (viewSpec.graphs) {\n graphIds = resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, viewSpec.graphs)\n } else {\n graphIds = []\n }\n return resolveViewForScenarioId(\n resolvedScenarios,\n viewSpec.title,\n viewSpec.subtitle,\n viewSpec.scenarioId,\n graphIds,\n viewSpec.graphOrder || 'default'\n )\n } else {\n return resolveViewWithRowSpecs(\n modelOutputs,\n resolvedScenarios,\n viewSpec.title,\n viewSpec.subtitle,\n viewSpec.rows\n )\n }\n })\n break\n }\n case 'view-group-with-scenarios': {\n // Resolve to one view for each scenario (with the same set of graphs for each view)\n const graphIds = resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, viewGroupSpec.graphs)\n const graphOrder = viewGroupSpec.graphOrder || 'default'\n views = []\n for (const refSpec of viewGroupSpec.scenarios) {\n switch (refSpec.kind) {\n case 'scenario-ref':\n // Add a view for the scenario\n views.push(resolveViewForScenarioRefSpec(resolvedScenarios, refSpec, graphIds, graphOrder))\n break\n case 'scenario-group-ref': {\n const resolvedGroup = resolvedScenarioGroups.getGroupForId(refSpec.groupId)\n if (resolvedGroup) {\n // Add a view for each scenario in the group\n for (const scenario of resolvedGroup.scenarios) {\n switch (scenario.kind) {\n case 'unresolved-scenario-ref':\n views.push(unresolvedViewForScenarioId(undefined, undefined, scenario.scenarioId))\n break\n case 'scenario':\n views.push(resolveViewForScenario(undefined, undefined, scenario, graphIds, graphOrder))\n break\n default:\n assertNever(scenario)\n }\n }\n } else {\n // Add an unresolved view that covers the whole group\n views.push(unresolvedViewForScenarioGroupId(undefined, undefined, refSpec.groupId))\n }\n break\n }\n default:\n assertNever(refSpec)\n }\n }\n break\n }\n default:\n assertNever(viewGroupSpec)\n }\n\n return {\n kind: 'view-group',\n title: viewGroupSpec.title,\n views\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { ModelSpec } from './bundle-types'\nimport type { InputVar } from './var-types'\n\n/**\n * Wraps a `ModelSpec` to provide easier/faster lookups of input variables by variable\n * name or alias.\n */\nexport class ModelInputs {\n /** All inputs keyed by lookup name (lowercase variable name or alias). */\n private readonly inputsByLookupName: Map<string, InputVar> = new Map()\n\n /** All input ID aliases. */\n private readonly inputIdAliases: Set<string> = new Set()\n\n constructor(modelSpec: ModelSpec) {\n // Add the inputs from the primary map\n for (const inputVar of modelSpec.inputVars.values()) {\n // Add an entry with the input's variable name as the key\n const varNameKey = inputVar.varName.toLowerCase()\n this.inputsByLookupName.set(varNameKey, inputVar)\n\n if (inputVar.inputId) {\n // Also automatically add an entry using the input's ID as an alias. Keep the original\n // input ID for the set of aliases (to preserve original case), but use lowercase for\n // the key, like we do for all other keys.\n const idAlias = `id ${inputVar.inputId}`\n this.inputIdAliases.add(idAlias)\n const idKey = idAlias.toLowerCase()\n this.inputsByLookupName.set(idKey, inputVar)\n }\n }\n\n if (modelSpec.inputAliases) {\n // Add the inputs from the alias map, keyed by alias\n for (const [alias, varId] of modelSpec.inputAliases.entries()) {\n const aliasKey = alias.toLowerCase()\n if (this.inputsByLookupName.has(aliasKey)) {\n console.warn(\n `WARNING: Input variable already defined with a name that collides with alias '${alias}', skipping`\n )\n continue\n }\n\n const inputVar = modelSpec.inputVars.get(varId)\n if (inputVar === undefined) {\n console.warn(`WARNING: No input variable found for '${varId}' associated with alias '${alias}', skipping`)\n continue\n }\n\n this.inputsByLookupName.set(aliasKey, inputVar)\n }\n }\n }\n\n /**\n * Return the set of `id XYZ` aliases for all input variables.\n */\n getAllInputIdAliases(): string[] {\n return [...this.inputIdAliases]\n }\n\n /**\n * Return the `InputVar` that matches the requested name (either by variable name or alias).\n *\n * @param name The variable name or alias to match.\n */\n getInputVarForName(name: string): InputVar | undefined {\n return this.inputsByLookupName.get(name.toLowerCase())\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { InputSetting, ScenarioSpec } from '../../../_shared/scenario-spec-types'\nimport {\n allInputsAtPositionSpec,\n inputSettingsSpec,\n positionSetting,\n valueSetting\n} from '../../../_shared/scenario-specs'\n\nimport type {\n ComparisonScenarioInput,\n ComparisonScenarioInputState,\n ComparisonScenarioSettings\n} from '../../_shared/comparison-resolved-types'\n\n/**\n * Create a pair of `ScenarioSpec` instances that can be used to run each model given a\n * `ComparisonScenarioSettings` object.\n */\nexport function scenarioSpecsFromSettings(\n settings: ComparisonScenarioSettings\n): [ScenarioSpec | undefined, ScenarioSpec | undefined] {\n switch (settings.kind) {\n case 'all-inputs-settings': {\n // In this case, the same `Scenario` can be used on both sides\n const scenario = allInputsAtPositionSpec(settings.position)\n return [scenario, scenario]\n }\n case 'input-settings': {\n // In this case, create separate `Scenario` instances for each side, in case the\n // input variable names are different\n const specL = scenarioSpecFromInputs(settings.inputs, 'left')\n const specR = scenarioSpecFromInputs(settings.inputs, 'right')\n return [specL, specR]\n }\n default:\n assertNever(settings)\n }\n}\n\n/**\n * Create a `ScenarioSpec` instance that can be used to run a specific model for the\n * given input settings.\n */\nfunction scenarioSpecFromInputs(inputs: ComparisonScenarioInput[], side: 'left' | 'right'): ScenarioSpec | undefined {\n const settings: InputSetting[] = []\n\n // Create an `InputSetting` for each input\n for (const input of inputs) {\n const state = side === 'left' ? input.stateL : input.stateR\n\n // If any inputs on this side could not be resolved, return undefined so that we don't try\n // to fetch data for this side\n if (state.inputVar === undefined) {\n return undefined\n }\n\n // Create a scenario setting for this input\n settings.push(inputSettingFromResolvedInputState(state))\n }\n\n return inputSettingsSpec(settings)\n}\n\n/**\n * Create an `InputSetting` instance for the given resolved input that can be used to\n * build a `ScenarioSpec`.\n */\nexport function inputSettingFromResolvedInputState(state: ComparisonScenarioInputState): InputSetting {\n const varId = state.inputVar.varId\n if (state.position) {\n return positionSetting(varId, state.position)\n } else {\n return valueSetting(varId, state.value as number)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { DatasetKey } from '../../_shared/types'\nimport type { BundleGraphId, LoadedBundle, ModelSpec, NamedBundle } from '../../bundle/bundle-types'\n\nimport type { ComparisonDataset, ComparisonScenario, ComparisonViewGroup } from '../_shared/comparison-resolved-types'\n\nimport type { ComparisonDatasets } from './comparison-datasets'\nimport type { ComparisonScenarios } from './comparison-scenarios'\nimport type { ComparisonSpecs, ComparisonSpecsSource } from './comparison-spec-types'\nimport { parseComparisonSpecs } from './parse/comparison-parser'\nimport type { ComparisonResolvedDefs } from './resolve/comparison-resolver'\nimport { resolveComparisonSpecs } from './resolve/comparison-resolver'\n\n/**\n * Describes an extra plot to be shown in a comparison graph.\n */\nexport interface ComparisonPlot {\n /** The dataset key for the plot. */\n datasetKey: DatasetKey\n /** The plot color. */\n color: string\n /** The plot style. If undefined, defaults to 'normal'. */\n style?: 'normal' | 'dashed'\n /** The plot line width, in px units. If undefined, a default width will be used. */\n lineWidth?: number\n}\n\nexport interface ComparisonDatasetOptions {\n /**\n * The mapping of renamed dataset keys (old or \"left\" name as the map key,\n * new or \"right\" name as the value).\n */\n renamedDatasetKeys?: Map<DatasetKey, DatasetKey>\n /**\n * An optional function that allows for limiting the datasets that are compared\n * for a given scenario. By default, all datasets are compared for a given\n * scenario, but if a custom function is provided, it can return a subset of\n * datasets (for example, to omit datasets that are not relevant).\n */\n datasetKeysForScenario?: (allDatasetKeys: DatasetKey[], scenario: ComparisonScenario) => DatasetKey[]\n /**\n * An optional function that allows for including additional reference plots\n * on a comparison graph for a given dataset and scenario. By default, no\n * additional reference plots are included, but if a custom function is\n * provided, it can return an array of `ComparisonPlot` objects.\n */\n referencePlotsForDataset?: (dataset: ComparisonDataset, scenario: ComparisonScenario) => ComparisonPlot[]\n /**\n * An optional function that allows for customizing the set of context graphs\n * that are shown for a given dataset and scenario. By default, all graphs in\n * which the dataset appears will be shown, but if a custom function is provided,\n * it can return a different set of graphs (for example, to omit graphs that are\n * not relevant under the given scenario).\n */\n contextGraphIdsForDataset?: (dataset: ComparisonDataset, scenario: ComparisonScenario) => BundleGraphId[]\n}\n\nexport interface ComparisonOptions {\n /** The left-side (\"baseline\") bundle being compared. */\n baseline: NamedBundle\n /**\n * The array of thresholds used to color differences, e.g., [1, 5, 10] will use\n * buckets of 0%, 0-1%, 1-5%, 5-10%, and >10%.\n */\n thresholds: number[]\n /**\n * The requested comparison scenario and view specifications. These can be\n * specified in YAML or JSON files, or using `Spec` objects.\n */\n specs: (ComparisonSpecs | ComparisonSpecsSource)[]\n /** Optional configuration for the datasets that are compared for different scenarios. */\n datasets?: ComparisonDatasetOptions\n}\n\nexport interface ComparisonConfig {\n /** The loaded left-side (\"baseline\") bundle being compared. */\n bundleL: LoadedBundle\n /** The loaded right-side (\"current\") bundle being compared. */\n bundleR: LoadedBundle\n /**\n * The array of thresholds used to color differences, e.g., [1, 5, 10] will use\n * buckets of 0%, 0-1%, 1-5%, 5-10%, and >10%.\n */\n thresholds: number[]\n /** The set of resolved scenarios that will be compared. */\n scenarios: ComparisonScenarios\n /** The set of resolved datasets that will be compared. */\n datasets: ComparisonDatasets\n /** The set of resolved view groups. */\n viewGroups: ComparisonViewGroup[]\n}\n\n/**\n * Expand and resolve all the scenario and view specs in the provided sources, which can\n * be a mix of YAML, JSON, and object specs.\n *\n * @param modelSpecL The model spec for the \"left\" bundle being compared.\n * @param modelSpecR The model spec for the \"right\" bundle being compared.\n * @param specSources The scenario and view spec sources.\n */\nexport function resolveComparisonSpecsFromSources(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n specSources: (ComparisonSpecs | ComparisonSpecsSource)[]\n): ComparisonResolvedDefs {\n const combinedSpecs: ComparisonSpecs = {\n scenarios: [],\n scenarioGroups: [],\n graphGroups: [],\n viewGroups: []\n }\n\n for (const specSource of specSources) {\n let specs: ComparisonSpecs\n if ('kind' in specSource) {\n const parseResult = parseComparisonSpecs(specSource)\n if (parseResult.isOk()) {\n specs = parseResult.value\n } else {\n // TODO: Fail fast instead of logging errors?\n const filenamePart = specSource.filename ? ` in ${specSource.filename}` : ''\n console.error(`ERROR: Failed to parse comparison spec${filenamePart}, skipping`)\n continue\n }\n } else {\n specs = specSource\n }\n combinedSpecs.scenarios.push(...(specs.scenarios || []))\n combinedSpecs.scenarioGroups.push(...(specs.scenarioGroups || []))\n combinedSpecs.graphGroups.push(...(specs.graphGroups || []))\n combinedSpecs.viewGroups.push(...(specs.viewGroups || []))\n }\n\n return resolveComparisonSpecs(modelSpecL, modelSpecR, combinedSpecs)\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { BundleGraphId, ModelSpec } from '../../bundle/bundle-types'\nimport type { OutputVar } from '../../bundle/var-types'\nimport type { DatasetKey } from '../../_shared/types'\nimport type { ComparisonDataset, ComparisonScenario } from '../_shared/comparison-resolved-types'\nimport type { ComparisonDatasetOptions, ComparisonPlot } from './comparison-config'\n\n/**\n * Provides access to the set of dataset definitions (`ComparisonDataset` instances) that are used\n * when comparing the two models.\n */\nexport interface ComparisonDatasets {\n /**\n * Return all `ComparisonDataset` instances that are available for comparisons.\n */\n getAllDatasets(): IterableIterator<ComparisonDataset>\n\n /**\n * Return the dataset metadata for the given key.\n *\n * @param datasetKey The key for the dataset.\n */\n getDataset(datasetKey: DatasetKey): ComparisonDataset | undefined\n\n /**\n * Return the keys for the datasets that should be compared for the given scenario.\n *\n * @param scenario The scenario definition.\n */\n getDatasetKeysForScenario(scenario: ComparisonScenario): DatasetKey[]\n\n /**\n * Return the reference plots that should be shown in the comparison graph for the\n * given dataset and scenario.\n *\n * @param datasetKey The key for the dataset.\n * @param scenario The scenario for which the dataset will be displayed.\n */\n getReferencePlotsForDataset(datasetKey: DatasetKey, scenario: ComparisonScenario): ComparisonPlot[]\n\n /**\n * Return the context graph IDs that should be shown for the given dataset and scenario.\n *\n * @param datasetKey The key for the dataset.\n * @param scenario The scenario for which the dataset will be displayed.\n */\n getContextGraphIdsForDataset(datasetKey: DatasetKey, scenario: ComparisonScenario): BundleGraphId[]\n}\n\n/**\n * Create an instance of the `ComparisonDatasets` interface that sources the output\n * variables from the given models.\n *\n * @param modelSpecL The model spec for the \"left\" bundle being compared.\n * @param modelSpecR The model spec for the \"right\" bundle being compared.\n * @param datasetOptions The custom configuration for the datasets to be compared.\n */\nexport function getComparisonDatasets(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n datasetOptions?: ComparisonDatasetOptions\n): ComparisonDatasets {\n return new ComparisonDatasetsImpl(modelSpecL, modelSpecR, datasetOptions)\n}\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 */\nclass ComparisonDatasetsImpl implements ComparisonDatasets {\n private readonly allDatasets: Map<DatasetKey, ComparisonDataset>\n private readonly allOutputVarKeys: DatasetKey[]\n private readonly modelOutputVarKeys: DatasetKey[]\n\n /**\n * @param modelSpecL The model spec for the \"left\" bundle being compared.\n * @param modelSpecR The model spec for the \"right\" bundle being compared.\n * @param datasetOptions The custom configuration for the datasets to be compared.\n */\n constructor(\n private readonly modelSpecL: ModelSpec,\n private readonly modelSpecR: ModelSpec,\n private readonly datasetOptions?: ComparisonDatasetOptions\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 = datasetOptions?.renamedDatasetKeys\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(modelSpecL.outputVars, false)\n addOutputVars(modelSpecR.outputVars, true)\n this.allOutputVarKeys = Array.from(allOutputVarKeysSet)\n this.modelOutputVarKeys = Array.from(modelOutputVarKeysSet)\n\n // Create `ComparisonDataset` instances for all available keys\n this.allDatasets = new Map()\n for (const datasetKeyL of this.allOutputVarKeys) {\n const datasetKeyR = renamedDatasetKeys?.get(datasetKeyL) || datasetKeyL\n const outputVarL = modelSpecL.outputVars.get(datasetKeyL)\n const outputVarR = modelSpecR.outputVars.get(datasetKeyR)\n this.allDatasets.set(datasetKeyL, {\n kind: 'dataset',\n key: datasetKeyL,\n outputVarL,\n outputVarR\n })\n }\n }\n\n // from ComparisonDatasets interface\n getAllDatasets(): IterableIterator<ComparisonDataset> {\n return this.allDatasets.values()\n }\n\n // from ComparisonDatasets interface\n getDataset(datasetKey: DatasetKey): ComparisonDataset | undefined {\n return this.allDatasets.get(datasetKey)\n }\n\n // from ComparisonDatasets interface\n getDatasetKeysForScenario(scenario: ComparisonScenario): DatasetKey[] {\n if (this.datasetOptions?.datasetKeysForScenario !== undefined) {\n // Delegate to the custom filter function\n return this.datasetOptions.datasetKeysForScenario(this.allOutputVarKeys, scenario)\n } else {\n // Use the default filtering\n if (scenario.settings.kind === 'all-inputs-settings' && scenario.settings.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\n // from ComparisonDatasets interface\n getReferencePlotsForDataset(datasetKey: DatasetKey, scenario: ComparisonScenario): ComparisonPlot[] {\n if (this.datasetOptions?.referencePlotsForDataset !== undefined) {\n // Delegate to the custom function\n const dataset = this.getDataset(datasetKey)\n if (dataset !== undefined) {\n return this.datasetOptions.referencePlotsForDataset(dataset, scenario)\n }\n }\n return []\n }\n\n // from ComparisonDatasets interface\n getContextGraphIdsForDataset(datasetKey: DatasetKey, scenario: ComparisonScenario): BundleGraphId[] {\n const dataset = this.getDataset(datasetKey)\n if (dataset === undefined) {\n return []\n }\n if (this.datasetOptions?.contextGraphIdsForDataset !== undefined) {\n // Delegate to the custom filter function\n return this.datasetOptions.contextGraphIdsForDataset(dataset, scenario)\n } else {\n // Use the default filtering, which uses the graph specs advertised by the bundles\n // to determine which context graphs are associated with the given dataset\n return getContextGraphIdsForDataset(this.modelSpecL, this.modelSpecR, dataset)\n }\n }\n}\n\nfunction getContextGraphIdsForDataset(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n dataset: ComparisonDataset\n): BundleGraphId[] {\n // Get the union of all graph IDs (appearing in either left or right) in which this\n // dataset appears\n const contextGraphIds: Set<BundleGraphId> = new Set()\n function addGraphs(modelSpec: ModelSpec, outputVar: OutputVar | undefined): void {\n for (const graphSpec of modelSpec.graphSpecs || []) {\n for (const graphDatasetSpec of graphSpec.datasets) {\n if (graphDatasetSpec.datasetKey === outputVar?.datasetKey) {\n contextGraphIds.add(graphSpec.id)\n break\n }\n }\n }\n }\n addGraphs(modelSpecL, dataset.outputVarL)\n addGraphs(modelSpecR, dataset.outputVarR)\n return [...contextGraphIds]\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { ComparisonScenario, ComparisonScenarioKey } from '../_shared/comparison-resolved-types'\n\nexport interface ComparisonScenarios {\n /**\n * Return all `ComparisonScenario` instances that are available for comparisons.\n */\n getAllScenarios(): IterableIterator<ComparisonScenario>\n\n /**\n * Return the scenario definition for the given key.\n *\n * @param key The key for the scenario.\n */\n getScenario(key: ComparisonScenarioKey): ComparisonScenario | undefined\n}\n\n/**\n * Create an instance of the `ComparisonScenarios` interface that uses the given\n * array of `ComparisonScenario` instances.\n *\n * @param scenarios The comparison scenario definitions.\n */\nexport function getComparisonScenarios(scenarios: ComparisonScenario[]): ComparisonScenarios {\n return new ComparisonScenariosImpl(scenarios)\n}\n\n/**\n * Provides access to the set of input scenario definitions (`ComparisonScenario` instances) that are used\n * when comparing the two models.\n */\nclass ComparisonScenariosImpl {\n private readonly scenarioDefs: Map<ComparisonScenarioKey, ComparisonScenario> = new Map()\n\n constructor(scenarios: ComparisonScenario[]) {\n // Create a map to allow for looking up by `ComparisonScenarioKey`\n for (const scenario of scenarios) {\n this.scenarioDefs.set(scenario.key, scenario)\n }\n }\n\n /**\n * Return all `ComparisonScenario` instances that are available for comparisons.\n */\n getAllScenarios(): IterableIterator<ComparisonScenario> {\n return this.scenarioDefs.values()\n }\n\n /**\n * Return the scenario definition for the given key.\n *\n * @param key The key for the scenario.\n */\n getScenario(key: ComparisonScenarioKey): ComparisonScenario | undefined {\n return this.scenarioDefs.get(key)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { ScenarioSpec } from '../_shared/scenario-spec-types'\nimport type { BundleGraphId, BundleModel } from '../bundle/bundle-types'\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: (scenarioSpec: ScenarioSpec, datasetKeys: DatasetKey[]) => {\n return promiseQueue.add(() => sourceModel.getDatasetsForScenario(scenarioSpec, datasetKeys))\n },\n getGraphDataForScenario: (scenarioSpec: ScenarioSpec, graphId: BundleGraphId) => {\n return promiseQueue.add(() => sourceModel.getGraphDataForScenario(scenarioSpec, 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 { ScenarioSpec } from '../_shared/scenario-spec-types'\nimport type { DatasetKey, DatasetMap } from '../_shared/types'\nimport type { BundleModel, LoadedBundle, NamedBundle } from '../bundle/bundle-types'\n\nimport type { CheckConfig } from '../check/check-config'\n\nimport type { ComparisonConfig } from '../comparison/config/comparison-config'\nimport { resolveComparisonSpecsFromSources } from '../comparison/config/comparison-config'\nimport { getComparisonDatasets } from '../comparison/config/comparison-datasets'\nimport { getComparisonScenarios } from '../comparison/config/comparison-scenarios'\n\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 comparisonConfig: ComparisonConfig\n if (options.comparison === 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.comparison.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.comparison.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 (scenarioSpec: ScenarioSpec, 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(scenarioSpec, 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 getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),\n getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)\n }\n currentBundle = {\n ...origCurrentBundle,\n model: adjBundleModelR\n }\n\n // Combine and resolve the provided comparison specifications\n const modelSpecL = baselineBundle.model.modelSpec\n const modelSpecR = currentBundle.model.modelSpec\n const comparisonDefs = resolveComparisonSpecsFromSources(modelSpecL, modelSpecR, options.comparison.specs)\n\n // Initialize the configuration for comparisons\n comparisonConfig = {\n bundleL: baselineBundle,\n bundleR: currentBundle,\n thresholds: options.comparison.thresholds,\n scenarios: getComparisonScenarios(comparisonDefs.scenarios),\n datasets: getComparisonDatasets(modelSpecL, modelSpecR, options.comparison.datasets),\n viewGroups: comparisonDefs.viewGroups\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 comparison: comparisonConfig\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 { assertNever } from 'assert-never'\n\nimport { TaskQueue } from '../_shared/task-queue'\nimport { allInputsAtPositionSpec } from '../_shared/scenario-specs'\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 scenarioSpec = allInputsAtPositionSpec('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(scenarioSpec, [])\n return {\n runTimeL: result.modelRunTime\n }\n }\n case 'right': {\n const result = await bundleModelR.getDatasetsForScenario(scenarioSpec, [])\n return {\n runTimeR: result.modelRunTime\n }\n }\n case 'both': {\n const [resultL, resultR] = await Promise.all([\n bundleModelL.getDatasetsForScenario(scenarioSpec, []),\n bundleModelR.getDatasetsForScenario(scenarioSpec, [])\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 type { ScenarioSpec, ScenarioSpecUid } from '../_shared/scenario-spec-types'\nimport type { Dataset, DatasetKey } from '../_shared/types'\n\nexport interface DatasetPair {\n datasetL?: Dataset\n datasetR?: Dataset\n}\n\n/** A function that performs an action on a pair of fetched datasets. */\nexport type DataAction = (datasets: DatasetPair) => void\n\n/** A task that performs an action on one or both dataset requested from the models. */\nexport interface DataTask {\n /** The key for the dataset to be fetched from each model for a given scenario. */\n datasetKey: DatasetKey\n /** The action to be performed with the fetched datasets. */\n dataAction: DataAction\n}\n\nexport interface DataRequest {\n scenarioSpecL?: ScenarioSpec\n scenarioSpecR?: ScenarioSpec\n dataTasks: DataTask[]\n}\n\nexport interface DataPlan {\n requests: DataRequest[]\n}\n\n/**\n * Tracks all requests for data (from model runs) that are made by \"check\" and/or \"comparison\" tests.\n *\n * A data request can be made for both models (\"left\" AND \"right\", as is the case for most comparison tests),\n * or for just one model (e.g., \"right\" only, as is the case for \"check\" tests, or \"left\" only in the case of\n * comparison tests where an input can only be tested in the \"left\" model).\n *\n * The goal is to take all requests into account to create a plan that requires the least number of model\n * runs. Each request ideally fetches data by running both \"left\" and \"right\" models in parallel. When\n * we build up the list of requests, we track which ones:\n * - access both models (\"LR\" requests)\n * - access left model only (\"L\" requests)\n * - access right model only (\"R\" requests)\n *\n * After all requests have been made, `buildPlan` builds an optimal set of data requests by folding\n * \"L\" and \"R\" requests in with compatible \"LR\" requests. For example, suppose we have one \"check\" test\n * (that only needs the \"right\" model), and one \"comparison\" test (that needs both \"left\" and \"right\"),\n * and both tests are exercising the same input scenario:\n *\n * request1: { scenarioL: undefined, scenarioR: scenario1, dataTasks: [task1] }\n * request2: { scenarioL: scenario1, scenarioR: scenario1, dataTasks: [task2] }\n *\n * In this case, we can merge them into a single request that runs the models once in parallel:\n *\n * requestM: { scenarioL: scenario1, scenarioR: scenario1, dataTasks: [task1, task2] }\n */\nexport class DataPlanner {\n /** Tasks that need to access both \"left\" and \"right\" models in parallel. */\n private readonly taskSetsLR: Map<ScenarioPairUid, DataTaskSet> = new Map()\n\n /** Tasks that only need to access the \"left\" model. */\n private readonly taskSetsL: Map<ScenarioSpecUid, DataTaskSet> = new Map()\n\n /** Tasks that only need to access the \"right\" model. */\n private readonly taskSetsR: Map<ScenarioSpecUid, DataTaskSet> = new Map()\n\n private complete = false\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 request to the plan for the given scenario(s) and data task.\n *\n * @param scenarioSpecL The input scenario used to configure the \"left\" model, or undefined if no data\n * is needed from the left model.\n * @param scenarioSpecR The input scenario used to configure the \"right\" model, or undefined if no data\n * is needed from the right model.\n * @param datasetKey The key for the dataset to be fetched from each model for the given scenario.\n * @param dataAction The action to be performed with the fetched datasets.\n */\n addRequest(\n scenarioSpecL: ScenarioSpec | undefined,\n scenarioSpecR: ScenarioSpec | undefined,\n datasetKey: DatasetKey,\n dataAction: DataAction\n ): void {\n if (scenarioSpecL === undefined && scenarioSpecR === undefined) {\n console.warn('WARNING: Both scenario specs are undefined for DataPlanner request, skipping')\n return\n }\n\n // Determine which set this request will be added to\n let taskSetsMap: Map<ScenarioPairUid, DataTaskSet>\n let uid: string\n if (scenarioSpecL && scenarioSpecR) {\n taskSetsMap = this.taskSetsLR\n uid = scenarioPairUid(scenarioSpecL, scenarioSpecR)\n } else if (scenarioSpecR) {\n taskSetsMap = this.taskSetsR\n uid = scenarioSpecR.uid\n } else {\n taskSetsMap = this.taskSetsL\n uid = scenarioSpecL.uid\n }\n\n // Add the task to the appropriate task set (creating a new set if needed)\n let taskSet = taskSetsMap.get(uid)\n if (!taskSet) {\n taskSet = new DataTaskSet(scenarioSpecL, scenarioSpecR)\n taskSetsMap.set(uid, taskSet)\n }\n taskSet.addTask({\n datasetKey,\n dataAction\n })\n }\n\n /**\n * Build a plan that minimizes the number of data fetches needed.\n */\n buildPlan(): DataPlan {\n if (this.complete) {\n throw new Error('DataPlanner.buildPlan() can only be called once')\n }\n this.complete = true\n\n // Create mappings to make it easy to look up \"LR\" task sets using only\n // an \"L\" or \"R\" uid. This will create a mapping to the first available\n // set with an \"L\" uid on the left side (in case there are multiple with\n // the same \"L\" uid but different \"R\" uids), and same approach for \"R\" uids.\n const lKeyMappings: Map<ScenarioSpecUid, ScenarioPairUid> = new Map()\n const rKeyMappings: Map<ScenarioSpecUid, ScenarioPairUid> = new Map()\n for (const lrUid of this.taskSetsLR.keys()) {\n const [lUid, rUid] = lrUid.split('::')\n if (!lKeyMappings.has(lUid)) {\n lKeyMappings.set(lUid, lrUid)\n }\n if (!rKeyMappings.has(rUid)) {\n rKeyMappings.set(rUid, lrUid)\n }\n }\n\n // See if we can fold \"L-only\" and \"R-only\" requests into an existing \"LR\" set\n function merge(\n taskSetsLR: Map<ScenarioPairUid, DataTaskSet>,\n taskSetsForSide: Map<ScenarioSpecUid, DataTaskSet>,\n keyMappingsForSide: Map<ScenarioSpecUid, ScenarioPairUid>\n ) {\n for (const [keyForSide, taskSetForSide] of taskSetsForSide.entries()) {\n const lrKey = keyMappingsForSide.get(keyForSide)\n if (lrKey) {\n // Fold the one-side-only requests into an existing \"LR\" set\n const taskSetLR = taskSetsLR.get(lrKey)\n taskSetLR.merge(taskSetForSide)\n\n // Remove from the one-side-only set\n taskSetsForSide.delete(keyForSide)\n }\n }\n }\n merge(this.taskSetsLR, this.taskSetsL, lKeyMappings)\n merge(this.taskSetsLR, this.taskSetsR, rKeyMappings)\n\n // Build the final array of requests\n const requests: DataRequest[] = []\n const batchSize = this.batchSize\n function addRequests(taskSets: IterableIterator<DataTaskSet>) {\n for (const taskSet of taskSets) {\n requests.push(...taskSet.buildRequests(batchSize))\n }\n }\n addRequests(this.taskSetsLR.values())\n addRequests(this.taskSetsL.values())\n addRequests(this.taskSetsR.values())\n\n return {\n requests\n }\n }\n}\n\n/**\n * Groups together all data tasks that are associated with a pair of scenarios.\n */\nclass DataTaskSet {\n private readonly modelTasks: Map<DatasetKey, DataTask[]> = new Map()\n private readonly modelImplTasks: Map<DatasetKey, DataTask[]> = new Map()\n\n constructor(\n private readonly scenarioSpecL: ScenarioSpec | undefined,\n private readonly scenarioSpecR: ScenarioSpec | undefined\n ) {}\n\n /**\n * Add a task that will be performed when the data is fetched for the scenario(s)\n * associated with this task set.\n *\n * @param dataTask A task that performs an action using the fetched dataset(s).\n */\n addTask(dataTask: DataTask): void {\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 (dataTask.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(dataTask.datasetKey)\n if (!tasks) {\n tasks = []\n taskMap.set(dataTask.datasetKey, tasks)\n }\n tasks.push(dataTask)\n }\n\n /**\n * Add all tasks from the given set into this set.\n *\n * @param otherTaskSet The other task set that will be merged into this one.\n */\n merge(otherTaskSet: DataTaskSet): void {\n for (const tasks of otherTaskSet.modelTasks.values()) {\n for (const task of tasks) {\n this.addTask(task)\n }\n }\n for (const tasks of otherTaskSet.modelImplTasks.values()) {\n for (const task of tasks) {\n this.addTask(task)\n }\n }\n }\n\n /**\n * Create one or more data requests that can be used to fetch data for the configured scenario(s).\n *\n * @param batchSize The maximum number of impl vars that can be fetched with a single request.\n * This is usually the same as the number of 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 scenarioSpecL: this.scenarioSpecL,\n scenarioSpecR: this.scenarioSpecR,\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 scenarioSpecL: this.scenarioSpecL,\n scenarioSpecR: this.scenarioSpecR,\n dataTasks\n })\n }\n }\n\n return dataRequests\n }\n}\n\n/**\n * An ID that includes one or two scenario UIDs to uniquely identify a pair of scenarios.\n * The format is `<left_uid>::<right_uid>`.\n */\ntype ScenarioPairUid = string\n\n/**\n * Create a unique identifier for the given scenarios.\n *\n * For example, a request that includes scenarios for both left and right might look like:\n * scenario1::scenario1\n *\n * A request that only accesses the right model might use a key like:\n * ::scenario1\n *\n * A request that only accesses the left model might use a key like:\n * scenario1::\n *\n * @param scenarioSpecL The input scenario used to configure the \"left\" model, or undefined if no data\n * is needed from the left model.\n * @param scenarioSpecR The input scenario used to configure the \"right\" model, or undefined if no data\n * is needed from the right model.\n */\nfunction scenarioPairUid(\n scenarioSpecL: ScenarioSpec | undefined,\n scenarioSpecR: ScenarioSpec | undefined\n): ScenarioPairUid {\n const uidL = scenarioSpecL?.uid || ''\n const uidR = scenarioSpecR?.uid || ''\n return `${uidL}::${uidR}`\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(undefined, dataRef.scenario.spec, 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(undefined, checkTask.scenario.spec, 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.spec === 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 type { DataPlanner } from '../../data/data-planner'\nimport type { ComparisonConfig } from '../config/comparison-config'\nimport { diffDatasets } from '../diff-datasets/diff-datasets'\nimport type { ComparisonTestReport } from '../report/comparison-report-types'\n\n/**\n * Prepare all comparison tests and add them to the given data planner.\n *\n * @param comparisonConfig The comparison configuration.\n * @param dataPlanner The planner that will plan out data fetches for the compare tests.\n * @return A function that will build the compare reports after the data requests are all processed.\n */\nexport function runComparisons(\n comparisonConfig: ComparisonConfig,\n dataPlanner: DataPlanner\n): () => ComparisonTestReport[] {\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 testReports: ComparisonTestReport[] = []\n for (const scenario of comparisonConfig.scenarios.getAllScenarios()) {\n // Get the keys of the datasets of interest for this scenario\n const datasetKeys = comparisonConfig.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(scenario.specL, scenario.specR, datasetKey, datasets => {\n // Diff the two datasets\n const diffReport = diffDatasets(datasets.datasetL, datasets.datasetR)\n testReports.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 test 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 testReports\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { DatasetsResult } from '../_shared/data-source'\nimport type { ScenarioSpec } from '../_shared/scenario-spec-types'\nimport { TaskQueue } from '../_shared/task-queue'\nimport type { DatasetKey } from '../_shared/types'\n\nimport type { BundleModel } from '../bundle/bundle-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 { runComparisons } from '../comparison/run/comparison-runner'\nimport type { ComparisonReport, ComparisonTestReport } from '../comparison/report/comparison-report-types'\n\nimport type { Config } from '../config/config-types'\n\nimport { PerfStats } from '../perf/perf-stats'\n\nimport type { SuiteReport } from './suite-report-types'\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 buildComparisonTestReports: () => ComparisonTestReport[]\n if (this.config.comparison) {\n buildComparisonTestReports = runComparisons(this.config.comparison, dataPlanner)\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 comparisonReport: ComparisonReport\n if (this.config.comparison) {\n comparisonReport = {\n testReports: buildComparisonTestReports(),\n perfReportL: this.perfStatsL.toReport(),\n perfReportR: this.perfStatsR.toReport()\n }\n }\n this.callbacks.onComplete?.({\n checkReport,\n comparisonReport\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 comparisonReport: ComparisonReport\n if (this.config.comparison) {\n comparisonReport = {\n testReports: [],\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 comparisonReport\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 async function getDatasets(\n bundleModel: BundleModel | undefined,\n scenarioSpec: ScenarioSpec | undefined\n ): Promise<DatasetsResult> {\n if (bundleModel && scenarioSpec) {\n return bundleModel.getDatasetsForScenario(scenarioSpec, datasetKeys)\n } else {\n return undefined\n }\n }\n\n // Run the model(s) in parallel and extract the requested datasets\n const bundleModelL = this.config.comparison?.bundleL.model\n const bundleModelR = this.config.comparison?.bundleR.model || this.config.check.bundle.model\n const [datasetsResultL, datasetsResultR] = await Promise.all([\n getDatasets(bundleModelL, request.scenarioSpecL),\n getDatasets(bundleModelR, request.scenarioSpecR)\n ])\n\n // Update the performance stats\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.dataAction({\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) 2023 Climate Interactive / New Venture Fund\n\nimport { checkSummaryFromReport } from '../check/check-summary'\n\nimport type { ComparisonSummary } from '../comparison/report/comparison-report-types'\nimport { comparisonSummaryFromReport } from '../comparison/report/comparison-reporting'\n\nimport type { SuiteReport, SuiteSummary } from './suite-report-types'\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 comparison report to terse summaries\n let comparisonSummary: ComparisonSummary\n if (suiteReport.comparisonReport) {\n comparisonSummary = comparisonSummaryFromReport(suiteReport.comparisonReport)\n }\n\n return {\n checkSummary,\n comparisonSummary\n }\n}\n"],"mappings":"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","names":["import_assert_never","assertNever","assertNever","import_assert_never","Ajv","yaml","import_assert_never","import_assert_never","import_assert_never","assertNever","inputVar","assertNever","import_assert_never","import_assert_never","import_ajv","import_assert_never","import_neverthrow","import_yaml","Ajv","yaml","assertNever","import_assert_never","import_assert_never","inputPosition","inputValueAtPosition","import_assert_never","buildCheckReport","_a","_b","_c","_d"]}
1
+ {"version":3,"sources":["../src/index.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/_shared/scenario-specs.ts","../src/comparison/run/comparison-data-coordinator.ts","../src/comparison/diff-datasets/diff-datasets.ts","../src/comparison/diff-graphs/diff-graphs.ts","../src/comparison/report/comparison-reporting.ts","../src/comparison/report/buckets.ts","../src/comparison/report/comparison-group-scores.ts","../src/comparison/report/comparison-grouping.ts","../src/comparison/config/parse/comparison-parser.ts","../src/comparison/config/parse/comparison.schema.js","../src/comparison/config/resolve/comparison-resolver.ts","../src/bundle/model-inputs.ts","../src/comparison/config/resolve/comparison-scenario-specs.ts","../src/comparison/config/comparison-config.ts","../src/comparison/config/comparison-datasets.ts","../src/comparison/config/comparison-scenarios.ts","../src/config/synchronized-model.ts","../src/config/config.ts","../src/perf/perf-runner.ts","../src/perf/perf-stats.ts","../src/data/data-planner.ts","../src/check/check-runner.ts","../src/comparison/run/comparison-runner.ts","../src/suite/suite-runner.ts","../src/suite/suite-reporting.ts"],"sourcesContent":["// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\n//\n// _shared\n//\n\nexport * from './_shared/data-source'\nexport * from './_shared/scenario-spec-types'\nexport * from './_shared/types'\n\n//\n// bundle\n//\n\nexport type {\n Bundle,\n BundleGraphData,\n BundleGraphDatasetSpec,\n BundleGraphId,\n BundleGraphSpec,\n BundleGraphView,\n BundleModel,\n DatasetGroupName,\n InputAliasName,\n InputGroupName,\n InputSettingGroupId,\n LegendItem,\n LinkItem,\n LoadedBundle,\n ModelSpec,\n NamedBundle\n} from './bundle/bundle-types'\n\nexport type { Dimension, ImplVar, InputId, InputVar, OutputVar, RelatedItem, Subscript } from './bundle/var-types'\n\n//\n// check\n//\n\nexport type { CheckDataRequestKey } from './check/check-data-coordinator'\nexport { CheckDataCoordinator } from './check/check-data-coordinator'\n\nexport type { CheckResult, CheckResultErrorInfo } from './check/check-func'\n\nexport type { CheckKey } from './check/check-planner'\n\nexport type { CheckPredicateOp } from './check/check-predicate'\n\nexport type {\n CheckDatasetReport,\n CheckGroupReport,\n CheckPredicateOpConstantRef,\n CheckPredicateOpDataRef,\n CheckPredicateOpRef,\n CheckPredicateReport,\n CheckReport,\n CheckScenarioReport,\n CheckStatus,\n CheckTestReport\n} from './check/check-report'\nexport { scenarioMessage, datasetMessage, predicateMessage } from './check/check-report'\n\nexport type { CheckScenario, CheckScenarioError, CheckScenarioInputDesc } from './check/check-scenario'\n\nexport type {\n CheckPredicateTimeOptions,\n CheckPredicateTimeRange,\n CheckPredicateTimeSingle,\n CheckPredicateTimeSpec\n} from './check/check-spec'\n\nexport type { CheckPredicateSummary, CheckSummary } from './check/check-summary'\nexport { checkSummaryFromReport, checkReportFromSummary } from './check/check-summary'\n\n//\n// comparison\n//\n\nexport * from './comparison/_shared/comparison-resolved-types'\n\nexport * from './comparison/config/comparison-spec-types'\nexport type {\n ComparisonConfig,\n ComparisonDatasetOptions,\n ComparisonOptions,\n ComparisonPlot,\n ComparisonReportDetailItem,\n ComparisonReportDetailRow,\n ComparisonReportOptions,\n ComparisonReportSummaryRow,\n ComparisonReportSummarySection\n} from './comparison/config/comparison-config'\nexport type { ComparisonScenarios } from './comparison/config/comparison-scenarios'\nexport type { ComparisonDatasets } from './comparison/config/comparison-datasets'\n\nexport type { ComparisonDataRequestKey } from './comparison/run/comparison-data-coordinator'\nexport { ComparisonDataCoordinator } from './comparison/run/comparison-data-coordinator'\n\nexport * from './comparison/diff-datasets/diff-datasets'\n\nexport * from './comparison/diff-graphs/diff-graphs'\n\nexport * from './comparison/report/comparison-report-types'\nexport { comparisonSummaryFromReport } from './comparison/report/comparison-reporting'\n\nexport * from './comparison/report/comparison-group-types'\nexport * from './comparison/report/comparison-group-scores'\nexport { categorizeComparisonTestSummaries } from './comparison/report/comparison-grouping'\n\n//\n// config\n//\n\nexport type { Config, ConfigInitOptions, ConfigOptions } from './config/config-types'\n\nexport { createConfig } from './config/config'\n\n//\n// perf\n//\n\nexport { PerfRunner } from './perf/perf-runner'\n\nexport type { PerfReport } from './perf/perf-stats'\nexport { PerfStats } from './perf/perf-stats'\n\n//\n// suite\n//\n\nexport type { RunSuiteCallbacks, RunSuiteOptions } from './suite/suite-runner'\nexport { runSuite } from './suite/suite-runner'\n\nexport * from './suite/suite-report-types'\n\nexport { suiteSummaryFromReport } from './suite/suite-reporting'\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 { ScenarioSpec } from '../_shared/scenario-spec-types'\nimport type { Dataset, DatasetKey } from '../_shared/types'\nimport type { BundleModel } from '../bundle/bundle-types'\n\nexport type CheckDataRequestKey = string\n\ninterface DataRequest {\n scenarioSpec: ScenarioSpec\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.scenarioSpec, [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 scenarioSpec: ScenarioSpec,\n datasetKey: DatasetKey,\n onResponse: (dataset: Dataset) => void\n ): void {\n const request: DataRequest = {\n scenarioSpec,\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'\n\nimport type { InputPosition } from '../_shared/scenario-spec-types'\n\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.spec === 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 refScenarioSpec = dataRef.scenario?.spec\n if (!refScenarioSpec) {\n return\n }\n if (predOp.scenario === 'inherit') {\n opValue += ` (w/ same scenario)`\n } else {\n if (refScenarioSpec.kind === 'all-inputs' && refScenarioSpec.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.spec === 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.spec.kind === 'all-inputs') {\n // This is an \"all inputs\" scenario\n const position = checkScenario.spec.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 `ScenarioSpec` kind for the \"all inputs\n // in group\" case, so we use a multi-setting `ScenarioSpec`; therefore we have to\n // dig out the position from the first setting\n let position: InputPosition = 'at-default'\n if (checkScenario.spec.settings[0].kind === 'position') {\n position = checkScenario.spec.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 * @return The converted check report.\n */\nexport function checkReportFromSummary(checkConfig: CheckConfig, checkSummary: CheckSummary): 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, false)\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 check definitions'\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.spec === 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.spec && refDataset.datasetKey) {\n dataRefKey = `${refScenario.spec.uid}::${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 let matchedGroupName: string\n let matchedGroupDatasetKeys: DatasetKey[]\n if (modelSpec.datasetGroups) {\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, datasetKeys] of modelSpec.datasetGroups) {\n if (group.toLowerCase() === groupToMatch) {\n matchedGroupName = group\n matchedGroupDatasetKeys = datasetKeys\n break\n }\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, ScenarioSpec } from '../_shared/scenario-spec-types'\nimport {\n allInputsAtPositionSpec,\n inputAtPositionSpec,\n inputSettingsSpec,\n positionSetting,\n valueSetting\n} from '../_shared/scenario-specs'\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 spec used to configure the model with the matched input(s); can be undefined if input(s) failed to match. */\n spec?: ScenarioSpec\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 // TODO: Use ModelInputs here\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 if (modelSpec.inputGroups) {\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\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 return {\n spec: allInputsAtPositionSpec(position),\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 return {\n spec: inputAtPositionSpec(varId, position),\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 spec: ScenarioSpec\n if (inputDescs.every(desc => desc.inputVar !== undefined)) {\n // All inputs were resolved, so create a `ScenarioSpec` that includes them all\n const settings = inputDescs.map(inputDesc => {\n const varId = inputDesc.inputVar.varId\n if (inputDesc.position) {\n return positionSetting(varId, inputDesc.position)\n } else {\n return valueSetting(varId, inputDesc.value)\n }\n })\n spec = inputSettingsSpec(settings)\n } else {\n // One or more inputs could not be resolved; leave `spec` undefined\n // so that we can report the error later\n spec = undefined\n }\n\n return {\n spec,\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 type { InputPosition, InputSetting, ScenarioSpec } from './scenario-spec-types'\nimport type { VarId } from './types'\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 inputSettingsSpec(settings: InputSetting[]): ScenarioSpec {\n const uidParts = settings.map(setting => {\n switch (setting.kind) {\n case 'position':\n return `${setting.inputVarId}_at_${keyForInputPosition(setting.position)}`\n case 'value':\n return `${setting.inputVarId}_at_${setting.value}`\n default:\n assertNever(setting)\n }\n })\n // TODO: Ideally we would reduce the long UID to a short hash here when there are many settings\n const uid = `inputs_${uidParts.sort().join('_')}`\n\n return {\n kind: 'input-settings',\n uid,\n settings\n }\n}\n\nexport function inputAtPositionSpec(inputVarId: VarId, position: InputPosition): ScenarioSpec {\n return inputSettingsSpec([positionSetting(inputVarId, position)])\n}\n\nexport function inputAtValueSpec(inputVarId: VarId, value: number): ScenarioSpec {\n return inputSettingsSpec([valueSetting(inputVarId, value)])\n}\n\nexport function allInputsAtPositionSpec(position: InputPosition): ScenarioSpec {\n return {\n kind: 'all-inputs',\n uid: `all_inputs_at_${keyForInputPosition(position)}`,\n position\n }\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","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { ScenarioSpec } from '../../_shared/scenario-spec-types'\nimport { TaskQueue } from '../../_shared/task-queue'\nimport type { DatasetKey, DatasetMap } from '../../_shared/types'\n\nimport type { BundleModel, BundleGraphData, BundleGraphId } from '../../bundle/bundle-types'\nimport type { DatasetsResult } from '../../_shared/data-source'\n\nexport type ComparisonDataRequestKey = string\n\ninterface DatasetRequest {\n kind: 'dataset'\n scenarioSpecL?: ScenarioSpec\n scenarioSpecR?: ScenarioSpec\n datasetKeys: DatasetKey[]\n}\n\ninterface GraphDataRequest {\n kind: 'graph-data'\n scenarioSpecL?: ScenarioSpec\n scenarioSpecR?: ScenarioSpec\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 graphDataL?: BundleGraphData\n graphDataR?: BundleGraphData\n}\n\ntype DataResponse = DatasetResponse | GraphDataResponse\n\n/**\n * Coordinates loading of data in parallel from two models.\n */\nexport class ComparisonDataCoordinator {\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 // Run the models in parallel\n switch (request.kind) {\n case 'dataset':\n return this.processDatasetRequest(request)\n case 'graph-data':\n return this.processGraphDataRequest(request)\n default:\n assertNever(request)\n }\n }\n })\n }\n\n private async processDatasetRequest(request: DatasetRequest): Promise<DatasetResponse> {\n // Helper function that fetches data from a particular model\n async function fetchDatasets(\n bundleModel: BundleModel,\n scenarioSpec: ScenarioSpec | undefined\n ): Promise<DatasetsResult> {\n if (scenarioSpec) {\n return bundleModel.getDatasetsForScenario(scenarioSpec, request.datasetKeys)\n } else {\n return undefined\n }\n }\n\n // Run the model(s) in parallel and extract the requested datasets\n const [resultL, resultR] = await Promise.all([\n fetchDatasets(this.bundleModelL, request.scenarioSpecL),\n fetchDatasets(this.bundleModelR, request.scenarioSpecR)\n ])\n\n return {\n kind: 'dataset',\n datasetMapL: resultL?.datasetMap,\n datasetMapR: resultR?.datasetMap\n }\n }\n\n private async processGraphDataRequest(request: GraphDataRequest): Promise<GraphDataResponse> {\n // Helper function that fetches data from a particular model\n async function fetchGraphData(\n bundleModel: BundleModel,\n scenarioSpec: ScenarioSpec | undefined\n ): Promise<BundleGraphData> {\n if (scenarioSpec) {\n return bundleModel.getGraphDataForScenario(scenarioSpec, request.graphId)\n } else {\n return undefined\n }\n }\n\n // Run the model(s) in parallel and extract the requested graph data\n const [graphDataL, graphDataR] = await Promise.all([\n fetchGraphData(this.bundleModelL, request.scenarioSpecL),\n fetchGraphData(this.bundleModelR, request.scenarioSpecR)\n ])\n\n return {\n kind: 'graph-data',\n graphDataL,\n graphDataR\n }\n }\n\n requestDatasetMaps(\n requestKey: ComparisonDataRequestKey,\n scenarioSpecL: ScenarioSpec,\n scenarioSpecR: ScenarioSpec,\n datasetKeys: DatasetKey[],\n onResponse: (datasetMapL?: DatasetMap, datasetMapR?: DatasetMap) => void\n ): void {\n const request: DatasetRequest = {\n kind: 'dataset',\n scenarioSpecL,\n scenarioSpecR,\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: ComparisonDataRequestKey,\n scenarioSpecL: ScenarioSpec,\n scenarioSpecR: ScenarioSpec,\n graphId: BundleGraphId,\n onResponse: (graphDataL?: BundleGraphData, graphDataR?: BundleGraphData) => void\n ): void {\n const request: GraphDataRequest = {\n kind: 'graph-data',\n scenarioSpecL,\n scenarioSpecR,\n graphId\n }\n this.taskQueue.addTask(requestKey, request, response => {\n if (response.kind === 'graph-data') {\n onResponse(response.graphDataL, response.graphDataR)\n }\n })\n }\n\n cancelRequest(key: ComparisonDataRequestKey): void {\n this.taskQueue.cancelTask(key)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { Dataset } from '../../_shared/types'\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","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { DatasetKey } from '../../_shared/types'\nimport type { BundleGraphSpec } from '../../bundle/bundle-types'\nimport type { ComparisonScenarioKey } from '../_shared/comparison-resolved-types'\nimport type { ComparisonTestSummary } from '../report/comparison-report-types'\n\nexport type GraphInclusion = 'neither' | 'left-only' | 'right-only' | 'both'\n\nexport interface GraphComparisonMetadataReport {\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 GraphComparisonDatasetReport {\n /** The dataset key. */\n datasetKey: DatasetKey\n /** The max diff for this dataset. */\n maxDiff?: number\n}\n\nexport interface GraphComparisonReport {\n /** Indicates which bundles the graph is defined in. */\n inclusion: GraphInclusion\n /** The metadata fields with differences. */\n metadataReports: GraphComparisonMetadataReport[]\n /** The datasets with differences. */\n datasetReports: GraphComparisonDatasetReport[]\n}\n\n/**\n * Comparison 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 key of the scenario used for comparing datasets.\n * @param testSummaries The set of test summaries from a previous comparison run.\n */\nexport function diffGraphs(\n graphL: BundleGraphSpec | undefined,\n graphR: BundleGraphSpec | undefined,\n scenarioKey: ComparisonScenarioKey,\n testSummaries: ComparisonTestSummary[]\n): GraphComparisonReport {\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 // Comparison the metadata for the two graphs\n const metadataReports: GraphComparisonMetadataReport[] = []\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 // Comparison the datasets for the two graphs\n const datasetReports: GraphComparisonDatasetReport[] = []\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 testSummary = testSummaries.find(summary => summary.d === datasetKey && summary.s === scenarioKey)\n // TODO: Flag as an error if we don't have a ComparisonTestSummary\n // for the datasets?\n const maxDiff = testSummary?.md\n datasetReports.push({\n datasetKey,\n maxDiff\n })\n }\n }\n\n return {\n inclusion,\n metadataReports,\n datasetReports\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { ComparisonConfig } from '../config/comparison-config'\nimport type { ComparisonReport, ComparisonSummary, ComparisonTestSummary } from './comparison-report-types'\n\n/**\n * Convert a full `ComparisonReport` to a simplified `ComparisonSummary` that includes\n * the minimum set of fields needed to keep the file smaller when there are many\n * reported differences. This only includes comparison results for which there\n * is a non-zero `maxDiff` value.\n *\n * @param comparisonReport The full comparison report.\n * @return The terse summary.\n */\nexport function comparisonSummaryFromReport(comparisonReport: ComparisonReport): ComparisonSummary {\n const terseSummaries: ComparisonTestSummary[] = []\n\n for (const r of comparisonReport.testReports) {\n if (r.diffReport.validity === 'both' && r.diffReport.maxDiff > 0) {\n terseSummaries.push({\n s: r.scenarioKey,\n d: r.datasetKey,\n md: r.diffReport.maxDiff\n })\n }\n }\n\n return {\n testSummaries: terseSummaries,\n perfReportL: comparisonReport.perfReportL,\n perfReportR: comparisonReport.perfReportR\n }\n}\n\n/**\n * Restore the full set of summaries that was generated by `runComparison` (one summary\n * for each scenario/dataset pairing based on the `ComparisonConfig`).\n *\n * @param comparisonConfig The config that contains the scenarios used to reconstruct the\n * full set of summaries.\n * @param terseSummaries The set of terse summaries for comparisons that produced a\n * non-zero `maxDiff` result.\n */\nexport function restoreFromTerseSummaries(\n comparisonConfig: ComparisonConfig,\n terseSummaries: ComparisonTestSummary[]\n): ComparisonTestSummary[] {\n // Put the provided summaries in a map for faster lookup\n const existingSummaries: Map<string, ComparisonTestSummary> = new Map()\n for (const summary of terseSummaries) {\n const key = `${summary.s}::${summary.d}`\n existingSummaries.set(key, summary)\n }\n\n // Get the full set of scenario/dataset pairs\n const allTestSummaries: ComparisonTestSummary[] = []\n for (const scenario of comparisonConfig.scenarios.getAllScenarios()) {\n const datasetKeys = comparisonConfig.datasets.getDatasetKeysForScenario(scenario)\n for (const datasetKey of datasetKeys) {\n // If we have a summary in the array that was passed in, it means\n // the `maxDiff` was non-zero, so include that value, otherwise\n // assume zero\n const key = `${scenario.key}::${datasetKey}`\n const existingSummary = existingSummaries.get(key)\n const maxDiff = existingSummary?.md || 0\n allTestSummaries.push({\n s: scenario.key,\n d: datasetKey,\n md: maxDiff\n })\n }\n }\n\n return allTestSummaries\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nexport function getBucketIndex(diffPct: number, thresholds: number[]): number {\n // When there are no differences, put it in the first (green) bucket\n if (diffPct === 0) {\n return 0\n }\n\n // Otherwise, test the given diff value against each threshold (using\n // strict \"less than\" comparison)\n for (let i = 0; i < thresholds.length; i++) {\n if (diffPct < thresholds[i]) {\n return i + 1\n }\n }\n\n // When the difference is greater than or equal to the last threshold,\n // put it in the final bucket\n return thresholds.length + 1\n}\n","// Copyright (c) 2024 Climate Interactive / New Venture Fund\n\nimport { getBucketIndex } from './buckets'\nimport type { ComparisonGroupScores } from './comparison-group-types'\nimport type { ComparisonTestSummary } from './comparison-report-types'\n\n/**\n * Compute the overall scores for the given group of comparison test summaries.\n *\n * @param testSummaries The comparison test summaries to consider.\n * @param thresholds The array of thresholds that determine the buckets into which\n * the scores will be summarized.\n */\nexport function getScoresForTestSummaries(\n testSummaries: ComparisonTestSummary[],\n thresholds: number[]\n): ComparisonGroupScores {\n // Add up scores and group them into buckets\n const diffCountByBucket = Array(thresholds.length + 2).fill(0)\n const totalMaxDiffByBucket = Array(thresholds.length + 2).fill(0)\n let totalDiffCount = 0\n for (const testSummary of testSummaries) {\n const bucketIndex = getBucketIndex(testSummary.md, thresholds)\n diffCountByBucket[bucketIndex]++\n totalMaxDiffByBucket[bucketIndex] += testSummary.md\n totalDiffCount++\n }\n\n // Get the percentage of diffs for each bucket relative to the total number\n // of comparisons in the given set\n let diffPercentByBucket: number[]\n if (totalDiffCount > 0) {\n diffPercentByBucket = diffCountByBucket.map(count => (count / totalDiffCount) * 100)\n } else {\n diffPercentByBucket = []\n }\n\n return {\n totalDiffCount,\n totalMaxDiffByBucket,\n diffCountByBucket,\n diffPercentByBucket\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { ComparisonConfig } from '../config/comparison-config'\nimport type { ComparisonDataset, ComparisonScenario } from '../_shared/comparison-resolved-types'\n\nimport type {\n ComparisonCategorizedResults,\n ComparisonGroup,\n ComparisonGroupKey,\n ComparisonGroupKind,\n ComparisonGroupScores,\n ComparisonGroupSummariesByCategory,\n ComparisonGroupSummary\n} from './comparison-group-types'\nimport type { ComparisonTestSummary } from './comparison-report-types'\nimport { restoreFromTerseSummaries } from './comparison-reporting'\nimport { getScoresForTestSummaries } from './comparison-group-scores'\n\n/**\n * Given a set of terse test summaries (which only includes summaries for tests with non-zero `maxDiff`\n * scores), restore the full set of summaries and then categorize them.\n *\n * @param comparisonConfig The comparison configuration.\n * @param terseSummaries The set of terse test summaries.\n */\nexport function categorizeComparisonTestSummaries(\n comparisonConfig: ComparisonConfig,\n terseSummaries: ComparisonTestSummary[]\n): ComparisonCategorizedResults {\n // Restore the full set of test results\n const allTestSummaries = restoreFromTerseSummaries(comparisonConfig, terseSummaries)\n\n // Categorize the results by scenario\n const groupsByScenario = groupComparisonTestSummaries(allTestSummaries, 'by-scenario')\n const byScenario = categorizeComparisonGroups(comparisonConfig, [...groupsByScenario.values()])\n\n // Categorize the results by dataset\n const groupsByDataset = groupComparisonTestSummaries(allTestSummaries, 'by-dataset')\n const byDataset = categorizeComparisonGroups(comparisonConfig, [...groupsByDataset.values()])\n\n return {\n allTestSummaries,\n byScenario,\n byDataset\n }\n}\n\n/**\n * Group the given comparison test summaries, returning one `ComparisonGroup` for each group.\n */\nexport function groupComparisonTestSummaries(\n testSummaries: ComparisonTestSummary[],\n groupKind: ComparisonGroupKind\n): Map<ComparisonGroupKey, ComparisonGroup> {\n const groups: Map<ComparisonGroupKey, ComparisonGroup> = new Map()\n\n for (const testSummary of testSummaries) {\n let groupKey: ComparisonGroupKey\n switch (groupKind) {\n case 'by-dataset':\n groupKey = testSummary.d\n break\n case 'by-scenario':\n groupKey = testSummary.s\n break\n default:\n assertNever(groupKind)\n }\n\n const group = groups.get(groupKey)\n if (group) {\n group.testSummaries.push(testSummary)\n } else {\n groups.set(groupKey, {\n kind: groupKind,\n key: groupKey,\n testSummaries: [testSummary]\n })\n }\n }\n\n return groups\n}\n\n/**\n * Given a set of `ComparisonGroup` instances, organize and sort the groups into the\n * following categories:\n *\n * withErrors\n * groups with items that have errors (are not valid) for both \"left\" and \"right\" models\n *\n * onlyInLeft\n * groups with items that are only valid for the \"left\" model (for example, datasets that\n * were removed and no longer available in the \"right\" model)\n *\n * onlyInRight\n * groups with items that are only valid for the \"right\" model (for example, scenarios\n * for inputs that were added in the \"right\" model)\n *\n * withDiffs\n * groups with one or more comparisons that have non-zero `maxDiff` scores; the groups\n * will be sorted by `maxDiff`, with higher scores at the front of the array\n *\n * withoutDiffs\n * groups where all comparisons have `maxDiff` scores of zero (no differences between\n * \"left\" and \"right\")\n */\nexport function categorizeComparisonGroups(\n comparisonConfig: ComparisonConfig,\n allGroups: ComparisonGroup[]\n): ComparisonGroupSummariesByCategory {\n const allGroupSummaries: Map<ComparisonGroupKey, ComparisonGroupSummary> = new Map()\n const withErrors: ComparisonGroupSummary[] = []\n const onlyInLeft: ComparisonGroupSummary[] = []\n const onlyInRight: ComparisonGroupSummary[] = []\n let withDiffs: ComparisonGroupSummary[] = []\n const withoutDiffs: ComparisonGroupSummary[] = []\n\n function addSummaryForGroup(\n group: ComparisonGroup,\n root: ComparisonDataset | ComparisonScenario,\n validInL: boolean,\n validInR: boolean\n ): void {\n // Compute the scores if the dataset/scenario is valid in both\n let scores: ComparisonGroupScores\n if (validInL && validInR) {\n scores = getScoresForTestSummaries(group.testSummaries, comparisonConfig.thresholds)\n }\n\n // Create the group summary\n const groupSummary: ComparisonGroupSummary = {\n root,\n group,\n scores\n }\n\n // Add to the map of all summaries\n allGroupSummaries.set(group.key, groupSummary)\n\n // Categorize the group\n if (validInL && validInR) {\n // The dataset/scenario is valid in both; see if there were any diffs\n if (scores.totalDiffCount !== scores.diffCountByBucket[0]) {\n withDiffs.push(groupSummary)\n } else {\n withoutDiffs.push(groupSummary)\n }\n } else if (validInL) {\n // The dataset/scenario is valid in \"left\" only\n onlyInLeft.push(groupSummary)\n } else if (validInR) {\n // The dataset/scenario is valid in \"right\" only\n onlyInRight.push(groupSummary)\n } else {\n // The dataset/scenario is not valid in either \"left\" or \"right\"\n withErrors.push(groupSummary)\n }\n }\n\n // Add a summary for each group\n for (const group of allGroups.values()) {\n switch (group.kind) {\n case 'by-dataset': {\n // Get the `ComparisonDataset` instance for this dataset key\n const dataset = comparisonConfig.datasets.getDataset(group.key)\n const validInL = dataset?.outputVarL !== undefined\n const validInR = dataset?.outputVarR !== undefined\n addSummaryForGroup(group, dataset, validInL, validInR)\n break\n }\n case 'by-scenario': {\n // Get the `ComparisonScenario` instance for this scenario key\n const scenario = comparisonConfig.scenarios.getScenario(group.key)\n const validInL = scenario?.specL !== undefined\n const validInR = scenario?.specR !== undefined\n addSummaryForGroup(group, scenario, validInL, validInR)\n break\n }\n default:\n assertNever(group.kind)\n }\n }\n\n if (withDiffs.length > 1) {\n // Sort the `withDiffs` summaries by score and name/title\n if (withDiffs[0].group.kind === 'by-dataset') {\n withDiffs = sortDatasetGroupSummaries(withDiffs)\n } else if (withDiffs[0].group.kind === 'by-scenario') {\n withDiffs = sortScenarioGroupSummaries(withDiffs)\n }\n }\n\n // TODO: Sort the `withoutDiffs` summaries according to the order that they were defined\n // in the config\n\n return {\n allGroupSummaries,\n withErrors,\n onlyInLeft,\n onlyInRight,\n withDiffs,\n withoutDiffs\n }\n}\n\n/**\n * NOTE: This currently can only be used in cases where the dataset/variable is\n * defined in both \"left\" and \"right\".\n */\nfunction sortDatasetGroupSummaries(summaries: ComparisonGroupSummary[]): ComparisonGroupSummary[] {\n return summaries.sort((a, b) => {\n const scoreResult = compareScores(a.scores, b.scores)\n if (scoreResult !== 0) {\n // Sort by score first (higher scores followed by lower scores)\n return -scoreResult\n } else {\n // Sort by variable/source name alphabetically if scores match. Note\n // that we use the \"left\" name for sorting purposes for now, in the case\n // where a variable was renamed\n const aVar = (a.root as ComparisonDataset).outputVarL\n const bVar = (b.root as ComparisonDataset).outputVarR\n const aSource = aVar.sourceName?.toLowerCase() || ''\n const bSource = bVar.sourceName?.toLowerCase() || ''\n if (aSource !== bSource) {\n // Sort by source name (non-model variables follow model variables)\n return aSource.localeCompare(bSource)\n } else {\n // Sort by dataset name alphabetically\n const aName = aVar.varName.toLowerCase()\n const bName = bVar.varName.toLowerCase()\n return aName.localeCompare(bName)\n }\n }\n })\n}\n\n/**\n * NOTE: This currently can only be used in cases where the scenario is\n * defined in both \"left\" and \"right\".\n */\nfunction sortScenarioGroupSummaries(summaries: ComparisonGroupSummary[]): ComparisonGroupSummary[] {\n return summaries.sort((a, b) => {\n const scoreResult = compareScores(a.scores, b.scores)\n if (scoreResult !== 0) {\n // Sort by score first (higher scores followed by lower scores)\n return -scoreResult\n } else {\n // Sort by scenario title alphabetically if scores match\n const aScenario = a.root as ComparisonScenario\n const bScenario = b.root as ComparisonScenario\n const aTitle = aScenario.title.toLowerCase()\n const bTitle = bScenario.title.toLowerCase()\n if (aTitle !== bTitle) {\n // Sort by scenario name\n return aTitle.localeCompare(bTitle)\n } else {\n // Sort by scenario subtitle alphabetically\n const aSubtitle = aScenario.subtitle?.toLowerCase() || ''\n const bSubtitle = bScenario.subtitle?.toLowerCase() || ''\n return aSubtitle.localeCompare(bSubtitle)\n }\n }\n })\n}\n\n/**\n * Return 1 if the most significant score in `a` is higher than that of `b`, -1 if `b` is higher than `a`,\n * or 0 if they are the same.\n */\nfunction compareScores(a: ComparisonGroupScores, b: ComparisonGroupScores): 1 | 0 | -1 {\n if (a.totalMaxDiffByBucket.length !== b.totalMaxDiffByBucket.length) {\n return 0\n }\n\n // Start with the highest threshold bucket (i.e., comparisons with the biggest differences),\n // and then work backwards\n const len = a.totalMaxDiffByBucket.length\n for (let i = len - 1; i >= 0; i--) {\n const aTotal = a.totalMaxDiffByBucket[i]\n const bTotal = b.totalMaxDiffByBucket[i]\n if (aTotal > bTotal) {\n return 1\n } else if (aTotal < bTotal) {\n return -1\n }\n }\n\n // No differences\n return 0\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport Ajv from 'ajv'\nimport assertNever from 'assert-never'\nimport type { Result } from 'neverthrow'\nimport { err, ok } from 'neverthrow'\nimport yaml from 'yaml'\n\nimport type {\n ComparisonDatasetSpec,\n ComparisonGraphGroupSpec,\n ComparisonScenarioGroupRefSpec,\n ComparisonScenarioGroupSpec,\n ComparisonScenarioId,\n ComparisonScenarioInputPosition,\n ComparisonScenarioInputSpec,\n ComparisonScenarioRefSpec,\n ComparisonScenarioSpec,\n ComparisonSpecs,\n ComparisonSpecsSource,\n ComparisonViewBoxSpec,\n ComparisonViewGraphOrder,\n ComparisonViewGraphsSpec,\n ComparisonViewGroupSpec,\n ComparisonViewRowSpec,\n ComparisonViewSpec\n} from '../comparison-spec-types'\n\nimport jsonSchema from './comparison.schema'\n\n/*\n * Note that the following types match the JSON/YAML schema, so they use snake case (underscores)\n * instead of the usual camel case. They also are in an unusual (not user friendly) format, with\n * different optional fields in the same interface instead of using discriminated union types.\n * These \"Parsed\" types are internal to this file; the parsed types are converted to the public\n * \"Spec\" types defined in `comparison-spec.ts`, which use camel case names and union types. The\n * goal of this split is to allow a user to define comparisons using either JSON/YAML format, or\n * programmatically using the \"Spec\" types. The rest of the code deals only with the \"Spec\" types,\n * so the \"Parsed\" types are just an implementation detail of the parser.\n *\n * TODO: Share the parsing types and code with the `check` module.\n */\n\n//\n// DATASETS\n//\n\ntype ParsedDatasetName = string\ntype ParsedDatasetSource = string\n\ninterface ParsedDataset {\n name?: ParsedDatasetName\n source?: ParsedDatasetSource\n}\n\n//\n// SCENARIOS\n//\n\ntype ParsedScenarioId = string\n\ntype ParsedScenarioTitle = string\ntype ParsedScenarioSubtitle = string\n\ntype ParsedScenarioInputName = string\n\ntype ParsedScenarioInputPosition = 'default' | 'min' | 'max'\n\n/**\n * A single input setting for a scenario. An input can be set to a specific number value,\n * or it can be set to a \"position\" (default / min / max), which depends on how a particular\n * model input is configured.\n */\ninterface ParsedScenarioInput {\n input: ParsedScenarioInputName\n at: ParsedScenarioInputPosition | number\n}\n\n/**\n * A definition of an input scenario. A scenario can set one input to a value/position, or it\n * can set multiple inputs to particular values/positions.\n */\ninterface ParsedScenario {\n id?: ParsedScenarioId\n title?: ParsedScenarioTitle\n subtitle?: ParsedScenarioSubtitle\n preset?: 'matrix'\n with?: ParsedScenarioInputName | ParsedScenarioInput[]\n with_inputs?: 'all'\n // with_inputs_in?: string\n at?: ParsedScenarioInputPosition | number\n}\n\n/** A single item in an array of scenario definitions. */\ninterface ParsedScenarioArrayItem {\n scenario: ParsedScenario\n}\n\n/** A reference to a scenario definition with optional title/subtitle overrides. */\ninterface ParsedScenarioRefObject {\n id: ParsedScenarioId\n title?: ParsedScenarioTitle\n subtitle?: ParsedScenarioSubtitle\n}\n\n/** A reference to a scenario definition. */\ninterface ParsedScenarioRef {\n scenario_ref: ParsedScenarioId | ParsedScenarioRefObject\n}\n\n//\n// SCENARIO GROUPS\n//\n\ntype ParsedScenarioGroupId = string\n\ntype ParsedScenarioGroupTitle = string\n\ntype ParsedScenarioGroupScenariosItem = ParsedScenarioArrayItem | ParsedScenarioRef\n\n/**\n * A definition of a group of input scenarios. Multiple scenarios can be grouped together under a single name, and\n * can later be referenced by group ID in a view definition.\n */\ninterface ParsedScenarioGroup {\n id?: ParsedScenarioGroupId\n title: ParsedScenarioGroupTitle\n scenarios: ParsedScenarioGroupScenariosItem[]\n}\n\n/** A single item in an array of scenario group definitions. */\ninterface ParsedScenarioGroupArrayItem {\n scenario_group: ParsedScenarioGroup\n}\n\n/** A reference to a scenario group definition. */\ninterface ParsedScenarioGroupRef {\n scenario_group_ref: ParsedScenarioGroupId\n}\n\n//\n// GRAPHS\n//\n\ntype ParsedGraphsPreset = 'all'\n\ntype ParsedGraphId = string\n\n//\n// GRAPH GROUPS\n//\n\ntype ParsedGraphGroupId = string\n\n/**\n * A definition of a group of graphs. Multiple graphs can be grouped together and can later be\n * referenced by group ID in a view definition.\n */\ninterface ParsedGraphGroup {\n id: ParsedGraphGroupId\n graphs: ParsedGraphId[]\n}\n\n/** A single item in an array of graph group definitions. */\ninterface ParsedGraphGroupArrayItem {\n graph_group: ParsedGraphGroup\n}\n\n/** A reference to a graph group definition. */\ninterface ParsedGraphGroupRef {\n graph_group_ref: ParsedGraphGroupId\n}\n\n//\n// VIEWS\n//\n\ntype ParsedViewTitle = string\ntype ParsedViewSubtitle = string\n\ntype ParsedViewRowTitle = string\ntype ParsedViewRowSubtitle = string\n\ntype ParsedViewBoxTitle = string\ntype ParsedViewBoxSubtitle = string\n\ntype ParsedViewGraphs = ParsedGraphsPreset | ParsedGraphId[] | ParsedGraphGroupRef\n\n/** A definition of a comparison (dataset + scenario) box. */\ninterface ParsedViewBox {\n title: ParsedViewBoxTitle\n subtitle?: ParsedViewBoxSubtitle\n dataset: ParsedDataset\n scenario_ref: ParsedScenarioId\n}\n\n/** A single comparison box in an array of view comparison box definitions. */\ninterface ParsedViewRowBoxesItem {\n box: ParsedViewBox\n}\n\n/** A definition of a row of comparison boxes. */\ninterface ParsedViewRow {\n title: ParsedViewRowTitle\n subtitle?: ParsedViewRowSubtitle\n boxes: ParsedViewRowBoxesItem[]\n}\n\n/** A single row item in an array of view row definitions. */\ninterface ParsedViewRowsItem {\n row: ParsedViewRow\n}\n\n/**\n * A definition of a view. A view presents a set of graphs, either for a single input scenario\n * or for a mix of different dataset/scenario combinations.\n */\ninterface ParsedView {\n title?: ParsedViewTitle\n subtitle?: ParsedViewSubtitle\n scenario_ref?: ParsedScenarioId\n rows?: ParsedViewRowsItem[]\n graphs?: ParsedViewGraphs\n graph_order?: ComparisonViewGraphOrder\n}\n\n//\n// VIEW GROUPS\n//\n\ntype ParsedViewGroupTitle = string\n\ninterface ParsedViewGroupViewsItem {\n view: ParsedView\n}\n\ntype ParsedViewGroupScenariosItem = ParsedScenarioRef | ParsedScenarioGroupRef\n\n/** A definition of a group of views. */\ninterface ParsedViewGroup {\n title: ParsedViewGroupTitle\n views?: ParsedViewGroupViewsItem[]\n scenarios?: ParsedViewGroupScenariosItem[]\n graphs?: ParsedViewGraphs\n graph_order?: ComparisonViewGraphOrder\n}\n\n/** A single item in an array of view definitions. */\ninterface ParsedViewGroupArrayItem {\n view_group: ParsedViewGroup\n}\n\n//\n// TOP-LEVEL DEFS\n//\n\ntype ParsedTopLevelDefItem =\n | ParsedScenarioArrayItem\n | ParsedScenarioGroupArrayItem\n | ParsedGraphGroupArrayItem\n | ParsedViewGroupArrayItem\n\n/**\n * Parse the comparison test definitions in the given JSON or YAML strings.\n *\n * @param specSource The JSON or YAML formatted string to parse.\n */\nexport function parseComparisonSpecs(specSource: ComparisonSpecsSource): Result<ComparisonSpecs, Error> {\n const scenarios: ComparisonScenarioSpec[] = []\n const scenarioGroups: ComparisonScenarioGroupSpec[] = []\n const graphGroups: ComparisonGraphGroupSpec[] = []\n const viewGroups: ComparisonViewGroupSpec[] = []\n\n // Prepare the JSON 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<ComparisonSpec> = jsonSchema\n const validate = ajv.compile<ParsedTopLevelDefItem[]>(jsonSchema)\n\n // Parse the JSON or YAML string\n let parsed: unknown\n switch (specSource.kind) {\n case 'json':\n parsed = JSON.parse(specSource.content)\n break\n case 'yaml':\n parsed = yaml.parse(specSource.content)\n break\n default:\n assertNever(specSource.kind)\n }\n\n // Validate and convert the parsed objects to specs\n if (validate(parsed)) {\n for (const specItem of parsed) {\n if ('scenario' in specItem) {\n scenarios.push(scenarioSpecFromParsed(specItem.scenario))\n } else if ('scenario_group' in specItem) {\n scenarioGroups.push(scenarioGroupSpecFromParsed(specItem.scenario_group))\n } else if ('graph_group' in specItem) {\n graphGroups.push(graphGroupSpecFromParsed(specItem.graph_group))\n } else if ('view_group' in specItem) {\n viewGroups.push(viewGroupSpecFromParsed(specItem.view_group))\n }\n }\n } else {\n let msg = 'Failed to parse YAML comparison definitions'\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 return ok({\n scenarios,\n scenarioGroups,\n graphGroups,\n viewGroups\n })\n}\n\n//\n// DATASETS\n//\n\nfunction datasetSpecFromParsed(parsedDataset: ParsedDataset): ComparisonDatasetSpec {\n return {\n kind: 'dataset',\n name: parsedDataset.name,\n source: parsedDataset.source\n }\n}\n\n//\n// SCENARIOS\n//\n\nfunction scenarioSpecFromParsed(parsedScenario: ParsedScenario): ComparisonScenarioSpec {\n if (parsedScenario.preset === 'matrix') {\n // Create matrix spec\n return {\n kind: 'scenario-matrix'\n }\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 ParsedScenarioInputPosition\n // TODO...\n // }\n\n if (parsedScenario.with !== undefined) {\n let inputSpecs: ComparisonScenarioInputSpec[]\n if (Array.isArray(parsedScenario.with)) {\n // Create one scenario that contains the given input settings\n const parsedInputs = parsedScenario.with as ParsedScenarioInput[]\n inputSpecs = parsedInputs.map(inputSpecFromParsed)\n } else {\n // Create a single \"input at <position|value>\" scenario\n inputSpecs = [\n inputSpecFromParsed({\n input: parsedScenario.with,\n at: parsedScenario.at\n })\n ]\n }\n return {\n kind: 'scenario-with-inputs',\n id: parsedScenario.id,\n title: parsedScenario.title,\n subtitle: parsedScenario.subtitle,\n inputs: inputSpecs\n }\n }\n\n if (parsedScenario.with_inputs === 'all') {\n // Create an \"all inputs at <position>\" scenario\n return {\n kind: 'scenario-with-all-inputs',\n id: parsedScenario.id,\n title: parsedScenario.title,\n subtitle: parsedScenario.subtitle,\n position: parsedScenario.at as ComparisonScenarioInputPosition\n }\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 ParsedScenarioInputPosition\n // TODO...\n // }\n\n // Internal error\n throw new Error(`Unable to convert parsed scenario: ${JSON.stringify(parsedScenario)}`)\n}\n\nfunction inputSpecFromParsed(parsedInput: ParsedScenarioInput): ComparisonScenarioInputSpec {\n if (typeof parsedInput.at === 'number') {\n const value = parsedInput.at as number\n return {\n kind: 'input-at-value',\n inputName: parsedInput.input,\n value\n }\n } else {\n return {\n kind: 'input-at-position',\n inputName: parsedInput.input,\n position: parsedInput.at as ComparisonScenarioInputPosition\n }\n }\n}\n\n//\n// SCENARIO GROUPS\n//\n\nfunction scenarioGroupSpecFromParsed(parsedScenarioGroup: ParsedScenarioGroup): ComparisonScenarioGroupSpec {\n const scenarioSpecs: (ComparisonScenarioSpec | ComparisonScenarioRefSpec)[] = parsedScenarioGroup.scenarios.map(\n parsedScenarioOrRef => {\n if ('scenario_ref' in parsedScenarioOrRef) {\n return scenarioRefSpecFromParsed(parsedScenarioOrRef)\n } else {\n return scenarioSpecFromParsed(parsedScenarioOrRef.scenario)\n }\n }\n )\n return {\n kind: 'scenario-group',\n id: parsedScenarioGroup.id,\n title: parsedScenarioGroup.title,\n scenarios: scenarioSpecs\n }\n}\n\nfunction scenarioRefSpecFromParsed(parsedScenarioRef: ParsedScenarioRef): ComparisonScenarioRefSpec {\n if (typeof parsedScenarioRef.scenario_ref === 'string') {\n return {\n kind: 'scenario-ref',\n scenarioId: parsedScenarioRef.scenario_ref\n }\n } else {\n return {\n kind: 'scenario-ref',\n scenarioId: parsedScenarioRef.scenario_ref.id,\n title: parsedScenarioRef.scenario_ref.title,\n subtitle: parsedScenarioRef.scenario_ref.subtitle\n }\n }\n}\n\nfunction scenarioGroupRefSpecFromParsed(parsedGroupRef: ParsedScenarioGroupRef): ComparisonScenarioGroupRefSpec {\n return {\n kind: 'scenario-group-ref',\n groupId: parsedGroupRef.scenario_group_ref\n }\n}\n\n//\n// GRAPH GROUPS\n//\n\nfunction graphGroupSpecFromParsed(parsedGraphGroup: ParsedGraphGroup): ComparisonGraphGroupSpec {\n return {\n kind: 'graph-group',\n id: parsedGraphGroup.id,\n graphIds: parsedGraphGroup.graphs\n }\n}\n\n//\n// VIEWS\n//\n\nfunction viewSpecFromParsed(parsedView: ParsedView): ComparisonViewSpec {\n let scenarioId: ComparisonScenarioId\n let rows: ComparisonViewRowSpec[]\n if (parsedView.scenario_ref !== undefined) {\n scenarioId = parsedView.scenario_ref\n } else if (parsedView.rows !== undefined) {\n rows = parsedView.rows.map(item => viewRowSpecFromParsed(item.row))\n }\n\n let graphs: ComparisonViewGraphsSpec\n if (parsedView.graphs !== undefined) {\n graphs = viewGraphsSpecFromParsed(parsedView.graphs)\n }\n\n return {\n kind: 'view',\n title: parsedView.title,\n subtitle: parsedView.subtitle,\n scenarioId,\n rows,\n graphs,\n graphOrder: parsedView.graph_order\n }\n}\n\nfunction viewRowSpecFromParsed(parsedRow: ParsedViewRow): ComparisonViewRowSpec {\n return {\n kind: 'view-row',\n title: parsedRow.title,\n subtitle: parsedRow.subtitle,\n boxes: parsedRow.boxes.map(item => viewBoxSpecFromParsed(item.box))\n }\n}\n\nfunction viewBoxSpecFromParsed(parsedBox: ParsedViewBox): ComparisonViewBoxSpec {\n return {\n kind: 'view-box',\n title: parsedBox.title,\n subtitle: parsedBox.subtitle,\n dataset: datasetSpecFromParsed(parsedBox.dataset),\n scenarioId: parsedBox.scenario_ref\n }\n}\n\nfunction viewGraphsSpecFromParsed(parsedGraphs: ParsedViewGraphs): ComparisonViewGraphsSpec {\n if (parsedGraphs === 'all') {\n return {\n kind: 'graphs-preset',\n preset: 'all'\n }\n } else if (Array.isArray(parsedGraphs)) {\n return {\n kind: 'graphs-array',\n graphIds: parsedGraphs\n }\n } else if ('graph_group_ref' in parsedGraphs) {\n return {\n kind: 'graph-group-ref',\n groupId: parsedGraphs.graph_group_ref\n }\n } else {\n // Internal error (this should have already been rejected by the validator)\n throw new Error('Invalid graphs spec in comparison view')\n }\n}\n\n//\n// VIEW GROUPS\n//\n\nfunction viewGroupSpecFromParsed(parsedViewGroup: ParsedViewGroup): ComparisonViewGroupSpec {\n if (parsedViewGroup.views !== undefined) {\n // This is a view group with an array of views\n return {\n kind: 'view-group-with-views',\n title: parsedViewGroup.title,\n views: parsedViewGroup.views.map(item => viewSpecFromParsed(item.view))\n }\n } else if (parsedViewGroup.scenarios !== undefined) {\n // This is a view group with an array of scenario (or scenario group) references\n const scenarios: (ComparisonScenarioRefSpec | ComparisonScenarioGroupRefSpec)[] = parsedViewGroup.scenarios.map(\n parsedScenarioOrGroupRef => {\n if ('scenario_ref' in parsedScenarioOrGroupRef) {\n return scenarioRefSpecFromParsed(parsedScenarioOrGroupRef)\n } else if ('scenario_group_ref' in parsedScenarioOrGroupRef) {\n return scenarioGroupRefSpecFromParsed(parsedScenarioOrGroupRef)\n } else {\n // Internal error (this should have already been rejected by the validator)\n throw new Error('Invalid view group')\n }\n }\n )\n return {\n kind: 'view-group-with-scenarios',\n title: parsedViewGroup.title,\n scenarios,\n graphs: viewGraphsSpecFromParsed(parsedViewGroup.graphs)\n }\n } else {\n // Internal error (this should have already been rejected by the validator)\n throw new Error('Invalid view group')\n }\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 Comparison Test',\n type: 'array',\n description: 'A group of model comparison scenarios and views.',\n items: {\n $ref: '#/$defs/top_level_array_item'\n },\n\n $defs: {\n //\n // TOP-LEVEL\n //\n\n top_level_array_item: {\n oneOf: [\n { $ref: '#/$defs/scenario_array_item' },\n { $ref: '#/$defs/scenario_group_array_item' },\n { $ref: '#/$defs/graph_group_array_item' },\n { $ref: '#/$defs/view_group_array_item' }\n ]\n },\n\n //\n // DATASETS\n //\n\n dataset: {\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 //\n // SCENARIOS\n //\n\n scenario_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenario: {\n $ref: '#/$defs/scenario'\n }\n },\n required: ['scenario']\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 id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\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 id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\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 id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\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 id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\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_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 //\n // SCENARIO GROUPS\n //\n\n scenario_group_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenario_group: {\n $ref: '#/$defs/scenario_group'\n }\n },\n required: ['scenario_group']\n },\n\n scenario_group: {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n scenarios: {\n type: 'array',\n items: {\n $ref: '#/$defs/scenario_group_scenarios_array_item'\n },\n minItems: 1\n }\n },\n required: ['title', 'scenarios']\n },\n\n scenario_group_scenarios_array_item: {\n oneOf: [{ $ref: '#/$defs/scenario_array_item' }, { $ref: '#/$defs/scenario_ref_array_item' }]\n },\n\n scenario_ref_id: {\n type: 'string'\n },\n\n scenario_ref_object: {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string'\n },\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n }\n },\n required: ['id']\n },\n\n scenario_ref: {\n oneOf: [{ $ref: '#/$defs/scenario_ref_id' }, { $ref: '#/$defs/scenario_ref_object' }]\n },\n\n scenario_ref_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenario_ref: {\n $ref: '#/$defs/scenario_ref'\n }\n },\n required: ['scenario_ref']\n },\n\n scenario_group_ref: {\n type: 'object',\n additionalProperties: false,\n properties: {\n scenario_group_ref: {\n type: 'string'\n }\n },\n required: ['scenario_group_ref']\n },\n\n //\n // GRAPHS\n //\n\n graphs_preset: {\n type: 'string',\n enum: ['all']\n },\n\n graphs_array: {\n type: 'array',\n items: {\n type: 'string'\n },\n minItems: 1\n },\n\n graph_group_ref: {\n type: 'object',\n additionalProperties: false,\n properties: {\n graph_group_ref: {\n type: 'string'\n }\n },\n required: ['graph_group_ref']\n },\n\n //\n // GRAPH GROUPS\n //\n\n graph_group_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n graph_group: {\n $ref: '#/$defs/graph_group'\n }\n },\n required: ['graph_group']\n },\n\n graph_group: {\n type: 'object',\n additionalProperties: false,\n properties: {\n id: {\n type: 'string'\n },\n graphs: {\n $ref: '#/$defs/graphs_array'\n }\n },\n required: ['id', 'graphs']\n },\n\n //\n // VIEWS\n //\n\n view: {\n oneOf: [{ $ref: '#/$defs/view_with_scenario' }, { $ref: '#/$defs/view_with_rows' }]\n },\n\n view_with_scenario: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\n scenario_ref: {\n type: 'string'\n },\n graphs: {\n $ref: '#/$defs/view_graphs'\n },\n graph_order: {\n $ref: '#/$defs/view_graph_order'\n }\n },\n required: ['scenario_ref']\n },\n\n view_with_rows: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\n rows: {\n $ref: '#/$defs/view_rows_array'\n }\n },\n required: ['title', 'rows']\n },\n\n view_rows_array: {\n type: 'array',\n items: {\n $ref: '#/$defs/view_rows_array_item'\n },\n minItems: 1\n },\n\n view_rows_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n row: {\n $ref: '#/$defs/view_row'\n }\n }\n },\n\n view_row: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\n boxes: {\n $ref: '#/$defs/view_boxes_array'\n }\n },\n required: ['title', 'boxes']\n },\n\n view_boxes_array: {\n type: 'array',\n items: {\n $ref: '#/$defs/view_boxes_array_item'\n },\n minItems: 1\n },\n\n view_boxes_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n box: {\n $ref: '#/$defs/view_box'\n }\n }\n },\n\n view_box: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n subtitle: {\n type: 'string'\n },\n dataset: {\n $ref: '#/$defs/dataset'\n },\n scenario_ref: {\n $ref: '#/$defs/scenario_ref_id'\n }\n },\n required: ['title', 'dataset', 'scenario_ref']\n },\n\n view_graphs: {\n oneOf: [{ $ref: '#/$defs/graphs_preset' }, { $ref: '#/$defs/graphs_array' }, { $ref: '#/$defs/graph_group_ref' }]\n },\n\n view_graph_order: {\n type: 'string',\n enum: ['default', 'grouped-by-diffs']\n },\n\n //\n // VIEW GROUPS\n //\n\n view_group_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n view_group: {\n $ref: '#/$defs/view_group'\n }\n },\n required: ['view_group']\n },\n\n view_group: {\n oneOf: [\n { $ref: '#/$defs/view_group_with_views_array' },\n { $ref: '#/$defs/view_group_shorthand_with_scenarios_array' }\n ]\n },\n\n view_group_with_views_array: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n views: {\n type: 'array',\n items: {\n $ref: '#/$defs/view_group_views_array_item'\n },\n minItems: 1\n }\n },\n required: ['title', 'views']\n },\n\n view_group_views_array_item: {\n type: 'object',\n additionalProperties: false,\n properties: {\n view: {\n $ref: '#/$defs/view'\n }\n }\n },\n\n view_group_shorthand_with_scenarios_array: {\n type: 'object',\n additionalProperties: false,\n properties: {\n title: {\n type: 'string'\n },\n scenarios: {\n type: 'array',\n items: {\n $ref: '#/$defs/view_group_scenarios_array_item'\n },\n minItems: 1\n },\n graphs: {\n $ref: '#/$defs/view_graphs'\n }\n },\n required: ['title', 'scenarios', 'graphs']\n },\n\n view_group_scenarios_array_item: {\n oneOf: [{ $ref: '#/$defs/scenario_ref_array_item' }, { $ref: '#/$defs/scenario_group_ref' }]\n }\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { InputPosition, InputSetting, ScenarioSpec } from '../../../_shared/scenario-spec-types'\nimport { inputSettingsSpec } from '../../../_shared/scenario-specs'\nimport type { VarId } from '../../../_shared/types'\n\nimport type { BundleGraphId, InputSettingGroupId, ModelSpec } from '../../../bundle/bundle-types'\nimport { ModelInputs } from '../../../bundle/model-inputs'\nimport type { InputId, InputVar, OutputVar } from '../../../bundle/var-types'\n\nimport type {\n ComparisonDataset,\n ComparisonScenario,\n ComparisonScenarioAllInputsSettings,\n ComparisonScenarioGroup,\n ComparisonScenarioInput,\n ComparisonScenarioInputSettings,\n ComparisonScenarioInputState,\n ComparisonScenarioKey,\n ComparisonUnresolvedScenarioRef,\n ComparisonUnresolvedView,\n ComparisonView,\n ComparisonViewBox,\n ComparisonViewGroup,\n ComparisonViewRow\n} from '../../_shared/comparison-resolved-types'\n\nimport type {\n ComparisonDatasetName,\n ComparisonDatasetSource,\n ComparisonGraphGroupId,\n ComparisonGraphGroupSpec,\n ComparisonGraphId,\n ComparisonScenarioGroupId,\n ComparisonScenarioGroupSpec,\n ComparisonScenarioInputName,\n ComparisonScenarioId,\n ComparisonScenarioInputPosition,\n ComparisonScenarioInputSpec,\n ComparisonScenarioRefSpec,\n ComparisonScenarioSpec,\n ComparisonScenarioSubtitle,\n ComparisonScenarioTitle,\n ComparisonSpecs,\n ComparisonViewBoxSpec,\n ComparisonViewGraphOrder,\n ComparisonViewGraphsSpec,\n ComparisonViewGroupSpec,\n ComparisonViewRowSpec,\n ComparisonViewSubtitle,\n ComparisonViewTitle\n} from '../comparison-spec-types'\nimport { inputSettingFromResolvedInputState, scenarioSpecsFromSettings } from './comparison-scenario-specs'\n\nexport interface ComparisonResolvedDefs {\n /** The set of resolved scenarios. */\n scenarios: ComparisonScenario[]\n /** The set of resolved scenario groups. */\n scenarioGroups: ComparisonScenarioGroup[]\n /** The set of resolved view groups. */\n viewGroups: ComparisonViewGroup[]\n}\n\ntype GenKey = () => ComparisonScenarioKey\n\n/**\n * Expand and resolve all the provided scenario and view specs. This will inspect all the\n * requested specs, resolve references to input variables and scenarios, and then return\n * the definitions for the fully resolved scenarios and views.\n *\n * @param modelSpecL The model spec for the \"left\" bundle being compared.\n * @param modelSpecR The model spec for the \"right\" bundle being compared.\n * @param specs The scenario and view specs that were parsed from YAML/JSON definitions.\n */\nexport function resolveComparisonSpecs(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n specs: ComparisonSpecs\n): ComparisonResolvedDefs {\n let key = 1\n const genKey: GenKey = () => {\n return `${key++}` as ComparisonScenarioKey\n }\n\n // Create `ModelInputs` instances to make lookups easier\n const modelInputsL = new ModelInputs(modelSpecL)\n const modelInputsR = new ModelInputs(modelSpecR)\n\n // Create a `ModelOutputs` instance to make lookups easier\n const modelOutputs = new ModelOutputs(modelSpecL, modelSpecR)\n\n // Resolve the top-level scenario specs and convert to `ComparisonScenario` instances\n const resolvedScenarios = new ResolvedScenarios()\n for (const scenarioSpec of specs.scenarios || []) {\n resolvedScenarios.add(resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioSpec, genKey))\n }\n\n // Resolve scenarios that are defined inside scenario groups and add them to the set of scenarios.\n // Note that we track the key for each scenario in the group so that we can preserve the key when\n // creating a copy/reference in the next step.\n interface PartiallyResolvedScenarioGroup {\n spec: ComparisonScenarioGroupSpec\n scenarios: (ComparisonScenario | ComparisonScenarioRefSpec)[]\n }\n const partiallyResolvedScenarioGroups: PartiallyResolvedScenarioGroup[] = []\n for (const scenarioGroupSpec of specs.scenarioGroups || []) {\n const scenariosForGroup: (ComparisonScenario | ComparisonScenarioRefSpec)[] = []\n for (const scenarioItem of scenarioGroupSpec.scenarios) {\n if (scenarioItem.kind === 'scenario-ref') {\n scenariosForGroup.push(scenarioItem)\n } else {\n const scenarios = resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioItem, genKey)\n resolvedScenarios.add(scenarios)\n scenariosForGroup.push(...scenarios)\n }\n }\n partiallyResolvedScenarioGroups.push({\n spec: scenarioGroupSpec,\n scenarios: scenariosForGroup\n })\n }\n\n // Now that all scenarios have been resolved, resolve the groups themselves. Note that we do this as a\n // secondary pass after resolving all top-level and nested scenario definitions so that groups can refer\n // to scenarios that are defined elsewhere (order does not matter).\n const resolvedScenarioGroups = new ResolvedScenarioGroups()\n for (const partiallyResolvedGroup of partiallyResolvedScenarioGroups) {\n const scenariosForGroup: (ComparisonScenario | ComparisonUnresolvedScenarioRef)[] = []\n for (const scenarioItem of partiallyResolvedGroup.scenarios) {\n if (scenarioItem.kind === 'scenario-ref') {\n // See if we have a scenario defined for this ID\n const referencedScenario = resolvedScenarios.getScenarioForId(scenarioItem.scenarioId)\n if (referencedScenario) {\n // Found it; create a copy of it to allow for adding the title/subtitle overrides if provided.\n // Note that we use the same key as the original so that report references work correctly.\n const resolvedScenario = { ...referencedScenario }\n if (scenarioItem.title) {\n resolvedScenario.title = scenarioItem.title\n }\n if (scenarioItem.subtitle) {\n resolvedScenario.subtitle = scenarioItem.subtitle\n }\n scenariosForGroup.push(resolvedScenario)\n } else {\n // Not found, add an unresolved ref item to the group\n scenariosForGroup.push({\n kind: 'unresolved-scenario-ref',\n scenarioId: scenarioItem.scenarioId\n })\n }\n } else {\n // Add the fully resolved scenario to this group (using the same key as the original)\n scenariosForGroup.push(scenarioItem)\n }\n }\n resolvedScenarioGroups.add({\n kind: 'scenario-group',\n id: partiallyResolvedGroup.spec.id,\n title: partiallyResolvedGroup.spec.title,\n scenarios: scenariosForGroup\n })\n }\n\n // Resolve the top-level graph group specs\n const resolvedGraphGroups = new ResolvedGraphGroups()\n for (const graphGroupSpec of specs.graphGroups || []) {\n resolvedGraphGroups.add(graphGroupSpec)\n }\n\n // Resolve the view groups\n const resolvedViewGroups: ComparisonViewGroup[] = []\n for (const viewGroupSpec of specs.viewGroups || []) {\n const resolvedViewGroup = resolveViewGroupFromSpec(\n modelSpecL,\n modelSpecR,\n modelOutputs,\n resolvedScenarios,\n resolvedScenarioGroups,\n resolvedGraphGroups,\n viewGroupSpec\n )\n resolvedViewGroups.push(resolvedViewGroup)\n }\n\n return {\n scenarios: resolvedScenarios.getAll(),\n scenarioGroups: resolvedScenarioGroups.getAll(),\n viewGroups: resolvedViewGroups\n }\n}\n\n//\n// DATASETS\n//\n\n// TODO: This isn't very efficient because it iterates over all datasets to match by\n// variable and source name, but it likely is not used as much as scenario lookup; it\n// is probably OK for now but should be improved eventually\nclass ModelOutputs {\n constructor(private readonly modelSpecL: ModelSpec, private readonly modelSpecR: ModelSpec) {}\n\n getDatasetForName(name: ComparisonDatasetName, source?: ComparisonDatasetSource): ComparisonDataset | undefined {\n // TODO: This doesn't currently handle renames; ideally this would delegate to the\n // existing `ComparisonDatasets` type, which does account for renamed variables.\n // For now, only look in the \"right\" model spec, and get the variable from the \"left\"\n // model spec if the keys match.\n function findOutputVar(modelSpec: ModelSpec): OutputVar | undefined {\n for (const outputVar of modelSpec.outputVars.values()) {\n if (outputVar.varName === name && outputVar.sourceName === source) {\n return outputVar\n }\n }\n return undefined\n }\n\n const outputVarR = findOutputVar(this.modelSpecR)\n if (outputVarR) {\n // XXX: See if there is a matching item in the \"left\" model spec\n const datasetKey = outputVarR.datasetKey\n const outputVarL = this.modelSpecL.outputVars.get(datasetKey)\n return {\n kind: 'dataset',\n key: datasetKey,\n outputVarL,\n outputVarR\n }\n } else {\n return undefined\n }\n }\n}\n\n//\n// SCENARIOS\n//\n\nclass ResolvedScenarios {\n /** The array of all resolved scenarios. */\n private readonly resolvedScenarios: ComparisonScenario[] = []\n\n /** The set of resolved scenarios that include an ID, keyed by ID. */\n private readonly resolvedScenariosById: Map<ComparisonScenarioId, ComparisonScenario> = new Map()\n\n add(scenarios: ComparisonScenario[]): void {\n for (const resolvedScenario of scenarios) {\n // Add the scenario to the general set\n this.resolvedScenarios.push(resolvedScenario)\n\n // Also add to the map of scenarios with an ID, if one is defined\n if (resolvedScenario.id !== undefined) {\n // See if ID is already used\n // TODO: Mark this as an error in the interface rather than throwing\n if (this.resolvedScenariosById.has(resolvedScenario.id)) {\n throw new Error(`Multiple scenarios defined with the same id (${resolvedScenario.id})`)\n }\n this.resolvedScenariosById.set(resolvedScenario.id, resolvedScenario)\n }\n }\n }\n\n getAll(): ComparisonScenario[] {\n return this.resolvedScenarios\n }\n\n getScenarioForId(id: ComparisonScenarioId): ComparisonScenario | undefined {\n return this.resolvedScenariosById.get(id)\n }\n}\n\n/**\n * Return one or more resolved `ComparisonScenario` instances for the given scenario spec.\n */\nfunction resolveScenariosFromSpec(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n scenarioSpec: ComparisonScenarioSpec,\n genKey: GenKey\n): ComparisonScenario[] {\n switch (scenarioSpec.kind) {\n case 'scenario-matrix':\n // Create a matrix of scenarios\n return resolveScenarioMatrix(modelInputsL, modelInputsR, genKey)\n\n case 'scenario-with-all-inputs': {\n // Create an \"all inputs at <position>\" scenario\n const position = inputPosition(scenarioSpec.position)\n return [\n resolveScenarioWithAllInputsAtPosition(\n genKey(),\n scenarioSpec.id,\n scenarioSpec.title,\n scenarioSpec.subtitle,\n position\n )\n ]\n }\n\n case 'scenario-with-inputs': {\n // Create one scenario that contains the given input setting(s)\n return [\n resolveScenarioForInputSpecs(\n modelInputsL,\n modelInputsR,\n genKey(),\n scenarioSpec.id,\n scenarioSpec.title,\n scenarioSpec.subtitle,\n scenarioSpec.inputs\n )\n ]\n }\n\n case 'scenario-with-distinct-inputs': {\n // Create one scenario in which the inputs are configured differently\n // for the two models\n return [\n resolveScenarioForDistinctInputSpecs(\n modelInputsL,\n modelInputsR,\n genKey(),\n scenarioSpec.id,\n scenarioSpec.title,\n scenarioSpec.subtitle,\n scenarioSpec.inputsL,\n scenarioSpec.inputsR\n )\n ]\n }\n\n case 'scenario-with-setting-group': {\n // Create one scenario in which the inputs are configured according to the\n // setting group defined for each model\n return [\n resolveScenarioForSettingGroup(\n modelInputsL,\n modelInputsR,\n genKey(),\n scenarioSpec.id,\n scenarioSpec.title,\n scenarioSpec.subtitle,\n scenarioSpec.settingGroupId\n )\n ]\n }\n\n default:\n assertNever(scenarioSpec)\n }\n}\n\n/**\n * Return a matrix of scenarios that covers all inputs for the given model.\n */\nfunction resolveScenarioMatrix(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n genKey: GenKey\n): ComparisonScenario[] {\n const resolvedScenarios: ComparisonScenario[] = []\n\n // Add an \"all inputs at default\" scenario\n resolvedScenarios.push(\n resolveScenarioWithAllInputsAtPosition(genKey(), undefined, undefined, undefined, 'at-default')\n )\n\n // Get the union of all input IDs appearing on either side\n const inputIdAliases: Set<InputId> = new Set()\n modelInputsL.getAllInputIdAliases().forEach(alias => inputIdAliases.add(alias))\n modelInputsR.getAllInputIdAliases().forEach(alias => inputIdAliases.add(alias))\n\n // Create two scenarios for each input, one with the input at its minimum, and one\n // with the input at its maximum. If the input only exists on one side, we still\n // create a scenario for it, but it will be flagged in the UI to make it clear\n // that the input configuration has changed.\n for (const inputIdAlias of inputIdAliases) {\n const inputAtMin: ComparisonScenarioInputSpec = {\n kind: 'input-at-position',\n inputName: inputIdAlias,\n position: 'min'\n }\n const inputAtMax: ComparisonScenarioInputSpec = {\n kind: 'input-at-position',\n inputName: inputIdAlias,\n position: 'max'\n }\n resolvedScenarios.push(\n resolveScenarioForInputSpecs(modelInputsL, modelInputsR, genKey(), undefined, undefined, undefined, [inputAtMin])\n )\n resolvedScenarios.push(\n resolveScenarioForInputSpecs(modelInputsL, modelInputsR, genKey(), undefined, undefined, undefined, [inputAtMax])\n )\n }\n\n return resolvedScenarios\n}\n\n/**\n * Return a resolved `ComparisonScenario` with all inputs set to the given position.\n */\nfunction resolveScenarioWithAllInputsAtPosition(\n key: ComparisonScenarioKey,\n id: ComparisonScenarioId | undefined,\n title: ComparisonScenarioTitle | undefined,\n subtitle: ComparisonScenarioSubtitle | undefined,\n position: InputPosition\n): ComparisonScenario {\n // Create the settings and specs\n const settings: ComparisonScenarioAllInputsSettings = {\n kind: 'all-inputs-settings',\n position\n }\n const [specL, specR] = scenarioSpecsFromSettings(settings)\n\n // Create a `ComparisonScenario` with the settings\n return {\n kind: 'scenario',\n key,\n id,\n title,\n subtitle,\n settings,\n specL,\n specR\n }\n}\n\n/**\n * Return a resolved `ComparisonScenario` for the given inputs and positions/values.\n */\nfunction resolveScenarioForInputSpecs(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n key: ComparisonScenarioKey,\n id: ComparisonScenarioId | undefined,\n title: ComparisonScenarioTitle | undefined,\n subtitle: ComparisonScenarioSubtitle | undefined,\n inputSpecs: ComparisonScenarioInputSpec[]\n): ComparisonScenario {\n // Convert the input specs to `ComparisonScenarioInput` instances\n const resolvedInputs = inputSpecs.map(inputSpec => {\n switch (inputSpec.kind) {\n case 'input-at-position':\n return resolveInputForName(modelInputsL, modelInputsR, inputSpec.inputName, inputSpec.position)\n case 'input-at-value':\n return resolveInputForName(modelInputsL, modelInputsR, inputSpec.inputName, inputSpec.value)\n default:\n assertNever(inputSpec)\n }\n })\n\n // Create the settings and specs\n const settings: ComparisonScenarioInputSettings = {\n kind: 'input-settings',\n inputs: resolvedInputs\n }\n const [specL, specR] = scenarioSpecsFromSettings(settings)\n\n // Create a `ComparisonScenario` with the resolved inputs\n return {\n kind: 'scenario',\n key,\n id,\n title,\n subtitle,\n settings,\n specL,\n specR\n }\n}\n\n/**\n * Return a resolved `ComparisonScenario` for the given settings that are different\n * for the two models.\n */\nfunction resolveScenarioForDistinctInputSpecs(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n key: ComparisonScenarioKey,\n id: ComparisonScenarioId | undefined,\n title: ComparisonScenarioTitle | undefined,\n subtitle: ComparisonScenarioSubtitle | undefined,\n inputSpecsL: ComparisonScenarioInputSpec[],\n inputSpecsR: ComparisonScenarioInputSpec[]\n): ComparisonScenario {\n // TODO: Unlike the more typical \"scenario with inputs\" case, when we have \"distinct\"\n // inputs (separate sets of inputs for the two models) we only include the `settings`\n // array in the resulting `ComparisonScenario` if there are errors in resolving the\n // inputs. If all inputs were resolved successfully, then the `settings` array will\n // be empty. This is probably fine for now but it could stand to be redesigned.\n const inputsWithErrors: ComparisonScenarioInput[] = []\n\n // Resolve the input settings for the left and right sides separately\n const settingsL: InputSetting[] = []\n const settingsR: InputSetting[] = []\n\n // Helper function that resolves an input for the given model/side. If the input is\n // resolved successfully, an `InputSetting` will be saved for that side. Otherwise,\n // a `ComparisonScenarioInput` describing the error will be saved.\n function resolveInputSpec(\n side: 'left' | 'right',\n modelInputs: ModelInputs,\n inputSpec: ComparisonScenarioInputSpec\n ): void {\n let inputState: ComparisonScenarioInputState\n switch (inputSpec.kind) {\n case 'input-at-position':\n inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.position)\n break\n case 'input-at-value':\n inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.value)\n break\n default:\n assertNever(inputSpec)\n }\n\n if (inputState.error !== undefined) {\n // The input could not be resolved, so add it to the set of error inputs\n // TODO: For now we include an empty object (with undefined properties) for the\n // \"other\" side. Maybe we can make the state properties optional, or maybe we\n // just need a less awkward way of handling these input states in the \"distinct\"\n // inputs case.\n inputsWithErrors.push({\n requestedName: inputSpec.inputName,\n stateL: side === 'left' ? inputState : {},\n stateR: side === 'right' ? inputState : {}\n })\n } else {\n // The input was resolved, so create a scenario that works for this side\n const inputSetting = inputSettingFromResolvedInputState(inputState)\n if (side === 'left') {\n settingsL.push(inputSetting)\n } else {\n settingsR.push(inputSetting)\n }\n }\n }\n\n // Resolve the input settings for the left and right sides separately\n inputSpecsL.forEach(inputSpec => resolveInputSpec('left', modelInputsL, inputSpec))\n inputSpecsR.forEach(inputSpec => resolveInputSpec('right', modelInputsR, inputSpec))\n\n // Create a `ScenarioSpec` for each side if there were no errors\n let specL: ScenarioSpec\n let specR: ScenarioSpec\n if (inputsWithErrors.length === 0) {\n specL = inputSettingsSpec(settingsL)\n specR = inputSettingsSpec(settingsR)\n }\n\n // Create a `ComparisonScenario` with the resolved inputs\n return {\n kind: 'scenario',\n key,\n id,\n title,\n subtitle,\n settings: {\n kind: 'input-settings',\n inputs: inputsWithErrors\n },\n specL,\n specR\n }\n}\n\n/**\n * Return a resolved `ComparisonScenario` for the given input setting group\n */\nfunction resolveScenarioForSettingGroup(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n key: ComparisonScenarioKey,\n id: ComparisonScenarioId | undefined,\n title: ComparisonScenarioTitle | undefined,\n subtitle: ComparisonScenarioSubtitle | undefined,\n settingGroupId: InputSettingGroupId\n): ComparisonScenario {\n // XXX: Currently the `ComparisonScenarioInputSpec` must reference an input by\n // its full variable name or by its alias (\"id 3\"). But the lower-level\n // `InputSetting` interface only includes the input variable ID, so we need\n // to map from the variable ID to the variable name.\n function inputVarNameForVarId(modelInputs: ModelInputs, varId: VarId): ComparisonScenarioInputName {\n const inputVar = modelInputs.getInputVarForVarId(varId)\n if (inputVar !== undefined) {\n // Get the variable name\n return inputVar.varName\n } else {\n // XXX: It is not likely that a bundle will advertise an input setting group that references\n // an unknown variable, but in this case, we will use the variable ID as the variable name\n // and let the resolver fail later\n return varId\n }\n }\n\n // Convert an `InputPosition` into a `ComparisonScenarioInputPosition`\n function inputPositionForPosition(position: InputPosition): ComparisonScenarioInputPosition {\n switch (position) {\n case 'at-minimum':\n return 'min'\n case 'at-maximum':\n return 'max'\n case 'at-default':\n return 'default'\n default:\n throw new Error(`Unknown input position: ${position}`)\n }\n }\n\n // Convert an `InputSetting` into a `ComparisonScenarioInputSpec`\n function inputSpecForSetting(modelInputs: ModelInputs, inputSetting: InputSetting): ComparisonScenarioInputSpec {\n switch (inputSetting.kind) {\n case 'position':\n return {\n kind: 'input-at-position',\n inputName: inputVarNameForVarId(modelInputs, inputSetting.inputVarId),\n position: inputPositionForPosition(inputSetting.position)\n }\n case 'value':\n return {\n kind: 'input-at-value',\n inputName: inputVarNameForVarId(modelInputs, inputSetting.inputVarId),\n value: inputSetting.value\n }\n default:\n throw new Error(`Unknown input setting kind: ${inputSetting}`)\n }\n }\n\n // Convert the input settings given model into a set of `ComparisonScenarioInputSpec` instances\n function inputSpecsForSettingGroup(\n modelInputs: ModelInputs,\n inputSettings: InputSetting[]\n ): ComparisonScenarioInputSpec[] {\n return inputSettings.map(inputSetting => inputSpecForSetting(modelInputs, inputSetting))\n }\n\n // Convert the `InputSetting` instances to `ComparisonScenarioInputSpec` instances so that\n // we can use `resolveScenarioForDistinctInputSpecs` to handle the rest of the conversion.\n // Note that if the setting group is not defined for one or both models, the settings array\n // will be empty, but we will add the error state after the resolve step.\n const inputSettingsL = modelInputsL.modelSpec.inputSettingGroups?.get(settingGroupId) || []\n const inputSettingsR = modelInputsR.modelSpec.inputSettingGroups?.get(settingGroupId) || []\n const inputsL = inputSpecsForSettingGroup(modelInputsL, inputSettingsL)\n const inputsR = inputSpecsForSettingGroup(modelInputsR, inputSettingsR)\n\n // Resolve the scenario and inputs\n const scenario = resolveScenarioForDistinctInputSpecs(\n modelInputsL,\n modelInputsR,\n key,\n id,\n title,\n subtitle,\n inputsL,\n inputsR\n )\n\n // Helper function that converts a `ScenarioSpec` to a string for easier comparison (this\n // sorts the inputs by name and value)\n function scenarioString(scenario: ScenarioSpec): string {\n if (scenario.kind === 'input-settings') {\n const settings = scenario.settings.map(setting => {\n switch (setting.kind) {\n case 'position':\n return `${setting.inputVarId}=${setting.position}`\n case 'value':\n return `${setting.inputVarId}=${setting.value}`\n default:\n throw new Error(`Unexpected input setting kind: ${setting}`)\n }\n })\n return settings.sort().join('__')\n } else {\n throw new Error(`Unexpected scenario spec kind: ${scenario.kind}`)\n }\n }\n\n // Helper function that returns true if the two scenarios are identical (same inputs and values)\n function scenariosAreEqual(scenarioL: ScenarioSpec, scenarioR: ScenarioSpec): boolean {\n return scenarioString(scenarioL) === scenarioString(scenarioR)\n }\n\n // If the setting group is not defined for one or both models, add the error state\n function errorState(inputSettings: InputSetting[]): ComparisonScenarioInputState {\n if (inputSettings.length === 0) {\n return { error: { kind: 'unknown-input-setting-group' } }\n } else {\n return {}\n }\n }\n if (inputSettingsL.length === 0 || inputSettingsR.length === 0) {\n if (scenario.settings.kind === 'input-settings') {\n // Put the \"unknown group\" error at the beginning of the list\n const errorSetting: ComparisonScenarioInput = {\n requestedName: settingGroupId,\n stateL: errorState(inputSettingsL),\n stateR: errorState(inputSettingsR)\n }\n scenario.settings.inputs.unshift(errorSetting)\n }\n if (inputSettingsL.length === 0) {\n scenario.specL = undefined\n }\n if (inputSettingsR.length === 0) {\n scenario.specR = undefined\n }\n }\n\n // If the setting group is defined for both models, but the settings are different\n // (either the set of inputs is different or the values are different), add a warning\n // to the scenario so that it can be flagged in the UI\n if (scenario.settings.kind === 'input-settings') {\n if (scenario.specL && scenario.specR && !scenariosAreEqual(scenario.specL, scenario.specR)) {\n scenario.settings.settingsDiffer = true\n }\n }\n\n return scenario\n}\n\n/**\n * Return a resolved `ComparisonScenarioInput` for the given input name and position/value.\n */\nfunction resolveInputForName(\n modelInputsL: ModelInputs,\n modelInputsR: ModelInputs,\n inputName: string,\n at: ComparisonScenarioInputPosition | number\n): ComparisonScenarioInput {\n // Resolve the input in the context of each model\n return {\n requestedName: inputName,\n stateL: resolveInputForNameInModel(modelInputsL, inputName, at),\n stateR: resolveInputForNameInModel(modelInputsR, inputName, at)\n }\n}\n\n/**\n * Return a resolved `ComparisonScenarioInputState` for the given input name and position/value\n * in the context of a specific model.\n */\nfunction resolveInputForNameInModel(\n modelInputs: ModelInputs,\n inputName: string,\n at: ComparisonScenarioInputPosition | number\n): ComparisonScenarioInputState {\n // Find an input variable that matches the given name (either the actual variable name\n // or an alias)\n const inputVar = modelInputs.getInputVarForName(inputName)\n if (inputVar) {\n // Resolve the input at the given value or position\n return resolveInputVar(inputVar, at)\n } else {\n // No input variable found that matches the given name\n return {\n error: {\n kind: 'unknown-input'\n }\n }\n }\n}\n\n/**\n * Return a `ComparisonScenarioInputState` for the given input variable and position/value.\n */\nfunction resolveInputVar(\n inputVar: InputVar,\n at: ComparisonScenarioInputPosition | number\n): ComparisonScenarioInputState {\n if (typeof at === 'number') {\n const value = at as number\n return resolveInputVarAtValue(inputVar, value)\n } else {\n const position = inputPosition(at as ComparisonScenarioInputPosition)\n return resolveInputVarAtPosition(inputVar, position)\n }\n}\n\n/**\n * Return a `ComparisonScenarioInputState` for the input at the given position.\n */\nfunction resolveInputVarAtPosition(inputVar: InputVar, position: InputPosition): ComparisonScenarioInputState {\n return {\n inputVar,\n position,\n value: inputValueAtPosition(inputVar, position)\n }\n}\n\n/**\n * Return a `ComparisonScenarioInputState` for the input at the given value.\n */\nfunction resolveInputVarAtValue(inputVar: InputVar, value: number): ComparisonScenarioInputState {\n // Check that the value is in the valid range\n // TODO: This may not be valid for switch inputs\n if (value >= inputVar.minValue && value <= inputVar.maxValue) {\n return {\n inputVar,\n value\n }\n } else {\n return {\n error: {\n kind: 'invalid-value'\n }\n }\n }\n}\n\n/**\n * Convert a `ComparisonScenarioInputPosition` (from the parser) to an `InputPosition` (used by `Scenario`).\n */\nfunction inputPosition(position: ComparisonScenarioInputPosition): 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// SCENARIO GROUPS\n//\n\nclass ResolvedScenarioGroups {\n /** The array of all resolved scenario groups. */\n private readonly resolvedGroups: ComparisonScenarioGroup[] = []\n\n /** The set of resolved scenario groups that include an ID, keyed by ID. */\n private readonly resolvedGroupsById: Map<ComparisonScenarioGroupId, ComparisonScenarioGroup> = new Map()\n\n add(group: ComparisonScenarioGroup): void {\n // Add the group to the general set\n this.resolvedGroups.push(group)\n\n // Also add to the map of groups with an ID, if one is defined\n if (group.id !== undefined) {\n // See if ID is already used\n // TODO: Mark this as an error in the interface rather than throwing\n if (this.resolvedGroupsById.has(group.id)) {\n throw new Error(`Multiple scenario groups defined with the same id (${group.id})`)\n }\n this.resolvedGroupsById.set(group.id, group)\n }\n }\n\n getAll(): ComparisonScenarioGroup[] {\n return this.resolvedGroups\n }\n\n getGroupForId(id: ComparisonScenarioGroupId): ComparisonScenarioGroup | undefined {\n return this.resolvedGroupsById.get(id)\n }\n}\n\n//\n// GRAPH GROUPS\n//\n\n// TODO: This doesn't currently check that the referenced graph IDs are available in one or both\n// model specs\nclass ResolvedGraphGroups {\n /** The set of resolved graph groups, keyed by ID. */\n private readonly resolvedGroupsById: Map<ComparisonGraphGroupId, ComparisonGraphGroupSpec> = new Map()\n\n add(group: ComparisonGraphGroupSpec): void {\n // Add to the map of groups\n if (this.resolvedGroupsById.has(group.id)) {\n // TODO: Mark this as an error in the interface rather than throwing\n throw new Error(`Multiple graph groups defined with the same id (${group.id})`)\n }\n this.resolvedGroupsById.set(group.id, group)\n }\n\n getGroupForId(id: ComparisonGraphGroupId): ComparisonGraphGroupSpec | undefined {\n return this.resolvedGroupsById.get(id)\n }\n}\n\n//\n// VIEWS\n//\n\n/**\n * Return the graphs \"all\" preset or the graph IDs from a view graphs spec.\n */\nfunction resolveGraphsFromSpec(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n resolvedGraphGroups: ResolvedGraphGroups,\n graphsSpec: ComparisonViewGraphsSpec\n): ComparisonGraphId[] {\n switch (graphsSpec.kind) {\n case 'graphs-preset': {\n switch (graphsSpec.preset) {\n case 'all': {\n // Get the union of all graph IDs appearing in either left or right\n const graphIds: Set<BundleGraphId> = new Set()\n const addGraphIds = (modelSpec: ModelSpec) => {\n if (modelSpec.graphSpecs) {\n for (const graphSpec of modelSpec.graphSpecs) {\n graphIds.add(graphSpec.id)\n }\n }\n }\n addGraphIds(modelSpecL)\n addGraphIds(modelSpecR)\n return [...graphIds]\n }\n default:\n assertNever(graphsSpec.preset)\n }\n }\n // eslint-disable-next-line no-fallthrough\n case 'graphs-array':\n return graphsSpec.graphIds\n case 'graph-group-ref': {\n const groupSpec = resolvedGraphGroups.getGroupForId(graphsSpec.groupId)\n if (groupSpec === undefined) {\n // TODO: Mark this as an error in the interface rather than throwing\n throw new Error(`No graph group found for id ${graphsSpec.groupId}`)\n }\n return groupSpec.graphIds\n }\n default:\n assertNever(graphsSpec)\n }\n}\n\nfunction resolveViewForScenarioId(\n resolvedScenarios: ResolvedScenarios,\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n scenarioId: ComparisonScenarioId,\n graphIds: ComparisonGraphId[],\n graphOrder: ComparisonViewGraphOrder\n): ComparisonView | ComparisonUnresolvedView {\n const resolvedScenario = resolvedScenarios.getScenarioForId(scenarioId)\n if (resolvedScenario) {\n // Add the resolved view\n return resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graphIds, graphOrder)\n } else {\n // Add the unresolved view\n return unresolvedViewForScenarioId(viewTitle, viewSubtitle, scenarioId)\n }\n}\n\nfunction resolveViewForScenarioRefSpec(\n resolvedScenarios: ResolvedScenarios,\n refSpec: ComparisonScenarioRefSpec,\n graphIds: ComparisonGraphId[],\n graphOrder: ComparisonViewGraphOrder\n): ComparisonView | ComparisonUnresolvedView {\n const resolvedScenario = resolvedScenarios.getScenarioForId(refSpec.scenarioId)\n if (resolvedScenario) {\n // Add the resolved view\n return resolveViewForScenario(refSpec.title, refSpec.subtitle, resolvedScenario, graphIds, graphOrder)\n } else {\n // Add the unresolved view\n return unresolvedViewForScenarioId(undefined, undefined, refSpec.scenarioId)\n }\n}\n\nfunction resolveViewForScenario(\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n resolvedScenario: ComparisonScenario,\n graphIds: ComparisonGraphId[],\n graphOrder: ComparisonViewGraphOrder\n): ComparisonView {\n // If explicit title/subtitle were not provided for the view, infer them from the scenario\n // TODO: For now we only infer the subtitle if an explicit title was also not provided; this might\n // be surprising if the user wants the title inferred but wants to provide an explicit subtitle\n if (viewTitle === undefined) {\n viewTitle = resolvedScenario.title\n if (viewTitle === undefined) {\n viewTitle = 'Untitled view'\n }\n if (viewSubtitle === undefined) {\n viewSubtitle = resolvedScenario.subtitle\n }\n }\n\n return {\n kind: 'view',\n title: viewTitle,\n subtitle: viewSubtitle,\n scenario: resolvedScenario,\n graphIds,\n graphOrder\n }\n}\n\ninterface ComparisonUnresolvedViewBox {\n kind: 'unresolved-view-box'\n datasetName?: ComparisonDatasetName\n datasetSource?: ComparisonDatasetSource\n scenarioId?: ComparisonScenarioId\n}\n\nfunction resolveViewBoxForSpec(\n modelOutputs: ModelOutputs,\n resolvedScenarios: ResolvedScenarios,\n boxSpec: ComparisonViewBoxSpec\n): ComparisonViewBox | ComparisonUnresolvedViewBox {\n const resolvedDataset = modelOutputs.getDatasetForName(boxSpec.dataset.name, boxSpec.dataset.source)\n if (resolvedDataset === undefined) {\n return {\n kind: 'unresolved-view-box',\n datasetName: boxSpec.dataset.name,\n datasetSource: boxSpec.dataset.source\n }\n }\n\n const resolvedScenario = resolvedScenarios.getScenarioForId(boxSpec.scenarioId)\n if (resolvedScenario === undefined) {\n return {\n kind: 'unresolved-view-box',\n scenarioId: boxSpec.scenarioId\n }\n }\n\n return {\n kind: 'view-box',\n title: boxSpec.title,\n subtitle: boxSpec.subtitle,\n dataset: resolvedDataset,\n scenario: resolvedScenario\n }\n}\n\nfunction resolveViewRowForSpec(\n modelOutputs: ModelOutputs,\n resolvedScenarios: ResolvedScenarios,\n rowSpec: ComparisonViewRowSpec\n): ComparisonViewRow | ComparisonUnresolvedViewBox {\n const resolvedBoxes: ComparisonViewBox[] = []\n for (const boxSpec of rowSpec.boxes) {\n const box = resolveViewBoxForSpec(modelOutputs, resolvedScenarios, boxSpec)\n if (box.kind === 'view-box') {\n resolvedBoxes.push(box)\n } else {\n return box\n }\n }\n\n return {\n kind: 'view-row',\n title: rowSpec.title,\n subtitle: rowSpec.subtitle,\n boxes: resolvedBoxes\n }\n}\n\nfunction resolveViewWithRowSpecs(\n modelOutputs: ModelOutputs,\n resolvedScenarios: ResolvedScenarios,\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n rowSpecs: ComparisonViewRowSpec[]\n): ComparisonView | ComparisonUnresolvedView {\n const resolvedRows: ComparisonViewRow[] = []\n for (const rowSpec of rowSpecs) {\n const row = resolveViewRowForSpec(modelOutputs, resolvedScenarios, rowSpec)\n if (row.kind === 'view-row') {\n resolvedRows.push(row)\n } else {\n return unresolvedViewForScenarioId(viewTitle, viewSubtitle, row.scenarioId, row.datasetName, row.datasetSource)\n }\n }\n\n if (viewTitle === undefined) {\n viewTitle = 'Untitled view'\n }\n\n return {\n kind: 'view',\n title: viewTitle,\n subtitle: viewSubtitle,\n rows: resolvedRows,\n // TODO: The schema doesn't currently allow for graphs in a view that is defined\n // with rows. Probably we should have different view types to make this more clear.\n graphIds: [],\n graphOrder: 'default'\n }\n}\n\nfunction unresolvedViewForScenarioId(\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n scenarioId?: ComparisonScenarioId,\n datasetName?: ComparisonDatasetName,\n datasetSource?: ComparisonDatasetSource\n): ComparisonUnresolvedView {\n return {\n kind: 'unresolved-view',\n title: viewTitle,\n subtitle: viewSubtitle,\n scenarioId,\n datasetName,\n datasetSource\n }\n}\n\nfunction unresolvedViewForScenarioGroupId(\n viewTitle: ComparisonViewTitle | undefined,\n viewSubtitle: ComparisonViewSubtitle | undefined,\n scenarioGroupId: ComparisonScenarioGroupId\n): ComparisonUnresolvedView {\n return {\n kind: 'unresolved-view',\n title: viewTitle,\n subtitle: viewSubtitle,\n scenarioGroupId\n }\n}\n\n//\n// VIEW GROUPS\n//\n\n/**\n * Return a resolved `ComparisonViewGroup` instance for the given view group spec.\n */\nfunction resolveViewGroupFromSpec(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n modelOutputs: ModelOutputs,\n resolvedScenarios: ResolvedScenarios,\n resolvedScenarioGroups: ResolvedScenarioGroups,\n resolvedGraphGroups: ResolvedGraphGroups,\n viewGroupSpec: ComparisonViewGroupSpec\n): ComparisonViewGroup {\n let views: (ComparisonView | ComparisonUnresolvedView)[]\n\n switch (viewGroupSpec.kind) {\n case 'view-group-with-views': {\n // Resolve each view\n views = viewGroupSpec.views.map(viewSpec => {\n if (viewSpec.scenarioId) {\n let graphIds: ComparisonGraphId[]\n if (viewSpec.graphs) {\n graphIds = resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, viewSpec.graphs)\n } else {\n graphIds = []\n }\n return resolveViewForScenarioId(\n resolvedScenarios,\n viewSpec.title,\n viewSpec.subtitle,\n viewSpec.scenarioId,\n graphIds,\n viewSpec.graphOrder || 'default'\n )\n } else {\n return resolveViewWithRowSpecs(\n modelOutputs,\n resolvedScenarios,\n viewSpec.title,\n viewSpec.subtitle,\n viewSpec.rows\n )\n }\n })\n break\n }\n case 'view-group-with-scenarios': {\n // Resolve to one view for each scenario (with the same set of graphs for each view)\n const graphIds = resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, viewGroupSpec.graphs)\n const graphOrder = viewGroupSpec.graphOrder || 'default'\n views = []\n for (const refSpec of viewGroupSpec.scenarios) {\n switch (refSpec.kind) {\n case 'scenario-ref':\n // Add a view for the scenario\n views.push(resolveViewForScenarioRefSpec(resolvedScenarios, refSpec, graphIds, graphOrder))\n break\n case 'scenario-group-ref': {\n const resolvedGroup = resolvedScenarioGroups.getGroupForId(refSpec.groupId)\n if (resolvedGroup) {\n // Add a view for each scenario in the group\n for (const scenario of resolvedGroup.scenarios) {\n switch (scenario.kind) {\n case 'unresolved-scenario-ref':\n views.push(unresolvedViewForScenarioId(undefined, undefined, scenario.scenarioId))\n break\n case 'scenario':\n views.push(resolveViewForScenario(undefined, undefined, scenario, graphIds, graphOrder))\n break\n default:\n assertNever(scenario)\n }\n }\n } else {\n // Add an unresolved view that covers the whole group\n views.push(unresolvedViewForScenarioGroupId(undefined, undefined, refSpec.groupId))\n }\n break\n }\n default:\n assertNever(refSpec)\n }\n }\n break\n }\n default:\n assertNever(viewGroupSpec)\n }\n\n return {\n kind: 'view-group',\n title: viewGroupSpec.title,\n views\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { VarId } from '../_shared/types'\nimport type { ModelSpec } from './bundle-types'\nimport type { InputVar } from './var-types'\n\n/**\n * Wraps a `ModelSpec` to provide easier/faster lookups of input variables by variable\n * name or alias.\n */\nexport class ModelInputs {\n /** All inputs keyed by lookup name (lowercase variable name or alias). */\n private readonly inputsByLookupName: Map<string, InputVar> = new Map()\n\n /** All input ID aliases. */\n private readonly inputIdAliases: Set<string> = new Set()\n\n constructor(public readonly modelSpec: ModelSpec) {\n // Add the inputs from the primary map\n for (const inputVar of modelSpec.inputVars.values()) {\n // Add an entry with the input's variable name as the key\n const varNameKey = inputVar.varName.toLowerCase()\n this.inputsByLookupName.set(varNameKey, inputVar)\n\n if (inputVar.inputId) {\n // Also automatically add an entry using the input's ID as an alias. Keep the original\n // input ID for the set of aliases (to preserve original case), but use lowercase for\n // the key, like we do for all other keys.\n const idAlias = `id ${inputVar.inputId}`\n this.inputIdAliases.add(idAlias)\n const idKey = idAlias.toLowerCase()\n this.inputsByLookupName.set(idKey, inputVar)\n }\n }\n\n if (modelSpec.inputAliases) {\n // Add the inputs from the alias map, keyed by alias\n for (const [alias, varId] of modelSpec.inputAliases.entries()) {\n const aliasKey = alias.toLowerCase()\n if (this.inputsByLookupName.has(aliasKey)) {\n console.warn(\n `WARNING: Input variable already defined with a name that collides with alias '${alias}', skipping`\n )\n continue\n }\n\n const inputVar = modelSpec.inputVars.get(varId)\n if (inputVar === undefined) {\n console.warn(`WARNING: No input variable found for '${varId}' associated with alias '${alias}', skipping`)\n continue\n }\n\n this.inputsByLookupName.set(aliasKey, inputVar)\n }\n }\n }\n\n /**\n * Return the set of `id XYZ` aliases for all input variables.\n */\n getAllInputIdAliases(): string[] {\n return [...this.inputIdAliases]\n }\n\n /**\n * Return the `InputVar` that matches the requested name (either by variable name or alias).\n *\n * @param name The variable name or alias to match.\n */\n getInputVarForName(name: string): InputVar | undefined {\n return this.inputsByLookupName.get(name.toLowerCase())\n }\n\n /**\n * Return the `InputVar` that matches the requested variable ID.\n *\n * @param varId The variable identifier to match.\n */\n getInputVarForVarId(varId: VarId): InputVar | undefined {\n return this.modelSpec.inputVars.get(varId)\n }\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport { assertNever } from 'assert-never'\n\nimport type { InputSetting, ScenarioSpec } from '../../../_shared/scenario-spec-types'\nimport {\n allInputsAtPositionSpec,\n inputSettingsSpec,\n positionSetting,\n valueSetting\n} from '../../../_shared/scenario-specs'\n\nimport type {\n ComparisonScenarioInput,\n ComparisonScenarioInputState,\n ComparisonScenarioSettings\n} from '../../_shared/comparison-resolved-types'\n\n/**\n * Create a pair of `ScenarioSpec` instances that can be used to run each model given a\n * `ComparisonScenarioSettings` object.\n */\nexport function scenarioSpecsFromSettings(\n settings: ComparisonScenarioSettings\n): [ScenarioSpec | undefined, ScenarioSpec | undefined] {\n switch (settings.kind) {\n case 'all-inputs-settings': {\n // In this case, the same `Scenario` can be used on both sides\n const scenario = allInputsAtPositionSpec(settings.position)\n return [scenario, scenario]\n }\n case 'input-settings': {\n // In this case, create separate `Scenario` instances for each side, in case the\n // input variable names are different\n const specL = scenarioSpecFromInputs(settings.inputs, 'left')\n const specR = scenarioSpecFromInputs(settings.inputs, 'right')\n return [specL, specR]\n }\n default:\n assertNever(settings)\n }\n}\n\n/**\n * Create a `ScenarioSpec` instance that can be used to run a specific model for the\n * given input settings.\n */\nfunction scenarioSpecFromInputs(inputs: ComparisonScenarioInput[], side: 'left' | 'right'): ScenarioSpec | undefined {\n const settings: InputSetting[] = []\n\n // Create an `InputSetting` for each input\n for (const input of inputs) {\n const state = side === 'left' ? input.stateL : input.stateR\n\n // If any inputs on this side could not be resolved, return undefined so that we don't try\n // to fetch data for this side\n if (state.inputVar === undefined) {\n return undefined\n }\n\n // Create a scenario setting for this input\n settings.push(inputSettingFromResolvedInputState(state))\n }\n\n return inputSettingsSpec(settings)\n}\n\n/**\n * Create an `InputSetting` instance for the given resolved input that can be used to\n * build a `ScenarioSpec`.\n */\nexport function inputSettingFromResolvedInputState(state: ComparisonScenarioInputState): InputSetting {\n const varId = state.inputVar.varId\n if (state.position) {\n return positionSetting(varId, state.position)\n } else {\n return valueSetting(varId, state.value as number)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { DatasetKey } from '../../_shared/types'\nimport type { BundleGraphId, LoadedBundle, ModelSpec, NamedBundle } from '../../bundle/bundle-types'\n\nimport type { ComparisonDataset, ComparisonScenario, ComparisonViewGroup } from '../_shared/comparison-resolved-types'\nimport type { ComparisonGroupSummariesByCategory, ComparisonGroupSummary } from '../report/comparison-group-types'\nimport type { ComparisonTestSummary } from '../report/comparison-report-types'\n\nimport type { ComparisonDatasets } from './comparison-datasets'\nimport type { ComparisonScenarios } from './comparison-scenarios'\nimport type { ComparisonSpecs, ComparisonSpecsSource } from './comparison-spec-types'\nimport { parseComparisonSpecs } from './parse/comparison-parser'\nimport type { ComparisonResolvedDefs } from './resolve/comparison-resolver'\nimport { resolveComparisonSpecs } from './resolve/comparison-resolver'\n\n/**\n * Describes an extra plot to be shown in a comparison graph.\n */\nexport interface ComparisonPlot {\n /** The dataset key for the plot. */\n datasetKey: DatasetKey\n /** The plot color. */\n color: string\n /** The plot style. If undefined, defaults to 'normal'. */\n style?: 'normal' | 'dashed'\n /** The plot line width, in px units. If undefined, a default width will be used. */\n lineWidth?: number\n}\n\nexport interface ComparisonDatasetOptions {\n /**\n * The mapping of renamed dataset keys (old or \"left\" name as the map key,\n * new or \"right\" name as the value).\n */\n renamedDatasetKeys?: Map<DatasetKey, DatasetKey>\n /**\n * An optional function that allows for limiting the datasets that are compared\n * for a given scenario. By default, all datasets are compared for a given\n * scenario, but if a custom function is provided, it can return a subset of\n * datasets (for example, to omit datasets that are not relevant).\n */\n datasetKeysForScenario?: (allDatasetKeys: DatasetKey[], scenario: ComparisonScenario) => DatasetKey[]\n /**\n * An optional function that allows for including additional reference plots\n * on a comparison graph for a given dataset and scenario. By default, no\n * additional reference plots are included, but if a custom function is\n * provided, it can return an array of `ComparisonPlot` objects.\n */\n referencePlotsForDataset?: (dataset: ComparisonDataset, scenario: ComparisonScenario) => ComparisonPlot[]\n /**\n * An optional function that allows for customizing the set of context graphs\n * that are shown for a given dataset and scenario. By default, all graphs in\n * which the dataset appears will be shown, but if a custom function is provided,\n * it can return a different set of graphs (for example, to omit graphs that are\n * not relevant under the given scenario).\n */\n contextGraphIdsForDataset?: (dataset: ComparisonDataset, scenario: ComparisonScenario) => BundleGraphId[]\n}\n\n/**\n * Describes a row in the comparison report summary view.\n */\nexport interface ComparisonReportSummaryRow {\n /** The group summary represented by the row. */\n groupSummary: ComparisonGroupSummary\n /** The custom title for the row (this overrides the default title derived from the summary). */\n title?: string\n /** The custom subtitle for the row (this overrides the default subtitle derived from the summary). */\n subtitle?: string\n}\n\n/**\n * Describes a section in the comparison report summary view.\n */\nexport interface ComparisonReportSummarySection {\n /** The text to display for the section header. */\n headerText: string\n /** The summary rows to display in the section. */\n rows: ComparisonReportSummaryRow[]\n /**\n * The initial expanded state of the section. If undefined, defaults to 'expanded-if-diffs',\n * meaning the section will be initially expanded only if any rows have differences, otherwise\n * it will be initially collapsed.\n */\n initialState?: 'collapsed' | 'expanded' | 'expanded-if-diffs'\n}\n\n/**\n * Describes an item (box) in the comparison report detail view.\n */\nexport interface ComparisonReportDetailItem {\n /** The title of the item. */\n title: string\n /** The subtitle of the item (if any). */\n subtitle?: string\n /** The scenario for the item. */\n scenario: ComparisonScenario\n /** The test summary for the item. */\n testSummary: ComparisonTestSummary\n}\n\n/**\n * Describes a row in the comparison report detail view.\n */\nexport interface ComparisonReportDetailRow {\n /** The title of the row. */\n title: string\n /** The subtitle of the row (if any). */\n subtitle?: string\n /** The score for the row (the meaning of the value depends on the chosen statistical method). */\n score: number\n /** The items in this row (one item per box). */\n items: ComparisonReportDetailItem[]\n}\n\nexport interface ComparisonReportOptions {\n /**\n * An optional function that allows for customizing the order and grouping of\n * sections and rows in the \"comparisons by scenario\" summary view.\n *\n * @param summaries The comparison summaries, one summary per scenario.\n * @returns The sections to display in the \"comparisons by scenario\" summary view.\n */\n summarySectionsForComparisonsByScenario?: (\n summaries: ComparisonGroupSummariesByCategory\n ) => ComparisonReportSummarySection[]\n\n /**\n * An optional function that allows for customizing the order and grouping of\n * sections and rows in the \"comparisons by dataset\" summary view.\n *\n * @param summaries The comparison summaries, one summary per dataset.\n * @returns The sections to display in the \"comparisons by dataset\" summary view.\n */\n summarySectionsForComparisonsByDataset?: (\n summaries: ComparisonGroupSummariesByCategory\n ) => ComparisonReportSummarySection[]\n\n /**\n * An optional function that allows for customizing the order of rows and boxes\n * in the detail view for a scenario.\n *\n * @param rows The original rows to be displayed in the detail view for a scenario.\n * @returns The customized rows to display in the detail view for a scenario.\n */\n detailRowsForScenario?: (rows: ComparisonReportDetailRow[]) => ComparisonReportDetailRow[]\n\n /**\n * An optional function that allows for customizing the order of rows and boxes\n * in the detail view for a dataset.\n *\n * @param rows The original rows to be displayed in the detail view for a dataset.\n * @returns The customized rows to display in the detail view for a dataset.\n */\n detailRowsForDataset?: (rows: ComparisonReportDetailRow[]) => ComparisonReportDetailRow[]\n}\n\nexport interface ComparisonOptions {\n /** The left-side (\"baseline\") bundle being compared. */\n baseline: NamedBundle\n /**\n * The array of thresholds used to color differences, e.g., [1, 5, 10] will use\n * buckets of 0%, 0-1%, 1-5%, 5-10%, and >10%.\n */\n thresholds: number[]\n /**\n * The requested comparison scenario and view specifications. These can be\n * specified in YAML or JSON files, or using `Spec` objects.\n */\n specs: (ComparisonSpecs | ComparisonSpecsSource)[]\n /** Optional configuration for the datasets that are compared for different scenarios. */\n datasets?: ComparisonDatasetOptions\n /** Options for customizing the comparison report. */\n report?: ComparisonReportOptions\n}\n\nexport interface ComparisonConfig {\n /** The loaded left-side (\"baseline\") bundle being compared. */\n bundleL: LoadedBundle\n /** The loaded right-side (\"current\") bundle being compared. */\n bundleR: LoadedBundle\n /**\n * The array of thresholds used to color differences, e.g., [1, 5, 10] will use\n * buckets of 0%, 0-1%, 1-5%, 5-10%, and >10%.\n */\n thresholds: number[]\n /** The set of resolved scenarios that will be compared. */\n scenarios: ComparisonScenarios\n /** The set of resolved datasets that will be compared. */\n datasets: ComparisonDatasets\n /** The set of resolved view groups. */\n viewGroups: ComparisonViewGroup[]\n /** Options for customizing the comparison report. */\n reportOptions?: ComparisonReportOptions\n}\n\n/**\n * Expand and resolve all the scenario and view specs in the provided sources, which can\n * be a mix of YAML, JSON, and object specs.\n *\n * @param modelSpecL The model spec for the \"left\" bundle being compared.\n * @param modelSpecR The model spec for the \"right\" bundle being compared.\n * @param specSources The scenario and view spec sources.\n */\nexport function resolveComparisonSpecsFromSources(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n specSources: (ComparisonSpecs | ComparisonSpecsSource)[]\n): ComparisonResolvedDefs {\n const combinedSpecs: ComparisonSpecs = {\n scenarios: [],\n scenarioGroups: [],\n graphGroups: [],\n viewGroups: []\n }\n\n for (const specSource of specSources) {\n let specs: ComparisonSpecs\n if ('kind' in specSource) {\n const parseResult = parseComparisonSpecs(specSource)\n if (parseResult.isOk()) {\n specs = parseResult.value\n } else {\n // TODO: Fail fast instead of logging errors?\n const filenamePart = specSource.filename ? ` in ${specSource.filename}` : ''\n console.error(`ERROR: Failed to parse comparison spec${filenamePart}, skipping`)\n continue\n }\n } else {\n specs = specSource\n }\n combinedSpecs.scenarios.push(...(specs.scenarios || []))\n combinedSpecs.scenarioGroups.push(...(specs.scenarioGroups || []))\n combinedSpecs.graphGroups.push(...(specs.graphGroups || []))\n combinedSpecs.viewGroups.push(...(specs.viewGroups || []))\n }\n\n return resolveComparisonSpecs(modelSpecL, modelSpecR, combinedSpecs)\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { BundleGraphId, ModelSpec } from '../../bundle/bundle-types'\nimport type { OutputVar } from '../../bundle/var-types'\nimport type { DatasetKey } from '../../_shared/types'\nimport type { ComparisonDataset, ComparisonScenario } from '../_shared/comparison-resolved-types'\nimport type { ComparisonDatasetOptions, ComparisonPlot } from './comparison-config'\n\n/**\n * Provides access to the set of dataset definitions (`ComparisonDataset` instances) that are used\n * when comparing the two models.\n */\nexport interface ComparisonDatasets {\n /**\n * Return all `ComparisonDataset` instances that are available for comparisons.\n */\n getAllDatasets(): IterableIterator<ComparisonDataset>\n\n /**\n * Return the dataset metadata for the given key.\n *\n * @param datasetKey The key for the dataset.\n */\n getDataset(datasetKey: DatasetKey): ComparisonDataset | undefined\n\n /**\n * Return the keys for the datasets that should be compared for the given scenario.\n *\n * @param scenario The scenario definition.\n */\n getDatasetKeysForScenario(scenario: ComparisonScenario): DatasetKey[]\n\n /**\n * Return the reference plots that should be shown in the comparison graph for the\n * given dataset and scenario.\n *\n * @param datasetKey The key for the dataset.\n * @param scenario The scenario for which the dataset will be displayed.\n */\n getReferencePlotsForDataset(datasetKey: DatasetKey, scenario: ComparisonScenario): ComparisonPlot[]\n\n /**\n * Return the context graph IDs that should be shown for the given dataset and scenario.\n *\n * @param datasetKey The key for the dataset.\n * @param scenario The scenario for which the dataset will be displayed.\n */\n getContextGraphIdsForDataset(datasetKey: DatasetKey, scenario: ComparisonScenario): BundleGraphId[]\n}\n\n/**\n * Create an instance of the `ComparisonDatasets` interface that sources the output\n * variables from the given models.\n *\n * @param modelSpecL The model spec for the \"left\" bundle being compared.\n * @param modelSpecR The model spec for the \"right\" bundle being compared.\n * @param datasetOptions The custom configuration for the datasets to be compared.\n */\nexport function getComparisonDatasets(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n datasetOptions?: ComparisonDatasetOptions\n): ComparisonDatasets {\n return new ComparisonDatasetsImpl(modelSpecL, modelSpecR, datasetOptions)\n}\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 */\nclass ComparisonDatasetsImpl implements ComparisonDatasets {\n private readonly allDatasets: Map<DatasetKey, ComparisonDataset>\n private readonly allOutputVarKeys: DatasetKey[]\n private readonly modelOutputVarKeys: DatasetKey[]\n\n /**\n * @param modelSpecL The model spec for the \"left\" bundle being compared.\n * @param modelSpecR The model spec for the \"right\" bundle being compared.\n * @param datasetOptions The custom configuration for the datasets to be compared.\n */\n constructor(\n private readonly modelSpecL: ModelSpec,\n private readonly modelSpecR: ModelSpec,\n private readonly datasetOptions?: ComparisonDatasetOptions\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 = datasetOptions?.renamedDatasetKeys\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(modelSpecL.outputVars, false)\n addOutputVars(modelSpecR.outputVars, true)\n this.allOutputVarKeys = Array.from(allOutputVarKeysSet)\n this.modelOutputVarKeys = Array.from(modelOutputVarKeysSet)\n\n // Create `ComparisonDataset` instances for all available keys\n this.allDatasets = new Map()\n for (const datasetKeyL of this.allOutputVarKeys) {\n const datasetKeyR = renamedDatasetKeys?.get(datasetKeyL) || datasetKeyL\n const outputVarL = modelSpecL.outputVars.get(datasetKeyL)\n const outputVarR = modelSpecR.outputVars.get(datasetKeyR)\n this.allDatasets.set(datasetKeyL, {\n kind: 'dataset',\n key: datasetKeyL,\n outputVarL,\n outputVarR\n })\n }\n }\n\n // from ComparisonDatasets interface\n getAllDatasets(): IterableIterator<ComparisonDataset> {\n return this.allDatasets.values()\n }\n\n // from ComparisonDatasets interface\n getDataset(datasetKey: DatasetKey): ComparisonDataset | undefined {\n return this.allDatasets.get(datasetKey)\n }\n\n // from ComparisonDatasets interface\n getDatasetKeysForScenario(scenario: ComparisonScenario): DatasetKey[] {\n if (this.datasetOptions?.datasetKeysForScenario !== undefined) {\n // Delegate to the custom filter function\n return this.datasetOptions.datasetKeysForScenario(this.allOutputVarKeys, scenario)\n } else {\n // Use the default filtering\n if (scenario.settings.kind === 'all-inputs-settings' && scenario.settings.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\n // from ComparisonDatasets interface\n getReferencePlotsForDataset(datasetKey: DatasetKey, scenario: ComparisonScenario): ComparisonPlot[] {\n if (this.datasetOptions?.referencePlotsForDataset !== undefined) {\n // Delegate to the custom function\n const dataset = this.getDataset(datasetKey)\n if (dataset !== undefined) {\n return this.datasetOptions.referencePlotsForDataset(dataset, scenario)\n }\n }\n return []\n }\n\n // from ComparisonDatasets interface\n getContextGraphIdsForDataset(datasetKey: DatasetKey, scenario: ComparisonScenario): BundleGraphId[] {\n const dataset = this.getDataset(datasetKey)\n if (dataset === undefined) {\n return []\n }\n if (this.datasetOptions?.contextGraphIdsForDataset !== undefined) {\n // Delegate to the custom filter function\n return this.datasetOptions.contextGraphIdsForDataset(dataset, scenario)\n } else {\n // Use the default filtering, which uses the graph specs advertised by the bundles\n // to determine which context graphs are associated with the given dataset\n return getContextGraphIdsForDataset(this.modelSpecL, this.modelSpecR, dataset)\n }\n }\n}\n\nfunction getContextGraphIdsForDataset(\n modelSpecL: ModelSpec,\n modelSpecR: ModelSpec,\n dataset: ComparisonDataset\n): BundleGraphId[] {\n // Get the union of all graph IDs (appearing in either left or right) in which this\n // dataset appears\n const contextGraphIds: Set<BundleGraphId> = new Set()\n function addGraphs(modelSpec: ModelSpec, outputVar: OutputVar | undefined): void {\n for (const graphSpec of modelSpec.graphSpecs || []) {\n for (const graphDatasetSpec of graphSpec.datasets) {\n if (graphDatasetSpec.datasetKey === outputVar?.datasetKey) {\n contextGraphIds.add(graphSpec.id)\n break\n }\n }\n }\n }\n addGraphs(modelSpecL, dataset.outputVarL)\n addGraphs(modelSpecR, dataset.outputVarR)\n return [...contextGraphIds]\n}\n","// Copyright (c) 2023 Climate Interactive / New Venture Fund\n\nimport type { ComparisonScenario, ComparisonScenarioKey } from '../_shared/comparison-resolved-types'\n\nexport interface ComparisonScenarios {\n /**\n * Return all `ComparisonScenario` instances that are available for comparisons.\n */\n getAllScenarios(): IterableIterator<ComparisonScenario>\n\n /**\n * Return the scenario definition for the given key.\n *\n * @param key The key for the scenario.\n */\n getScenario(key: ComparisonScenarioKey): ComparisonScenario | undefined\n}\n\n/**\n * Create an instance of the `ComparisonScenarios` interface that uses the given\n * array of `ComparisonScenario` instances.\n *\n * @param scenarios The comparison scenario definitions.\n */\nexport function getComparisonScenarios(scenarios: ComparisonScenario[]): ComparisonScenarios {\n return new ComparisonScenariosImpl(scenarios)\n}\n\n/**\n * Provides access to the set of input scenario definitions (`ComparisonScenario` instances) that are used\n * when comparing the two models.\n */\nclass ComparisonScenariosImpl {\n private readonly scenarioDefs: Map<ComparisonScenarioKey, ComparisonScenario> = new Map()\n\n constructor(scenarios: ComparisonScenario[]) {\n // Create a map to allow for looking up by `ComparisonScenarioKey`\n for (const scenario of scenarios) {\n this.scenarioDefs.set(scenario.key, scenario)\n }\n }\n\n /**\n * Return all `ComparisonScenario` instances that are available for comparisons.\n */\n getAllScenarios(): IterableIterator<ComparisonScenario> {\n return this.scenarioDefs.values()\n }\n\n /**\n * Return the scenario definition for the given key.\n *\n * @param key The key for the scenario.\n */\n getScenario(key: ComparisonScenarioKey): ComparisonScenario | undefined {\n return this.scenarioDefs.get(key)\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { ScenarioSpec } from '../_shared/scenario-spec-types'\nimport type { BundleGraphId, BundleModel } from '../bundle/bundle-types'\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: (scenarioSpec: ScenarioSpec, datasetKeys: DatasetKey[]) => {\n return promiseQueue.add(() => sourceModel.getDatasetsForScenario(scenarioSpec, datasetKeys))\n },\n getGraphDataForScenario: (scenarioSpec: ScenarioSpec, graphId: BundleGraphId) => {\n return promiseQueue.add(() => sourceModel.getGraphDataForScenario(scenarioSpec, 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 { ScenarioSpec } from '../_shared/scenario-spec-types'\nimport type { DatasetKey, DatasetMap } from '../_shared/types'\nimport type { BundleModel, LoadedBundle, NamedBundle } from '../bundle/bundle-types'\n\nimport type { CheckConfig } from '../check/check-config'\n\nimport type { ComparisonConfig } from '../comparison/config/comparison-config'\nimport { resolveComparisonSpecsFromSources } from '../comparison/config/comparison-config'\nimport { getComparisonDatasets } from '../comparison/config/comparison-datasets'\nimport { getComparisonScenarios } from '../comparison/config/comparison-scenarios'\n\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 comparisonConfig: ComparisonConfig\n if (options.comparison === 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.comparison.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.comparison.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 (scenarioSpec: ScenarioSpec, 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(scenarioSpec, 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 getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),\n getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)\n }\n currentBundle = {\n ...origCurrentBundle,\n model: adjBundleModelR\n }\n\n // Combine and resolve the provided comparison specifications\n const modelSpecL = baselineBundle.model.modelSpec\n const modelSpecR = currentBundle.model.modelSpec\n const comparisonDefs = resolveComparisonSpecsFromSources(modelSpecL, modelSpecR, options.comparison.specs)\n\n // Initialize the configuration for comparisons\n comparisonConfig = {\n bundleL: baselineBundle,\n bundleR: currentBundle,\n thresholds: options.comparison.thresholds,\n scenarios: getComparisonScenarios(comparisonDefs.scenarios),\n datasets: getComparisonDatasets(modelSpecL, modelSpecR, options.comparison.datasets),\n viewGroups: comparisonDefs.viewGroups,\n reportOptions: options.comparison.report\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 comparison: comparisonConfig\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 { assertNever } from 'assert-never'\n\nimport { TaskQueue } from '../_shared/task-queue'\nimport { allInputsAtPositionSpec } from '../_shared/scenario-specs'\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 scenarioSpec = allInputsAtPositionSpec('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(scenarioSpec, [])\n return {\n runTimeL: result.modelRunTime\n }\n }\n case 'right': {\n const result = await bundleModelR.getDatasetsForScenario(scenarioSpec, [])\n return {\n runTimeR: result.modelRunTime\n }\n }\n case 'both': {\n const [resultL, resultR] = await Promise.all([\n bundleModelL.getDatasetsForScenario(scenarioSpec, []),\n bundleModelR.getDatasetsForScenario(scenarioSpec, [])\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 type { ScenarioSpec, ScenarioSpecUid } from '../_shared/scenario-spec-types'\nimport type { Dataset, DatasetKey } from '../_shared/types'\n\nexport interface DatasetPair {\n datasetL?: Dataset\n datasetR?: Dataset\n}\n\n/** A function that performs an action on a pair of fetched datasets. */\nexport type DataAction = (datasets: DatasetPair) => void\n\n/** A task that performs an action on one or both dataset requested from the models. */\nexport interface DataTask {\n /** The key for the dataset to be fetched from each model for a given scenario. */\n datasetKey: DatasetKey\n /** The action to be performed with the fetched datasets. */\n dataAction: DataAction\n}\n\nexport interface DataRequest {\n scenarioSpecL?: ScenarioSpec\n scenarioSpecR?: ScenarioSpec\n dataTasks: DataTask[]\n}\n\nexport interface DataPlan {\n requests: DataRequest[]\n}\n\n/**\n * Tracks all requests for data (from model runs) that are made by \"check\" and/or \"comparison\" tests.\n *\n * A data request can be made for both models (\"left\" AND \"right\", as is the case for most comparison tests),\n * or for just one model (e.g., \"right\" only, as is the case for \"check\" tests, or \"left\" only in the case of\n * comparison tests where an input can only be tested in the \"left\" model).\n *\n * The goal is to take all requests into account to create a plan that requires the least number of model\n * runs. Each request ideally fetches data by running both \"left\" and \"right\" models in parallel. When\n * we build up the list of requests, we track which ones:\n * - access both models (\"LR\" requests)\n * - access left model only (\"L\" requests)\n * - access right model only (\"R\" requests)\n *\n * After all requests have been made, `buildPlan` builds an optimal set of data requests by folding\n * \"L\" and \"R\" requests in with compatible \"LR\" requests. For example, suppose we have one \"check\" test\n * (that only needs the \"right\" model), and one \"comparison\" test (that needs both \"left\" and \"right\"),\n * and both tests are exercising the same input scenario:\n *\n * request1: { scenarioL: undefined, scenarioR: scenario1, dataTasks: [task1] }\n * request2: { scenarioL: scenario1, scenarioR: scenario1, dataTasks: [task2] }\n *\n * In this case, we can merge them into a single request that runs the models once in parallel:\n *\n * requestM: { scenarioL: scenario1, scenarioR: scenario1, dataTasks: [task1, task2] }\n */\nexport class DataPlanner {\n /** Tasks that need to access both \"left\" and \"right\" models in parallel. */\n private readonly taskSetsLR: Map<ScenarioPairUid, DataTaskSet> = new Map()\n\n /** Tasks that only need to access the \"left\" model. */\n private readonly taskSetsL: Map<ScenarioSpecUid, DataTaskSet> = new Map()\n\n /** Tasks that only need to access the \"right\" model. */\n private readonly taskSetsR: Map<ScenarioSpecUid, DataTaskSet> = new Map()\n\n private complete = false\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 request to the plan for the given scenario(s) and data task.\n *\n * @param scenarioSpecL The input scenario used to configure the \"left\" model, or undefined if no data\n * is needed from the left model.\n * @param scenarioSpecR The input scenario used to configure the \"right\" model, or undefined if no data\n * is needed from the right model.\n * @param datasetKey The key for the dataset to be fetched from each model for the given scenario.\n * @param dataAction The action to be performed with the fetched datasets.\n */\n addRequest(\n scenarioSpecL: ScenarioSpec | undefined,\n scenarioSpecR: ScenarioSpec | undefined,\n datasetKey: DatasetKey,\n dataAction: DataAction\n ): void {\n if (scenarioSpecL === undefined && scenarioSpecR === undefined) {\n console.warn('WARNING: Both scenario specs are undefined for DataPlanner request, skipping')\n return\n }\n\n // Determine which set this request will be added to\n let taskSetsMap: Map<ScenarioPairUid, DataTaskSet>\n let uid: string\n if (scenarioSpecL && scenarioSpecR) {\n taskSetsMap = this.taskSetsLR\n uid = scenarioPairUid(scenarioSpecL, scenarioSpecR)\n } else if (scenarioSpecR) {\n taskSetsMap = this.taskSetsR\n uid = scenarioSpecR.uid\n } else {\n taskSetsMap = this.taskSetsL\n uid = scenarioSpecL.uid\n }\n\n // Add the task to the appropriate task set (creating a new set if needed)\n let taskSet = taskSetsMap.get(uid)\n if (!taskSet) {\n taskSet = new DataTaskSet(scenarioSpecL, scenarioSpecR)\n taskSetsMap.set(uid, taskSet)\n }\n taskSet.addTask({\n datasetKey,\n dataAction\n })\n }\n\n /**\n * Build a plan that minimizes the number of data fetches needed.\n */\n buildPlan(): DataPlan {\n if (this.complete) {\n throw new Error('DataPlanner.buildPlan() can only be called once')\n }\n this.complete = true\n\n // Create mappings to make it easy to look up \"LR\" task sets using only\n // an \"L\" or \"R\" uid. This will create a mapping to the first available\n // set with an \"L\" uid on the left side (in case there are multiple with\n // the same \"L\" uid but different \"R\" uids), and same approach for \"R\" uids.\n const lKeyMappings: Map<ScenarioSpecUid, ScenarioPairUid> = new Map()\n const rKeyMappings: Map<ScenarioSpecUid, ScenarioPairUid> = new Map()\n for (const lrUid of this.taskSetsLR.keys()) {\n const [lUid, rUid] = lrUid.split('::')\n if (!lKeyMappings.has(lUid)) {\n lKeyMappings.set(lUid, lrUid)\n }\n if (!rKeyMappings.has(rUid)) {\n rKeyMappings.set(rUid, lrUid)\n }\n }\n\n // See if we can fold \"L-only\" and \"R-only\" requests into an existing \"LR\" set\n function merge(\n taskSetsLR: Map<ScenarioPairUid, DataTaskSet>,\n taskSetsForSide: Map<ScenarioSpecUid, DataTaskSet>,\n keyMappingsForSide: Map<ScenarioSpecUid, ScenarioPairUid>\n ) {\n for (const [keyForSide, taskSetForSide] of taskSetsForSide.entries()) {\n const lrKey = keyMappingsForSide.get(keyForSide)\n if (lrKey) {\n // Fold the one-side-only requests into an existing \"LR\" set\n const taskSetLR = taskSetsLR.get(lrKey)\n taskSetLR.merge(taskSetForSide)\n\n // Remove from the one-side-only set\n taskSetsForSide.delete(keyForSide)\n }\n }\n }\n merge(this.taskSetsLR, this.taskSetsL, lKeyMappings)\n merge(this.taskSetsLR, this.taskSetsR, rKeyMappings)\n\n // Build the final array of requests\n const requests: DataRequest[] = []\n const batchSize = this.batchSize\n function addRequests(taskSets: IterableIterator<DataTaskSet>) {\n for (const taskSet of taskSets) {\n requests.push(...taskSet.buildRequests(batchSize))\n }\n }\n addRequests(this.taskSetsLR.values())\n addRequests(this.taskSetsL.values())\n addRequests(this.taskSetsR.values())\n\n return {\n requests\n }\n }\n}\n\n/**\n * Groups together all data tasks that are associated with a pair of scenarios.\n */\nclass DataTaskSet {\n private readonly modelTasks: Map<DatasetKey, DataTask[]> = new Map()\n private readonly modelImplTasks: Map<DatasetKey, DataTask[]> = new Map()\n\n constructor(\n private readonly scenarioSpecL: ScenarioSpec | undefined,\n private readonly scenarioSpecR: ScenarioSpec | undefined\n ) {}\n\n /**\n * Add a task that will be performed when the data is fetched for the scenario(s)\n * associated with this task set.\n *\n * @param dataTask A task that performs an action using the fetched dataset(s).\n */\n addTask(dataTask: DataTask): void {\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 (dataTask.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(dataTask.datasetKey)\n if (!tasks) {\n tasks = []\n taskMap.set(dataTask.datasetKey, tasks)\n }\n tasks.push(dataTask)\n }\n\n /**\n * Add all tasks from the given set into this set.\n *\n * @param otherTaskSet The other task set that will be merged into this one.\n */\n merge(otherTaskSet: DataTaskSet): void {\n for (const tasks of otherTaskSet.modelTasks.values()) {\n for (const task of tasks) {\n this.addTask(task)\n }\n }\n for (const tasks of otherTaskSet.modelImplTasks.values()) {\n for (const task of tasks) {\n this.addTask(task)\n }\n }\n }\n\n /**\n * Create one or more data requests that can be used to fetch data for the configured scenario(s).\n *\n * @param batchSize The maximum number of impl vars that can be fetched with a single request.\n * This is usually the same as the number of 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 scenarioSpecL: this.scenarioSpecL,\n scenarioSpecR: this.scenarioSpecR,\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 scenarioSpecL: this.scenarioSpecL,\n scenarioSpecR: this.scenarioSpecR,\n dataTasks\n })\n }\n }\n\n return dataRequests\n }\n}\n\n/**\n * An ID that includes one or two scenario UIDs to uniquely identify a pair of scenarios.\n * The format is `<left_uid>::<right_uid>`.\n */\ntype ScenarioPairUid = string\n\n/**\n * Create a unique identifier for the given scenarios.\n *\n * For example, a request that includes scenarios for both left and right might look like:\n * scenario1::scenario1\n *\n * A request that only accesses the right model might use a key like:\n * ::scenario1\n *\n * A request that only accesses the left model might use a key like:\n * scenario1::\n *\n * @param scenarioSpecL The input scenario used to configure the \"left\" model, or undefined if no data\n * is needed from the left model.\n * @param scenarioSpecR The input scenario used to configure the \"right\" model, or undefined if no data\n * is needed from the right model.\n */\nfunction scenarioPairUid(\n scenarioSpecL: ScenarioSpec | undefined,\n scenarioSpecR: ScenarioSpec | undefined\n): ScenarioPairUid {\n const uidL = scenarioSpecL?.uid || ''\n const uidR = scenarioSpecR?.uid || ''\n return `${uidL}::${uidR}`\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(undefined, dataRef.scenario.spec, 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(undefined, checkTask.scenario.spec, 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.spec === 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 type { DataPlanner } from '../../data/data-planner'\nimport type { ComparisonConfig } from '../config/comparison-config'\nimport { diffDatasets } from '../diff-datasets/diff-datasets'\nimport type { ComparisonTestReport } from '../report/comparison-report-types'\n\n/**\n * Prepare all comparison tests and add them to the given data planner.\n *\n * @param comparisonConfig The comparison configuration.\n * @param dataPlanner The planner that will plan out data fetches for the compare tests.\n * @return A function that will build the compare reports after the data requests are all processed.\n */\nexport function runComparisons(\n comparisonConfig: ComparisonConfig,\n dataPlanner: DataPlanner\n): () => ComparisonTestReport[] {\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 testReports: ComparisonTestReport[] = []\n for (const scenario of comparisonConfig.scenarios.getAllScenarios()) {\n // Get the keys of the datasets of interest for this scenario\n const datasetKeys = comparisonConfig.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(scenario.specL, scenario.specR, datasetKey, datasets => {\n // Diff the two datasets\n const diffReport = diffDatasets(datasets.datasetL, datasets.datasetR)\n testReports.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 test 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 testReports\n }\n}\n","// Copyright (c) 2021-2022 Climate Interactive / New Venture Fund\n\nimport type { DatasetsResult } from '../_shared/data-source'\nimport type { ScenarioSpec } from '../_shared/scenario-spec-types'\nimport { TaskQueue } from '../_shared/task-queue'\nimport type { DatasetKey } from '../_shared/types'\n\nimport type { BundleModel } from '../bundle/bundle-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 { runComparisons } from '../comparison/run/comparison-runner'\nimport type { ComparisonReport, ComparisonTestReport } from '../comparison/report/comparison-report-types'\n\nimport type { Config } from '../config/config-types'\n\nimport { PerfStats } from '../perf/perf-stats'\n\nimport type { SuiteReport } from './suite-report-types'\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 buildComparisonTestReports: () => ComparisonTestReport[]\n if (this.config.comparison) {\n buildComparisonTestReports = runComparisons(this.config.comparison, dataPlanner)\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 comparisonReport: ComparisonReport\n if (this.config.comparison) {\n comparisonReport = {\n testReports: buildComparisonTestReports(),\n perfReportL: this.perfStatsL.toReport(),\n perfReportR: this.perfStatsR.toReport()\n }\n }\n this.callbacks.onComplete?.({\n checkReport,\n comparisonReport\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 comparisonReport: ComparisonReport\n if (this.config.comparison) {\n comparisonReport = {\n testReports: [],\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 comparisonReport\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 async function getDatasets(\n bundleModel: BundleModel | undefined,\n scenarioSpec: ScenarioSpec | undefined\n ): Promise<DatasetsResult> {\n if (bundleModel && scenarioSpec) {\n return bundleModel.getDatasetsForScenario(scenarioSpec, datasetKeys)\n } else {\n return undefined\n }\n }\n\n // Run the model(s) in parallel and extract the requested datasets\n const bundleModelL = this.config.comparison?.bundleL.model\n const bundleModelR = this.config.comparison?.bundleR.model || this.config.check.bundle.model\n const [datasetsResultL, datasetsResultR] = await Promise.all([\n getDatasets(bundleModelL, request.scenarioSpecL),\n getDatasets(bundleModelR, request.scenarioSpecR)\n ])\n\n // Update the performance stats\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.dataAction({\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) 2023 Climate Interactive / New Venture Fund\n\nimport { checkSummaryFromReport } from '../check/check-summary'\n\nimport type { ComparisonSummary } from '../comparison/report/comparison-report-types'\nimport { comparisonSummaryFromReport } from '../comparison/report/comparison-reporting'\n\nimport type { SuiteReport, SuiteSummary } from './suite-report-types'\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 comparison report to terse summaries\n let comparisonSummary: ComparisonSummary\n if (suiteReport.comparisonReport) {\n comparisonSummary = comparisonSummaryFromReport(suiteReport.comparisonReport)\n }\n\n return {\n checkSummary,\n comparisonSummary\n }\n}\n"],"mappings":"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","names":["import_assert_never","assertNever","assertNever","import_assert_never","Ajv","yaml","import_assert_never","import_assert_never","import_assert_never","assertNever","inputVar","assertNever","import_assert_never","import_assert_never","import_ajv","import_assert_never","import_neverthrow","import_yaml","Ajv","yaml","assertNever","import_assert_never","import_assert_never","inputPosition","scenario","inputValueAtPosition","import_assert_never","buildCheckReport","_a","_b","_c","_d"]}