mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 16:28:51 -06:00
web: ThermalUnits: onDataChanged
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
import { ColumnDefinition } from "tabulator-tables";
|
||||
import { offerDownload } from "../Common/io";
|
||||
import {
|
||||
changeThermalUnitData,
|
||||
createThermalUnit,
|
||||
deleteGenerator,
|
||||
renameGenerator,
|
||||
@@ -194,17 +195,17 @@ const ThermalUnitsComponent = (props: CaseBuilderSectionProps) => {
|
||||
field: string,
|
||||
newValue: string,
|
||||
): ValidationError | null => {
|
||||
// const [newScenario, err] = changeThermalUnitData(
|
||||
// name,
|
||||
// field,
|
||||
// newValue,
|
||||
// props.scenario,
|
||||
// );
|
||||
// if (err) {
|
||||
// props.onError(err.message);
|
||||
// return err;
|
||||
// }
|
||||
// props.onDataChanged(newScenario);
|
||||
const [newScenario, err] = changeThermalUnitData(
|
||||
name,
|
||||
field,
|
||||
newValue,
|
||||
props.scenario,
|
||||
);
|
||||
if (err) {
|
||||
props.onError(err.message);
|
||||
return err;
|
||||
}
|
||||
props.onDataChanged(newScenario);
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@@ -350,10 +350,9 @@ const DataTable = (props: DataTableProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
const onCellEdited = (cell: CellComponent) => {
|
||||
let newValue = cell.getValue();
|
||||
let oldValue = cell.getOldValue();
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (newValue == oldValue) return;
|
||||
let newValue = `${cell.getValue()}`;
|
||||
let oldValue = `${cell.getOldValue()}`;
|
||||
if (newValue === oldValue) return;
|
||||
if (cell.getField() === "Name") {
|
||||
if (newValue === "") {
|
||||
const err = props.onRowDeleted(oldValue);
|
||||
|
||||
@@ -108,7 +108,26 @@ export const changeNumberData = (
|
||||
];
|
||||
};
|
||||
|
||||
export const changeNumberVecData = (
|
||||
export const changeBooleanData = (
|
||||
field: string,
|
||||
newValueStr: string,
|
||||
container: { [key: string]: any },
|
||||
): [{ [key: string]: any }, ValidationError | null] => {
|
||||
// Parse value
|
||||
const [newValueBool, err] = parseBool(newValueStr);
|
||||
if (err) return [container, err];
|
||||
|
||||
// Build the new object
|
||||
return [
|
||||
{
|
||||
...container,
|
||||
[field]: newValueBool,
|
||||
},
|
||||
null,
|
||||
];
|
||||
};
|
||||
|
||||
export const changeNumberVecTData = (
|
||||
field: string,
|
||||
time: string,
|
||||
newValueStr: string,
|
||||
@@ -136,6 +155,43 @@ export const changeNumberVecData = (
|
||||
];
|
||||
};
|
||||
|
||||
export const changeNumberVecNData = (
|
||||
field: string,
|
||||
offset: string,
|
||||
newValueStr: string,
|
||||
container: { [key: string]: any },
|
||||
): [{ [key: string]: any }, ValidationError | null] => {
|
||||
const oldVec = container[field];
|
||||
const newVec = [...container[field]];
|
||||
const idx = parseInt(offset) - 1;
|
||||
|
||||
if (newValueStr === "") {
|
||||
// Trim the vector
|
||||
newVec.splice(idx, oldVec.length - idx);
|
||||
} else {
|
||||
// Parse new value
|
||||
const [newValueFloat, err] = parseNumber(newValueStr);
|
||||
if (err) return [container, err];
|
||||
|
||||
// Increase the length of the vector
|
||||
if (idx >= oldVec.length) {
|
||||
for (let i = oldVec.length; i < idx; i++) {
|
||||
newVec[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign new value
|
||||
newVec[idx] = newValueFloat;
|
||||
}
|
||||
return [
|
||||
{
|
||||
...container,
|
||||
[field]: newVec,
|
||||
},
|
||||
null,
|
||||
];
|
||||
};
|
||||
|
||||
export const changeData = (
|
||||
field: string,
|
||||
newValueStr: string,
|
||||
@@ -143,9 +199,9 @@ export const changeData = (
|
||||
colSpecs: ColumnSpec[],
|
||||
scenario: UnitCommitmentScenario,
|
||||
): [{ [key: string]: any }, ValidationError | null] => {
|
||||
const match = field.match(/^([^0-9]+)(\d+:\d+)?$/);
|
||||
const match = field.match(/^([^0-9]+)([0-9:]+)?$/);
|
||||
const fieldName = match![1]!.trim();
|
||||
const fieldTime = match![2];
|
||||
const fieldOffset = match![2];
|
||||
for (const spec of colSpecs) {
|
||||
if (spec.title !== fieldName) continue;
|
||||
switch (spec.type) {
|
||||
@@ -156,13 +212,22 @@ export const changeData = (
|
||||
case "number":
|
||||
return changeNumberData(fieldName, newValueStr, container);
|
||||
case "number[T]":
|
||||
return changeNumberVecData(
|
||||
return changeNumberVecTData(
|
||||
fieldName,
|
||||
fieldTime!,
|
||||
fieldOffset!,
|
||||
newValueStr,
|
||||
container,
|
||||
scenario,
|
||||
);
|
||||
case "number[N]":
|
||||
return changeNumberVecNData(
|
||||
fieldName,
|
||||
fieldOffset!,
|
||||
newValueStr,
|
||||
container,
|
||||
);
|
||||
case "boolean":
|
||||
return changeBooleanData(fieldName, newValueStr, container);
|
||||
default:
|
||||
throw Error(`Unknown type: ${spec.type}`);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TEST_DATA_1, TEST_DATA_BLANK } from "../fixtures.test";
|
||||
import assert from "node:assert";
|
||||
import {
|
||||
changeProfiledUnitData,
|
||||
changeThermalUnitData,
|
||||
createProfiledUnit,
|
||||
createThermalUnit,
|
||||
deleteGenerator,
|
||||
@@ -63,6 +64,58 @@ test("changeProfiledUnitData", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("changeThermalUnitData", () => {
|
||||
let scenario = TEST_DATA_1;
|
||||
let err: ValidationError | null;
|
||||
[scenario, err] = changeThermalUnitData(
|
||||
"g1",
|
||||
"Ramp up limit (MW)",
|
||||
"99",
|
||||
scenario,
|
||||
);
|
||||
assert(!err);
|
||||
[scenario, err] = changeThermalUnitData(
|
||||
"g1",
|
||||
"Startup costs ($) 2",
|
||||
"99",
|
||||
scenario,
|
||||
);
|
||||
assert(!err);
|
||||
[scenario, err] = changeThermalUnitData(
|
||||
"g1",
|
||||
"Production cost curve ($) 7",
|
||||
"99",
|
||||
scenario,
|
||||
);
|
||||
assert(!err);
|
||||
[scenario, err] = changeThermalUnitData(
|
||||
"g1",
|
||||
"Production cost curve (MW) 3",
|
||||
"",
|
||||
scenario,
|
||||
);
|
||||
assert(!err);
|
||||
[scenario, err] = changeThermalUnitData("g1", "Must run?", "true", scenario);
|
||||
assert(!err);
|
||||
assert.deepEqual(scenario.Generators["g1"], {
|
||||
Bus: "b1",
|
||||
Type: "Thermal",
|
||||
"Production cost curve (MW)": [100.0, 110],
|
||||
"Production cost curve ($)": [1400.0, 1600.0, 2200.0, 2400.0, 0, 0, 99],
|
||||
"Startup costs ($)": [300.0, 99.0],
|
||||
"Startup delays (h)": [1, 4],
|
||||
"Ramp up limit (MW)": 99,
|
||||
"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?": true,
|
||||
});
|
||||
});
|
||||
|
||||
test("changeProfiledUnitData with invalid bus", () => {
|
||||
let scenario = TEST_DATA_1;
|
||||
let err = null;
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
renameItemInObject,
|
||||
} from "./commonOps";
|
||||
import { ProfiledUnitsColumnSpec } from "../../components/CaseBuilder/ProfiledUnits";
|
||||
import { ThermalUnitsColumnSpec } from "../../components/CaseBuilder/ThermalUnits";
|
||||
|
||||
const assertBusesNotEmpty = (
|
||||
scenario: UnitCommitmentScenario,
|
||||
@@ -109,6 +110,32 @@ export const changeProfiledUnitData = (
|
||||
];
|
||||
};
|
||||
|
||||
export const changeThermalUnitData = (
|
||||
generator: string,
|
||||
field: string,
|
||||
newValueStr: string,
|
||||
scenario: UnitCommitmentScenario,
|
||||
): [UnitCommitmentScenario, ValidationError | null] => {
|
||||
const [newGen, err] = changeData(
|
||||
field,
|
||||
newValueStr,
|
||||
scenario.Generators[generator]!,
|
||||
ThermalUnitsColumnSpec,
|
||||
scenario,
|
||||
);
|
||||
if (err) return [scenario, err];
|
||||
return [
|
||||
{
|
||||
...scenario,
|
||||
Generators: {
|
||||
...scenario.Generators,
|
||||
[generator]: newGen,
|
||||
} as Generators,
|
||||
},
|
||||
null,
|
||||
];
|
||||
};
|
||||
|
||||
export const deleteGenerator = (
|
||||
name: string,
|
||||
scenario: UnitCommitmentScenario,
|
||||
|
||||
Reference in New Issue
Block a user