mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 08:18:51 -06:00
web: Add support for transmission contingencies
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
|||||||
changeTransmissionLineData,
|
changeTransmissionLineData,
|
||||||
createTransmissionLine,
|
createTransmissionLine,
|
||||||
deleteTransmissionLine,
|
deleteTransmissionLine,
|
||||||
|
rebuildContingencies,
|
||||||
renameTransmissionLine,
|
renameTransmissionLine,
|
||||||
} from "../../core/Operations/transmissionOps";
|
} from "../../core/Operations/transmissionOps";
|
||||||
import { offerDownload } from "../Common/io";
|
import { offerDownload } from "../Common/io";
|
||||||
@@ -68,6 +69,11 @@ export const TransmissionLinesColumnSpec: ColumnSpec[] = [
|
|||||||
type: "number",
|
type: "number",
|
||||||
width: 60,
|
width: 60,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Contingency?",
|
||||||
|
type: "lineContingency",
|
||||||
|
width: 50,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const generateTransmissionLinesData = (
|
const generateTransmissionLinesData = (
|
||||||
@@ -93,6 +99,7 @@ const TransmissionLinesComponent = (props: CaseBuilderSectionProps) => {
|
|||||||
|
|
||||||
const onUpload = () => {
|
const onUpload = () => {
|
||||||
fileUploadElem.current!.showFilePicker((csv: any) => {
|
fileUploadElem.current!.showFilePicker((csv: any) => {
|
||||||
|
// Parse the CSV data
|
||||||
const [newLines, err] = parseCsv(
|
const [newLines, err] = parseCsv(
|
||||||
csv,
|
csv,
|
||||||
TransmissionLinesColumnSpec,
|
TransmissionLinesColumnSpec,
|
||||||
@@ -102,9 +109,19 @@ const TransmissionLinesComponent = (props: CaseBuilderSectionProps) => {
|
|||||||
props.onError(err.message);
|
props.onError(err.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove contingency field from line and rebuild the contingencies section
|
||||||
|
const lineContingencies = new Set<String>();
|
||||||
|
Object.entries(newLines).forEach(([lineName, line]: [string, any]) => {
|
||||||
|
if (line["Contingency?"]) lineContingencies.add(lineName);
|
||||||
|
delete line["Contingency?"];
|
||||||
|
});
|
||||||
|
const contingencies = rebuildContingencies(lineContingencies);
|
||||||
|
|
||||||
const newScenario = {
|
const newScenario = {
|
||||||
...props.scenario,
|
...props.scenario,
|
||||||
"Transmission lines": newLines,
|
"Transmission lines": newLines,
|
||||||
|
Contingencies: contingencies,
|
||||||
};
|
};
|
||||||
props.onDataChanged(newScenario);
|
props.onDataChanged(newScenario);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
parseNumber,
|
parseNumber,
|
||||||
} from "../../../core/Operations/commonOps";
|
} from "../../../core/Operations/commonOps";
|
||||||
import { UnitCommitmentScenario } from "../../../core/Data/types";
|
import { UnitCommitmentScenario } from "../../../core/Data/types";
|
||||||
|
import { getContingencyTransmissionLines } from "../../../core/Operations/transmissionOps";
|
||||||
|
|
||||||
export interface ColumnSpec {
|
export interface ColumnSpec {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -28,7 +29,8 @@ export interface ColumnSpec {
|
|||||||
| "number[N]"
|
| "number[N]"
|
||||||
| "number[T]"
|
| "number[T]"
|
||||||
| "busRef"
|
| "busRef"
|
||||||
| "boolean";
|
| "boolean"
|
||||||
|
| "lineContingency";
|
||||||
length?: number;
|
length?: number;
|
||||||
width: number;
|
width: number;
|
||||||
}
|
}
|
||||||
@@ -52,6 +54,7 @@ export const generateTableColumns = (
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "boolean":
|
case "boolean":
|
||||||
|
case "lineContingency":
|
||||||
columns.push({
|
columns.push({
|
||||||
...columnsCommonAttrs,
|
...columnsCommonAttrs,
|
||||||
title: spec.title,
|
title: spec.title,
|
||||||
@@ -117,6 +120,7 @@ export const generateTableData = (
|
|||||||
): any[] => {
|
): any[] => {
|
||||||
const data: any[] = [];
|
const data: any[] = [];
|
||||||
const timeslots = generateTimeslots(scenario);
|
const timeslots = generateTimeslots(scenario);
|
||||||
|
let contingencyLines = null;
|
||||||
for (const [entryName, entryData] of Object.entries(container) as [
|
for (const [entryName, entryData] of Object.entries(container) as [
|
||||||
string,
|
string,
|
||||||
any,
|
any,
|
||||||
@@ -135,6 +139,13 @@ export const generateTableData = (
|
|||||||
case "busRef":
|
case "busRef":
|
||||||
entry[spec.title] = entryData[spec.title];
|
entry[spec.title] = entryData[spec.title];
|
||||||
break;
|
break;
|
||||||
|
case "lineContingency":
|
||||||
|
if (contingencyLines === null) {
|
||||||
|
contingencyLines = getContingencyTransmissionLines(scenario);
|
||||||
|
console.log(contingencyLines);
|
||||||
|
}
|
||||||
|
entry[spec.title] = contingencyLines.has(entryName);
|
||||||
|
break;
|
||||||
case "number[T]":
|
case "number[T]":
|
||||||
for (let i = 0; i < timeslots.length; i++) {
|
for (let i = 0; i < timeslots.length; i++) {
|
||||||
entry[`${spec.title} ${timeslots[i]}`] = entryData[spec.title][i];
|
entry[`${spec.title} ${timeslots[i]}`] = entryData[spec.title][i];
|
||||||
@@ -287,12 +298,12 @@ export const parseCsv = (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "boolean": {
|
case "boolean":
|
||||||
|
case "lineContingency":
|
||||||
const [val, err] = parseBool(row[spec.title]);
|
const [val, err] = parseBool(row[spec.title]);
|
||||||
if (err) return [data, { message: err.message + rowRef }];
|
if (err) return [data, { message: err.message + rowRef }];
|
||||||
data[name][spec.title] = val;
|
data[name][spec.title] = val;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw Error(`Unknown type: ${spec.type}`);
|
throw Error(`Unknown type: ${spec.type}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ export const TEST_DATA_1: UnitCommitmentScenario = {
|
|||||||
"Demand (MW)": [50, 50, 50, 50, 50],
|
"Demand (MW)": [50, 50, 50, 50, 50],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Contingencies: {
|
||||||
|
l1: {
|
||||||
|
"Affected generators": [],
|
||||||
|
"Affected lines": ["l1"],
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TEST_DATA_2: UnitCommitmentScenario = {
|
export const TEST_DATA_2: UnitCommitmentScenario = {
|
||||||
@@ -101,6 +107,7 @@ export const TEST_DATA_2: UnitCommitmentScenario = {
|
|||||||
b2: { "Load (MW)": [10, 20, 30, 40] },
|
b2: { "Load (MW)": [10, 20, 30, 40] },
|
||||||
b3: { "Load (MW)": [0, 30, 0, 40] },
|
b3: { "Load (MW)": [0, 30, 0, 40] },
|
||||||
},
|
},
|
||||||
|
Contingencies: {},
|
||||||
Generators: {},
|
Generators: {},
|
||||||
"Transmission lines": {},
|
"Transmission lines": {},
|
||||||
"Storage units": {},
|
"Storage units": {},
|
||||||
@@ -115,6 +122,7 @@ export const TEST_DATA_BLANK: UnitCommitmentScenario = {
|
|||||||
"Time step (min)": 60,
|
"Time step (min)": 60,
|
||||||
},
|
},
|
||||||
Buses: {},
|
Buses: {},
|
||||||
|
Contingencies: {},
|
||||||
Generators: {},
|
Generators: {},
|
||||||
"Transmission lines": {},
|
"Transmission lines": {},
|
||||||
"Storage units": {},
|
"Storage units": {},
|
||||||
|
|||||||
@@ -22,4 +22,5 @@ export const BLANK_SCENARIO: UnitCommitmentScenario = {
|
|||||||
"Transmission lines": {},
|
"Transmission lines": {},
|
||||||
"Storage units": {},
|
"Storage units": {},
|
||||||
"Price-sensitive loads": {},
|
"Price-sensitive loads": {},
|
||||||
|
Contingencies: {},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ export interface PriceSensitiveLoad {
|
|||||||
"Demand (MW)": number[];
|
"Demand (MW)": number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Contingency {
|
||||||
|
"Affected lines": string[];
|
||||||
|
"Affected generators": string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface UnitCommitmentScenario {
|
export interface UnitCommitmentScenario {
|
||||||
Parameters: {
|
Parameters: {
|
||||||
Version: string;
|
Version: string;
|
||||||
@@ -87,6 +92,9 @@ export interface UnitCommitmentScenario {
|
|||||||
"Price-sensitive loads": {
|
"Price-sensitive loads": {
|
||||||
[name: string]: PriceSensitiveLoad;
|
[name: string]: PriceSensitiveLoad;
|
||||||
};
|
};
|
||||||
|
Contingencies: {
|
||||||
|
[name: string]: Contingency;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTypedGenerators = <T extends any>(
|
const getTypedGenerators = <T extends any>(
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
import { validate, ValidationError } from "../Data/validate";
|
import { validate, ValidationError } from "../Data/validate";
|
||||||
import { UnitCommitmentScenario } from "../Data/types";
|
import { UnitCommitmentScenario } from "../Data/types";
|
||||||
import { migrate } from "../Data/migrate";
|
import { migrate } from "../Data/migrate";
|
||||||
|
import {
|
||||||
|
getContingencyTransmissionLines,
|
||||||
|
rebuildContingencies,
|
||||||
|
} from "./transmissionOps";
|
||||||
|
|
||||||
export const preprocess = (
|
export const preprocess = (
|
||||||
data: any,
|
data: any,
|
||||||
@@ -57,5 +61,10 @@ export const preprocess = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const scenario = result as unknown as UnitCommitmentScenario;
|
const scenario = result as unknown as UnitCommitmentScenario;
|
||||||
|
|
||||||
|
// Rebuild contingencies
|
||||||
|
const contingencyLines = getContingencyTransmissionLines(scenario);
|
||||||
|
scenario["Contingencies"] = rebuildContingencies(contingencyLines);
|
||||||
|
|
||||||
return [scenario, null];
|
return [scenario, null];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
changeTransmissionLineData,
|
changeTransmissionLineData,
|
||||||
createTransmissionLine,
|
createTransmissionLine,
|
||||||
deleteTransmissionLine,
|
deleteTransmissionLine,
|
||||||
|
getContingencyTransmissionLines,
|
||||||
|
rebuildContingencies,
|
||||||
renameTransmissionLine,
|
renameTransmissionLine,
|
||||||
} from "./transmissionOps";
|
} from "./transmissionOps";
|
||||||
import { ValidationError } from "../Data/validate";
|
import { ValidationError } from "../Data/validate";
|
||||||
@@ -32,6 +34,12 @@ test("renameTransmissionLine", () => {
|
|||||||
"Emergency flow limit (MW)": 20000.0,
|
"Emergency flow limit (MW)": 20000.0,
|
||||||
"Flow limit penalty ($/MW)": 5000.0,
|
"Flow limit penalty ($/MW)": 5000.0,
|
||||||
});
|
});
|
||||||
|
assert.deepEqual(newScenario["Contingencies"], {
|
||||||
|
l3: {
|
||||||
|
"Affected lines": ["l3"],
|
||||||
|
"Affected generators": [],
|
||||||
|
},
|
||||||
|
});
|
||||||
assert.equal(Object.keys(newScenario["Transmission lines"]).length, 1);
|
assert.equal(Object.keys(newScenario["Transmission lines"]).length, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -72,4 +80,23 @@ test("changeTransmissionLineData", () => {
|
|||||||
test("deleteTransmissionLine", () => {
|
test("deleteTransmissionLine", () => {
|
||||||
const newScenario = deleteTransmissionLine("l1", TEST_DATA_1);
|
const newScenario = deleteTransmissionLine("l1", TEST_DATA_1);
|
||||||
assert.equal(Object.keys(newScenario["Transmission lines"]).length, 0);
|
assert.equal(Object.keys(newScenario["Transmission lines"]).length, 0);
|
||||||
|
assert.equal(Object.keys(newScenario["Contingencies"]).length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getContingencyTransmissionLines", () => {
|
||||||
|
const contLines = getContingencyTransmissionLines(TEST_DATA_1);
|
||||||
|
assert.deepEqual(contLines, new Set(["l1"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("rebuildContingencies", () => {
|
||||||
|
assert.deepEqual(rebuildContingencies(new Set(["l1", "l2"])), {
|
||||||
|
l1: {
|
||||||
|
"Affected lines": ["l1"],
|
||||||
|
"Affected generators": [],
|
||||||
|
},
|
||||||
|
l2: {
|
||||||
|
"Affected lines": ["l2"],
|
||||||
|
"Affected generators": [],
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,11 +8,16 @@ import {
|
|||||||
assertBusesNotEmpty,
|
assertBusesNotEmpty,
|
||||||
changeData,
|
changeData,
|
||||||
generateUniqueName,
|
generateUniqueName,
|
||||||
|
parseBool,
|
||||||
renameItemInObject,
|
renameItemInObject,
|
||||||
} from "./commonOps";
|
} from "./commonOps";
|
||||||
import { ValidationError } from "../Data/validate";
|
import { ValidationError } from "../Data/validate";
|
||||||
import { TransmissionLinesColumnSpec } from "../../components/CaseBuilder/TransmissionLines";
|
import { TransmissionLinesColumnSpec } from "../../components/CaseBuilder/TransmissionLines";
|
||||||
import { TransmissionLine, UnitCommitmentScenario } from "../Data/types";
|
import {
|
||||||
|
Contingency,
|
||||||
|
TransmissionLine,
|
||||||
|
UnitCommitmentScenario,
|
||||||
|
} from "../Data/types";
|
||||||
|
|
||||||
export const createTransmissionLine = (
|
export const createTransmissionLine = (
|
||||||
scenario: UnitCommitmentScenario,
|
scenario: UnitCommitmentScenario,
|
||||||
@@ -51,7 +56,24 @@ export const renameTransmissionLine = (
|
|||||||
scenario["Transmission lines"],
|
scenario["Transmission lines"],
|
||||||
);
|
);
|
||||||
if (err) return [scenario, err];
|
if (err) return [scenario, err];
|
||||||
return [{ ...scenario, "Transmission lines": newLine }, null];
|
|
||||||
|
// Update transmission line contingencies
|
||||||
|
let newContingencies = scenario["Contingencies"];
|
||||||
|
const contingencyLines = getContingencyTransmissionLines(scenario);
|
||||||
|
if (contingencyLines.has(oldName)) {
|
||||||
|
contingencyLines.delete(oldName);
|
||||||
|
contingencyLines.add(newName);
|
||||||
|
newContingencies = rebuildContingencies(contingencyLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
...scenario,
|
||||||
|
"Transmission lines": newLine,
|
||||||
|
Contingencies: newContingencies,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const changeTransmissionLineData = (
|
export const changeTransmissionLineData = (
|
||||||
@@ -60,6 +82,19 @@ export const changeTransmissionLineData = (
|
|||||||
newValueStr: string,
|
newValueStr: string,
|
||||||
scenario: UnitCommitmentScenario,
|
scenario: UnitCommitmentScenario,
|
||||||
): [UnitCommitmentScenario, ValidationError | null] => {
|
): [UnitCommitmentScenario, ValidationError | null] => {
|
||||||
|
if (field === "Contingency?") {
|
||||||
|
// Parse boolean value
|
||||||
|
const [newValue, err] = parseBool(newValueStr);
|
||||||
|
if (err) return [scenario, err];
|
||||||
|
|
||||||
|
// Rebuild contingencies
|
||||||
|
const contLines = getContingencyTransmissionLines(scenario);
|
||||||
|
if (newValue) contLines.add(line);
|
||||||
|
else contLines.delete(line);
|
||||||
|
const newContingencies = rebuildContingencies(contLines);
|
||||||
|
|
||||||
|
return [{ ...scenario, Contingencies: newContingencies }, null];
|
||||||
|
} else {
|
||||||
const [newLine, err] = changeData(
|
const [newLine, err] = changeData(
|
||||||
field,
|
field,
|
||||||
newValueStr,
|
newValueStr,
|
||||||
@@ -78,6 +113,7 @@ export const changeTransmissionLineData = (
|
|||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
];
|
];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteTransmissionLine = (
|
export const deleteTransmissionLine = (
|
||||||
@@ -85,5 +121,43 @@ export const deleteTransmissionLine = (
|
|||||||
scenario: UnitCommitmentScenario,
|
scenario: UnitCommitmentScenario,
|
||||||
): UnitCommitmentScenario => {
|
): UnitCommitmentScenario => {
|
||||||
const { [name]: _, ...newLines } = scenario["Transmission lines"];
|
const { [name]: _, ...newLines } = scenario["Transmission lines"];
|
||||||
return { ...scenario, "Transmission lines": newLines };
|
|
||||||
|
// Update transmission line contingencies
|
||||||
|
let newContingencies = scenario["Contingencies"];
|
||||||
|
const contingencyLines = getContingencyTransmissionLines(scenario);
|
||||||
|
if (contingencyLines.has(name)) {
|
||||||
|
contingencyLines.delete(name);
|
||||||
|
newContingencies = rebuildContingencies(contingencyLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...scenario,
|
||||||
|
"Transmission lines": newLines,
|
||||||
|
Contingencies: newContingencies,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getContingencyTransmissionLines = (
|
||||||
|
scenario: UnitCommitmentScenario,
|
||||||
|
): Set<String> => {
|
||||||
|
let result: Set<String> = new Set();
|
||||||
|
Object.entries(scenario.Contingencies).forEach(([name, contingency]) => {
|
||||||
|
if (contingency["Affected lines"].length !== 1)
|
||||||
|
throw Error("not implemented");
|
||||||
|
result.add(contingency["Affected lines"][0]!!);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const rebuildContingencies = (
|
||||||
|
contingencyLines: Set<String>,
|
||||||
|
): { [name: string]: Contingency } => {
|
||||||
|
const result: { [name: string]: Contingency } = {};
|
||||||
|
contingencyLines.forEach((lineName) => {
|
||||||
|
result[lineName as string] = {
|
||||||
|
"Affected lines": [lineName as string],
|
||||||
|
"Affected generators": [],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user