public static List <Cycle> ExtractCycles(List <DataPoint> points) { Log.Info($"ExtractCycles started for '{points.Count}' points"); //Cycles can be not in order, so we have to fix it first SortedList <int, List <DataPoint> > sortedList = new SortedList <int, List <DataPoint> >(); foreach (var p in points) { if (sortedList.ContainsKey(p.CycleIndex)) { sortedList[p.CycleIndex].Add(p); } else { sortedList.Add(p.CycleIndex, new List <DataPoint> { p }); } } List <DataPoint> updatedPointsAfterSorting = new List <DataPoint>(); List <Cycle> cycles = new List <Cycle>(); double? lastRestVoltageFound = null; foreach (KeyValuePair <int, List <DataPoint> > sortedCycleKvp in sortedList) { var currentPoint = updatedPointsAfterSorting.Count; int cycleIndex = sortedCycleKvp.Key; List <DataPoint> cycleDataPoints = sortedCycleKvp.Value.OrderBy(c => c.Time).ToList(); updatedPointsAfterSorting.AddRange(cycleDataPoints); var cycle = new Cycle { Index = cycleIndex, FirstPointIndex = currentPoint, PointCount = cycleDataPoints.Count }; foreach (DataPoint cyclePoint in cycleDataPoints) { if (cycle.Time == null) { //Set first point time cycle.Time = cyclePoint.Time; } if (cyclePoint.Energy != null) { cycle.ChargeEnergy = cyclePoint.Energy; } if (cyclePoint.DischargeEnergy != null) { cycle.DischargeEnergy = cyclePoint.DischargeEnergy; } //Only update if last point is not on the REST step, because it is 0 or useless if (cyclePoint.Current != null && cyclePoint.CycleStep != CycleStep.Rest) { if (cyclePoint.CycleStep == CycleStep.ChargeCC || cyclePoint.CycleStep == CycleStep.ChargeCV) { cycle.EndCurrent = cyclePoint.Current; } else if (cyclePoint.CycleStep == CycleStep.Discharge) { cycle.DischargeEndCurrent = cyclePoint.Current; } } // find last rest value for a given cycle before charge and discharge if (cyclePoint.CycleStep == CycleStep.Rest) { lastRestVoltageFound = cyclePoint.Voltage.Value; } if (cyclePoint.Voltage != null && cyclePoint.CycleStep != CycleStep.Rest) { if (cyclePoint.CycleStep == CycleStep.ChargeCC || cyclePoint.CycleStep == CycleStep.ChargeCV) { cycle.EndVoltage = cyclePoint.Voltage; if (cycle.EndRestVoltage == null) { cycle.EndRestVoltage = lastRestVoltageFound; } } else if (cyclePoint.CycleStep == CycleStep.Discharge) { cycle.DischargeEndVoltage = cyclePoint.Voltage; if (cycle.EndRestDischargeVoltage == null) { cycle.EndRestDischargeVoltage = lastRestVoltageFound; } } } if (cyclePoint.Temperature != null && cyclePoint.CycleStep != CycleStep.Rest) { cycle.Temperature = cyclePoint.Temperature; } // find first discharge value for a given cycle if (cyclePoint.CycleStep == CycleStep.Discharge) { if (cycle.StartDischargeVoltage == null && cycle.StartDischargeCurrent == null) { cycle.StartDischargeCurrent = cyclePoint.Current; cycle.StartDischargeVoltage = cyclePoint.Voltage; } if (cycle.EndRestDischargeVoltage != null && cycle.StartResistanceDischargeCurrent == null && cycle.StartResistanceDischargeVoltage == null) { cycle.StartResistanceDischargeCurrent = cyclePoint.Current; cycle.StartResistanceDischargeVoltage = cyclePoint.Voltage; } } // find first charge value for a given cycle if ((cyclePoint.CycleStep == CycleStep.ChargeCC || cyclePoint.CycleStep == CycleStep.ChargeCV)) { if (cycle.StartChargeVoltage == null && cycle.StartCurrent == null) { cycle.StartCurrent = cyclePoint.Current; cycle.StartChargeVoltage = cyclePoint.Voltage; } if (cycle.EndRestVoltage != null && cycle.StartResistanceVoltage == null && cycle.StartResistanceCurrent == null) { cycle.StartResistanceCurrent = cyclePoint.Current; cycle.StartResistanceVoltage = cyclePoint.Voltage; } } currentPoint++; //Update cycle time on the last step if (currentPoint == cycleDataPoints.Count) { cycle.Time = cyclePoint.Time - cycle.Time; } } if (cycle.EndRestVoltage == null) { cycle.EndRestVoltage = lastRestVoltageFound; } if (cycle.EndRestDischargeVoltage == null) { cycle.EndRestDischargeVoltage = lastRestVoltageFound; } cycles.Add(cycle); } double maxChargeCapacity = double.MinValue; int indexMaxChargeCapacity = int.MinValue; double maxDischargeCapacity = double.MinValue; int indexDischargeCapacity = int.MinValue; for (var i = 0; i < cycles.Count; i++) { var cycle = cycles[i]; cycle.MidVoltage = GetMidVoltage(updatedPointsAfterSorting, cycle.FirstPointIndex, cycle.PointCount); cycle.ChargeCapacity = GetChargeCapacity(updatedPointsAfterSorting, cycle.FirstPointIndex, cycle.PointCount); cycle.DischargeCapacity = GetDischargeCapacity(updatedPointsAfterSorting, cycle.FirstPointIndex, cycle.PointCount); PopulateResistance(cycle, cycles, i); if (cycle.ChargeCapacity.HasValue) { // Update value first time when we found charge capacity if (Math.Abs(maxChargeCapacity - double.MinValue) < Tolerance) { maxChargeCapacity = cycle.ChargeCapacity.Value; indexMaxChargeCapacity = i; } // Only pick max value from the first maxCyclesForCalculation cycles. else if (cycle.ChargeCapacity.Value > maxChargeCapacity && i < MaxStartCyclesForCalculation) { maxChargeCapacity = cycle.ChargeCapacity.Value; } } if (cycle.DischargeCapacity.HasValue) { // Update value first time when we found discharge capacity if (Math.Abs(maxDischargeCapacity - double.MinValue) < Tolerance) { maxDischargeCapacity = cycle.DischargeCapacity.Value; indexDischargeCapacity = i; } // Only pick max value from the first maxCyclesForCalculation cycles. else if (cycle.DischargeCapacity.Value > maxDischargeCapacity && i < MaxStartCyclesForCalculation) { maxDischargeCapacity = cycle.DischargeCapacity.Value; } } cycle.Power = cycle.EndCurrent * cycle.EndVoltage; cycle.DischargePower = cycle.DischargeEndCurrent * cycle.EndVoltage; } Log.Info($"indexMaxChargeCapacity: '{indexMaxChargeCapacity}', indexDischargeCapacity: '{indexDischargeCapacity}' maxChargeCapacity: '{maxChargeCapacity}', maxDischargeCapacity: '{maxDischargeCapacity}'"); UpdateCapacityRetention(cycles, indexMaxChargeCapacity, indexDischargeCapacity, maxChargeCapacity, maxDischargeCapacity); Log.Info($"ExtractCycles finished '{cycles.Count}' cycles extracted."); return(cycles); }
private static IEnumerable <Point2Y> Plot2Y(List <DataPoint> points, bool is2Y, Cycle cycle, Func <DataPoint, double?> getX, Func <DataPoint, double?[]> getY, Parameters parameters, Func <DataPoint, bool> filter = null) { return(ApplyYFilter(Plot2Y(points, is2Y, cycle.FirstPointIndex, cycle.PointCount, getX, getY, filter).ToList(), parameters, is2Y)); }
private static void PopulateResistance(Cycle cycle, List <Cycle> cycles, int i) { //check for division on zero if (cycle.StartResistanceCurrent != null) { if (cycle.StartCurrent != 0) { double?chargeResistance = (cycle.StartResistanceVoltage - cycle.EndRestVoltage) / cycle.StartResistanceCurrent; if (chargeResistance != null) { //Resistance is only positive, so we need to take absolute value //Resistance is calculated in Ohms so we have to multiply by 1000 cycle.ResistanceOhms = Math.Abs(chargeResistance.Value) * 1000; } } else { cycles[i].ResistanceOhms = 0; } } else { //pick values from the next cycle if it exists if (i > 0 && i < cycles.Count) { Cycle previousCycle = cycles[i - 1]; if (previousCycle != null && previousCycle.ResistanceOhms == null && cycle.StartResistanceCurrent != null && cycle.StartResistanceCurrent != 0 && previousCycle.EndRestVoltage != null) { double?chargeResistance = (cycle.StartResistanceVoltage - previousCycle.EndRestVoltage) / cycle.StartResistanceCurrent; previousCycle.ResistanceOhms = Math.Abs(chargeResistance.Value) * 1000; } } } //check for division on zero if (cycle.StartResistanceDischargeCurrent != null) { if (cycle.StartResistanceDischargeCurrent != 0) { double?dischargeResistance = (cycle.StartResistanceDischargeVoltage - cycle.EndRestDischargeVoltage) / cycle.StartResistanceDischargeCurrent; if (dischargeResistance != null) { //Resistance is only positive, so we need to take absolute value //Resistance is calculated in Ohms so we have to multiply by 1000 cycle.DischargeResistance = Math.Abs(dischargeResistance.Value) * 1000; } } else { cycle.DischargeResistance = 0; } } else { //pick values from the next cycle if it exists if (i > 0 && i < cycles.Count) { Cycle previousCycle = cycles[i - 1]; if (previousCycle != null && previousCycle.DischargeResistance == null && cycle.StartResistanceDischargeCurrent != null && cycle.StartResistanceDischargeCurrent != 0 && previousCycle.EndRestVoltage != null) { double?dischargeResistance = (cycle.StartResistanceDischargeVoltage - previousCycle.EndRestVoltage) / cycle.StartResistanceDischargeCurrent; previousCycle.DischargeResistance = Math.Abs(dischargeResistance.Value) * 1000; } } } }