This commit is contained in:
@@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
Alle wesentlichen Aenderungen an Enelix EMS werden in dieser Datei dokumentiert.
|
Alle wesentlichen Aenderungen an Enelix EMS werden in dieser Datei dokumentiert.
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- Manager: Positives Budget wird bei gleicher Prioritaet nach der kleinsten
|
||||||
|
naechsten erreichbaren Sollleistung verteilt. Nur bei gleicher Leistungsstufe
|
||||||
|
entscheiden exakter Energiebezug und anschliessend die Instanz-ID. Einzelstufen
|
||||||
|
und ganzzahlige Bereiche teilen sich das Budget; reservierte Lasten und die
|
||||||
|
negative Defizitregelung bleiben unveraendert. Keine Konfigurationsmigration.
|
||||||
|
|
||||||
## 0.1 Build 1 - Erste Beta - 2026-09-29
|
## 0.1 Build 1 - Erste Beta - 2026-09-29
|
||||||
|
|
||||||
### Enthalten
|
### Enthalten
|
||||||
|
|||||||
@@ -310,15 +310,36 @@ Das gilt auch bei `AenderungMoeglich=false`: Ein fixes Angebot wie `[11000]`
|
|||||||
wird mit genau `11000 W` zugeteilt.
|
wird mit genau `11000 W` zugeteilt.
|
||||||
|
|
||||||
Die Verbraucher werden nach der gemeldeten PV- beziehungsweise Peak-Priorität
|
Die Verbraucher werden nach der gemeldeten PV- beziehungsweise Peak-Priorität
|
||||||
sortiert. Bei gleicher Priorität bevorzugt der Manager wie Enelix 1 den
|
sortiert. Zuerst reserviert der Manager die richtungsneutralen erlaubten Werte
|
||||||
Verbraucher mit der bisher kleineren bezogenen Energie; verglichen wird in
|
sowie die gemessene Leistung fester oder gesperrter Verbraucher.
|
||||||
2-kWh-Gruppen. Erst wenn auch diese Gruppe gleich ist, entscheidet stabil die
|
|
||||||
Instanz-ID. Zuerst reserviert der Manager für jeden Verbraucher den kleinsten
|
Positives Restbudget wird innerhalb jeder Priorität schrittweise verteilt:
|
||||||
erlaubten Leistungswert. Nur das danach
|
Zuerst kommt die kleinste nächste erreichbare **absolute Sollleistung**, nicht
|
||||||
verbleibende Budget wird in Prioritätsreihenfolge aufgefüllt. Einzelwerte und
|
die kleinste Erhöhung. Bei gleicher nächster Sollleistung entscheidet die exakt
|
||||||
ganzzahlige Bereiche werden direkt verarbeitet; Lücken werden nie durch
|
gezählte bezogene Energie in Wh, danach stabil die Instanz-ID. Die bisherigen
|
||||||
unzulässige Werte geschlossen. Eine verbleibende Abweichung wird im
|
2-kWh-Gruppen werden für diese Vergabe nicht mehr verwendet. Vom Budget wird nur
|
||||||
`Verbraucherstatus` dokumentiert.
|
die Differenz zum bereits zugeteilten Sollwert abgezogen. Ein nicht finanzierbarer
|
||||||
|
Schritt wird übersprungen; andere passende Schritte und danach tiefere
|
||||||
|
Prioritäten können das verbleibende Budget nutzen.
|
||||||
|
|
||||||
|
Beispiel bei gleicher Priorität: A bietet `[0,100,200,400,800]` bei 10 kWh,
|
||||||
|
B bietet `[0,100,110,500,780,1500]` bei 12 kWh. Die Zuteilungsfolge bei
|
||||||
|
ausreichendem Budget ist `A100, B100, B110, A200, A400, B500, B780, A800, B1500`.
|
||||||
|
Die zugehörigen Gesamtbudgets sind 100, 200, 210, 310, 510, 900, 1180, 1580 und
|
||||||
|
2300 W. Energie entscheidet nur bei gleichen nächsten Leistungsstufen, nicht
|
||||||
|
mehr über die vollständige Versorgung eines Verbrauchers vor allen anderen.
|
||||||
|
|
||||||
|
Ganzzahlige Bereiche folgen derselben Reihenfolge wie einzelne Wattstufen,
|
||||||
|
werden aber über gemeinsame Leistungsniveaus effizient verarbeitet. Lücken
|
||||||
|
werden nie durch unzulässige Werte geschlossen. Bei negativem Restbudget bleibt
|
||||||
|
die bestehende Defizitregelung einschliesslich ihrer Energiegruppen unverändert.
|
||||||
|
Eine verbleibende Abweichung wird im `Verbraucherstatus` dokumentiert.
|
||||||
|
|
||||||
|
Für das Update sind keine neuen Properties oder eine Migration nötig. Bestehende
|
||||||
|
Energiezähler, Sperrzeiten und Freigaben bleiben erhalten. Nach dem Modulupdate
|
||||||
|
gilt die neue Vergabe beim nächsten Regellauf. Vor einem Update einer laufenden
|
||||||
|
Anlage Einstellungen sichern und die neuen Sollwerte kontrolliert prüfen;
|
||||||
|
die Git-Veröffentlichung allein aktualisiert keine laufende Installation.
|
||||||
|
|
||||||
Die Betriebsart wechselt unterhalb des Solar-Sollwerts zu `PV` und oberhalb
|
Die Betriebsart wechselt unterhalb des Solar-Sollwerts zu `PV` und oberhalb
|
||||||
der wirksamen Lastspitzengrenze zu `Peak`. Zwischen den beiden Zielwerten
|
der wirksamen Lastspitzengrenze zu `Peak`. Zwischen den beiden Zielwerten
|
||||||
|
|||||||
+95
-6
@@ -208,6 +208,7 @@ final class ManagerRegler
|
|||||||
'InstanzID' => $eintrag['InstanzID'],
|
'InstanzID' => $eintrag['InstanzID'],
|
||||||
'Prioritaet' => $daten[$prioritaetsfeld],
|
'Prioritaet' => $daten[$prioritaetsfeld],
|
||||||
'Energiegruppe' => (int) round((float) $energie / 2000.0),
|
'Energiegruppe' => (int) round((float) $energie / 2000.0),
|
||||||
|
'Energie_Wh' => (float) $energie,
|
||||||
'Leistungswerte_W' => $daten['Leistungswerte_W'],
|
'Leistungswerte_W' => $daten['Leistungswerte_W'],
|
||||||
'Festwert' => $festwert,
|
'Festwert' => $festwert,
|
||||||
'Festgehalten' => $festgehalten,
|
'Festgehalten' => $festgehalten,
|
||||||
@@ -254,6 +255,7 @@ final class ManagerRegler
|
|||||||
}
|
}
|
||||||
|
|
||||||
$rest = $verteilbudget - $nichtSteuerbar - $ausgangssumme;
|
$rest = $verteilbudget - $nichtSteuerbar - $ausgangssumme;
|
||||||
|
$gruppen = [];
|
||||||
foreach ($steuerbar as $eintrag) {
|
foreach ($steuerbar as $eintrag) {
|
||||||
if ($eintrag['Festwert'] || $eintrag['Festgehalten']) {
|
if ($eintrag['Festwert'] || $eintrag['Festgehalten']) {
|
||||||
continue;
|
continue;
|
||||||
@@ -262,13 +264,8 @@ final class ManagerRegler
|
|||||||
$instanzID = $eintrag['InstanzID'];
|
$instanzID = $eintrag['InstanzID'];
|
||||||
$ausgangswert = $sollwerte[$instanzID];
|
$ausgangswert = $sollwerte[$instanzID];
|
||||||
if ($positiveRichtung) {
|
if ($positiveRichtung) {
|
||||||
if ($rest <= 0) {
|
$gruppen[$eintrag['Prioritaet']][] = $eintrag;
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
$neuerSollwert = self::groessterLeistungswertBis(
|
|
||||||
$eintrag['Leistungswerte_W'],
|
|
||||||
$ausgangswert + $rest
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
if ($rest >= 0) {
|
if ($rest >= 0) {
|
||||||
continue;
|
continue;
|
||||||
@@ -281,6 +278,9 @@ final class ManagerRegler
|
|||||||
$rest -= $neuerSollwert - $ausgangswert;
|
$rest -= $neuerSollwert - $ausgangswert;
|
||||||
$sollwerte[$instanzID] = $neuerSollwert;
|
$sollwerte[$instanzID] = $neuerSollwert;
|
||||||
}
|
}
|
||||||
|
foreach ($gruppen as $gruppe) {
|
||||||
|
self::verteilePositiveGruppe($gruppe, $sollwerte, $rest);
|
||||||
|
}
|
||||||
|
|
||||||
$zugeteilt = $nichtSteuerbar;
|
$zugeteilt = $nichtSteuerbar;
|
||||||
foreach ($steuerbar as $eintrag) {
|
foreach ($steuerbar as $eintrag) {
|
||||||
@@ -298,6 +298,95 @@ final class ManagerRegler
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<array<string, mixed>> $gruppe Verbraucher derselben Prioritaet
|
||||||
|
* @param array<int, int> $sollwerte
|
||||||
|
*/
|
||||||
|
private static function verteilePositiveGruppe(array $gruppe, array &$sollwerte, int &$rest): void
|
||||||
|
{
|
||||||
|
while ($rest > 0) {
|
||||||
|
$kandidaten = self::naechsteLeistungsschritte($gruppe, $sollwerte, $rest);
|
||||||
|
if ($kandidaten === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ganze Leistungsniveaus gemeinsam vergeben, ohne Bereiche Watt fuer Watt zu durchlaufen.
|
||||||
|
$unten = $kandidaten[0]['NaechsteLeistung_W'] - 1;
|
||||||
|
$oben = $unten;
|
||||||
|
foreach ($kandidaten as $eintrag) {
|
||||||
|
$oben = max($oben, $sollwerte[$eintrag['InstanzID']] + $rest);
|
||||||
|
}
|
||||||
|
while ($unten < $oben) {
|
||||||
|
$mitte = $unten + intdiv($oben - $unten + 1, 2);
|
||||||
|
$bedarf = 0;
|
||||||
|
foreach ($kandidaten as $eintrag) {
|
||||||
|
$bisher = $sollwerte[$eintrag['InstanzID']];
|
||||||
|
$bedarf += max(0, self::groessterLeistungswertBis(
|
||||||
|
$eintrag['Leistungswerte_W'],
|
||||||
|
$mitte
|
||||||
|
) - $bisher);
|
||||||
|
if ($bedarf > $rest) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($bedarf <= $rest) {
|
||||||
|
$unten = $mitte;
|
||||||
|
} else {
|
||||||
|
$oben = $mitte - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($kandidaten as $eintrag) {
|
||||||
|
$id = $eintrag['InstanzID'];
|
||||||
|
$neu = max($sollwerte[$id], self::groessterLeistungswertBis(
|
||||||
|
$eintrag['Leistungswerte_W'],
|
||||||
|
$unten
|
||||||
|
));
|
||||||
|
$rest -= $neu - $sollwerte[$id];
|
||||||
|
$sollwerte[$id] = $neu;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reicht es nicht fuer das ganze naechste Niveau, entscheidet Leistung, Energie, ID.
|
||||||
|
$kandidaten = self::naechsteLeistungsschritte($gruppe, $sollwerte, $rest);
|
||||||
|
if ($kandidaten === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$erster = $kandidaten[0];
|
||||||
|
$id = $erster['InstanzID'];
|
||||||
|
$rest -= $erster['NaechsteLeistung_W'] - $sollwerte[$id];
|
||||||
|
$sollwerte[$id] = $erster['NaechsteLeistung_W'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<array<string, mixed>> $gruppe
|
||||||
|
* @param array<int, int> $sollwerte
|
||||||
|
* @return list<array<string, mixed>>
|
||||||
|
*/
|
||||||
|
private static function naechsteLeistungsschritte(array $gruppe, array $sollwerte, int $rest): array
|
||||||
|
{
|
||||||
|
$kandidaten = [];
|
||||||
|
foreach ($gruppe as $eintrag) {
|
||||||
|
$bisher = $sollwerte[$eintrag['InstanzID']];
|
||||||
|
foreach ($eintrag['Leistungswerte_W'] as $angebot) {
|
||||||
|
$naechste = is_int($angebot) ? $angebot : max($angebot['Von_W'], $bisher + 1);
|
||||||
|
if ($naechste <= $bisher || (!is_int($angebot) && $naechste > $angebot['Bis_W'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($naechste - $bisher <= $rest) {
|
||||||
|
$eintrag['NaechsteLeistung_W'] = $naechste;
|
||||||
|
$kandidaten[] = $eintrag;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usort($kandidaten, static function (array $links, array $rechts): int {
|
||||||
|
return [$links['NaechsteLeistung_W'], $links['Energie_Wh'], $links['InstanzID']]
|
||||||
|
<=> [$rechts['NaechsteLeistung_W'], $rechts['Energie_Wh'], $rechts['InstanzID']];
|
||||||
|
});
|
||||||
|
|
||||||
|
return $kandidaten;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $daten
|
* @param array<string, mixed> $daten
|
||||||
*/
|
*/
|
||||||
|
|||||||
+170
-3
@@ -166,7 +166,7 @@ final class ManagerReglerTest extends TestCase
|
|||||||
self::assertSame(500, $ergebnis['Restabweichung_W']);
|
self::assertSame(500, $ergebnis['Restabweichung_W']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGleichePrioritaetBevorzugtWieEnelix1DenVerbraucherMitWenigerEnergie(): void
|
public function testGleicheNaechsteLeistungBevorzugtWenigerEnergie(): void
|
||||||
{
|
{
|
||||||
$ergebnis = ManagerRegler::verteile(
|
$ergebnis = ManagerRegler::verteile(
|
||||||
0.0,
|
0.0,
|
||||||
@@ -183,7 +183,7 @@ final class ManagerReglerTest extends TestCase
|
|||||||
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEnergievergleichVerwendetWieEnelix1ZweiKilowattstundenGruppen(): void
|
public function testEnergiegleichstandVergleichtExakteWhStattZweiKilowattstundenGruppen(): void
|
||||||
{
|
{
|
||||||
$ergebnis = ManagerRegler::verteile(
|
$ergebnis = ManagerRegler::verteile(
|
||||||
0.0,
|
0.0,
|
||||||
@@ -196,7 +196,174 @@ final class ManagerReglerTest extends TestCase
|
|||||||
[10 => 999.0, 20 => 0.0]
|
[10 => 999.0, 20 => 0.0]
|
||||||
);
|
);
|
||||||
|
|
||||||
self::assertSame([10 => 3000, 20 => 0], $ergebnis['Sollwerte']);
|
self::assertSame([10 => 0, 20 => 3000], $ergebnis['Sollwerte']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @dataProvider beispielBudgets */
|
||||||
|
public function testBestaetigteStufenfolge(int $budget, int $a, int $b): void
|
||||||
|
{
|
||||||
|
foreach (['PrioritaetPV', 'PrioritaetPeak'] as $feld) {
|
||||||
|
$ergebnis = ManagerRegler::verteile(0.0, (float) $budget, [
|
||||||
|
$this->verbraucher(20, 2, [0, 100, 110, 500, 780, 1500], 0),
|
||||||
|
$this->verbraucher(10, 2, [0, 100, 200, 400, 800], 0),
|
||||||
|
], $feld, [10 => 10000.0, 20 => 12000.0]);
|
||||||
|
|
||||||
|
self::assertSame([10 => $a, 20 => $b], $ergebnis['Sollwerte']);
|
||||||
|
self::assertSame($budget - $a - $b, $ergebnis['Restabweichung_W']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beispielBudgets(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[0, 0, 0], [99, 0, 0], [100, 100, 0], [199, 100, 0],
|
||||||
|
[200, 100, 100], [209, 100, 100], [210, 100, 110],
|
||||||
|
[309, 100, 110], [310, 200, 110], [509, 200, 110],
|
||||||
|
[510, 400, 110], [899, 400, 110], [900, 400, 500],
|
||||||
|
[1179, 400, 500], [1180, 400, 780], [1579, 400, 780],
|
||||||
|
[1580, 800, 780], [2299, 800, 780], [2300, 800, 1500],
|
||||||
|
[3000, 800, 1500],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLeistungsniveauGehtVorEnergieUndSchrittdifferenz(): void
|
||||||
|
{
|
||||||
|
$ergebnis = ManagerRegler::verteile(0.0, 220.0, [
|
||||||
|
$this->verbraucher(10, 0, [0, 100, 200], 0),
|
||||||
|
$this->verbraucher(20, 0, [0, 110, 120], 0),
|
||||||
|
], 'PrioritaetPV', [10 => 0.0, 20 => 50000.0]);
|
||||||
|
|
||||||
|
self::assertSame([10 => 100, 20 => 120], $ergebnis['Sollwerte']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPicoUndGoETeilenBudgetTrotzGrossemEnergieunterschied(): void
|
||||||
|
{
|
||||||
|
$ergebnis = ManagerRegler::verteile(0.0, 12312.0, [
|
||||||
|
$this->verbraucher(31986, 2, array_merge([0], range(4104, 12312, 684)), 0),
|
||||||
|
$this->verbraucher(43878, 2, array_merge([0], range(4104, 10944, 684)), 0),
|
||||||
|
], 'PrioritaetPV', [31986 => 41192.78, 43878 => 128082.87]);
|
||||||
|
|
||||||
|
self::assertSame([31986 => 6156, 43878 => 6156], $ergebnis['Sollwerte']);
|
||||||
|
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNegativeDefizitverteilungBehaeltBisherigeEnergiegruppen(): void
|
||||||
|
{
|
||||||
|
$ergebnis = ManagerRegler::verteile(300.0, 0.0, [
|
||||||
|
$this->verbraucher(10, 0, [['Von_W' => -300, 'Bis_W' => 300]], 0),
|
||||||
|
$this->verbraucher(20, 0, [['Von_W' => -300, 'Bis_W' => 300]], 0),
|
||||||
|
], 'PrioritaetPeak', [10 => 999.0, 20 => 0.0]);
|
||||||
|
|
||||||
|
self::assertSame([10 => -300, 20 => 0], $ergebnis['Sollwerte']);
|
||||||
|
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGleicheLeistungUndEnergieEntscheidetStabilNachInstanzID(): void
|
||||||
|
{
|
||||||
|
$ergebnis = ManagerRegler::verteile(0.0, 100.0, [
|
||||||
|
$this->verbraucher(20, 0, [0, 100], 0),
|
||||||
|
$this->verbraucher(10, 0, [0, 100], 0),
|
||||||
|
], 'PrioritaetPV');
|
||||||
|
|
||||||
|
self::assertSame([10 => 100, 20 => 0], $ergebnis['Sollwerte']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNichtPassenderSchrittBlockiertKeineAndereErreichbareStufe(): void
|
||||||
|
{
|
||||||
|
$ergebnis = ManagerRegler::verteile(0.0, 210.0, [
|
||||||
|
$this->verbraucher(10, 0, [0, 100, 200], 0),
|
||||||
|
$this->verbraucher(20, 0, [0, 150], 0),
|
||||||
|
], 'PrioritaetPV');
|
||||||
|
|
||||||
|
self::assertSame([10 => 200, 20 => 0], $ergebnis['Sollwerte']);
|
||||||
|
self::assertSame(10, $ergebnis['Restabweichung_W']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPeakVerwendetPeakPrioritaetVorLeistungUndEnergie(): void
|
||||||
|
{
|
||||||
|
$a = $this->verbraucher(10, 0, [0, 100, 200], 0);
|
||||||
|
$b = $this->verbraucher(20, 9, [0, 300], 0);
|
||||||
|
$a['Daten']['PrioritaetPeak'] = 9;
|
||||||
|
$b['Daten']['PrioritaetPeak'] = 0;
|
||||||
|
$ergebnis = ManagerRegler::verteile(0.0, 300.0, [$a, $b], 'PrioritaetPeak', [20 => 50000.0]);
|
||||||
|
|
||||||
|
self::assertSame([20 => 300, 10 => 0], $ergebnis['Sollwerte']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGrosseBereicheWerdenGemeinsamOhneWattweiseSchleifeVerteilt(): void
|
||||||
|
{
|
||||||
|
$ergebnis = ManagerRegler::verteile(0.0, 1000000001.0, [
|
||||||
|
$this->verbraucher(10, 0, [['Von_W' => 0, 'Bis_W' => 1000000000]], 0),
|
||||||
|
$this->verbraucher(20, 0, [['Von_W' => 0, 'Bis_W' => 1000000000]], 0),
|
||||||
|
], 'PrioritaetPV', [10 => 999.0, 20 => 0.0]);
|
||||||
|
|
||||||
|
self::assertSame([10 => 500000000, 20 => 500000001], $ergebnis['Sollwerte']);
|
||||||
|
self::assertSame(0, $ergebnis['Restabweichung_W']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBereicheUndEinzelwerteGegenWattweiseReferenz(): void
|
||||||
|
{
|
||||||
|
$angebote = [
|
||||||
|
[0, ['Von_W' => 3, 'Bis_W' => 9], 15],
|
||||||
|
[0, 5, ['Von_W' => 7, 'Bis_W' => 12], 18],
|
||||||
|
[0, 4, 8, ['Von_W' => 14, 'Bis_W' => 20]],
|
||||||
|
];
|
||||||
|
$energien = [10 => 10000.5, 20 => 10000.0, 30 => 10000.0];
|
||||||
|
$verbraucher = [];
|
||||||
|
$diskret = [];
|
||||||
|
foreach ($angebote as $index => $angebot) {
|
||||||
|
$id = ($index + 1) * 10;
|
||||||
|
$verbraucher[] = $this->verbraucher($id, 2, $angebot, 0);
|
||||||
|
$diskret[$id] = [];
|
||||||
|
foreach ($angebot as $wert) {
|
||||||
|
foreach (is_int($wert) ? [$wert] : range($wert['Von_W'], $wert['Bis_W']) as $watt) {
|
||||||
|
$diskret[$id][] = $watt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ($budget = 0; $budget <= 60; $budget++) {
|
||||||
|
$erwartet = [10 => 0, 20 => 0, 30 => 0];
|
||||||
|
$rest = $budget;
|
||||||
|
while (true) {
|
||||||
|
$schritte = [];
|
||||||
|
foreach ($diskret as $id => $werte) {
|
||||||
|
foreach ($werte as $wert) {
|
||||||
|
if ($wert > $erwartet[$id]) {
|
||||||
|
if ($wert - $erwartet[$id] <= $rest) {
|
||||||
|
$schritte[] = [$wert, $energien[$id], $id];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($schritte === []) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sort($schritte);
|
||||||
|
[$wert, , $id] = $schritte[0];
|
||||||
|
$rest -= $wert - $erwartet[$id];
|
||||||
|
$erwartet[$id] = $wert;
|
||||||
|
}
|
||||||
|
$ergebnis = ManagerRegler::verteile(0.0, (float) $budget, array_reverse($verbraucher),
|
||||||
|
'PrioritaetPV', $energien);
|
||||||
|
$ist = $ergebnis['Sollwerte'];
|
||||||
|
ksort($ist);
|
||||||
|
self::assertSame($erwartet, $ist, 'Budget: ' . $budget);
|
||||||
|
self::assertSame($rest, $ergebnis['Restabweichung_W']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNichtVerfuegbareLastWirdReserviertUndNichtNeuZugeteilt(): void
|
||||||
|
{
|
||||||
|
$a = $this->verbraucher(10, 0, [0, 100, 200], 100);
|
||||||
|
$a['Daten']['Verfuegbar'] = false;
|
||||||
|
$ergebnis = ManagerRegler::verteile(100.0, 200.0, [
|
||||||
|
$a,
|
||||||
|
$this->verbraucher(20, 0, [0, 100, 200], 0),
|
||||||
|
], 'PrioritaetPV');
|
||||||
|
|
||||||
|
self::assertSame(100, $ergebnis['NichtSteuerbar_W']);
|
||||||
|
self::assertSame([20 => 100], $ergebnis['Sollwerte']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFestwertWirdUnabhaengigVonAenderungMoeglichZugewiesen(): void
|
public function testFestwertWirdUnabhaengigVonAenderungMoeglichZugewiesen(): void
|
||||||
|
|||||||
Reference in New Issue
Block a user