web: ProfiledUnits: Rename and delete

web
Alinson S. Xavier 3 months ago
parent 8397571c11
commit 86aababf33

@ -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];
};

@ -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;
};

@ -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 = {

Loading…
Cancel
Save