From ac52c098e65f901e1aa699bf7a933787d0a40a53 Mon Sep 17 00:00:00 2001 From: atef Date: Fri, 2 Aug 2024 13:39:51 +0200 Subject: [PATCH] Add curtailing algorithm and all what needed for execution --- csharp/App/SaliMax/src/Program.cs | 148 +++++++++++++++++++----------- 1 file changed, 94 insertions(+), 54 deletions(-) diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index f8d00aa7b..a64c9c8fb 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -1,4 +1,4 @@ -#define Amax +#undef Amax #undef GridLimit using System.Diagnostics; @@ -54,6 +54,7 @@ internal static class Program private static readonly Channel RelaysChannel; private static readonly Channel BatteriesChannel; + private static Boolean _curtailFlag = false; private const String VpnServerIp = "10.2.0.11"; private static Boolean _subscribedToQueue = false; private static Boolean _subscribeToQueueForTheFirstTime = false; @@ -232,7 +233,7 @@ internal static class Program record.ControlConstants(); record.ControlSystemState(); - record.ControlPvPower(record.Config.CurtailP); + record.ControlPvPower(record.Config.CurtailP, record.Config.PvInstalledPower); var essControl = record.ControlEss().WriteLine(); @@ -332,6 +333,8 @@ internal static class Program else if (IsPowerOfTwo(battery.LimpBitMap)) { "1 String is disabled".WriteLine(); + Console.WriteLine(" ****************** "); + warningList.Add(new AlarmOrWarning { Date = DateTime.Now.ToString("yyyy-MM-dd"), @@ -345,7 +348,8 @@ internal static class Program else { "2 or more string are disabled".WriteLine(); - + Console.WriteLine(" ****************** "); + alarmList.Add(new AlarmOrWarning { Date = DateTime.Now.ToString("yyyy-MM-dd"), @@ -532,69 +536,87 @@ internal static class Program } // This will be used for provider throttling, this example is only for either 100% or 0 % - private static void ControlPvPower(this StatusRecord r, UInt16 exportLimit = 100) - { - const UInt16 stableFactor = 500; - var inverters = r.AcDc.Devices; - var configFile = r.Config; - var systemProduction = inverters.Count * 25000; // this should be from to the PV - var limitSystemProduction = systemProduction * exportLimit / 100; - var devicesConfig = r.AcDc.Devices.All(d => d.Control.Ac.GridType == GridType.GridTied400V50Hz) ? configFile.GridTie : configFile.IslandMode; // TODO if any of the grid tie mode - var targetReferenceVoltage = devicesConfig.AcDc.ReferenceDcLinkVoltage; - - - if (r.PvOnDc.Dc.Power != null && r.PvOnDc.Dc.Power > limitSystemProduction) + private static void ControlPvPower(this StatusRecord r, UInt16 exportLimit = 0, UInt16 pvInstalledPower = 20) + { + if (r.GridMeter?.Ac.Power.Active == null) { - exportLimit.WriteLine(" exportLimit"); - systemProduction.WriteLine(" systemProduction"); - limitSystemProduction.WriteLine(" limit System export"); - targetReferenceVoltage.WriteLine("targetReferenceVoltage"); + Console.WriteLine(" No reading from Grid meter"); + return; + } - if (r.GridMeter?.Ac.Power.Active != null) + if (pvInstalledPower == 0) + { + Console.WriteLine(" No curtailing, because Pv installed is equal to 0"); + return; + } + + const Int32 constantDeadBand = 5000; // magic number + const Double voltageRange = 100; // 100 Voltage configured rang for PV slope, if the configured slope change this must change also + var configFile = r.Config; + var inverters = r.AcDc.Devices; + var systemExportLimit = -exportLimit * 1000 ; // Conversion from Kw in W + var stepSize = ClampStepSize((UInt16)Math.Floor(voltageRange/ pvInstalledPower)); // in Voltage per 1 Kw + var deadBand = constantDeadBand/stepSize; + + // LINQ query to select distinct ActiveUpperVoltage + var result = r.AcDc.Devices + .Select(device => device?.Status?.DcVoltages?.Active?.ActiveUpperVoltage) + .Select(voltage => voltage.Value) // Extract the value since we've confirmed it's non-null + .Distinct() + .ToList(); + + var upperVoltage = result.Count == 1 ? result[0] : 0; + + /************* For debugging purposes ********************/ + + systemExportLimit.WriteLine(" Export Limit in W"); + upperVoltage.WriteLine(" Upper Voltage"); + r.GridMeter.Ac.Power.Active.WriteLine(" Active Export"); + Console.WriteLine(" ****************** "); + + /*********************************************************/ + + if (r.GridMeter.Ac.Power.Active < systemExportLimit) + { + _curtailFlag = true; + upperVoltage = IncreaseInverterUpperLimit(upperVoltage, stepSize); + upperVoltage.WriteLine("Upper Voltage Increased: New Upper limit"); + } + else + { + if (_curtailFlag) { - if (r.GridMeter.Ac.Power.Active < -limitSystemProduction) + if (r.GridMeter.Ac.Power.Active > (systemExportLimit + deadBand)) { - "We are openning the window".WriteLine(); + upperVoltage = DecreaseInverterUpperLimit(upperVoltage, stepSize); - //r.Config.GridSetPoint = -limitSystemProduction + stableFactor; - //r.Config.GridSetPoint.WriteLine(" Grid set point"); - var maxDcLinkVoltage = (UInt16)(r.Config.GridTie.AcDc.MaxDcLinkVoltage + 10); - - r.Config.GridTie.AcDc.MaxDcLinkVoltage = maxDcLinkVoltage; - maxDcLinkVoltage.WriteLine("maxDcLinkVoltage"); - - } - else if (r.GridMeter.Ac.Power.Active > -limitSystemProduction + stableFactor * 4) - { - "We are closing the window".WriteLine(); - - //r.Config.GridSetPoint = -limitSystemProduction + stableFactor; - //r.Config.GridSetPoint.WriteLine(" Grid set point"); - - if ((r.Config.GridTie.AcDc.MaxDcLinkVoltage - r.Config.GridTie.AcDc.MinDcLinkVoltage) > 60) + if (upperVoltage <= configFile.GridTie.AcDc.MaxDcLinkVoltage) { - var maxDcLinkVoltage = (UInt16)(r.Config.GridTie.AcDc.MaxDcLinkVoltage - 10); - r.Config.GridTie.AcDc.MaxDcLinkVoltage = maxDcLinkVoltage; - maxDcLinkVoltage.WriteLine("maxDcLinkVoltage"); + _curtailFlag = false; + upperVoltage = configFile.GridTie.AcDc.MaxDcLinkVoltage; + upperVoltage.WriteLine(" New Upper limit"); + Console.WriteLine("Upper Voltage decreased: Smaller than the default value, value clamped"); } else { - "do nothing".WriteLine(); + Console.WriteLine("Upper Voltage decreased: New Upper limit"); + upperVoltage.WriteLine(" New Upper limit"); } - } else - { - r.Config.GridTie.AcDc.MaxDcLinkVoltage.WriteLine("maxDcLinkVoltage"); - r.Config.GridTie.DcDc.UpperDcLinkVoltage.WriteLine("maxDcDcLinkVoltage"); + { + deadBand.WriteLine("W :We are in Dead band area"); + upperVoltage.WriteLine(" same Upper limit from last cycle"); } } - - //if (exportLimit == 100) - //{ - // r.Config.GridSetPoint = 0; - //} + else + { + Console.WriteLine("Curtail Flag is false , no need to curtail"); + upperVoltage.WriteLine(" same Upper limit from last cycle"); + } } + inverters.ForEach(d => d.Control.Dc.MaxVoltage = upperVoltage); + Console.WriteLine(" ****************** "); } // why this is not in Controller? @@ -615,6 +637,26 @@ internal static class Program }); } + private static Double IncreaseInverterUpperLimit(Double upperLimit, Double stepSize) + { + return upperLimit + stepSize; + } + + private static Double DecreaseInverterUpperLimit(Double upperLimit, Double stepSize) + { + return upperLimit - stepSize; + } + + private static UInt16 ClampStepSize(UInt16 stepSize) + { + return stepSize switch + { + > 5 => 5, + <= 1 => 1, + _ => stepSize + }; + } + private static void ApplyAcDcDefaultSettings(this SystemControlRegisters? sc) { if (sc is null) @@ -797,8 +839,6 @@ internal static class Program else if (config.CalibrationChargeState == CalibrationChargeType.AdditionallyOnce) { status.Config.DayAndTimeForAdditionalCalibration = config.CalibrationChargeDate; - } - + } } - } \ No newline at end of file