From 3934f1973ccc7cfd92ca56718c3f909bdb5adcdc Mon Sep 17 00:00:00 2001 From: jacobvjk Date: Tue, 6 Oct 2026 12:09:11 +0200 Subject: [PATCH] fix(schema): Power timeseries technologies match the taxonomy A Power row's technology must satisfy both the all-sector enum and the Power list in the schema's if/then. The Power list had geothermal, battery and hydrogen, which the all-sector enum lacked, so they could never validate (#977). The Power list is now exactly the taxonomy's Power technology keys, which the plots and CSV download resolve every row against: geothermal and energyStorage (cookbook decision 0020; matches the Energy storage segment) are added to the all-sector enum, battery and hydrogen are dropped. A schema test keeps the two lists in agreement. No current data uses any of the changed values. Co-Authored-By: Claude Opus 5.5 --- src/schema/pathwayTimeseries.v2.json | 6 ++- src/schema/pathwayTimeseries.v2.test.ts | 53 +++++++++++++++++++++++++ src/types/pathwayTimeseries.v2.d.ts | 2 + 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/schema/pathwayTimeseries.v2.test.ts diff --git a/src/schema/pathwayTimeseries.v2.json b/src/schema/pathwayTimeseries.v2.json index 8e098abb..d37ce13d 100644 --- a/src/schema/pathwayTimeseries.v2.json +++ b/src/schema/pathwayTimeseries.v2.json @@ -129,6 +129,8 @@ "nuclear", "biomass", "hydro", + "geothermal", + "energyStorage", "renewables", "electricVehicles", "hydrogenVehicles", @@ -295,6 +297,7 @@ "then": { "properties": { "technology": { + "$comment": "Exactly the Power technology keys of src/utils/timeseriesTaxonomy.ts, which the plots and the CSV download look every row up in; pathwayTimeseries.v2.test.ts checks the two agree. Every value must also be in the all-sector enum above, because both lists apply to a Power row: `geothermal`, `battery` and `hydrogen` used to be listed here only, so they could never validate (#977). Storage is `energyStorage` (cookbook decision 0020), matching the `Energy storage` segment.", "type": ["string", "null"], "enum": [ "coal", @@ -306,9 +309,8 @@ "solar", "biomass", "geothermal", - "hydrogen", + "energyStorage", "renewables", - "battery", "other", null ] diff --git a/src/schema/pathwayTimeseries.v2.test.ts b/src/schema/pathwayTimeseries.v2.test.ts new file mode 100644 index 00000000..39e844cb --- /dev/null +++ b/src/schema/pathwayTimeseries.v2.test.ts @@ -0,0 +1,53 @@ +import { describe, it, expect } from "vitest"; +import timeseriesJson from "./pathwayTimeseries.v2.json" with { type: "json" }; +import { commonSchemas } from "./common/index.ts"; +import { validateFilesBySchema } from "../utils/validateData"; +import { SECTORS_BY_KEY } from "../utils/timeseriesTaxonomy"; +import ieaSteps from "../data/iea/IEA-STEPS-2024_timeseries.json" with { type: "json" }; + +/** The slice of the schema these assertions read. */ +interface RowSchema { + properties: { technology: { enum: (string | null)[] } }; + then: { properties: { technology: { enum: (string | null)[] } } }; +} + +const row = ( + timeseriesJson as unknown as { + properties: { data: { items: RowSchema } }; + } +).properties.data.items; +const allSectors = row.properties.technology.enum; +const power = row.then.properties.technology.enum; + +describe("pathwayTimeseries.v2 Power technologies (#977)", () => { + it("are exactly the taxonomy's Power technology keys", () => { + // The plots and the CSV download look every row's technology up in the + // taxonomy, so the schema must not allow one it cannot resolve. + const taxonomy = Object.keys(SECTORS_BY_KEY.power.technologies ?? {}); + expect(power.filter((t) => t !== null).sort()).toEqual(taxonomy.sort()); + expect(power).toContain(null); // a sector-level row has no technology + }); + + it("are all in the all-sector enum too, since both lists apply", () => { + expect(power.filter((t) => !allSectors.includes(t))).toEqual([]); + }); + + const validate = (technology: string) => { + const doc = structuredClone(ieaSteps) as { + data: { technology: unknown }[]; + }; + doc.data = [{ ...doc.data[0], technology }]; + return validateFilesBySchema( + [{ name: "row.json", data: doc }], + [timeseriesJson, ...commonSchemas], + ); + }; + + it.each(["geothermal", "energyStorage"])("accepts a %s row", (technology) => { + expect(validate(technology).invalid).toEqual([]); + }); + + it.each(["battery", "hydrogen"])("rejects the retired %s", (technology) => { + expect(validate(technology).invalid).toHaveLength(1); + }); +}); diff --git a/src/types/pathwayTimeseries.v2.d.ts b/src/types/pathwayTimeseries.v2.d.ts index 6237d9be..baf7601a 100644 --- a/src/types/pathwayTimeseries.v2.d.ts +++ b/src/types/pathwayTimeseries.v2.d.ts @@ -115,6 +115,8 @@ export interface PathwayTimeseriesV2 { | "nuclear" | "biomass" | "hydro" + | "geothermal" + | "energyStorage" | "renewables" | "electricVehicles" | "hydrogenVehicles"