Ignore battery charging above inverter AC rating (#3015)

This commit is contained in:
andig 2022-03-27 17:42:02 +02:00 • committed by GitHub
parent d4db91b798
commit d8a6efc35a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 23 deletions

View file

@ -2,6 +2,7 @@ package core
import (
"github.com/avast/retry-go/v3"
"github.com/evcc-io/evcc/util"
)
var (
@ -25,13 +26,14 @@ func powerToCurrent(power float64, phases int) float64 {
// sitePower returns the available delta power that the charger might additionally consume
// negative value: available power (grid export), positive value: grid import
func sitePower(grid, battery, residual float64) float64 {
func sitePower(log *util.Logger, maxGrid, grid, battery, residual float64) float64 {
// For hybrid inverters, battery can be charged from DC power in excess of
// inverter AC rating. This must be offset by the grid consumption when calculating
// available site power.
// (https://github.com/evcc-io/evcc/issues/2734)
if grid > 0 && battery < 0 && grid-battery > 50 {
// inverter AC rating. This battery charge must not be counted as available for AC consumption.
// https://github.com/evcc-io/evcc/issues/2734, https://github.com/evcc-io/evcc/issues/2986
if maxGrid > 0 && grid > maxGrid && battery < 0 {
log.TRACE.Printf("ignoring excess DC charging due to grid consumption: %.0fW > %.0fW", grid, maxGrid)
battery = 0
}
return grid + battery + residual
}

View file

@ -31,12 +31,13 @@ type Site struct {
log *util.Logger
// configuration
Title string `mapstructure:"title"` // UI title
Voltage float64 `mapstructure:"voltage"` // Operating voltage. 230V for Germany.
ResidualPower float64 `mapstructure:"residualPower"` // PV meter only: household usage. Grid meter: household safety margin
Meters MetersConfig // Meter references
PrioritySoC float64 `mapstructure:"prioritySoC"` // prefer battery up to this SoC
BufferSoC float64 `mapstructure:"bufferSoC"` // ignore battery above this SoC
Title string `mapstructure:"title"` // UI title
Voltage float64 `mapstructure:"voltage"` // Operating voltage. 230V for Germany.
ResidualPower float64 `mapstructure:"residualPower"` // PV meter only: household usage. Grid meter: household safety margin
Meters MetersConfig // Meter references
PrioritySoC float64 `mapstructure:"prioritySoC"` // prefer battery up to this SoC
BufferSoC float64 `mapstructure:"bufferSoC"` // ignore battery above this SoC
MaxGridSupplyWhileBatteryCharging float64 `mapstructure:"maxGridSupplyWhileBatteryCharging"` // ignore battery charging if AC consumption is above this value
// meters
gridMeter api.Meter // Grid usage meter
@ -376,7 +377,8 @@ func (site *Site) sitePower(totalChargePower float64) (float64, error) {
site.batteryBuffered = batteryPower > 0 && site.BufferSoC > 0 && socs > site.BufferSoC
}
sitePower := sitePower(site.gridPower, batteryPower, site.ResidualPower)
sitePower := sitePower(site.log, site.MaxGridSupplyWhileBatteryCharging, site.gridPower, batteryPower, site.ResidualPower)
site.log.DEBUG.Printf("site power: %.0fW", sitePower)
return sitePower, nil

View file

@ -2,27 +2,31 @@ package core
import (
"testing"
"github.com/evcc-io/evcc/util"
)
func TestSitePower(t *testing.T) {
tc := []struct {
grid, battery, site float64
maxGrid, grid, battery, site float64
}{
{0, 0, 0}, // silent night
{0, 1, 1}, // battery discharging
{0, -1, -1}, // battery charging -> negative result cannot occur in reality
{1, 0, 1}, // grid import
{1, 1, 2}, // grid import + battery discharging
{-1, 0, -1}, // grid export
{-1, -1, -2}, // grid export + battery charging
// {0, 0, 0, 0}, // silent night
// {0, 0, 1, 1}, // battery discharging
// {0, 0, -1, -1}, // battery charging -> negative result cannot occur in reality
// {0, 1, 0, 1}, // grid import
// {0, 1, 1, 2}, // grid import + battery discharging
// {0, -1, 0, -1}, // grid export
// {0, -1, -1, -2}, // grid export + battery charging
{0, 1, -1, 0}, // grid import + battery charging -> should not happen
{0.5, 1, -1, 1}, // grid import + DC battery charging
}
log := util.NewLogger("foo")
for _, tc := range tc {
res := sitePower(tc.grid, tc.battery, 0)
res := sitePower(log, tc.maxGrid, tc.grid, tc.battery, 0)
if res != tc.site {
t.Errorf("sitePower wanted %.f, got %.f", tc.site, res)
}
}
}
// TODO add test case for battery priority charging