diff --git a/src/fitness.js b/src/fitness.js index 890ef51..b4abf6a 100644 --- a/src/fitness.js +++ b/src/fitness.js @@ -83,12 +83,12 @@ const calculateChargeScore = (props) => { batteryMaxInputPower, averageConsumption, currentCharge, - batteryCapacity, + batteryMaxEnergy, } = props let cost = price * (averageConsumption * duration) let charge = batteryMaxInputPower * duration - const overCharge = currentCharge + charge - batteryCapacity + const overCharge = currentCharge + charge - batteryMaxEnergy if (overCharge > 0) { charge -= overCharge // apply penalty for overcharge @@ -112,13 +112,14 @@ const fitnessFunction = (props) => (phenotype) => { const { totalDuration, priceData, - batteryCapacity, + batteryMaxEnergy, batteryMaxInputPower, averageConsumption, } = props let score = 0 - let currentCharge = 0 + const soc = props.soc ?? 0 + let currentCharge = (soc / 100) * batteryMaxEnergy for (const interval of fillInNormalPeriodsGenerator( totalDuration, @@ -130,7 +131,7 @@ const fitnessFunction = (props) => (phenotype) => { duration: interval.duration / 60, currentCharge: currentCharge, totalDuration: totalDuration, - batteryCapacity: batteryCapacity, + batteryMaxEnergy: batteryMaxEnergy, batteryMaxInputPower: batteryMaxInputPower, averageConsumption: averageConsumption, }) diff --git a/src/strategy-battery-charging-functions.js b/src/strategy-battery-charging-functions.js index 3bc2c93..993d518 100644 --- a/src/strategy-battery-charging-functions.js +++ b/src/strategy-battery-charging-functions.js @@ -180,7 +180,6 @@ const toSchedule = (p, start) => { const calculateBatteryChargingStrategy = (config) => { const { - priceData, populationSize, numberOfPricePeriods, generations, @@ -188,8 +187,17 @@ const calculateBatteryChargingStrategy = (config) => { batteryMaxEnergy, batteryMaxInputPower, averageConsumption, + soc, } = config + let priceData = config.priceData + if (Number.isInteger(soc)) { + let now = Date.now() + now = new Date(now - (now % (60 * 60 * 1000))) + + priceData = priceData.filter((v) => new Date(v.start) >= now) + } + if (priceData === undefined || priceData.length === 0) return [] let totalDuration = 0 @@ -209,6 +217,7 @@ const calculateBatteryChargingStrategy = (config) => { batteryMaxEnergy, batteryMaxInputPower, averageConsumption, + soc, }), population: generatePopulation( totalDuration, diff --git a/src/strategy-battery-charging.js b/src/strategy-battery-charging.js index fb921ce..59cbeb7 100644 --- a/src/strategy-battery-charging.js +++ b/src/strategy-battery-charging.js @@ -21,6 +21,7 @@ const node = (RED) => { this.on('input', async (msg, send, done) => { const priceData = msg.payload?.priceData || [] + const soc = msg.payload?.soc ?? 0 const schedule = calculateBatteryChargingStrategy({ priceData, @@ -32,6 +33,7 @@ const node = (RED) => { batteryMaxOutputPower, batteryMaxInputPower, averageConsumption, + soc, }) if (msg.payload) { diff --git a/test/fitness.test.js b/test/fitness.test.js index a781e2a..5813bea 100644 --- a/test/fitness.test.js +++ b/test/fitness.test.js @@ -1,3 +1,4 @@ +const { expect } = require('@jest/globals') const { fitnessFunction, splitIntoHourIntervals, @@ -38,85 +39,6 @@ describe('Fitness - splitIntoHourIntervals', () => { { start: 120, activity: 1, duration: 30 }, ]) }) - - // test('should calculate fitness score with no periods', () => { - // const priceData = [ - // { value: 1, start: '2022-12-01T00:00:00.000Z' }, - // { value: 1, start: '2022-12-01T01:00:00.000Z' }, - // ] - // let endTime = 2 * 60 - // let batteryMaxEnergy = 1 - // let batteryMaxInputPower = 1 - // let averageConsumption = 1 - - // let score = fitnessFunction({ - // priceData, - // endTime, - // batteryMaxEnergy, - // batteryMaxInputPower, - // averageConsumption, - // })([]) - // expect(score).toBe(-2) - // }) - - // test('should calculate fitness score with one charge period', () => { - // const priceData = [ - // { value: 1, start: '2022-12-01T00:00:00.000Z' }, - // { value: 1, start: '2022-12-01T01:00:00.000Z' }, - // ] - // let endTime = 2 * 60 - // let batteryMaxEnergy = 1 - // let batteryMaxInputPower = 1 - // let averageConsumption = 1 - - // let score = fitnessFunction({ priceData, endTime, batteryMaxEnergy, batteryMaxInputPower, averageConsumption })([ - // { start: 0, activity: 1, duration: 60 }, - // ]) - // expect(score).toBe(-3) - // }) - - // test('should calculate fitness score with one discharge period', () => { - // const priceData = [ - // { value: 1, start: '2022-12-01T00:00:00.000Z' }, - // { value: 1, start: '2022-12-01T01:00:00.000Z' }, - // ] - // let endTime = 2 * 60 - // let batteryMaxEnergy = 1 - // let batteryMaxInputPower = 1 - // let averageConsumption = 1 - - // let score = fitnessFunction({ priceData, endTime, batteryMaxEnergy, batteryMaxInputPower, averageConsumption })([ - // { start: 0, activity: -1, duration: 60 }, - // ]) - // expect(score).toBe(-2) - // }) - - // test('should calculate fitness score with charged battery', () => { - // const priceData = [ - // { value: 1, start: '2022-12-01T00:00:00.000Z' }, - // { value: 2, start: '2022-12-01T01:00:00.000Z' }, - // ] - // let endTime = 2 * 60 - // let batteryMaxEnergy = 1 - // let batteryMaxInputPower = 1 - // let averageConsumption = 1 - - // let fitness = fitnessFunction({ priceData, endTime, batteryMaxEnergy, batteryMaxInputPower, averageConsumption }) - - // expect( - // fitness([ - // { start: 0, activity: 1, duration: 60 }, - // { start: 60, activity: -1, duration: 60 }, - // ]) - // ).toBe(-2) - - // expect( - // fitness([ - // { start: 0, activity: 1, duration: 90 }, - // { start: 90, activity: -1, duration: 30 }, - // ]) - // ).toBe(-3) - // }) }) describe('Fitness - fillInNormalPeriods', () => { @@ -242,7 +164,7 @@ describe('Fitness - calculateChargeScore', () => { price: 2, averageConsumption: 1, currentCharge: 5, - batteryCapacity: 5, + batteryMaxEnergy: 5, batteryMaxInputPower: 1, }) ).toEqual([2, 0]) @@ -255,7 +177,7 @@ describe('Fitness - calculateChargeScore', () => { price: 2, averageConsumption: 1, currentCharge: 5, - batteryCapacity: 5, + batteryMaxEnergy: 5, batteryMaxInputPower: 1, }) ).toEqual([1, 0]) @@ -268,7 +190,7 @@ describe('Fitness - calculateChargeScore', () => { price: 2, averageConsumption: 1, currentCharge: 0, - batteryCapacity: 5, + batteryMaxEnergy: 5, batteryMaxInputPower: 1, }) ).toEqual([4, 1]) @@ -281,7 +203,7 @@ describe('Fitness - calculateChargeScore', () => { price: 2, averageConsumption: 1, currentCharge: 4.5, - batteryCapacity: 5, + batteryMaxEnergy: 5, batteryMaxInputPower: 1, }) ).toEqual([3, 0.5]) @@ -311,7 +233,30 @@ describe('Fitness - calculateNormalScore', () => { }) describe('Fitness', () => { - test('should new fitness', () => { + test('should calculate fitness', () => { + const priceData = [ + { value: 1, start: '2022-12-01T00:00:00.000Z' }, + { value: 1, start: '2022-12-01T01:00:00.000Z' }, + ] + const totalDuration = 2 * 60 + const batteryMaxEnergy = 1 + const batteryMaxInputPower = 1 + const averageConsumption = 1 + const score = fitnessFunction({ + priceData, + totalDuration, + batteryMaxEnergy, + batteryMaxInputPower, + batteryMaxEnergy, + averageConsumption, + })([ + { start: 30, duration: 60, activity: 1 }, + { start: 90, duration: 30, activity: -1 }, + ]) + expect(score).toEqual(-2.5) + }) + + test('should calculate fitness with soc', () => { const priceData = [ { value: 1, start: '2022-12-01T00:00:00.000Z' }, { value: 1, start: '2022-12-01T01:00:00.000Z' }, @@ -320,16 +265,19 @@ describe('Fitness', () => { const batteryMaxEnergy = 1 const batteryMaxInputPower = 1 const averageConsumption = 1 + const soc = 100 const score = fitnessFunction({ priceData, totalDuration, batteryMaxEnergy, batteryMaxInputPower, + batteryMaxEnergy, averageConsumption, + soc, })([ { start: 30, duration: 60, activity: 1 }, { start: 90, duration: 30, activity: -1 }, ]) - console.log(score) + expect(score).toEqual(-1.5) }) }) diff --git a/test/strategy-battery-charging-functions.test.js b/test/strategy-battery-charging-functions.test.js index 12ddcd2..ed1f3d1 100644 --- a/test/strategy-battery-charging-functions.test.js +++ b/test/strategy-battery-charging-functions.test.js @@ -40,21 +40,25 @@ describe('Crossover', () => { describe('Calculate', () => { test('calculate', () => { + let now = Date.now() + now = now - (now % (60 * 60 * 1000)) const priceData = [ - { value: 1, start: '2022-12-01T00:00:00.000Z' }, - { value: 2, start: '2022-12-01T01:00:00.000Z' }, - { value: 5, start: '2022-12-01T02:00:00.000Z' }, + { value: 1, start: new Date(now).toString() }, + { value: 500, start: new Date(now + 60 * 60 * 1000).toString() }, + { value: 500, start: new Date(now + 60 * 60 * 1000 * 2).toString() }, ] const populationSize = 100 - const numberOfPricePeriods = 8 + const numberOfPricePeriods = 2 const generations = 500 const mutationRate = 0.03 - const batteryMaxEnergy = 5 // kWh - const batteryMaxOutputPower = 2.5 // kW - const batteryMaxInputPower = 2.5 // kW + const batteryMaxEnergy = 3 // kWh + const batteryMaxOutputPower = 3 // kW + const batteryMaxInputPower = 3 // kW const averageConsumption = 1.5 // kW + const soc = 0 + const config = { priceData, populationSize, @@ -65,7 +69,19 @@ describe('Calculate', () => { batteryMaxOutputPower, batteryMaxInputPower, averageConsumption, + soc, } - console.log(calculateBatteryChargingStrategy(config)) + const schedule = calculateBatteryChargingStrategy(config) + console.log(schedule) + + expect(schedule.length).toBeGreaterThan(0) + expect(schedule[1]).toMatchObject({ + activity: 1, + name: 'charging', + }) + expect(schedule[3]).toMatchObject({ + activity: -1, + name: 'discharging', + }) }) })