mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 00:08:52 -06:00
web: ProfiledUnits: Rename and delete
This commit is contained in:
@@ -30,7 +30,7 @@ import {
|
||||
createBus,
|
||||
deleteBus,
|
||||
renameBus,
|
||||
} from "../../../core/Operations/busOperations";
|
||||
} from "../../../core/Operations/busOps";
|
||||
|
||||
export const BusesColumnSpec: ColumnSpec[] = [
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
changeParameter,
|
||||
changeTimeHorizon,
|
||||
changeTimeStep,
|
||||
} from "../../../core/Operations/parameterOperations";
|
||||
} from "../../../core/Operations/parameterOps";
|
||||
|
||||
interface ParametersProps {
|
||||
scenario: UnitCommitmentScenario;
|
||||
|
||||
@@ -23,7 +23,11 @@ import { ColumnDefinition } from "tabulator-tables";
|
||||
import { offerDownload } from "../../Common/io";
|
||||
import FileUploadElement from "../../Common/Buttons/FileUploadElement";
|
||||
import { useRef } from "react";
|
||||
import { createProfiledUnit } from "../../../core/Operations/profiledUnitOps";
|
||||
import {
|
||||
createProfiledUnit,
|
||||
deleteGenerator,
|
||||
} from "../../../core/Operations/generatorOps";
|
||||
import { ValidationError } from "../../../core/Validation/validate";
|
||||
|
||||
interface ProfiledUnitsProps {
|
||||
scenario: UnitCommitmentScenario;
|
||||
@@ -108,6 +112,12 @@ const ProfiledUnitsComponent = (props: ProfiledUnitsProps) => {
|
||||
props.onDataChanged(newScenario);
|
||||
};
|
||||
|
||||
const onDelete = (name: string): ValidationError | null => {
|
||||
const newScenario = deleteGenerator(name, props.scenario);
|
||||
props.onDataChanged(newScenario);
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SectionHeader title="Profiled Units">
|
||||
@@ -120,9 +130,7 @@ const ProfiledUnitsComponent = (props: ProfiledUnitsProps) => {
|
||||
<SectionButton icon={faUpload} tooltip="Upload" onClick={onUpload} />
|
||||
</SectionHeader>
|
||||
<DataTable
|
||||
onRowDeleted={() => {
|
||||
return null;
|
||||
}}
|
||||
onRowDeleted={onDelete}
|
||||
onRowRenamed={() => {
|
||||
return null;
|
||||
}}
|
||||
|
||||
@@ -4,12 +4,7 @@
|
||||
* Released under the modified BSD license. See COPYING.md for more details.
|
||||
*/
|
||||
|
||||
import {
|
||||
changeBusData,
|
||||
createBus,
|
||||
deleteBus,
|
||||
renameBus,
|
||||
} from "./busOperations";
|
||||
import { changeBusData, createBus, deleteBus, renameBus } from "./busOps";
|
||||
import assert from "node:assert";
|
||||
import { TEST_DATA_1 } from "../fixtures.test";
|
||||
|
||||
@@ -65,5 +60,5 @@ test("renameBus", () => {
|
||||
test("renameBus with duplicated name", () => {
|
||||
let [, err] = renameBus("b3", "b1", TEST_DATA_1);
|
||||
assert(err != null);
|
||||
assert.equal(err.message, `Bus b1 already exists`);
|
||||
assert.equal(err.message, `b1 already exists`);
|
||||
});
|
||||
@@ -4,19 +4,10 @@
|
||||
* Released under the modified BSD license. See COPYING.md for more details.
|
||||
*/
|
||||
|
||||
import { Buses, UnitCommitmentScenario } from "../fixtures";
|
||||
import { UnitCommitmentScenario } from "../fixtures";
|
||||
import { ValidationError } from "../Validation/validate";
|
||||
import { generateTimeslots } from "../../components/Common/Forms/DataTable";
|
||||
|
||||
export const generateUniqueName = (container: any, prefix: string): string => {
|
||||
let counter = 1;
|
||||
let name = `${prefix}${counter}`;
|
||||
while (name in container) {
|
||||
counter++;
|
||||
name = `${prefix}${counter}`;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
import { generateUniqueName, renameItemInObject } from "./commonOps";
|
||||
|
||||
export const createBus = (scenario: UnitCommitmentScenario) => {
|
||||
const name = generateUniqueName(scenario.Buses, "b");
|
||||
@@ -72,10 +63,7 @@ export const changeBusData = (
|
||||
|
||||
export const deleteBus = (bus: string, scenario: UnitCommitmentScenario) => {
|
||||
const { [bus]: _, ...newBuses } = scenario.Buses;
|
||||
return {
|
||||
...scenario,
|
||||
Buses: newBuses,
|
||||
};
|
||||
return { ...scenario, Buses: newBuses };
|
||||
};
|
||||
|
||||
export const renameBus = (
|
||||
@@ -83,22 +71,7 @@ export const renameBus = (
|
||||
newName: string,
|
||||
scenario: UnitCommitmentScenario,
|
||||
): [UnitCommitmentScenario, ValidationError | null] => {
|
||||
if (newName in scenario.Buses) {
|
||||
return [scenario, { message: `Bus ${newName} already exists` }];
|
||||
}
|
||||
const newBuses: Buses = Object.keys(scenario.Buses).reduce((acc, val) => {
|
||||
if (val === oldName) {
|
||||
acc[newName] = scenario.Buses[val]!;
|
||||
} else {
|
||||
acc[val] = scenario.Buses[val]!;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Buses);
|
||||
return [
|
||||
{
|
||||
...scenario,
|
||||
Buses: newBuses,
|
||||
},
|
||||
null,
|
||||
];
|
||||
const [newBuses, err] = renameItemInObject(oldName, newName, scenario.Buses);
|
||||
if (err) return [scenario, err];
|
||||
return [{ ...scenario, Buses: newBuses }, null];
|
||||
};
|
||||
39
web/src/core/Operations/commonOps.ts
Normal file
39
web/src/core/Operations/commonOps.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 "../Validation/validate";
|
||||
|
||||
export const renameItemInObject = <T>(
|
||||
oldName: string,
|
||||
newName: string,
|
||||
container: { [key: string]: T },
|
||||
): [{ [key: string]: T }, ValidationError | null] => {
|
||||
if (newName in container) {
|
||||
return [container, { message: `${newName} already exists` }];
|
||||
}
|
||||
const newContainer = Object.keys(container).reduce(
|
||||
(acc, val) => {
|
||||
if (val === oldName) {
|
||||
acc[newName] = container[val]!;
|
||||
} else {
|
||||
acc[val] = container[val]!;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: T },
|
||||
);
|
||||
return [newContainer, null];
|
||||
};
|
||||
|
||||
export const generateUniqueName = (container: any, prefix: string): string => {
|
||||
let counter = 1;
|
||||
let name = `${prefix}${counter}`;
|
||||
while (name in container) {
|
||||
counter++;
|
||||
name = `${prefix}${counter}`;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
81
web/src/core/Operations/generatorOps.test.ts
Normal file
81
web/src/core/Operations/generatorOps.test.ts
Normal file
@@ -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 { TEST_DATA_1, TEST_DATA_BLANK } from "../fixtures.test";
|
||||
import assert from "node:assert";
|
||||
import {
|
||||
createProfiledUnit,
|
||||
deleteGenerator,
|
||||
renameGenerator,
|
||||
} from "./generatorOps";
|
||||
|
||||
test("createProfiledUnit", () => {
|
||||
const [newScenario, err] = createProfiledUnit(TEST_DATA_1);
|
||||
assert(err === null);
|
||||
assert.deepEqual(newScenario.Generators, {
|
||||
pu1: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 12.5,
|
||||
"Maximum power (MW)": [10, 12, 13, 15, 20],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
pu2: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 120,
|
||||
"Maximum power (MW)": [50, 50, 50, 50, 50],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
pu3: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 0,
|
||||
"Maximum power (MW)": [0, 0, 0, 0, 0],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("createProfiledUnit with blank file", () => {
|
||||
const [_, err] = createProfiledUnit(TEST_DATA_BLANK);
|
||||
assert(err !== null);
|
||||
assert.equal(err.message, "Profiled unit requires an existing bus.");
|
||||
});
|
||||
|
||||
test("deleteGenerator", () => {
|
||||
const newScenario = deleteGenerator("pu1", TEST_DATA_1);
|
||||
assert.deepEqual(newScenario.Generators, {
|
||||
pu2: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 120,
|
||||
"Maximum power (MW)": [50, 50, 50, 50, 50],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("renameGenerator", () => {
|
||||
const [newScenario, err] = renameGenerator("pu1", "pu5", TEST_DATA_1);
|
||||
assert(err === null);
|
||||
assert.deepEqual(newScenario.Generators, {
|
||||
pu5: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 12.5,
|
||||
"Maximum power (MW)": [10, 12, 13, 15, 20],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
pu2: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 120,
|
||||
"Maximum power (MW)": [50, 50, 50, 50, 50],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
import { UnitCommitmentScenario } from "../fixtures";
|
||||
import { generateTimeslots } from "../../components/Common/Forms/DataTable";
|
||||
import { generateUniqueName } from "./busOperations";
|
||||
import { ValidationError } from "../Validation/validate";
|
||||
import { generateUniqueName, renameItemInObject } from "./commonOps";
|
||||
|
||||
export const createProfiledUnit = (
|
||||
scenario: UnitCommitmentScenario,
|
||||
@@ -35,3 +35,25 @@ export const createProfiledUnit = (
|
||||
null,
|
||||
];
|
||||
};
|
||||
|
||||
export const deleteGenerator = (
|
||||
name: string,
|
||||
scenario: UnitCommitmentScenario,
|
||||
): UnitCommitmentScenario => {
|
||||
const { [name]: _, ...newGenerators } = scenario.Generators;
|
||||
return { ...scenario, Generators: newGenerators };
|
||||
};
|
||||
|
||||
export const renameGenerator = (
|
||||
oldName: string,
|
||||
newName: string,
|
||||
scenario: UnitCommitmentScenario,
|
||||
): [UnitCommitmentScenario, ValidationError | null] => {
|
||||
const [newGen, err] = renameItemInObject(
|
||||
oldName,
|
||||
newName,
|
||||
scenario.Generators,
|
||||
);
|
||||
if (err) return [scenario, err];
|
||||
return [{ ...scenario, Generators: newGen }, null];
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
changeTimeHorizon,
|
||||
changeTimeStep,
|
||||
evaluatePwlFunction,
|
||||
} from "./parameterOperations";
|
||||
} from "./parameterOps";
|
||||
import assert from "node:assert";
|
||||
import { TEST_DATA_1, TEST_DATA_2 } from "../fixtures.test";
|
||||
|
||||
@@ -31,18 +31,16 @@ test("changeTimeHorizon: Shrink 1", () => {
|
||||
test("changeTimeHorizon: Shrink 2", () => {
|
||||
const [newScenario, err] = changeTimeHorizon(TEST_DATA_2, "1");
|
||||
assert(err === null);
|
||||
assert.deepEqual(newScenario, {
|
||||
Parameters: {
|
||||
Version: "0.4",
|
||||
"Power balance penalty ($/MW)": 1000.0,
|
||||
"Time horizon (h)": 1,
|
||||
"Time step (min)": 30,
|
||||
},
|
||||
Buses: {
|
||||
b1: { "Load (MW)": [30, 30] },
|
||||
b2: { "Load (MW)": [10, 20] },
|
||||
b3: { "Load (MW)": [0, 30] },
|
||||
},
|
||||
assert.deepEqual(newScenario.Parameters, {
|
||||
Version: "0.4",
|
||||
"Power balance penalty ($/MW)": 1000.0,
|
||||
"Time horizon (h)": 1,
|
||||
"Time step (min)": 30,
|
||||
});
|
||||
assert.deepEqual(newScenario.Buses, {
|
||||
b1: { "Load (MW)": [30, 30] },
|
||||
b2: { "Load (MW)": [10, 20] },
|
||||
b3: { "Load (MW)": [0, 30] },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,34 +89,30 @@ test("evaluatePwlFunction", () => {
|
||||
test("changeTimeStep", () => {
|
||||
let [scenario, err] = changeTimeStep(TEST_DATA_2, "15");
|
||||
assert(err === null);
|
||||
assert.deepEqual(scenario, {
|
||||
Parameters: {
|
||||
Version: "0.4",
|
||||
"Power balance penalty ($/MW)": 1000.0,
|
||||
"Time horizon (h)": 2,
|
||||
"Time step (min)": 15,
|
||||
},
|
||||
Buses: {
|
||||
b1: { "Load (MW)": [30, 30, 30, 30, 30, 30, 30, 30] },
|
||||
b2: { "Load (MW)": [10, 15, 20, 25, 30, 35, 40, 25] },
|
||||
b3: { "Load (MW)": [0, 15, 30, 15, 0, 20, 40, 20] },
|
||||
},
|
||||
assert.deepEqual(scenario.Parameters, {
|
||||
Version: "0.4",
|
||||
"Power balance penalty ($/MW)": 1000.0,
|
||||
"Time horizon (h)": 2,
|
||||
"Time step (min)": 15,
|
||||
});
|
||||
assert.deepEqual(scenario.Buses, {
|
||||
b1: { "Load (MW)": [30, 30, 30, 30, 30, 30, 30, 30] },
|
||||
b2: { "Load (MW)": [10, 15, 20, 25, 30, 35, 40, 25] },
|
||||
b3: { "Load (MW)": [0, 15, 30, 15, 0, 20, 40, 20] },
|
||||
});
|
||||
|
||||
[scenario, err] = changeTimeStep(TEST_DATA_2, "60");
|
||||
assert(err === null);
|
||||
assert.deepEqual(scenario, {
|
||||
Parameters: {
|
||||
Version: "0.4",
|
||||
"Power balance penalty ($/MW)": 1000.0,
|
||||
"Time horizon (h)": 2,
|
||||
"Time step (min)": 60,
|
||||
},
|
||||
Buses: {
|
||||
b1: { "Load (MW)": [30, 30] },
|
||||
b2: { "Load (MW)": [10, 30] },
|
||||
b3: { "Load (MW)": [0, 0] },
|
||||
},
|
||||
assert.deepEqual(scenario.Parameters, {
|
||||
Version: "0.4",
|
||||
"Power balance penalty ($/MW)": 1000.0,
|
||||
"Time horizon (h)": 2,
|
||||
"Time step (min)": 60,
|
||||
});
|
||||
assert.deepEqual(scenario.Buses, {
|
||||
b1: { "Load (MW)": [30, 30] },
|
||||
b2: { "Load (MW)": [10, 30] },
|
||||
b3: { "Load (MW)": [0, 0] },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +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 { TEST_DATA_1, TEST_DATA_BLANK } from "../fixtures.test";
|
||||
import assert from "node:assert";
|
||||
import { createProfiledUnit } from "./profiledUnitOps";
|
||||
|
||||
test("createUnit", () => {
|
||||
const [newScenario, err] = createProfiledUnit(TEST_DATA_1);
|
||||
assert(err === null);
|
||||
assert.deepEqual(newScenario.Generators, {
|
||||
pu1: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 12.5,
|
||||
"Maximum power (MW)": [10, 12, 13, 15, 20],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
pu2: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 0,
|
||||
"Maximum power (MW)": [0, 0, 0, 0, 0],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("createUnit with blank file", () => {
|
||||
const [newScenario, err] = createProfiledUnit(TEST_DATA_BLANK);
|
||||
assert(err !== null);
|
||||
assert.equal(err.message, "Profiled unit requires an existing bus.");
|
||||
});
|
||||
@@ -20,6 +20,13 @@ export const TEST_DATA_1: UnitCommitmentScenario = {
|
||||
"Maximum power (MW)": [10, 12, 13, 15, 20],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
pu2: {
|
||||
Bus: "b1",
|
||||
Type: "Profiled",
|
||||
"Cost ($/MW)": 120,
|
||||
"Maximum power (MW)": [50, 50, 50, 50, 50],
|
||||
"Minimum power (MW)": [0, 0, 0, 0, 0],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -35,6 +42,7 @@ export const TEST_DATA_2: UnitCommitmentScenario = {
|
||||
b2: { "Load (MW)": [10, 20, 30, 40] },
|
||||
b3: { "Load (MW)": [0, 30, 0, 40] },
|
||||
},
|
||||
Generators: {},
|
||||
};
|
||||
|
||||
export const TEST_DATA_BLANK: UnitCommitmentScenario = {
|
||||
|
||||
@@ -28,7 +28,7 @@ export interface UnitCommitmentScenario {
|
||||
"Time step (min)": number;
|
||||
};
|
||||
Buses: Buses;
|
||||
Generators?: Generators;
|
||||
Generators: Generators;
|
||||
}
|
||||
|
||||
export const BLANK_SCENARIO: UnitCommitmentScenario = {
|
||||
|
||||
Reference in New Issue
Block a user