UNPKG

node-red-contrib-genetic-charging-strategy

Version:

A module for Node-RED that adds a battery charging strategy to node-red-contrib-power-saver. It uses genetic algorithms to find the best schedule

146 lines (145 loc) 7.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fitnessFunction = exports.cost = exports.calculatePeriodScore = exports.calculateIntervalScore = exports.calculateChargeScore = exports.calculateNormalScore = exports.calculateDischargeScore = exports.allPeriods = exports.splitIntoHourIntervals = void 0; exports.splitIntoHourIntervalsGenerator = splitIntoHourIntervalsGenerator; exports.allPeriodsGenerator = allPeriodsGenerator; function* splitIntoHourIntervalsGenerator(seed) { var _a; let remainingDuration = (_a = seed.duration) !== null && _a !== void 0 ? _a : 0; let start = seed.start; while (remainingDuration > 0) { const i = Math.min(60 - (start % 60), remainingDuration); yield { start: start, duration: i, activity: seed.activity, }; start += i; remainingDuration -= i; } return; } const splitIntoHourIntervals = (seed) => [ ...splitIntoHourIntervalsGenerator(seed), ]; exports.splitIntoHourIntervals = splitIntoHourIntervals; function* allPeriodsGenerator(props, phenotype) { const { batteryMaxEnergy, soc, totalDuration } = props; const { excessPvEnergyUse, periods } = phenotype; let currentCharge = soc * batteryMaxEnergy; const addCosts = (period) => { const score = (0, exports.calculatePeriodScore)(props, period, excessPvEnergyUse, currentCharge); currentCharge += score[1]; period.cost = score[0]; period.charge = score[1]; return period; }; if (periods.isEmpty()) { return; } let node = periods.head; while (node) { const end = node.next ? node.next.data.start : totalDuration; const period = { ...node.data }; period.duration = end - period.start; yield addCosts(period); node = node.next; } } const allPeriods = (props, phenotype) => { return [...allPeriodsGenerator(props, phenotype)]; }; exports.allPeriods = allPeriods; const CHARGE = 1; const calculateDischargeScore = (props) => { const { exportPrice, importPrice, consumption, production, maxDischarge, maxCharge, excessPvEnergyUse, batteryIdleLoss, } = props; const consumedFromProduction = Math.min(consumption, production); const batteryChargeFromProduction = excessPvEnergyUse == CHARGE ? Math.min(production - consumedFromProduction, maxCharge) : 0; const consumedFromBattery = Math.min(consumption - consumedFromProduction, maxDischarge); const lossFromBattery = Math.min(batteryIdleLoss, maxDischarge); const soldFromProduction = production - consumedFromProduction - batteryChargeFromProduction; const consumedFromGrid = consumption - consumedFromProduction - consumedFromBattery; const cost = consumedFromGrid * importPrice - soldFromProduction * exportPrice; const charge = batteryChargeFromProduction - consumedFromBattery - lossFromBattery; return [cost, charge]; }; exports.calculateDischargeScore = calculateDischargeScore; const calculateNormalScore = (props) => { const { exportPrice, importPrice, maxDischarge, maxCharge, consumption, production, excessPvEnergyUse, batteryIdleLoss, } = props; const consumedFromProduction = Math.min(consumption, production); const batteryChargeFromProduction = excessPvEnergyUse == CHARGE ? Math.min(production - consumedFromProduction, maxCharge) : 0; const lossFromBattery = Math.min(batteryIdleLoss, maxDischarge); const soldFromProduction = production - consumedFromProduction - batteryChargeFromProduction; const consumedFromGrid = consumption - consumedFromProduction; const cost = importPrice * consumedFromGrid - exportPrice * soldFromProduction; const charge = batteryChargeFromProduction - lossFromBattery; return [cost, charge]; }; exports.calculateNormalScore = calculateNormalScore; const calculateChargeScore = (props) => { const { exportPrice, importPrice, consumption, production, maxCharge } = props; const consumedFromProduction = Math.min(consumption, production); const batteryChargeFromProduction = Math.min(production - consumedFromProduction, maxCharge); const soldFromProduction = production - consumedFromProduction - batteryChargeFromProduction; const consumedFromGrid = consumption - consumedFromProduction; const chargedFromGrid = maxCharge - batteryChargeFromProduction; const cost = (consumedFromGrid + chargedFromGrid) * importPrice - soldFromProduction * exportPrice; const charge = batteryChargeFromProduction + chargedFromGrid; return [cost, charge]; }; exports.calculateChargeScore = calculateChargeScore; const calculateIntervalScore = (props) => { switch (props.activity) { case -1: return (0, exports.calculateDischargeScore)(props); case 1: return (0, exports.calculateChargeScore)(props); case 0: default: return (0, exports.calculateNormalScore)(props); } }; exports.calculateIntervalScore = calculateIntervalScore; const calculatePeriodScore = (props, period, excessPvEnergyUse, _currentCharge) => { const { input, batteryMaxEnergy, batteryMaxInputPower, batteryMaxOutputPower, batteryIdleLoss } = props; let cost = 0; let currentCharge = _currentCharge; for (const interval of (0, exports.splitIntoHourIntervals)(period)) { const duration = interval.duration / 60; const maxCharge = Math.min(batteryMaxInputPower * duration, batteryMaxEnergy - currentCharge); const maxDischarge = Math.min(batteryMaxOutputPower * duration, currentCharge); const { importPrice, exportPrice, consumption, production } = input[Math.floor(interval.start / 60)]; const v = (0, exports.calculateIntervalScore)({ activity: interval.activity, importPrice, exportPrice, consumption: consumption * duration, production: production * duration, maxCharge, maxDischarge, excessPvEnergyUse, batteryIdleLoss: batteryIdleLoss * duration, }); cost += v[0]; currentCharge += v[1]; } return [cost, currentCharge - _currentCharge]; }; exports.calculatePeriodScore = calculatePeriodScore; const cost = (periods) => { return periods.reduce((acc, cur) => { var _a; return acc + ((_a = cur.cost) !== null && _a !== void 0 ? _a : 0); }, 0); }; exports.cost = cost; const fitnessFunction = (props) => (phenotype) => { const periods = (0, exports.allPeriods)(props, phenotype); let score = -(0, exports.cost)(periods); const averagePrice = props.input.reduce((acc, cur) => acc + cur.importPrice, 0) / props.input.length; score -= periods.reduce((acc, cur) => { if (cur.activity != 0 && cur.charge == 0 && cur.duration !== undefined) return acc + (cur.duration * averagePrice) / 60; else return acc; }, 0); return score; }; exports.fitnessFunction = fitnessFunction;