Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions meteo/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package meteo

import (
"strings"
"time"
)

Expand Down Expand Up @@ -180,32 +181,27 @@ func (ts *ForecastTimeStep) GetSymbolCode() *WeatherSymbol {

// IsDay checks if the weather symbol indicates daytime conditions
func (ws WeatherSymbol) IsDay() bool {
str := string(ws)
return len(str) >= 4 && str[len(str)-4:] == "_day"
return strings.HasSuffix(string(ws), "_day")
}

// IsNight checks if the weather symbol indicates nighttime conditions
func (ws WeatherSymbol) IsNight() bool {
str := string(ws)
return len(str) >= 6 && str[len(str)-6:] == "_night"
return strings.HasSuffix(string(ws), "_night")
}

// IsPolarTwilight checks if the weather symbol indicates polar twilight conditions
func (ws WeatherSymbol) IsPolarTwilight() bool {
str := string(ws)
return len(str) >= 14 && str[len(str)-14:] == "_polartwilight"
return strings.HasSuffix(string(ws), "_polartwilight")
}

// HasThunder checks if the weather symbol indicates thunder
func (ws WeatherSymbol) HasThunder() bool {
str := string(ws)
// Check if contains "thunder" anywhere in the symbol
for i := 0; i <= len(str)-7; i++ {
if str[i:i+7] == "thunder" {
return true
}
}
return false
return strings.Contains(string(ws), "thunder")
}

// HasSnow checks if the weather symbol indicates snow conditions
func (ws WeatherSymbol) HasSnow() bool {
return strings.Contains(string(ws), "snow")
}

// IntPtr is a helper function to get a pointer to an int value
Expand Down
31 changes: 31 additions & 0 deletions meteo/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,37 @@ func TestWeatherSymbol_HasThunder(t *testing.T) {
}
}

func TestWeatherSymbol_HasSnow(t *testing.T) {
tests := []struct {
symbol WeatherSymbol
expected bool
}{
{ClearSkyDay, false},
{Rain, false},
{LightSnow, true},
{Snow, true},
{HeavySnow, true},
{LightSnowShowersDay, true},
{SnowShowersNight, true},
{HeavySnowShowersDay, true},
{SnowAndThunder, true},
{LightSnowAndThunder, true},
{HeavySnowAndThunder, true},
{LightSnowShowersAndThunderDay, true},
{Cloudy, false},
{Fog, false},
}

for _, tt := range tests {
t.Run(string(tt.symbol), func(t *testing.T) {
result := tt.symbol.HasSnow()
if result != tt.expected {
t.Errorf("Expected HasSnow() = %v for symbol %s", tt.expected, tt.symbol)
}
})
}
}

func TestHelperFunctions(t *testing.T) {
// Test IntPtr
intVal := 42
Expand Down
2 changes: 2 additions & 0 deletions mpc/example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ func main() {
loadForecast = 2.5 // Higher during active hours
}

futureTime := time.Now().Add(time.Duration(i) * time.Hour)
forecast[i] = mpc.TimeSlot{
Hour: i,
Timestamp: futureTime.Unix(),
ImportPrice: importPrice,
ExportPrice: exportPrice,
SolarForecast: solarForecast,
Expand Down
19 changes: 19 additions & 0 deletions mpc/mpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,32 @@ type SystemConfig struct {
// TimeSlot represents one hour of operation
type TimeSlot struct {
Hour int
Timestamp int64 // Unix timestamp when this time slot begins
ImportPrice float64 // $/kWh
ExportPrice float64 // $/kWh
SolarForecast float64 // kW average for the hour
LoadForecast float64 // kW average for the hour
CloudCoverage float64 // % cloud coverage (0-100)
WeatherSymbol string // weather condition symbol
}

// ControlDecision represents the optimal control for one time slot
type ControlDecision struct {
Hour int
Timestamp int64 // Unix timestamp when this time slot begins
BatteryCharge float64 // kW (positive = charging)
BatteryDischarge float64 // kW (positive = discharging)
GridImport float64 // kW (positive = importing)
GridExport float64 // kW (positive = exporting)
BatterySOC float64 // percentage (0-1)
Profit float64 // $ for this hour
// Forecast data used for this decision
ImportPrice float64 // $/kWh
ExportPrice float64 // $/kWh
SolarForecast float64 // kW average for the hour
LoadForecast float64 // kW average for the hour
CloudCoverage float64 // % cloud coverage (0-100)
WeatherSymbol string // weather condition symbol
}

// MPCController implements Model Predictive Control
Expand Down Expand Up @@ -114,6 +125,13 @@ func (mpc *MPCController) Optimize(forecast []TimeSlot) []ControlDecision {
dp[t+1][newSOCIdx].decision = dec
dp[t+1][newSOCIdx].decision.BatterySOC = newSOC
dp[t+1][newSOCIdx].decision.Profit = profit
dp[t+1][newSOCIdx].decision.Timestamp = slot.Timestamp
dp[t+1][newSOCIdx].decision.ImportPrice = slot.ImportPrice
dp[t+1][newSOCIdx].decision.ExportPrice = slot.ExportPrice
dp[t+1][newSOCIdx].decision.SolarForecast = slot.SolarForecast
dp[t+1][newSOCIdx].decision.LoadForecast = slot.LoadForecast
dp[t+1][newSOCIdx].decision.CloudCoverage = slot.CloudCoverage
dp[t+1][newSOCIdx].decision.WeatherSymbol = slot.WeatherSymbol
dp[t+1][newSOCIdx].prevSOC = socIdx
}
}
Expand Down Expand Up @@ -179,6 +197,7 @@ func (mpc *MPCController) generateFeasibleDecisions(currentSOC float64, slot Tim
for _, action := range batteryActions {
dec := ControlDecision{
Hour: slot.Hour,
Timestamp: slot.Timestamp,
BatteryCharge: action.charge,
BatteryDischarge: action.discharge,
}
Expand Down
Loading