@energiok/node-red-contrib-pricecontrol-thermal 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DASHBOARD-TEMPLATE-3-MODES.md +4 -4
- package/package.json +1 -1
- package/src/strategy-smart-thermal-functions.js +210 -160
- package/src/strategy-smart-thermal.js +2 -0
- package/test/strategy-smart-thermal-functions.test.js +258 -94
- package/test/strategy-smart-thermal-node.test.js +36 -58
- package/nul +0 -0
- package/test-3day-prices.js +0 -96
- package/test-cold-weather.js +0 -43
- package/test-day-transition.js +0 -161
- package/test-find-params.js +0 -78
- package/test-hourly-alignment.js +0 -114
- package/test-price-analysis.js +0 -77
- package/test-runway.js +0 -118
- package/test-saturday-peak.js +0 -132
- package/test-step-by-step.js +0 -213
- package/test-temp-scaling.js +0 -62
- package/test-thermal-budget.js +0 -174
- package/test-timezone.js +0 -83
- package/test-utc-machine.js +0 -90
package/test-hourly-alignment.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test: Hourly alignment when minModeDuration >= 60
|
|
3
|
-
*
|
|
4
|
-
* When minModeDuration is 60 minutes or more, mode changes should
|
|
5
|
-
* align to hour boundaries (16:00, 17:00) instead of quarter-hours
|
|
6
|
-
* (16:15, 17:30) because electricity billing is hourly.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { createSchedule } = require('./src/strategy-smart-thermal-functions.js');
|
|
10
|
-
|
|
11
|
-
// 15-minute interval prices for a day
|
|
12
|
-
const prices = [
|
|
13
|
-
{ timestamp: "2026-01-17T14:00:00Z", price: 100 },
|
|
14
|
-
{ timestamp: "2026-01-17T14:15:00Z", price: 102 },
|
|
15
|
-
{ timestamp: "2026-01-17T14:30:00Z", price: 105 },
|
|
16
|
-
{ timestamp: "2026-01-17T14:45:00Z", price: 108 },
|
|
17
|
-
{ timestamp: "2026-01-17T15:00:00Z", price: 115 }, // 16:00 Oslo - getting expensive
|
|
18
|
-
{ timestamp: "2026-01-17T15:15:00Z", price: 120 },
|
|
19
|
-
{ timestamp: "2026-01-17T15:30:00Z", price: 125 },
|
|
20
|
-
{ timestamp: "2026-01-17T15:45:00Z", price: 128 },
|
|
21
|
-
{ timestamp: "2026-01-17T16:00:00Z", price: 130 }, // 17:00 Oslo - peak
|
|
22
|
-
{ timestamp: "2026-01-17T16:15:00Z", price: 128 },
|
|
23
|
-
{ timestamp: "2026-01-17T16:30:00Z", price: 125 },
|
|
24
|
-
{ timestamp: "2026-01-17T16:45:00Z", price: 122 },
|
|
25
|
-
{ timestamp: "2026-01-17T17:00:00Z", price: 118 }, // 18:00 Oslo
|
|
26
|
-
{ timestamp: "2026-01-17T17:15:00Z", price: 115 },
|
|
27
|
-
{ timestamp: "2026-01-17T17:30:00Z", price: 110 },
|
|
28
|
-
{ timestamp: "2026-01-17T17:45:00Z", price: 105 },
|
|
29
|
-
{ timestamp: "2026-01-17T18:00:00Z", price: 100 }, // 19:00 Oslo
|
|
30
|
-
{ timestamp: "2026-01-17T18:15:00Z", price: 95 },
|
|
31
|
-
{ timestamp: "2026-01-17T18:30:00Z", price: 90 },
|
|
32
|
-
{ timestamp: "2026-01-17T18:45:00Z", price: 88 },
|
|
33
|
-
{ timestamp: "2026-01-17T19:00:00Z", price: 85 }, // 20:00 Oslo - cheap
|
|
34
|
-
{ timestamp: "2026-01-17T19:15:00Z", price: 82 },
|
|
35
|
-
{ timestamp: "2026-01-17T19:30:00Z", price: 80 },
|
|
36
|
-
{ timestamp: "2026-01-17T19:45:00Z", price: 78 },
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
const baseConfig = {
|
|
40
|
-
numPriceGroups: 3,
|
|
41
|
-
minSavingsPercent: 5,
|
|
42
|
-
outputTimezone: "Europe/Oslo",
|
|
43
|
-
heatLossCoefficient: 0.05,
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
console.log("=== HOURLY ALIGNMENT TEST ===\n");
|
|
47
|
-
|
|
48
|
-
// Test with 30-minute min duration (should NOT align to hours)
|
|
49
|
-
console.log("--- With minModeDuration: 30 (NO hourly alignment) ---\n");
|
|
50
|
-
const schedule30 = createSchedule(prices, 5, { ...baseConfig, minModeDuration: 30 });
|
|
51
|
-
|
|
52
|
-
console.log("Oslo Time | Mode | Price");
|
|
53
|
-
console.log("-".repeat(35));
|
|
54
|
-
schedule30.forEach(entry => {
|
|
55
|
-
const oslo = entry.time.substring(11, 16);
|
|
56
|
-
console.log(`${oslo} | ${entry.mode.padEnd(6)} | ${entry.price.toFixed(0)} EUR`);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Find mode transitions
|
|
60
|
-
const transitions30 = [];
|
|
61
|
-
for (let i = 1; i < schedule30.length; i++) {
|
|
62
|
-
if (schedule30[i].mode !== schedule30[i-1].mode) {
|
|
63
|
-
transitions30.push(schedule30[i].time.substring(11, 16));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
console.log(`\nMode transitions at: ${transitions30.join(", ")}`);
|
|
67
|
-
|
|
68
|
-
// Test with 60-minute min duration (SHOULD align to hours)
|
|
69
|
-
console.log("\n--- With minModeDuration: 60 (WITH hourly alignment) ---\n");
|
|
70
|
-
const schedule60 = createSchedule(prices, 5, { ...baseConfig, minModeDuration: 60 });
|
|
71
|
-
|
|
72
|
-
console.log("Oslo Time | Mode | Price");
|
|
73
|
-
console.log("-".repeat(35));
|
|
74
|
-
schedule60.forEach(entry => {
|
|
75
|
-
const oslo = entry.time.substring(11, 16);
|
|
76
|
-
console.log(`${oslo} | ${entry.mode.padEnd(6)} | ${entry.price.toFixed(0)} EUR`);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Find mode transitions
|
|
80
|
-
const transitions60 = [];
|
|
81
|
-
for (let i = 1; i < schedule60.length; i++) {
|
|
82
|
-
if (schedule60[i].mode !== schedule60[i-1].mode) {
|
|
83
|
-
transitions60.push(schedule60[i].time.substring(11, 16));
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
console.log(`\nMode transitions at: ${transitions60.join(", ")}`);
|
|
87
|
-
|
|
88
|
-
// Verify transitions are on hour boundaries
|
|
89
|
-
const allOnHour = transitions60.every(t => t.endsWith(":00"));
|
|
90
|
-
console.log(`\nAll transitions on hour boundaries: ${allOnHour ? "YES ✓" : "NO ✗"}`);
|
|
91
|
-
|
|
92
|
-
// Test with 120-minute min duration
|
|
93
|
-
console.log("\n--- With minModeDuration: 120 (WITH hourly alignment) ---\n");
|
|
94
|
-
const schedule120 = createSchedule(prices, 5, { ...baseConfig, minModeDuration: 120 });
|
|
95
|
-
|
|
96
|
-
console.log("Oslo Time | Mode | Price");
|
|
97
|
-
console.log("-".repeat(35));
|
|
98
|
-
schedule120.forEach(entry => {
|
|
99
|
-
const oslo = entry.time.substring(11, 16);
|
|
100
|
-
console.log(`${oslo} | ${entry.mode.padEnd(6)} | ${entry.price.toFixed(0)} EUR`);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const transitions120 = [];
|
|
104
|
-
for (let i = 1; i < schedule120.length; i++) {
|
|
105
|
-
if (schedule120[i].mode !== schedule120[i-1].mode) {
|
|
106
|
-
transitions120.push(schedule120[i].time.substring(11, 16));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
console.log(`\nMode transitions at: ${transitions120.join(", ")}`);
|
|
110
|
-
|
|
111
|
-
console.log("\n=== CONCLUSION ===");
|
|
112
|
-
console.log("✓ minModeDuration < 60: transitions can be at any quarter-hour");
|
|
113
|
-
console.log("✓ minModeDuration >= 60: transitions aligned to hour boundaries");
|
|
114
|
-
console.log("✓ This matches electricity billing periods (hourly)");
|
package/test-price-analysis.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
const { createSchedule } = require('./src/strategy-smart-thermal-functions.js');
|
|
2
|
-
|
|
3
|
-
const prices = [{"price":83.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T23:00:00Z"},{"price":82.96,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T23:15:00Z"},{"price":81.91,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T23:30:00Z"},{"price":80.89,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T23:45:00Z"},{"price":81.62,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T00:00:00Z"},{"price":80.6,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T00:15:00Z"},{"price":79.84,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T00:30:00Z"},{"price":77.88,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T00:45:00Z"},{"price":78.22,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T01:00:00Z"},{"price":77.83,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T01:15:00Z"},{"price":75.03,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T01:30:00Z"},{"price":74.14,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T01:45:00Z"},{"price":72.9,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T02:00:00Z"},{"price":72.47,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T02:15:00Z"},{"price":72.89,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T02:30:00Z"},{"price":73.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T02:45:00Z"},{"price":74.65,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T03:00:00Z"},{"price":75.09,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T03:15:00Z"},{"price":77.46,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T03:30:00Z"},{"price":78.48,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T03:45:00Z"},{"price":80.92,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T04:00:00Z"},{"price":82.07,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T04:15:00Z"},{"price":83.29,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T04:30:00Z"},{"price":84.17,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T04:45:00Z"},{"price":83.98,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T05:00:00Z"},{"price":85.58,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T05:15:00Z"},{"price":86.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T05:30:00Z"},{"price":87.82,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T05:45:00Z"},{"price":87.25,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T06:00:00Z"},{"price":87.91,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T06:15:00Z"},{"price":88.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T06:30:00Z"},{"price":88.69,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T06:45:00Z"},{"price":88.3,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T07:00:00Z"},{"price":88.55,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T07:15:00Z"},{"price":88.88,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T07:30:00Z"},{"price":88.97,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T07:45:00Z"},{"price":88.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T08:00:00Z"},{"price":88.83,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T08:15:00Z"},{"price":88.86,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T08:30:00Z"},{"price":89.01,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T08:45:00Z"},{"price":88.37,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T09:00:00Z"},{"price":88.24,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T09:15:00Z"},{"price":88.21,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T09:30:00Z"},{"price":87.97,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T09:45:00Z"},{"price":89.66,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T10:00:00Z"},{"price":89.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T10:15:00Z"},{"price":89.48,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T10:30:00Z"},{"price":89.49,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T10:45:00Z"},{"price":88.75,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T11:00:00Z"},{"price":89.62,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T11:15:00Z"},{"price":90.03,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T11:30:00Z"},{"price":90.46,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T11:45:00Z"},{"price":90.12,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T12:00:00Z"},{"price":90.84,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T12:15:00Z"},{"price":92.33,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T12:30:00Z"},{"price":94.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T12:45:00Z"},{"price":91.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T13:00:00Z"},{"price":93.01,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T13:15:00Z"},{"price":93.14,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T13:30:00Z"},{"price":93.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T13:45:00Z"},{"price":92.2,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T14:00:00Z"},{"price":93.38,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T14:15:00Z"},{"price":93.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T14:30:00Z"},{"price":93.84,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T14:45:00Z"},{"price":94,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T15:00:00Z"},{"price":95.16,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T15:15:00Z"},{"price":96.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T15:30:00Z"},{"price":101.04,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T15:45:00Z"},{"price":96.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T16:00:00Z"},{"price":95.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T16:15:00Z"},{"price":95.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T16:30:00Z"},{"price":95.29,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T16:45:00Z"},{"price":95.16,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T17:00:00Z"},{"price":95,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T17:15:00Z"},{"price":95.31,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T17:30:00Z"},{"price":95.13,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T17:45:00Z"},{"price":95.2,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T18:00:00Z"},{"price":94.93,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T18:15:00Z"},{"price":94.79,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T18:30:00Z"},{"price":94.77,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T18:45:00Z"},{"price":94.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T19:00:00Z"},{"price":94.46,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T19:15:00Z"},{"price":94.3,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T19:30:00Z"},{"price":94.21,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T19:45:00Z"},{"price":94,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T20:00:00Z"},{"price":93.94,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T20:15:00Z"},{"price":93.7,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T20:30:00Z"},{"price":93.49,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T20:45:00Z"},{"price":93.77,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T21:00:00Z"},{"price":93.37,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T21:15:00Z"},{"price":93.02,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T21:30:00Z"},{"price":92.7,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T21:45:00Z"},{"price":93.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T22:00:00Z"},{"price":92.77,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T22:15:00Z"},{"price":92.08,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T22:30:00Z"},{"price":91.1,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T22:45:00Z"}];
|
|
4
|
-
|
|
5
|
-
const outdoorTemp = 5.0; // Example outdoor temperature
|
|
6
|
-
|
|
7
|
-
const config = {
|
|
8
|
-
heatLossCoefficient: 0.05,
|
|
9
|
-
minModeDuration: 60,
|
|
10
|
-
minSavingsPercent: 5,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const schedule = createSchedule(prices, outdoorTemp, config);
|
|
14
|
-
|
|
15
|
-
// Calculate average price
|
|
16
|
-
const avgPrice = prices.reduce((sum, p) => sum + p.price, 0) / prices.length;
|
|
17
|
-
console.log(`Average price: ${avgPrice.toFixed(2)} EUR/MWh\n`);
|
|
18
|
-
|
|
19
|
-
if (schedule.length > 0 && schedule[0].tuning) {
|
|
20
|
-
const tuning = schedule[0].tuning;
|
|
21
|
-
console.log('=== TUNING INFO ===');
|
|
22
|
-
console.log(`Outdoor temp: ${tuning.outdoorTemp}C`);
|
|
23
|
-
console.log(`Min savings percent: ${tuning.minSavingsPercent}%`);
|
|
24
|
-
console.log(`Max safe coast time: ${tuning.maxCoastMinutes.toFixed(0)} minutes (${(tuning.maxCoastMinutes / 60).toFixed(1)} hours)`);
|
|
25
|
-
console.log();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Group by hour and mode
|
|
29
|
-
const hourly = {};
|
|
30
|
-
schedule.forEach(entry => {
|
|
31
|
-
const date = new Date(entry.time);
|
|
32
|
-
const hour = date.getUTCHours();
|
|
33
|
-
const hourKey = `${hour.toString().padStart(2, '0')}:00`;
|
|
34
|
-
|
|
35
|
-
if (!hourly[hourKey]) {
|
|
36
|
-
hourly[hourKey] = { normal: 0, boost: 0, coast: 0, prices: [] };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
hourly[hourKey][entry.mode]++;
|
|
40
|
-
hourly[hourKey].prices.push(entry.price.toFixed(2));
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// Display results
|
|
44
|
-
console.log('Hour-by-hour breakdown:\n');
|
|
45
|
-
console.log('Time | Mode Distribution | Avg Price | Status');
|
|
46
|
-
console.log('------|----------------------------|-----------|--------');
|
|
47
|
-
|
|
48
|
-
Object.keys(hourly).sort().forEach(hour => {
|
|
49
|
-
const data = hourly[hour];
|
|
50
|
-
const total = data.normal + data.boost + data.coast;
|
|
51
|
-
const avgHourPrice = data.prices.reduce((sum, p) => sum + parseFloat(p), 0) / data.prices.length;
|
|
52
|
-
|
|
53
|
-
let dominantMode = 'normal';
|
|
54
|
-
if (data.boost > data.normal && data.boost > data.coast) dominantMode = 'boost';
|
|
55
|
-
if (data.coast > data.normal && data.coast > data.boost) dominantMode = 'coast';
|
|
56
|
-
|
|
57
|
-
const modeStr = `N:${data.normal} B:${data.boost} C:${data.coast}`;
|
|
58
|
-
const status = dominantMode === 'boost' ? '🟢 ON ' : dominantMode === 'coast' ? '🔴 OFF' : '🔵 NORM';
|
|
59
|
-
|
|
60
|
-
console.log(`${hour} | ${modeStr.padEnd(26)} | ${avgHourPrice.toFixed(2).padStart(9)} | ${status}`);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Summary
|
|
64
|
-
console.log('\n=== SUMMARY ===\n');
|
|
65
|
-
const totalIntervals = schedule.length;
|
|
66
|
-
const normalCount = schedule.filter(s => s.mode === 'normal').length;
|
|
67
|
-
const boostCount = schedule.filter(s => s.mode === 'boost').length;
|
|
68
|
-
const coastCount = schedule.filter(s => s.mode === 'coast').length;
|
|
69
|
-
|
|
70
|
-
console.log(`Total intervals: ${totalIntervals}`);
|
|
71
|
-
console.log(`Normal: ${normalCount} (${(normalCount/totalIntervals*100).toFixed(1)}%)`);
|
|
72
|
-
console.log(`Boost: ${boostCount} (${(boostCount/totalIntervals*100).toFixed(1)}%)`);
|
|
73
|
-
console.log(`Coast: ${coastCount} (${(coastCount/totalIntervals*100).toFixed(1)}%)`);
|
|
74
|
-
|
|
75
|
-
console.log('\n🟢 Boost = Pre-heat (heat pump ON at high temp)');
|
|
76
|
-
console.log('🔵 Normal = Regular operation (heat pump maintains setpoint)');
|
|
77
|
-
console.log('🔴 Coast = Turn off (heat pump OFF, temperature coasts down)');
|
package/test-runway.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test: Runway/lookahead extension for coast periods
|
|
3
|
-
*
|
|
4
|
-
* The Saturday evening scenario:
|
|
5
|
-
* - Coast ends at 18:00 (123.38 EUR)
|
|
6
|
-
* - 18:15-20:00 still expensive (121-125 EUR)
|
|
7
|
-
* - 20:15 onwards gets cheaper (106, 104, 99...)
|
|
8
|
-
*
|
|
9
|
-
* Expected: Coast should extend to ~20:00-20:15 to "land" on cheaper prices
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const { createSchedule } = require('./src/strategy-smart-thermal-functions.js');
|
|
13
|
-
|
|
14
|
-
// Saturday prices from the user's actual data
|
|
15
|
-
const saturdayPrices = [
|
|
16
|
-
{ timestamp: "2026-01-17T14:00:00Z", price: 100.32 },
|
|
17
|
-
{ timestamp: "2026-01-17T14:15:00Z", price: 105.32 },
|
|
18
|
-
{ timestamp: "2026-01-17T14:30:00Z", price: 110.81 },
|
|
19
|
-
{ timestamp: "2026-01-17T14:45:00Z", price: 113.24 },
|
|
20
|
-
{ timestamp: "2026-01-17T15:00:00Z", price: 111.59 },
|
|
21
|
-
{ timestamp: "2026-01-17T15:15:00Z", price: 120.96 },
|
|
22
|
-
{ timestamp: "2026-01-17T15:30:00Z", price: 122.54 }, // Coast starts
|
|
23
|
-
{ timestamp: "2026-01-17T15:45:00Z", price: 123.82 },
|
|
24
|
-
{ timestamp: "2026-01-17T16:00:00Z", price: 128.80 },
|
|
25
|
-
{ timestamp: "2026-01-17T16:15:00Z", price: 126.62 },
|
|
26
|
-
{ timestamp: "2026-01-17T16:30:00Z", price: 127.73 },
|
|
27
|
-
{ timestamp: "2026-01-17T16:45:00Z", price: 131.06 }, // Peak
|
|
28
|
-
{ timestamp: "2026-01-17T17:00:00Z", price: 123.38 }, // Coast ends here in old algorithm
|
|
29
|
-
{ timestamp: "2026-01-17T17:15:00Z", price: 121.69 }, // Still expensive!
|
|
30
|
-
{ timestamp: "2026-01-17T17:30:00Z", price: 125.39 },
|
|
31
|
-
{ timestamp: "2026-01-17T17:45:00Z", price: 124.59 },
|
|
32
|
-
{ timestamp: "2026-01-17T18:00:00Z", price: 122.52 },
|
|
33
|
-
{ timestamp: "2026-01-17T18:15:00Z", price: 121.64 },
|
|
34
|
-
{ timestamp: "2026-01-17T18:30:00Z", price: 120.84 },
|
|
35
|
-
{ timestamp: "2026-01-17T18:45:00Z", price: 113.53 },
|
|
36
|
-
{ timestamp: "2026-01-17T19:00:00Z", price: 119.74 },
|
|
37
|
-
{ timestamp: "2026-01-17T19:15:00Z", price: 106.23 }, // Getting cheaper
|
|
38
|
-
{ timestamp: "2026-01-17T19:30:00Z", price: 104.52 },
|
|
39
|
-
{ timestamp: "2026-01-17T19:45:00Z", price: 99.31 }, // Much cheaper - good landing zone
|
|
40
|
-
{ timestamp: "2026-01-17T20:00:00Z", price: 104.05 },
|
|
41
|
-
{ timestamp: "2026-01-17T20:15:00Z", price: 100.21 },
|
|
42
|
-
{ timestamp: "2026-01-17T20:30:00Z", price: 98.94 },
|
|
43
|
-
{ timestamp: "2026-01-17T20:45:00Z", price: 96.23 },
|
|
44
|
-
];
|
|
45
|
-
|
|
46
|
-
const config = {
|
|
47
|
-
heatLossCoefficient: 0.05,
|
|
48
|
-
minModeDuration: 60,
|
|
49
|
-
minSavingsPercent: 8,
|
|
50
|
-
minSavingsAbsolute: 0,
|
|
51
|
-
numPriceGroups: 5,
|
|
52
|
-
outputTimezone: "Europe/Oslo",
|
|
53
|
-
extremeWeatherThreshold: -15,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const outdoorTemp = 5;
|
|
57
|
-
|
|
58
|
-
console.log("=== RUNWAY/LOOKAHEAD TEST ===\n");
|
|
59
|
-
|
|
60
|
-
const schedule = createSchedule(saturdayPrices, outdoorTemp, config);
|
|
61
|
-
|
|
62
|
-
console.log("Tuning info:");
|
|
63
|
-
const tuning = schedule[0]?.tuning;
|
|
64
|
-
if (tuning) {
|
|
65
|
-
console.log(` Cheap threshold: ${tuning.clustering.cheapThreshold.toFixed(2)} EUR`);
|
|
66
|
-
console.log(` Expensive threshold: ${tuning.clustering.expensiveThreshold.toFixed(2)} EUR`);
|
|
67
|
-
console.log(` Landing threshold (midpoint): ${((tuning.clustering.cheapThreshold + tuning.clustering.expensiveThreshold) / 2).toFixed(2)} EUR`);
|
|
68
|
-
console.log(` Max coast intervals: ${tuning.maxCoastIntervals}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
console.log("\nSchedule (Oslo time):");
|
|
72
|
-
console.log("-".repeat(50));
|
|
73
|
-
schedule.forEach(entry => {
|
|
74
|
-
const oslo = entry.time.substring(11, 16);
|
|
75
|
-
const mode = entry.mode.padEnd(6);
|
|
76
|
-
const price = entry.price.toFixed(2);
|
|
77
|
-
const marker = entry.mode === "coast" ? " <-- COAST" : "";
|
|
78
|
-
console.log(` ${oslo} ${mode} ${price} EUR${marker}`);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Count coast intervals
|
|
82
|
-
const coastCount = schedule.filter(s => s.mode === "coast").length;
|
|
83
|
-
const coastMinutes = coastCount * 15;
|
|
84
|
-
|
|
85
|
-
console.log("\n" + "=".repeat(50));
|
|
86
|
-
console.log(`Total coast: ${coastCount} intervals (${coastMinutes} minutes)`);
|
|
87
|
-
|
|
88
|
-
// Find coast end time
|
|
89
|
-
const coastIntervals = schedule.filter(s => s.mode === "coast");
|
|
90
|
-
if (coastIntervals.length > 0) {
|
|
91
|
-
const lastCoast = coastIntervals[coastIntervals.length - 1];
|
|
92
|
-
console.log(`Coast ends at: ${lastCoast.time.substring(11, 16)} Oslo`);
|
|
93
|
-
|
|
94
|
-
// What's next after coast?
|
|
95
|
-
const lastCoastIndex = schedule.indexOf(lastCoast);
|
|
96
|
-
if (lastCoastIndex < schedule.length - 1) {
|
|
97
|
-
const nextAfterCoast = schedule[lastCoastIndex + 1];
|
|
98
|
-
console.log(`Next mode: ${nextAfterCoast.mode} at ${nextAfterCoast.time.substring(11, 16)} (${nextAfterCoast.price.toFixed(2)} EUR)`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log("\n=== ANALYSIS ===");
|
|
103
|
-
// Check if coast extends past 18:00
|
|
104
|
-
const extendedPast1800 = coastIntervals.some(c => {
|
|
105
|
-
const hour = parseInt(c.time.substring(11, 13));
|
|
106
|
-
return hour >= 19;
|
|
107
|
-
});
|
|
108
|
-
console.log(`Coast extends past 19:00 Oslo: ${extendedPast1800 ? "YES ✓" : "NO"}`);
|
|
109
|
-
|
|
110
|
-
// Check if we land on cheaper prices
|
|
111
|
-
const lastCoastEntry = coastIntervals[coastIntervals.length - 1];
|
|
112
|
-
const lastCoastIdx = schedule.indexOf(lastCoastEntry);
|
|
113
|
-
if (lastCoastIdx < schedule.length - 1) {
|
|
114
|
-
const landingPrice = schedule[lastCoastIdx + 1].price;
|
|
115
|
-
const peakPrice = Math.max(...coastIntervals.map(c => c.price));
|
|
116
|
-
const savings = peakPrice - landingPrice;
|
|
117
|
-
console.log(`Landing price: ${landingPrice.toFixed(2)} EUR (saving ${savings.toFixed(2)} EUR vs peak)`);
|
|
118
|
-
}
|
package/test-saturday-peak.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Debug: Why doesn't the Saturday peak get coasted?
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const { DateTime } = require("luxon");
|
|
6
|
-
const { createSchedule, classifyPricesWithCkmeans } = require('./src/strategy-smart-thermal-functions.js');
|
|
7
|
-
|
|
8
|
-
const prices = [{"price":97.41,"currency":"EUR","area":"NO1","timestamp":"2026-01-14T23:00:00Z"},{"price":95.18,"currency":"EUR","area":"NO1","timestamp":"2026-01-14T23:15:00Z"},{"price":92.83,"currency":"EUR","area":"NO1","timestamp":"2026-01-14T23:30:00Z"},{"price":90.1,"currency":"EUR","area":"NO1","timestamp":"2026-01-14T23:45:00Z"},{"price":94.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T00:00:00Z"},{"price":92.32,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T00:15:00Z"},{"price":90,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T00:30:00Z"},{"price":88.44,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T00:45:00Z"},{"price":90.22,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T01:00:00Z"},{"price":86.96,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T01:15:00Z"},{"price":84.8,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T01:30:00Z"},{"price":84.1,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T01:45:00Z"},{"price":83.83,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T02:00:00Z"},{"price":83.11,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T02:15:00Z"},{"price":81.99,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T02:30:00Z"},{"price":80.98,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T02:45:00Z"},{"price":80.33,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T03:00:00Z"},{"price":80.33,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T03:15:00Z"},{"price":80.96,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T03:30:00Z"},{"price":81.73,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T03:45:00Z"},{"price":81.46,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T04:00:00Z"},{"price":82.47,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T04:15:00Z"},{"price":84.17,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T04:30:00Z"},{"price":86.6,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T04:45:00Z"},{"price":88.49,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T05:00:00Z"},{"price":92.15,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T05:15:00Z"},{"price":93.8,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T05:30:00Z"},{"price":95.54,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T05:45:00Z"},{"price":95.2,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T06:00:00Z"},{"price":98.28,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T06:15:00Z"},{"price":101.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T06:30:00Z"},{"price":103.04,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T06:45:00Z"},{"price":98.12,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T07:00:00Z"},{"price":99.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T07:15:00Z"},{"price":99.67,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T07:30:00Z"},{"price":99.31,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T07:45:00Z"},{"price":107.96,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T08:00:00Z"},{"price":104.01,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T08:15:00Z"},{"price":100.59,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T08:30:00Z"},{"price":98.28,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T08:45:00Z"},{"price":108.27,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T09:00:00Z"},{"price":104.8,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T09:15:00Z"},{"price":100.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T09:30:00Z"},{"price":98.62,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T09:45:00Z"},{"price":98.98,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T10:00:00Z"},{"price":96.95,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T10:15:00Z"},{"price":95.96,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T10:30:00Z"},{"price":95.11,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T10:45:00Z"},{"price":99.43,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T11:00:00Z"},{"price":96.57,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T11:15:00Z"},{"price":96.77,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T11:30:00Z"},{"price":99.4,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T11:45:00Z"},{"price":101.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T12:00:00Z"},{"price":102.56,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T12:15:00Z"},{"price":104.22,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T12:30:00Z"},{"price":101.99,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T12:45:00Z"},{"price":98.83,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T13:00:00Z"},{"price":102.35,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T13:15:00Z"},{"price":101.57,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T13:30:00Z"},{"price":110.66,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T13:45:00Z"},{"price":99.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T14:00:00Z"},{"price":117.22,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T14:15:00Z"},{"price":124.09,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T14:30:00Z"},{"price":138.69,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T14:45:00Z"},{"price":121.14,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T15:00:00Z"},{"price":131.5,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T15:15:00Z"},{"price":135.11,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T15:30:00Z"},{"price":141.15,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T15:45:00Z"},{"price":147.79,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T16:00:00Z"},{"price":151.48,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T16:15:00Z"},{"price":140.68,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T16:30:00Z"},{"price":128.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T16:45:00Z"},{"price":130.32,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T17:00:00Z"},{"price":113.7,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T17:15:00Z"},{"price":112.49,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T17:30:00Z"},{"price":102.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T17:45:00Z"},{"price":107.13,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T18:00:00Z"},{"price":101.56,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T18:15:00Z"},{"price":100.83,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T18:30:00Z"},{"price":100,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T18:45:00Z"},{"price":98.71,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T19:00:00Z"},{"price":97.8,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T19:15:00Z"},{"price":95.31,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T19:30:00Z"},{"price":91.64,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T19:45:00Z"},{"price":91.14,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T20:00:00Z"},{"price":88.64,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T20:15:00Z"},{"price":88.24,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T20:30:00Z"},{"price":88.59,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T20:45:00Z"},{"price":91.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T21:00:00Z"},{"price":86.91,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T21:15:00Z"},{"price":86.86,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T21:30:00Z"},{"price":84.67,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T21:45:00Z"},{"price":85.21,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T22:00:00Z"},{"price":83.14,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T22:15:00Z"},{"price":81.22,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T22:30:00Z"},{"price":79.86,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T22:45:00Z"},{"price":83.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T23:00:00Z"},{"price":82.96,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T23:15:00Z"},{"price":81.91,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T23:30:00Z"},{"price":80.89,"currency":"EUR","area":"NO1","timestamp":"2026-01-15T23:45:00Z"},{"price":81.62,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T00:00:00Z"},{"price":80.6,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T00:15:00Z"},{"price":79.84,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T00:30:00Z"},{"price":77.88,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T00:45:00Z"},{"price":78.22,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T01:00:00Z"},{"price":77.83,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T01:15:00Z"},{"price":75.03,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T01:30:00Z"},{"price":74.14,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T01:45:00Z"},{"price":72.9,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T02:00:00Z"},{"price":72.47,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T02:15:00Z"},{"price":72.89,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T02:30:00Z"},{"price":73.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T02:45:00Z"},{"price":74.65,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T03:00:00Z"},{"price":75.09,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T03:15:00Z"},{"price":77.46,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T03:30:00Z"},{"price":78.48,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T03:45:00Z"},{"price":80.92,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T04:00:00Z"},{"price":82.07,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T04:15:00Z"},{"price":83.29,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T04:30:00Z"},{"price":84.17,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T04:45:00Z"},{"price":83.98,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T05:00:00Z"},{"price":85.58,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T05:15:00Z"},{"price":86.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T05:30:00Z"},{"price":87.82,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T05:45:00Z"},{"price":87.25,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T06:00:00Z"},{"price":87.91,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T06:15:00Z"},{"price":88.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T06:30:00Z"},{"price":88.69,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T06:45:00Z"},{"price":88.3,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T07:00:00Z"},{"price":88.55,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T07:15:00Z"},{"price":88.88,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T07:30:00Z"},{"price":88.97,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T07:45:00Z"},{"price":88.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T08:00:00Z"},{"price":88.83,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T08:15:00Z"},{"price":88.86,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T08:30:00Z"},{"price":89.01,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T08:45:00Z"},{"price":88.37,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T09:00:00Z"},{"price":88.24,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T09:15:00Z"},{"price":88.21,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T09:30:00Z"},{"price":87.97,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T09:45:00Z"},{"price":89.66,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T10:00:00Z"},{"price":89.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T10:15:00Z"},{"price":89.48,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T10:30:00Z"},{"price":89.49,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T10:45:00Z"},{"price":88.75,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T11:00:00Z"},{"price":89.62,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T11:15:00Z"},{"price":90.03,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T11:30:00Z"},{"price":90.46,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T11:45:00Z"},{"price":90.12,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T12:00:00Z"},{"price":90.84,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T12:15:00Z"},{"price":92.33,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T12:30:00Z"},{"price":94.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T12:45:00Z"},{"price":91.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T13:00:00Z"},{"price":93.01,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T13:15:00Z"},{"price":93.14,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T13:30:00Z"},{"price":93.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T13:45:00Z"},{"price":92.2,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T14:00:00Z"},{"price":93.38,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T14:15:00Z"},{"price":93.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T14:30:00Z"},{"price":93.84,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T14:45:00Z"},{"price":94,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T15:00:00Z"},{"price":95.16,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T15:15:00Z"},{"price":96.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T15:30:00Z"},{"price":101.04,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T15:45:00Z"},{"price":96.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T16:00:00Z"},{"price":95.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T16:15:00Z"},{"price":95.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T16:30:00Z"},{"price":95.29,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T16:45:00Z"},{"price":95.16,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T17:00:00Z"},{"price":95,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T17:15:00Z"},{"price":95.31,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T17:30:00Z"},{"price":95.13,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T17:45:00Z"},{"price":95.2,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T18:00:00Z"},{"price":94.93,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T18:15:00Z"},{"price":94.79,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T18:30:00Z"},{"price":94.77,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T18:45:00Z"},{"price":94.72,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T19:00:00Z"},{"price":94.46,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T19:15:00Z"},{"price":94.3,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T19:30:00Z"},{"price":94.21,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T19:45:00Z"},{"price":94,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T20:00:00Z"},{"price":93.94,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T20:15:00Z"},{"price":93.7,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T20:30:00Z"},{"price":93.49,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T20:45:00Z"},{"price":93.77,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T21:00:00Z"},{"price":93.37,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T21:15:00Z"},{"price":93.02,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T21:30:00Z"},{"price":92.7,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T21:45:00Z"},{"price":93.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T22:00:00Z"},{"price":92.77,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T22:15:00Z"},{"price":92.08,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T22:30:00Z"},{"price":91.1,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T22:45:00Z"},{"price":94.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T23:00:00Z"},{"price":93.55,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T23:15:00Z"},{"price":93.45,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T23:30:00Z"},{"price":91.08,"currency":"EUR","area":"NO1","timestamp":"2026-01-16T23:45:00Z"},{"price":91.02,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T00:00:00Z"},{"price":90.87,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T00:15:00Z"},{"price":90.71,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T00:30:00Z"},{"price":90.6,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T00:45:00Z"},{"price":90.37,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T01:00:00Z"},{"price":90.28,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T01:15:00Z"},{"price":90.22,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T01:30:00Z"},{"price":90.3,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T01:45:00Z"},{"price":91.28,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T02:00:00Z"},{"price":91.79,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T02:15:00Z"},{"price":91.4,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T02:30:00Z"},{"price":91.58,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T02:45:00Z"},{"price":91.51,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T03:00:00Z"},{"price":91.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T03:15:00Z"},{"price":91.3,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T03:30:00Z"},{"price":91.46,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T03:45:00Z"},{"price":91.79,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T04:00:00Z"},{"price":90.98,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T04:15:00Z"},{"price":92.16,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T04:30:00Z"},{"price":91.75,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T04:45:00Z"},{"price":91.78,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T05:00:00Z"},{"price":90.08,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T05:15:00Z"},{"price":91.78,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T05:30:00Z"},{"price":91.56,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T05:45:00Z"},{"price":91.3,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T06:00:00Z"},{"price":92.38,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T06:15:00Z"},{"price":94.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T06:30:00Z"},{"price":96.68,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T06:45:00Z"},{"price":93.5,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T07:00:00Z"},{"price":97.4,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T07:15:00Z"},{"price":97.65,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T07:30:00Z"},{"price":98.39,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T07:45:00Z"},{"price":99.67,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T08:00:00Z"},{"price":101.82,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T08:15:00Z"},{"price":99.42,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T08:30:00Z"},{"price":97.94,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T08:45:00Z"},{"price":105.19,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T09:00:00Z"},{"price":101.37,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T09:15:00Z"},{"price":98.71,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T09:30:00Z"},{"price":97.29,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T09:45:00Z"},{"price":98.66,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T10:00:00Z"},{"price":98.42,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T10:15:00Z"},{"price":98.17,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T10:30:00Z"},{"price":97.37,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T10:45:00Z"},{"price":98.92,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T11:00:00Z"},{"price":98.03,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T11:15:00Z"},{"price":98.5,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T11:30:00Z"},{"price":98.3,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T11:45:00Z"},{"price":98.26,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T12:00:00Z"},{"price":98.82,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T12:15:00Z"},{"price":98.55,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T12:30:00Z"},{"price":97.99,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T12:45:00Z"},{"price":97.43,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T13:00:00Z"},{"price":97.77,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T13:15:00Z"},{"price":100.74,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T13:30:00Z"},{"price":105.94,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T13:45:00Z"},{"price":100.32,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T14:00:00Z"},{"price":105.32,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T14:15:00Z"},{"price":110.81,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T14:30:00Z"},{"price":113.24,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T14:45:00Z"},{"price":111.59,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T15:00:00Z"},{"price":120.96,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T15:15:00Z"},{"price":122.54,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T15:30:00Z"},{"price":123.82,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T15:45:00Z"},{"price":128.8,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T16:00:00Z"},{"price":126.62,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T16:15:00Z"},{"price":127.73,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T16:30:00Z"},{"price":131.06,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T16:45:00Z"},{"price":123.38,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T17:00:00Z"},{"price":121.69,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T17:15:00Z"},{"price":125.39,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T17:30:00Z"},{"price":124.59,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T17:45:00Z"},{"price":122.52,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T18:00:00Z"},{"price":121.64,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T18:15:00Z"},{"price":120.84,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T18:30:00Z"},{"price":113.53,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T18:45:00Z"},{"price":119.74,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T19:00:00Z"},{"price":106.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T19:15:00Z"},{"price":104.52,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T19:30:00Z"},{"price":99.31,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T19:45:00Z"},{"price":104.05,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T20:00:00Z"},{"price":100.21,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T20:15:00Z"},{"price":98.94,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T20:30:00Z"},{"price":96.23,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T20:45:00Z"},{"price":98.37,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T21:00:00Z"},{"price":97.66,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T21:15:00Z"},{"price":98.35,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T21:30:00Z"},{"price":99.81,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T21:45:00Z"},{"price":99.04,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T22:00:00Z"},{"price":98.13,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T22:15:00Z"},{"price":97.85,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T22:30:00Z"},{"price":96,"currency":"EUR","area":"NO1","timestamp":"2026-01-17T22:45:00Z"}];
|
|
9
|
-
|
|
10
|
-
const outdoorTemp = 5;
|
|
11
|
-
|
|
12
|
-
// User's config
|
|
13
|
-
const userConfig = {
|
|
14
|
-
heatLossCoefficient: 0.06,
|
|
15
|
-
minModeDuration: 60,
|
|
16
|
-
minSavingsPercent: 10,
|
|
17
|
-
minSavingsAbsolute: 3,
|
|
18
|
-
numPriceGroups: 5, // <-- 5 groups means only top 20% is "coast"
|
|
19
|
-
outputTimezone: "Europe/Oslo",
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
console.log("=== DEBUGGING SATURDAY PEAK ===\n");
|
|
23
|
-
|
|
24
|
-
// First, let's see what the clustering produces
|
|
25
|
-
const priceValues = prices.map(p => p.price);
|
|
26
|
-
const effectiveVariationThreshold = userConfig.minSavingsPercent * 2; // 20%
|
|
27
|
-
|
|
28
|
-
console.log("User config:");
|
|
29
|
-
console.log(` numPriceGroups: ${userConfig.numPriceGroups}`);
|
|
30
|
-
console.log(` minSavingsPercent: ${userConfig.minSavingsPercent}%`);
|
|
31
|
-
console.log(` effectiveVariationThreshold: ${effectiveVariationThreshold}%`);
|
|
32
|
-
console.log();
|
|
33
|
-
|
|
34
|
-
const clustering = classifyPricesWithCkmeans(priceValues, effectiveVariationThreshold, userConfig.numPriceGroups);
|
|
35
|
-
|
|
36
|
-
console.log("Clustering results:");
|
|
37
|
-
console.log(` Number of clusters: ${clustering.numGroups}`);
|
|
38
|
-
console.log(` Cluster breaks (thresholds):`);
|
|
39
|
-
clustering.breaks.forEach((b, i) => {
|
|
40
|
-
console.log(` Break ${i + 1}: ${b.toFixed(2)} EUR/MWh`);
|
|
41
|
-
});
|
|
42
|
-
console.log(` Cluster means:`);
|
|
43
|
-
clustering.clusterMeans.forEach((m, i) => {
|
|
44
|
-
const label = i === 0 ? "BOOST" : i === clustering.numGroups - 1 ? "COAST" : "normal";
|
|
45
|
-
console.log(` Cluster ${i}: ${m.toFixed(2)} EUR/MWh → ${label}`);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// The last break is the coast threshold
|
|
49
|
-
const coastThreshold = clustering.breaks[clustering.breaks.length - 1];
|
|
50
|
-
console.log(`\n *** COAST THRESHOLD: > ${coastThreshold.toFixed(2)} EUR/MWh ***`);
|
|
51
|
-
|
|
52
|
-
// Now let's look at Saturday (Jan 17) prices around 17:45
|
|
53
|
-
console.log("\n=== SATURDAY (Jan 17) EVENING PRICES ===\n");
|
|
54
|
-
|
|
55
|
-
const saturdayPrices = prices.filter(p => p.timestamp.startsWith("2026-01-17T"));
|
|
56
|
-
const eveningPrices = saturdayPrices.filter(p => {
|
|
57
|
-
const hour = parseInt(p.timestamp.substring(11, 13));
|
|
58
|
-
return hour >= 14 && hour <= 20;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
console.log("Time (UTC) | Time (Oslo) | Price | Above coast threshold?");
|
|
62
|
-
console.log("-----------|-------------|----------|------------------------");
|
|
63
|
-
eveningPrices.forEach(p => {
|
|
64
|
-
const utcTime = DateTime.fromISO(p.timestamp);
|
|
65
|
-
const osloTime = utcTime.setZone("Europe/Oslo");
|
|
66
|
-
const aboveThreshold = p.price > coastThreshold;
|
|
67
|
-
const marker = aboveThreshold ? "✓ COAST" : "";
|
|
68
|
-
console.log(`${p.timestamp.substring(11, 16)} UTC | ${osloTime.toFormat("HH:mm")} Oslo | ${p.price.toFixed(2).padStart(8)} | ${marker}`);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// The peak at 17:45 Oslo = 16:45 UTC
|
|
72
|
-
const peak1745 = prices.find(p => p.timestamp === "2026-01-17T16:45:00Z");
|
|
73
|
-
console.log(`\n*** The 17:45 Oslo peak (16:45 UTC): ${peak1745?.price} EUR/MWh`);
|
|
74
|
-
console.log(`*** Coast threshold: ${coastThreshold.toFixed(2)} EUR/MWh`);
|
|
75
|
-
console.log(`*** Is ${peak1745?.price} > ${coastThreshold.toFixed(2)}? ${peak1745?.price > coastThreshold ? "YES → should coast" : "NO → won't coast"}`);
|
|
76
|
-
|
|
77
|
-
// Now run the full algorithm
|
|
78
|
-
console.log("\n=== FULL SCHEDULE FOR SATURDAY ===\n");
|
|
79
|
-
|
|
80
|
-
const schedule = createSchedule(prices, outdoorTemp, userConfig);
|
|
81
|
-
const saturdaySchedule = schedule.filter(s => s.time.startsWith("2026-01-17"));
|
|
82
|
-
|
|
83
|
-
console.log("Time (Oslo) | Mode | Price");
|
|
84
|
-
console.log("------------|--------|--------");
|
|
85
|
-
saturdaySchedule.filter(s => {
|
|
86
|
-
const hour = parseInt(s.time.substring(11, 13));
|
|
87
|
-
return hour >= 14 && hour <= 20;
|
|
88
|
-
}).forEach(entry => {
|
|
89
|
-
const marker = entry.mode === "coast" ? " ← COAST" : "";
|
|
90
|
-
console.log(`${entry.time.substring(11, 16)} | ${entry.mode.padEnd(6)} | ${entry.price.toFixed(2)}${marker}`);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// Summary
|
|
94
|
-
console.log("\n=== DIAGNOSIS ===\n");
|
|
95
|
-
console.log(`With 5 price groups, only prices above ${coastThreshold.toFixed(2)} EUR/MWh are classified as "coast".`);
|
|
96
|
-
console.log(`The Saturday 17:45 peak is ${peak1745?.price} EUR/MWh.`);
|
|
97
|
-
if (peak1745?.price > coastThreshold) {
|
|
98
|
-
console.log(`\nThis IS above the threshold, so it should coast.`);
|
|
99
|
-
console.log(`If it's not coasting, check thermal budget or min duration constraints.`);
|
|
100
|
-
} else {
|
|
101
|
-
console.log(`\nThis is BELOW the threshold.`);
|
|
102
|
-
console.log(`\nSolution: Use fewer price groups (3 instead of 5) to be more aggressive,`);
|
|
103
|
-
console.log(`or lower minSavingsPercent to include more prices in the coast band.`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Now compare with 3 groups
|
|
107
|
-
console.log("\n=== COMPARISON: 3 GROUPS vs 5 GROUPS ===\n");
|
|
108
|
-
|
|
109
|
-
const config3 = { ...userConfig, numPriceGroups: 3 };
|
|
110
|
-
const schedule3 = createSchedule(prices, outdoorTemp, config3);
|
|
111
|
-
const satSchedule3 = schedule3.filter(s => s.time.startsWith("2026-01-17"));
|
|
112
|
-
const satCoast3 = satSchedule3.filter(s => s.mode === "coast");
|
|
113
|
-
|
|
114
|
-
console.log("With 3 GROUPS:");
|
|
115
|
-
console.log(` Coast threshold: ${schedule3[0]?.tuning?.clustering?.expensiveThreshold?.toFixed(2)} EUR/MWh`);
|
|
116
|
-
console.log(` Saturday coast intervals: ${satCoast3.length}`);
|
|
117
|
-
if (satCoast3.length > 0) {
|
|
118
|
-
console.log(" Coast times (Oslo):");
|
|
119
|
-
satCoast3.forEach(x => console.log(` ${x.time.substring(11,16)} - ${x.price.toFixed(2)} EUR`));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const satCoast5 = saturdaySchedule.filter(s => s.mode === "coast");
|
|
123
|
-
console.log("\nWith 5 GROUPS (your config):");
|
|
124
|
-
console.log(` Coast threshold: ${coastThreshold.toFixed(2)} EUR/MWh`);
|
|
125
|
-
console.log(` Saturday coast intervals: ${satCoast5.length}`);
|
|
126
|
-
if (satCoast5.length > 0) {
|
|
127
|
-
console.log(" Coast times (Oslo):");
|
|
128
|
-
satCoast5.forEach(x => console.log(` ${x.time.substring(11,16)} - ${x.price.toFixed(2)} EUR`));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
console.log("\n=== RECOMMENDATION ===");
|
|
132
|
-
console.log("Use numPriceGroups: 3 for more aggressive coasting on days with smaller peaks.");
|