parent
cac9d7e230
commit
869498fa97
@ -1,4 +1,10 @@
|
|||||||
import { UnitCommitmentScenario } from "./fixtures";
|
/*
|
||||||
|
* UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
|
||||||
|
* Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved.
|
||||||
|
* Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { UnitCommitmentScenario } from "./types";
|
||||||
|
|
||||||
export const TEST_DATA_1: UnitCommitmentScenario = {
|
export const TEST_DATA_1: UnitCommitmentScenario = {
|
||||||
Parameters: {
|
Parameters: {
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
|
||||||
|
* Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved.
|
||||||
|
* Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import assert from "node:assert";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import pako from "pako";
|
||||||
|
import { migrateToV03, migrateToV04 } from "./migrate";
|
||||||
|
|
||||||
|
function readJsonGz(filename: string) {
|
||||||
|
const compressedData = fs.readFileSync(filename);
|
||||||
|
const decompressedData = pako.inflate(compressedData, { to: "string" });
|
||||||
|
return JSON.parse(decompressedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
test("migrateToV03", () => {
|
||||||
|
const jsonData = readJsonGz("../test/fixtures/ucjl-0.2.json.gz");
|
||||||
|
migrateToV03(jsonData);
|
||||||
|
assert.deepEqual(jsonData.Reserves, {
|
||||||
|
r1: {
|
||||||
|
"Amount (MW)": 100,
|
||||||
|
"Shortfall penalty ($/MW)": 1000,
|
||||||
|
Type: "spinning",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("migrateToV04", () => {
|
||||||
|
const jsonData = readJsonGz("../test/fixtures/ucjl-0.3.json.gz");
|
||||||
|
migrateToV04(jsonData);
|
||||||
|
assert.equal(jsonData.Generators["g1"].Type, "Thermal");
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
|
||||||
|
* Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved.
|
||||||
|
* Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ValidationError } from "./validate";
|
||||||
|
|
||||||
|
export const migrate = (json: any): ValidationError | null => {
|
||||||
|
const version = json.Parameters?.Version;
|
||||||
|
if (!version) {
|
||||||
|
return {
|
||||||
|
message:
|
||||||
|
"The provided input file cannot be loaded because it does not " +
|
||||||
|
"specify what version of UnitCommitment.jl it was written for.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!["0.2", "0.3", "0.4"].includes(version)) {
|
||||||
|
return { message: `Unsupported file version: ${version}` };
|
||||||
|
}
|
||||||
|
if (version < "0.3") migrateToV03(json);
|
||||||
|
if (version < "0.4") migrateToV04(json);
|
||||||
|
json.Parameters.Version = "0.4";
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const migrateToV03 = (json: any): void => {
|
||||||
|
if (json.Reserves && json.Reserves["Spinning (MW)"] != null) {
|
||||||
|
const amount = json.Reserves["Spinning (MW)"];
|
||||||
|
json.Reserves = {
|
||||||
|
r1: {
|
||||||
|
Type: "spinning",
|
||||||
|
"Amount (MW)": amount,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (json.Generators) {
|
||||||
|
for (const genName in json.Generators) {
|
||||||
|
const gen = json.Generators[genName];
|
||||||
|
if (gen["Provides spinning reserves?"] === true) {
|
||||||
|
gen["Reserve eligibility"] = ["r1"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const migrateToV04 = (json: any): void => {
|
||||||
|
if (json.Generators) {
|
||||||
|
for (const genName in json.Generators) {
|
||||||
|
const gen = json.Generators[genName];
|
||||||
|
if (gen.Type == null) {
|
||||||
|
gen.Type = "Thermal";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
|
||||||
|
* Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved.
|
||||||
|
* Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Buses } from "./fixtures";
|
||||||
|
|
||||||
|
export interface Generators {
|
||||||
|
[name: string]: ProfiledUnit | ThermalUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProfiledUnit {
|
||||||
|
Bus: string;
|
||||||
|
Type: "Profiled";
|
||||||
|
"Minimum power (MW)": number[];
|
||||||
|
"Maximum power (MW)": number[];
|
||||||
|
"Cost ($/MW)": number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ThermalUnit {
|
||||||
|
Bus: string;
|
||||||
|
Type: "Thermal";
|
||||||
|
"Production cost curve (MW)": number[];
|
||||||
|
"Production cost curve ($)": number[];
|
||||||
|
"Startup costs ($)": number[];
|
||||||
|
"Startup delays (h)": number[];
|
||||||
|
"Ramp up limit (MW)": number | "";
|
||||||
|
"Ramp down limit (MW)": number | "";
|
||||||
|
"Startup limit (MW)": number | "";
|
||||||
|
"Shutdown limit (MW)": number | "";
|
||||||
|
"Minimum downtime (h)": number;
|
||||||
|
"Minimum uptime (h)": number;
|
||||||
|
"Initial status (h)": number;
|
||||||
|
"Initial power (MW)": number;
|
||||||
|
"Must run?": boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransmissionLine {
|
||||||
|
"Source bus": string;
|
||||||
|
"Target bus": string;
|
||||||
|
"Susceptance (S)": number;
|
||||||
|
"Normal flow limit (MW)": number;
|
||||||
|
"Emergency flow limit (MW)": number;
|
||||||
|
"Flow limit penalty ($/MW)": number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UnitCommitmentScenario {
|
||||||
|
Parameters: {
|
||||||
|
Version: string;
|
||||||
|
"Power balance penalty ($/MW)": number;
|
||||||
|
"Time horizon (h)": number;
|
||||||
|
"Time step (min)": number;
|
||||||
|
};
|
||||||
|
Buses: Buses;
|
||||||
|
Generators: Generators;
|
||||||
|
"Transmission lines": {
|
||||||
|
[name: string]: TransmissionLine;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTypedGenerators = <T extends any>(
|
||||||
|
scenario: UnitCommitmentScenario,
|
||||||
|
type: string,
|
||||||
|
): {
|
||||||
|
[key: string]: T;
|
||||||
|
} => {
|
||||||
|
const selected: { [key: string]: T } = {};
|
||||||
|
for (const [name, gen] of Object.entries(scenario.Generators)) {
|
||||||
|
if (gen["Type"] === type) selected[name] = gen as T;
|
||||||
|
}
|
||||||
|
return selected;
|
||||||
|
};
|
||||||
|
export const getProfiledGenerators = (
|
||||||
|
scenario: UnitCommitmentScenario,
|
||||||
|
): { [key: string]: ProfiledUnit } =>
|
||||||
|
getTypedGenerators<ProfiledUnit>(scenario, "Profiled");
|
||||||
|
export const getThermalGenerators = (
|
||||||
|
scenario: UnitCommitmentScenario,
|
||||||
|
): { [key: string]: ThermalUnit } =>
|
||||||
|
getTypedGenerators<ThermalUnit>(scenario, "Thermal");
|
Loading…
Reference in new issue