$batterien * @return array */ public static function aggregiere( array $batterien, float $sdlReserveLadenW, float $sdlReserveEntladenW, float $reservezeitH ): array { if ($sdlReserveLadenW < 0.0 || $sdlReserveEntladenW < 0.0 || $reservezeitH < 0.0 ) { throw new InvalidArgumentException( 'SDL-Reserven und Reservezeit duerfen nicht negativ sein.' ); } $kapazitaet = 0.0; $energie = 0.0; $maxLaden = 0.0; $maxEntladen = 0.0; $istleistung = 0.0; foreach ($batterien as $batterie) { $kapazitaetKWh = (float) $batterie['Kapazitaet_kWh']; $ladezustand = (float) $batterie['Ladezustand_Prozent']; $ladenW = (float) $batterie['MaxLaden_W']; $entladenW = (float) $batterie['MaxEntladen_W']; if ($kapazitaetKWh <= 0.0 || $ladenW < 0.0 || $entladenW < 0.0) { throw new InvalidArgumentException( 'Kapazitaet muss positiv und Leistungsgrenzen duerfen nicht negativ sein.' ); } if ($ladezustand < 0.0 || $ladezustand > 100.0) { throw new InvalidArgumentException( 'Ladezustand muss zwischen 0 und 100 Prozent liegen.' ); } $kapazitaet += $kapazitaetKWh; $energie += $kapazitaetKWh * $ladezustand / 100.0; $maxLaden += $ladenW; $maxEntladen += $entladenW; $istleistung += (float) $batterie['Istleistung_W']; } if ($kapazitaet <= 0.0) { throw new InvalidArgumentException( 'Mindestens eine verfuegbare Batterie ist erforderlich.' ); } $sdlLadenW = min($sdlReserveLadenW, $maxLaden); $sdlEntladenW = min($sdlReserveEntladenW, $maxEntladen); $untereReserve = $sdlEntladenW * $reservezeitH / 1000.0; $obereReserve = $sdlLadenW * $reservezeitH / 1000.0; if ($untereReserve + $obereReserve > $kapazitaet) { throw new InvalidArgumentException( 'Die SDL-Energiereserven sind groesser als die Gesamtkapazitaet.' ); } return [ 'Kapazitaet_kWh' => $kapazitaet, 'Energie_kWh' => $energie, 'Ladezustand_Prozent' => $energie / $kapazitaet * 100.0, 'Istleistung_W' => $istleistung, 'MaxLaden_W' => $maxLaden, 'MaxEntladen_W' => $maxEntladen, 'SDLKapazitaet_kWh' => $untereReserve + $obereReserve, 'EigenverbrauchKapazitaet_kWh' => $kapazitaet - $untereReserve - $obereReserve, 'SDLMaxLaden_W' => $sdlLadenW, 'SDLMaxEntladen_W' => $sdlEntladenW, 'EigenverbrauchMaxLaden_W' => max(0.0, $maxLaden - $sdlLadenW), 'EigenverbrauchMaxEntladen_W' => max(0.0, $maxEntladen - $sdlEntladenW), 'UntereSDLReserve_kWh' => $untereReserve, 'ObereSDLReserve_kWh' => $obereReserve, ]; } /** * SDL wird zuerst begrenzt. Eigenverbrauch erhaelt die verbleibende * physische Leistung derselben Richtung. * * @param array $aggregat * @return array{Eigenverbrauch_W:float, SDL_W:float, Gesamt_W:float} */ public static function kombiniere( float $eigenverbrauchW, float $sdlW, array $aggregat ): array { $sdl = self::begrenze( $sdlW, (float) $aggregat['SDLMaxLaden_W'], (float) $aggregat['SDLMaxEntladen_W'] ); $ev = self::begrenze( $eigenverbrauchW, (float) $aggregat['EigenverbrauchMaxLaden_W'], (float) $aggregat['EigenverbrauchMaxEntladen_W'] ); if ($sdl > 0.0 && $ev > 0.0) { $ev = min($ev, max(0.0, (float) $aggregat['MaxLaden_W'] - $sdl)); } elseif ($sdl < 0.0 && $ev < 0.0) { $ev = max($ev, -max( 0.0, (float) $aggregat['MaxEntladen_W'] - abs($sdl) )); } $gesamt = self::begrenze( $sdl + $ev, (float) $aggregat['MaxLaden_W'], (float) $aggregat['MaxEntladen_W'] ); return [ 'Eigenverbrauch_W' => $ev, 'SDL_W' => $sdl, 'Gesamt_W' => $gesamt, ]; } /** * @param list $batterien * @return array */ public static function verteile(float $gesamtW, array $batterien): array { $ergebnis = []; foreach ($batterien as $batterie) { $ergebnis[(int) $batterie['Index']] = 0.0; } if (abs($gesamtW) < 0.5) { return $ergebnis; } $laden = $gesamtW > 0.0; usort( $batterien, static function (array $a, array $b) use ($laden): int { $vergleich = (float) $a['Ladezustand_Prozent'] <=> (float) $b['Ladezustand_Prozent']; return $laden ? $vergleich : -$vergleich; } ); $rest = abs($gesamtW); foreach ($batterien as $batterie) { if ($rest < 0.5) { break; } $grenze = $laden ? (float) $batterie['MaxLaden_W'] : (float) $batterie['MaxEntladen_W']; $anteil = min($rest, max(0.0, $grenze)); $ergebnis[(int) $batterie['Index']] = $laden ? $anteil : -$anteil; $rest -= $anteil; } return $ergebnis; } /** * Die SDL-Leistung wird bilanziell zuerst bedient. Die Differenz zur * gemessenen Gesamtleistung wird dem Eigenverbrauch zugeordnet. * * @return array{Eigenverbrauch_W:float, SDL_W:float} */ public static function ordneIstleistungZu( float $gesamtIstW, float $sdlAuftragW ): array { return [ 'Eigenverbrauch_W' => $gesamtIstW - $sdlAuftragW, 'SDL_W' => $sdlAuftragW, ]; } private static function begrenze( float $leistungW, float $maxLadenW, float $maxEntladenW ): float { return min($maxLadenW, max(-$maxEntladenW, $leistungW)); } }