Compare commits
No commits in common. 'eb3d39b1abc1db6682c92da38532f2244672fe21' and '012331c4bd2fcac1f50dd808f2e0da0181259e1b' have entirely different histories.
eb3d39b1ab
...
012331c4bd
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 { BusesColumnSpec, generateBusesData } from "./Buses";
|
|
||||||
import { generateCsv, parseCsv } from "../Common/Forms/DataTable";
|
|
||||||
import { TEST_DATA_1 } from "../../core/fixtures.test";
|
|
||||||
|
|
||||||
test("generate CSV", () => {
|
|
||||||
const [data, columns] = generateBusesData(TEST_DATA_1);
|
|
||||||
const actualCsv = generateCsv(data, columns);
|
|
||||||
const expectedCsv =
|
|
||||||
"Name,Load (MW) 00:00,Load (MW) 01:00,Load (MW) 02:00,Load (MW) 03:00,Load (MW) 04:00\n" +
|
|
||||||
"b1,35.79534,34.38835,33.45083,32.89729,33.25044\n" +
|
|
||||||
"b2,14.03739,13.48563,13.11797,12.9009,13.03939\n" +
|
|
||||||
"b3,27.3729,26.29698,25.58005,25.15675,25.4268";
|
|
||||||
assert.strictEqual(actualCsv, expectedCsv);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("parse CSV", () => {
|
|
||||||
const csvContents =
|
|
||||||
"Name,Load (MW) 00:00,Load (MW) 01:00,Load (MW) 02:00,Load (MW) 03:00,Load (MW) 04:00\n" +
|
|
||||||
"b1,0,1,2,3,4\n" +
|
|
||||||
"b3,27.3729,26.29698,25.58005,25.15675,25.4268";
|
|
||||||
const [newBuses, err] = parseCsv(csvContents, BusesColumnSpec, TEST_DATA_1);
|
|
||||||
assert(err === null);
|
|
||||||
assert.deepEqual(newBuses, {
|
|
||||||
b1: {
|
|
||||||
"Load (MW)": [0, 1, 2, 3, 4],
|
|
||||||
},
|
|
||||||
b3: {
|
|
||||||
"Load (MW)": [27.3729, 26.29698, 25.58005, 25.15675, 25.4268],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("parse CSV with duplicated names", () => {
|
|
||||||
const csvContents =
|
|
||||||
"Name,Load (MW) 00:00,Load (MW) 01:00,Load (MW) 02:00,Load (MW) 03:00,Load (MW) 04:00\n" +
|
|
||||||
"b1,0,0,0,0,0\n" +
|
|
||||||
"b1,0,0,0,0,0";
|
|
||||||
const [, err] = parseCsv(csvContents, BusesColumnSpec, TEST_DATA_1);
|
|
||||||
assert(err !== null);
|
|
||||||
assert.equal(err.message, `Name "b1" is duplicated (row 2)`);
|
|
||||||
});
|
|
@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 {
|
|
||||||
floatFormatter,
|
|
||||||
generateTableColumns,
|
|
||||||
parseCsv,
|
|
||||||
} from "../Common/Forms/DataTable";
|
|
||||||
import {
|
|
||||||
parseProfiledUnitsCsv,
|
|
||||||
ProfiledUnitsColumnSpec,
|
|
||||||
} from "./ProfiledUnits";
|
|
||||||
import { TEST_DATA_1 } from "../../core/fixtures.test";
|
|
||||||
import assert from "node:assert";
|
|
||||||
import {
|
|
||||||
getProfiledGenerators,
|
|
||||||
getThermalGenerators,
|
|
||||||
} from "../../core/fixtures";
|
|
||||||
|
|
||||||
test("parse CSV", () => {
|
|
||||||
const csvContents =
|
|
||||||
"Name,Bus,Cost ($/MW),Maximum power (MW) 00:00,Maximum power (MW) 01:00," +
|
|
||||||
"Maximum power (MW) 02:00,Maximum power (MW) 03:00," +
|
|
||||||
"Maximum power (MW) 04:00,Minimum power (MW) 00:00," +
|
|
||||||
"Minimum power (MW) 01:00,Minimum power (MW) 02:00," +
|
|
||||||
"Minimum power (MW) 03:00,Minimum power (MW) 04:00\n" +
|
|
||||||
"pu1,b1,50,260.25384545,72.89148068,377.17886108,336.66732361," +
|
|
||||||
"376.82781758,52.05076909,14.57829614,75.43577222,67.33346472,75.36556352\n" +
|
|
||||||
"pu2,b1,0,0,0,0,0,0,0,0,0,0,0";
|
|
||||||
const [scenario, err] = parseProfiledUnitsCsv(csvContents, TEST_DATA_1);
|
|
||||||
assert(err === null);
|
|
||||||
const thermalGens = getThermalGenerators(scenario);
|
|
||||||
const profGens = getProfiledGenerators(scenario);
|
|
||||||
assert.equal(Object.keys(thermalGens).length, 1);
|
|
||||||
assert.equal(Object.keys(profGens).length, 2);
|
|
||||||
|
|
||||||
assert.deepEqual(profGens, {
|
|
||||||
pu1: {
|
|
||||||
Bus: "b1",
|
|
||||||
"Minimum power (MW)": [
|
|
||||||
52.05076909, 14.57829614, 75.43577222, 67.33346472, 75.36556352,
|
|
||||||
],
|
|
||||||
"Maximum power (MW)": [
|
|
||||||
260.25384545, 72.89148068, 377.17886108, 336.66732361, 376.82781758,
|
|
||||||
],
|
|
||||||
"Cost ($/MW)": 50.0,
|
|
||||||
Type: "Profiled",
|
|
||||||
},
|
|
||||||
pu2: {
|
|
||||||
Bus: "b1",
|
|
||||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
|
||||||
"Maximum power (MW)": [0, 0, 0, 0, 0],
|
|
||||||
"Cost ($/MW)": 0.0,
|
|
||||||
Type: "Profiled",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("parse CSV with invalid bus", () => {
|
|
||||||
const csvContents =
|
|
||||||
"Name,Bus,Cost ($/MW),Maximum power (MW) 00:00,Maximum power (MW) 01:00," +
|
|
||||||
"Maximum power (MW) 02:00,Maximum power (MW) 03:00," +
|
|
||||||
"Maximum power (MW) 04:00,Minimum power (MW) 00:00," +
|
|
||||||
"Minimum power (MW) 01:00,Minimum power (MW) 02:00," +
|
|
||||||
"Minimum power (MW) 03:00,Minimum power (MW) 04:00\n" +
|
|
||||||
"pu1,b99,50,260.25384545,72.89148068,377.17886108,336.66732361," +
|
|
||||||
"376.82781758,52.05076909,14.57829614,75.43577222,67.33346472,75.36556352\n" +
|
|
||||||
"pu2,b1,0,0,0,0,0,0,0,0,0,0,0";
|
|
||||||
const [, err] = parseCsv(csvContents, ProfiledUnitsColumnSpec, TEST_DATA_1);
|
|
||||||
assert(err !== null);
|
|
||||||
assert.equal(err.message, 'Bus "b99" does not exist (row 1)');
|
|
||||||
});
|
|
||||||
|
|
||||||
test("generateTableColumns", () => {
|
|
||||||
const columns = generateTableColumns(TEST_DATA_1, ProfiledUnitsColumnSpec);
|
|
||||||
assert.equal(columns.length, 5);
|
|
||||||
assert.deepEqual(columns[0], {
|
|
||||||
editor: "input",
|
|
||||||
editorParams: {
|
|
||||||
selectContents: true,
|
|
||||||
},
|
|
||||||
field: "Name",
|
|
||||||
formatter: "plaintext",
|
|
||||||
headerHozAlign: "left",
|
|
||||||
headerSort: false,
|
|
||||||
headerWordWrap: true,
|
|
||||||
hozAlign: "left",
|
|
||||||
minWidth: 100,
|
|
||||||
resizable: false,
|
|
||||||
title: "Name",
|
|
||||||
});
|
|
||||||
assert.equal(columns[3]!["columns"]!.length, 5);
|
|
||||||
assert.deepEqual(columns[3]!["columns"]![0], {
|
|
||||||
editor: "input",
|
|
||||||
editorParams: {
|
|
||||||
selectContents: true,
|
|
||||||
},
|
|
||||||
field: "Maximum power (MW) 00:00",
|
|
||||||
formatter: floatFormatter,
|
|
||||||
headerHozAlign: "left",
|
|
||||||
headerSort: false,
|
|
||||||
headerWordWrap: true,
|
|
||||||
hozAlign: "left",
|
|
||||||
minWidth: 60,
|
|
||||||
resizable: false,
|
|
||||||
title: "00:00",
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,209 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 {
|
|
||||||
floatFormatter,
|
|
||||||
generateCsv,
|
|
||||||
generateTableColumns,
|
|
||||||
generateTableData,
|
|
||||||
} from "../Common/Forms/DataTable";
|
|
||||||
import { TEST_DATA_1 } from "../../core/fixtures.test";
|
|
||||||
import {
|
|
||||||
generateThermalUnitsData,
|
|
||||||
parseThermalUnitsCsv,
|
|
||||||
ThermalUnitsColumnSpec,
|
|
||||||
} from "./ThermalUnits";
|
|
||||||
import assert from "node:assert";
|
|
||||||
import {
|
|
||||||
getProfiledGenerators,
|
|
||||||
getThermalGenerators,
|
|
||||||
} from "../../core/fixtures";
|
|
||||||
|
|
||||||
test("generateTableColumns", () => {
|
|
||||||
const columns = generateTableColumns(TEST_DATA_1, ThermalUnitsColumnSpec);
|
|
||||||
assert.equal(columns[2]!["columns"]!.length, 10);
|
|
||||||
assert.deepEqual(columns[2]!["columns"]![0], {
|
|
||||||
editor: "input",
|
|
||||||
editorParams: {
|
|
||||||
selectContents: true,
|
|
||||||
},
|
|
||||||
field: "Production cost curve (MW) 1",
|
|
||||||
formatter: floatFormatter,
|
|
||||||
headerHozAlign: "left",
|
|
||||||
headerSort: false,
|
|
||||||
headerWordWrap: true,
|
|
||||||
hozAlign: "left",
|
|
||||||
minWidth: 60,
|
|
||||||
resizable: false,
|
|
||||||
title: "1",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("generateTableData", () => {
|
|
||||||
const data = generateTableData(
|
|
||||||
getThermalGenerators(TEST_DATA_1),
|
|
||||||
ThermalUnitsColumnSpec,
|
|
||||||
TEST_DATA_1,
|
|
||||||
);
|
|
||||||
assert.deepEqual(data[0], {
|
|
||||||
Name: "g1",
|
|
||||||
Bus: "b1",
|
|
||||||
"Initial power (MW)": 115,
|
|
||||||
"Initial status (h)": 12,
|
|
||||||
"Minimum downtime (h)": 4,
|
|
||||||
"Minimum uptime (h)": 4,
|
|
||||||
"Ramp down limit (MW)": 232.68,
|
|
||||||
"Ramp up limit (MW)": 232.68,
|
|
||||||
"Shutdown limit (MW)": 232.68,
|
|
||||||
"Startup limit (MW)": 232.68,
|
|
||||||
"Production cost curve ($) 1": 1400,
|
|
||||||
"Production cost curve ($) 2": 1600,
|
|
||||||
"Production cost curve ($) 3": 2200,
|
|
||||||
"Production cost curve ($) 4": 2400,
|
|
||||||
"Production cost curve ($) 5": "",
|
|
||||||
"Production cost curve ($) 6": "",
|
|
||||||
"Production cost curve ($) 7": "",
|
|
||||||
"Production cost curve ($) 8": "",
|
|
||||||
"Production cost curve ($) 9": "",
|
|
||||||
"Production cost curve ($) 10": "",
|
|
||||||
"Production cost curve (MW) 1": 100,
|
|
||||||
"Production cost curve (MW) 2": 110,
|
|
||||||
"Production cost curve (MW) 3": 130,
|
|
||||||
"Production cost curve (MW) 4": 135,
|
|
||||||
"Production cost curve (MW) 5": "",
|
|
||||||
"Production cost curve (MW) 6": "",
|
|
||||||
"Production cost curve (MW) 7": "",
|
|
||||||
"Production cost curve (MW) 8": "",
|
|
||||||
"Production cost curve (MW) 9": "",
|
|
||||||
"Production cost curve (MW) 10": "",
|
|
||||||
"Startup costs ($) 1": 300,
|
|
||||||
"Startup costs ($) 2": 400,
|
|
||||||
"Startup costs ($) 3": "",
|
|
||||||
"Startup costs ($) 4": "",
|
|
||||||
"Startup costs ($) 5": "",
|
|
||||||
"Startup delays (h) 1": 1,
|
|
||||||
"Startup delays (h) 2": 4,
|
|
||||||
"Startup delays (h) 3": "",
|
|
||||||
"Startup delays (h) 4": "",
|
|
||||||
"Startup delays (h) 5": "",
|
|
||||||
"Must run?": false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const expectedCsvContents =
|
|
||||||
"Name,Bus," +
|
|
||||||
"Production cost curve (MW) 1," +
|
|
||||||
"Production cost curve (MW) 2," +
|
|
||||||
"Production cost curve (MW) 3," +
|
|
||||||
"Production cost curve (MW) 4," +
|
|
||||||
"Production cost curve (MW) 5," +
|
|
||||||
"Production cost curve (MW) 6," +
|
|
||||||
"Production cost curve (MW) 7," +
|
|
||||||
"Production cost curve (MW) 8," +
|
|
||||||
"Production cost curve (MW) 9," +
|
|
||||||
"Production cost curve (MW) 10," +
|
|
||||||
"Production cost curve ($) 1," +
|
|
||||||
"Production cost curve ($) 2," +
|
|
||||||
"Production cost curve ($) 3," +
|
|
||||||
"Production cost curve ($) 4," +
|
|
||||||
"Production cost curve ($) 5," +
|
|
||||||
"Production cost curve ($) 6," +
|
|
||||||
"Production cost curve ($) 7," +
|
|
||||||
"Production cost curve ($) 8," +
|
|
||||||
"Production cost curve ($) 9," +
|
|
||||||
"Production cost curve ($) 10," +
|
|
||||||
"Startup costs ($) 1," +
|
|
||||||
"Startup costs ($) 2," +
|
|
||||||
"Startup costs ($) 3," +
|
|
||||||
"Startup costs ($) 4," +
|
|
||||||
"Startup costs ($) 5," +
|
|
||||||
"Startup delays (h) 1," +
|
|
||||||
"Startup delays (h) 2," +
|
|
||||||
"Startup delays (h) 3," +
|
|
||||||
"Startup delays (h) 4," +
|
|
||||||
"Startup delays (h) 5," +
|
|
||||||
"Minimum uptime (h),Minimum downtime (h),Ramp up limit (MW)," +
|
|
||||||
"Ramp down limit (MW),Startup limit (MW),Shutdown limit (MW)," +
|
|
||||||
"Initial status (h),Initial power (MW),Must run?\n" +
|
|
||||||
"g1,b1,100,110,130,135,,,,,,,1400,1600,2200,2400,,,,,,,300,400,,,,1,4,,,,4,4,232.68,232.68,232.68,232.68,12,115,false";
|
|
||||||
|
|
||||||
const invalidCsv =
|
|
||||||
"Name,Bus," +
|
|
||||||
"Production cost curve (MW) 1," +
|
|
||||||
"Production cost curve (MW) 2," +
|
|
||||||
"Production cost curve (MW) 3," +
|
|
||||||
"Production cost curve (MW) 4," +
|
|
||||||
"Production cost curve (MW) 5," +
|
|
||||||
"Production cost curve (MW) 6," +
|
|
||||||
"Production cost curve (MW) 7," +
|
|
||||||
"Production cost curve (MW) 8," +
|
|
||||||
"Production cost curve (MW) 9," +
|
|
||||||
"Production cost curve (MW) 10," +
|
|
||||||
"Production cost curve ($) 1," +
|
|
||||||
"Production cost curve ($) 2," +
|
|
||||||
"Production cost curve ($) 3," +
|
|
||||||
"Production cost curve ($) 4," +
|
|
||||||
"Production cost curve ($) 5," +
|
|
||||||
"Production cost curve ($) 6," +
|
|
||||||
"Production cost curve ($) 7," +
|
|
||||||
"Production cost curve ($) 8," +
|
|
||||||
"Production cost curve ($) 9," +
|
|
||||||
"Production cost curve ($) 10," +
|
|
||||||
"Startup costs ($) 1," +
|
|
||||||
"Startup costs ($) 2," +
|
|
||||||
"Startup costs ($) 3," +
|
|
||||||
"Startup costs ($) 4," +
|
|
||||||
"Startup costs ($) 5," +
|
|
||||||
"Startup delays (h) 1," +
|
|
||||||
"Startup delays (h) 2," +
|
|
||||||
"Startup delays (h) 3," +
|
|
||||||
"Startup delays (h) 4," +
|
|
||||||
"Startup delays (h) 5," +
|
|
||||||
"Minimum uptime (h),Minimum downtime (h),Ramp up limit (MW)," +
|
|
||||||
"Ramp down limit (MW),Startup limit (MW),Shutdown limit (MW)," +
|
|
||||||
"Initial status (h),Initial power (MW),Must run?\n" +
|
|
||||||
"g1,b1,100,110,130,x,,,,,,,1400,1600,2200,2400,,,,,,,300,400,,,,1,4,,,,4,4,232.68,232.68,232.68,232.68,12,115,false";
|
|
||||||
|
|
||||||
test("generateCSV", () => {
|
|
||||||
const [data, columns] = generateThermalUnitsData(TEST_DATA_1);
|
|
||||||
const actualCsvContents = generateCsv(data, columns);
|
|
||||||
assert.equal(actualCsvContents, expectedCsvContents);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("parseCSV", () => {
|
|
||||||
const [scenario, err] = parseThermalUnitsCsv(
|
|
||||||
expectedCsvContents,
|
|
||||||
TEST_DATA_1,
|
|
||||||
);
|
|
||||||
assert(!err);
|
|
||||||
const thermalGens = getThermalGenerators(scenario);
|
|
||||||
const profGens = getProfiledGenerators(scenario);
|
|
||||||
assert.equal(Object.keys(thermalGens).length, 1);
|
|
||||||
assert.equal(Object.keys(profGens).length, 2);
|
|
||||||
assert.deepEqual(thermalGens["g1"], {
|
|
||||||
Bus: "b1",
|
|
||||||
Type: "Thermal",
|
|
||||||
"Production cost curve (MW)": [100.0, 110.0, 130.0, 135.0],
|
|
||||||
"Production cost curve ($)": [1400.0, 1600.0, 2200.0, 2400.0],
|
|
||||||
"Startup costs ($)": [300.0, 400.0],
|
|
||||||
"Startup delays (h)": [1, 4],
|
|
||||||
"Ramp up limit (MW)": 232.68,
|
|
||||||
"Ramp down limit (MW)": 232.68,
|
|
||||||
"Startup limit (MW)": 232.68,
|
|
||||||
"Shutdown limit (MW)": 232.68,
|
|
||||||
"Minimum downtime (h)": 4,
|
|
||||||
"Minimum uptime (h)": 4,
|
|
||||||
"Initial status (h)": 12,
|
|
||||||
"Initial power (MW)": 115,
|
|
||||||
"Must run?": false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("parseCSV with invalid number[T]", () => {
|
|
||||||
const [, err] = parseThermalUnitsCsv(invalidCsv, TEST_DATA_1);
|
|
||||||
assert(err);
|
|
||||||
assert.equal(err.message, '"x" is not a valid number (row 1)');
|
|
||||||
});
|
|
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* 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 { BusesColumnSpec, generateBusesData } from "../../CaseBuilder/Buses";
|
||||||
|
import {
|
||||||
|
floatFormatter,
|
||||||
|
generateCsv,
|
||||||
|
generateTableColumns,
|
||||||
|
generateTableData,
|
||||||
|
parseCsv,
|
||||||
|
} from "./DataTable";
|
||||||
|
import { TEST_DATA_1 } from "../../../core/fixtures.test";
|
||||||
|
import { ProfiledUnitsColumnSpec } from "../../CaseBuilder/ProfiledUnits";
|
||||||
|
import { ThermalUnitsColumnSpec } from "../../CaseBuilder/ThermalUnits";
|
||||||
|
import { getThermalGenerators } from "../../../core/fixtures";
|
||||||
|
|
||||||
|
test("generateTableColumns (ProfiledUnits)", () => {
|
||||||
|
const columns = generateTableColumns(TEST_DATA_1, ProfiledUnitsColumnSpec);
|
||||||
|
assert.equal(columns.length, 5);
|
||||||
|
assert.deepEqual(columns[0], {
|
||||||
|
editor: "input",
|
||||||
|
editorParams: {
|
||||||
|
selectContents: true,
|
||||||
|
},
|
||||||
|
field: "Name",
|
||||||
|
formatter: "plaintext",
|
||||||
|
headerHozAlign: "left",
|
||||||
|
headerSort: false,
|
||||||
|
headerWordWrap: true,
|
||||||
|
hozAlign: "left",
|
||||||
|
minWidth: 100,
|
||||||
|
resizable: false,
|
||||||
|
title: "Name",
|
||||||
|
});
|
||||||
|
assert.equal(columns[3]!["columns"]!.length, 5);
|
||||||
|
assert.deepEqual(columns[3]!["columns"]![0], {
|
||||||
|
editor: "input",
|
||||||
|
editorParams: {
|
||||||
|
selectContents: true,
|
||||||
|
},
|
||||||
|
field: "Maximum power (MW) 00:00",
|
||||||
|
formatter: floatFormatter,
|
||||||
|
headerHozAlign: "left",
|
||||||
|
headerSort: false,
|
||||||
|
headerWordWrap: true,
|
||||||
|
hozAlign: "left",
|
||||||
|
minWidth: 60,
|
||||||
|
resizable: false,
|
||||||
|
title: "00:00",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("generateTableColumns (ThermalUnits)", () => {
|
||||||
|
const columns = generateTableColumns(TEST_DATA_1, ThermalUnitsColumnSpec);
|
||||||
|
assert.equal(columns[2]!["columns"]!.length, 10);
|
||||||
|
assert.deepEqual(columns[2]!["columns"]![0], {
|
||||||
|
editor: "input",
|
||||||
|
editorParams: {
|
||||||
|
selectContents: true,
|
||||||
|
},
|
||||||
|
field: "Production cost curve (MW) 1",
|
||||||
|
formatter: floatFormatter,
|
||||||
|
headerHozAlign: "left",
|
||||||
|
headerSort: false,
|
||||||
|
headerWordWrap: true,
|
||||||
|
hozAlign: "left",
|
||||||
|
minWidth: 60,
|
||||||
|
resizable: false,
|
||||||
|
title: "1",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("generateTableData (ThermalUnits)", () => {
|
||||||
|
const data = generateTableData(
|
||||||
|
getThermalGenerators(TEST_DATA_1),
|
||||||
|
ThermalUnitsColumnSpec,
|
||||||
|
TEST_DATA_1,
|
||||||
|
);
|
||||||
|
assert.deepEqual(data[0], {
|
||||||
|
Name: "gen1",
|
||||||
|
Bus: "b1",
|
||||||
|
"Initial power (MW)": 115,
|
||||||
|
"Initial status (h)": 12,
|
||||||
|
"Minimum downtime (h)": 4,
|
||||||
|
"Minimum uptime (h)": 4,
|
||||||
|
"Ramp down limit (MW)": 232.68,
|
||||||
|
"Ramp up limit (MW)": 232.68,
|
||||||
|
"Shutdown limit (MW)": 232.68,
|
||||||
|
"Startup limit (MW)": 232.68,
|
||||||
|
"Production cost curve ($) 1": 1400,
|
||||||
|
"Production cost curve ($) 2": 1600,
|
||||||
|
"Production cost curve ($) 3": 2200,
|
||||||
|
"Production cost curve ($) 4": 2400,
|
||||||
|
"Production cost curve ($) 5": "",
|
||||||
|
"Production cost curve ($) 6": "",
|
||||||
|
"Production cost curve ($) 7": "",
|
||||||
|
"Production cost curve ($) 8": "",
|
||||||
|
"Production cost curve ($) 9": "",
|
||||||
|
"Production cost curve ($) 10": "",
|
||||||
|
"Production cost curve (MW) 1": 100,
|
||||||
|
"Production cost curve (MW) 2": 110,
|
||||||
|
"Production cost curve (MW) 3": 130,
|
||||||
|
"Production cost curve (MW) 4": 135,
|
||||||
|
"Production cost curve (MW) 5": "",
|
||||||
|
"Production cost curve (MW) 6": "",
|
||||||
|
"Production cost curve (MW) 7": "",
|
||||||
|
"Production cost curve (MW) 8": "",
|
||||||
|
"Production cost curve (MW) 9": "",
|
||||||
|
"Production cost curve (MW) 10": "",
|
||||||
|
"Startup costs ($) 1": 300,
|
||||||
|
"Startup costs ($) 2": 400,
|
||||||
|
"Startup costs ($) 3": "",
|
||||||
|
"Startup costs ($) 4": "",
|
||||||
|
"Startup costs ($) 5": "",
|
||||||
|
"Startup delays (h) 1": 1,
|
||||||
|
"Startup delays (h) 2": 4,
|
||||||
|
"Startup delays (h) 3": "",
|
||||||
|
"Startup delays (h) 4": "",
|
||||||
|
"Startup delays (h) 5": "",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("generate CSV", () => {
|
||||||
|
const [data, columns] = generateBusesData(TEST_DATA_1);
|
||||||
|
const actualCsv = generateCsv(data, columns);
|
||||||
|
const expectedCsv =
|
||||||
|
"Name,Load (MW) 00:00,Load (MW) 01:00,Load (MW) 02:00,Load (MW) 03:00,Load (MW) 04:00\n" +
|
||||||
|
"b1,35.79534,34.38835,33.45083,32.89729,33.25044\n" +
|
||||||
|
"b2,14.03739,13.48563,13.11797,12.9009,13.03939\n" +
|
||||||
|
"b3,27.3729,26.29698,25.58005,25.15675,25.4268";
|
||||||
|
assert.strictEqual(actualCsv, expectedCsv);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parse CSV (Buses)", () => {
|
||||||
|
const csvContents =
|
||||||
|
"Name,Load (MW) 00:00,Load (MW) 01:00,Load (MW) 02:00,Load (MW) 03:00,Load (MW) 04:00\n" +
|
||||||
|
"b1,0,1,2,3,4\n" +
|
||||||
|
"b3,27.3729,26.29698,25.58005,25.15675,25.4268";
|
||||||
|
const [newBuses, err] = parseCsv(csvContents, BusesColumnSpec, TEST_DATA_1);
|
||||||
|
assert(err === null);
|
||||||
|
assert.deepEqual(newBuses, {
|
||||||
|
b1: {
|
||||||
|
"Load (MW)": [0, 1, 2, 3, 4],
|
||||||
|
},
|
||||||
|
b3: {
|
||||||
|
"Load (MW)": [27.3729, 26.29698, 25.58005, 25.15675, 25.4268],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parse CSV with duplicated names", () => {
|
||||||
|
const csvContents =
|
||||||
|
"Name,Load (MW) 00:00,Load (MW) 01:00,Load (MW) 02:00,Load (MW) 03:00,Load (MW) 04:00\n" +
|
||||||
|
"b1,0,0,0,0,0\n" +
|
||||||
|
"b1,0,0,0,0,0";
|
||||||
|
const [, err] = parseCsv(csvContents, BusesColumnSpec, TEST_DATA_1);
|
||||||
|
assert(err !== null);
|
||||||
|
assert.equal(err.message, `Name "b1" is duplicated (row 2)`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parse CSV (Profiled Units)", () => {
|
||||||
|
const csvContents =
|
||||||
|
"Name,Bus,Cost ($/MW),Maximum power (MW) 00:00,Maximum power (MW) 01:00," +
|
||||||
|
"Maximum power (MW) 02:00,Maximum power (MW) 03:00," +
|
||||||
|
"Maximum power (MW) 04:00,Minimum power (MW) 00:00," +
|
||||||
|
"Minimum power (MW) 01:00,Minimum power (MW) 02:00," +
|
||||||
|
"Minimum power (MW) 03:00,Minimum power (MW) 04:00\n" +
|
||||||
|
"pu1,b1,50,260.25384545,72.89148068,377.17886108,336.66732361," +
|
||||||
|
"376.82781758,52.05076909,14.57829614,75.43577222,67.33346472,75.36556352\n" +
|
||||||
|
"pu2,b1,0,0,0,0,0,0,0,0,0,0,0";
|
||||||
|
const [newGenerators, err] = parseCsv(
|
||||||
|
csvContents,
|
||||||
|
ProfiledUnitsColumnSpec,
|
||||||
|
TEST_DATA_1,
|
||||||
|
);
|
||||||
|
assert(err === null);
|
||||||
|
assert.deepEqual(newGenerators, {
|
||||||
|
pu1: {
|
||||||
|
Bus: "b1",
|
||||||
|
"Minimum power (MW)": [
|
||||||
|
52.05076909, 14.57829614, 75.43577222, 67.33346472, 75.36556352,
|
||||||
|
],
|
||||||
|
"Maximum power (MW)": [
|
||||||
|
260.25384545, 72.89148068, 377.17886108, 336.66732361, 376.82781758,
|
||||||
|
],
|
||||||
|
"Cost ($/MW)": 50.0,
|
||||||
|
},
|
||||||
|
pu2: {
|
||||||
|
Bus: "b1",
|
||||||
|
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||||
|
"Maximum power (MW)": [0, 0, 0, 0, 0],
|
||||||
|
"Cost ($/MW)": 0.0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parse CSV with invalid bus", () => {
|
||||||
|
const csvContents =
|
||||||
|
"Name,Bus,Cost ($/MW),Maximum power (MW) 00:00,Maximum power (MW) 01:00," +
|
||||||
|
"Maximum power (MW) 02:00,Maximum power (MW) 03:00," +
|
||||||
|
"Maximum power (MW) 04:00,Minimum power (MW) 00:00," +
|
||||||
|
"Minimum power (MW) 01:00,Minimum power (MW) 02:00," +
|
||||||
|
"Minimum power (MW) 03:00,Minimum power (MW) 04:00\n" +
|
||||||
|
"pu1,b99,50,260.25384545,72.89148068,377.17886108,336.66732361," +
|
||||||
|
"376.82781758,52.05076909,14.57829614,75.43577222,67.33346472,75.36556352\n" +
|
||||||
|
"pu2,b1,0,0,0,0,0,0,0,0,0,0,0";
|
||||||
|
const [, err] = parseCsv(csvContents, ProfiledUnitsColumnSpec, TEST_DATA_1);
|
||||||
|
assert(err !== null);
|
||||||
|
assert.equal(err.message, 'Bus "b99" does not exist (row 1)');
|
||||||
|
});
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 { parseBool } from "./commonOps";
|
|
||||||
import assert from "node:assert";
|
|
||||||
|
|
||||||
test("parseBool", () => {
|
|
||||||
// True values
|
|
||||||
for (const str of ["true", "TRUE", "1"]) {
|
|
||||||
let [v, err] = parseBool(str);
|
|
||||||
assert(!err);
|
|
||||||
assert.equal(v, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// False values
|
|
||||||
for (const str of ["false", "FALSE", "0"]) {
|
|
||||||
let [v, err] = parseBool(str);
|
|
||||||
assert(!err);
|
|
||||||
assert.equal(v, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid values
|
|
||||||
for (const str of ["qwe", ""]) {
|
|
||||||
let [, err] = parseBool(str);
|
|
||||||
assert(err);
|
|
||||||
}
|
|
||||||
});
|
|
Loading…
Reference in new issue