Add curtailing algorithm and all what needed for execution

This commit is contained in:
atef 2024-08-02 13:39:51 +02:00
parent 8d00d7ede9
commit ac52c098e6
1 changed files with 94 additions and 54 deletions

View File

@ -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,6 +348,7 @@ internal static class Program
else
{
"2 or more string are disabled".WriteLine();
Console.WriteLine(" ****************** ");
alarmList.Add(new AlarmOrWarning
{
@ -532,70 +536,88 @@ 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)
private static void ControlPvPower(this StatusRecord r, UInt16 exportLimit = 0, UInt16 pvInstalledPower = 20)
{
const UInt16 stableFactor = 500;
var inverters = r.AcDc.Devices;
if (r.GridMeter?.Ac.Power.Active == null)
{
Console.WriteLine(" No reading from Grid meter");
return;
}
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 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;
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();
if (r.PvOnDc.Dc.Power != null && r.PvOnDc.Dc.Power > limitSystemProduction)
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)
{
exportLimit.WriteLine(" exportLimit");
systemProduction.WriteLine(" systemProduction");
limitSystemProduction.WriteLine(" limit System export");
targetReferenceVoltage.WriteLine("targetReferenceVoltage");
if (r.GridMeter?.Ac.Power.Active != null)
{
if (r.GridMeter.Ac.Power.Active < -limitSystemProduction)
{
"We are openning the window".WriteLine();
//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)
{
var maxDcLinkVoltage = (UInt16)(r.Config.GridTie.AcDc.MaxDcLinkVoltage - 10);
r.Config.GridTie.AcDc.MaxDcLinkVoltage = maxDcLinkVoltage;
maxDcLinkVoltage.WriteLine("maxDcLinkVoltage");
_curtailFlag = true;
upperVoltage = IncreaseInverterUpperLimit(upperVoltage, stepSize);
upperVoltage.WriteLine("Upper Voltage Increased: New Upper limit");
}
else
{
"do nothing".WriteLine();
}
if (_curtailFlag)
{
if (r.GridMeter.Ac.Power.Active > (systemExportLimit + deadBand))
{
upperVoltage = DecreaseInverterUpperLimit(upperVoltage, stepSize);
if (upperVoltage <= configFile.GridTie.AcDc.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
{
r.Config.GridTie.AcDc.MaxDcLinkVoltage.WriteLine("maxDcLinkVoltage");
r.Config.GridTie.DcDc.UpperDcLinkVoltage.WriteLine("maxDcDcLinkVoltage");
Console.WriteLine("Upper Voltage decreased: New Upper limit");
upperVoltage.WriteLine(" New Upper limit");
}
}
//if (exportLimit == 100)
//{
// r.Config.GridSetPoint = 0;
//}
else
{
deadBand.WriteLine("W :We are in Dead band area");
upperVoltage.WriteLine(" same Upper limit from last cycle");
}
}
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?
private static void DistributePower(StatusRecord record, EssControl essControl)
@ -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)
@ -798,7 +840,5 @@ internal static class Program
{
status.Config.DayAndTimeForAdditionalCalibration = config.CalibrationChargeDate;
}
}
}