From 6572597ee9ec4f346a7ee57bf52fbff4405c076c Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Mon, 27 Feb 2017 17:03:18 +0100
Subject: [PATCH] extend sum conatiner, modal results to compute additional
 columns (time share per gear, etc.)

---
 .../OutputData/IModalDataContainer.cs         | 138 +++++++++++++++---
 .../OutputData/SummaryDataContainer.cs        |  48 ++++--
 2 files changed, 154 insertions(+), 32 deletions(-)

diff --git a/VectoCore/VectoCore/OutputData/IModalDataContainer.cs b/VectoCore/VectoCore/OutputData/IModalDataContainer.cs
index 9e8ca6989c..d0e966be5d 100644
--- a/VectoCore/VectoCore/OutputData/IModalDataContainer.cs
+++ b/VectoCore/VectoCore/OutputData/IModalDataContainer.cs
@@ -33,8 +33,10 @@ using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Linq;
+using System.Runtime.Remoting.Metadata.W3cXsd2001;
 using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.Models.Simulation.Data;
+using TUGraz.VectoCore.Models.Simulation.DataBus;
 using TUGraz.VectoCore.Models.Simulation.Impl;
 using TUGraz.VectoCore.Utils;
 
@@ -96,14 +98,14 @@ namespace TUGraz.VectoCore.OutputData
 
 	public static class ModalDataContainerExtensions
 	{
-		public static SI Max(this IModalDataContainer data, ModalResultField field)
+		public static T Max<T>(this IModalDataContainer data, ModalResultField field)
 		{
-			return data.GetValues<SI>(field).Max();
+			return data.GetValues<T>(field).Max();
 		}
 
-		public static SI Min(this IModalDataContainer data, ModalResultField field)
+		public static T Min<T>(this IModalDataContainer data, ModalResultField field)
 		{
-			return data.GetValues<SI>(field).Min();
+			return data.GetValues<T>(field).Min();
 		}
 
 		public static SI Average(this IEnumerable<SI> self, Func<SI, bool> filter)
@@ -153,7 +155,7 @@ namespace TUGraz.VectoCore.OutputData
 					a = x.Field<MeterPerSquareSecond>((int)ModalResultField.acc),
 					dt = x.Field<Second>((int)ModalResultField.simulationInterval)
 				})
-				.Where(x => x.a > 0.125).Sum(x => x.dt).DefaultIfNull(0);
+				.Sum(x => x.a > 0.125 ? x.dt : 0.SI<Second>()).DefaultIfNull(0);
 			return 100 * (accelerationTimeShare / data.Duration()).Cast<Scalar>();
 		}
 
@@ -164,7 +166,7 @@ namespace TUGraz.VectoCore.OutputData
 					a = x.Field<MeterPerSquareSecond>((int)ModalResultField.acc),
 					dt = x.Field<Second>((int)ModalResultField.simulationInterval)
 				})
-				.Where(x => x.a < -0.125).Sum(x => x.dt).DefaultIfNull(0);
+				.Sum(x => x.a < -0.125 ? x.dt : 0.SI<Second>()).DefaultIfNull(0);
 			return 100 * (decelerationTimeShare / data.Duration()).Cast<Scalar>();
 		}
 
@@ -176,7 +178,7 @@ namespace TUGraz.VectoCore.OutputData
 					a = x.Field<MeterPerSquareSecond>((int)ModalResultField.acc),
 					dt = x.Field<Second>((int)ModalResultField.simulationInterval)
 				})
-				.Where(x => x.v >= 0.1 && x.a.IsBetween(-0.125, 0.125)).Sum(x => x.dt).DefaultIfNull(0);
+				.Sum(x => x.v >= 0.1 && x.a.IsBetween(-0.125, 0.125) ? x.dt : 0.SI<Second>()).DefaultIfNull(0);
 			return 100 * (cruiseTime / data.Duration()).Cast<Scalar>();
 		}
 
@@ -187,7 +189,7 @@ namespace TUGraz.VectoCore.OutputData
 					v = x.Field<MeterPerSecond>((int)ModalResultField.v_act),
 					dt = x.Field<Second>((int)ModalResultField.simulationInterval)
 				})
-				.Where(x => x.v < 0.1).Sum(x => x.dt) ?? 0.SI<Second>();
+				.Sum(x => x.v < 0.1 ? x.dt : 0.SI<Second>()) ?? 0.SI<Second>();
 			return 100 * (stopTime / data.Duration()).Cast<Scalar>();
 		}
 
@@ -263,9 +265,9 @@ namespace TUGraz.VectoCore.OutputData
 
 		public static Meter Distance(this IModalDataContainer data)
 		{
-			var max = data.Max(ModalResultField.dist);
-			var min = data.Min(ModalResultField.dist);
-			return max == null || min == null ? null : (max - min).Cast<Meter>();
+			var max = data.Max<Meter>(ModalResultField.dist);
+			var min = data.Min<Meter>(ModalResultField.dist);
+			return max == null || min == null ? null : (max - min);
 		}
 
 		public static WattSecond WorkTotalMechanicalBrake(this IModalDataContainer data)
@@ -299,15 +301,6 @@ namespace TUGraz.VectoCore.OutputData
 			return data.TimeIntegral<WattSecond>(ModalResultField.P_air);
 		}
 
-		public static WattSecond EngineWorkPositive(this IModalDataContainer data)
-		{
-			return data.TimeIntegral<WattSecond>(ModalResultField.P_eng_out, x => x > 0);
-		}
-
-		public static WattSecond EngineWorkNegative(this IModalDataContainer data)
-		{
-			return data.TimeIntegral<WattSecond>(ModalResultField.P_eng_out, x => x < 0);
-		}
 
 		public static WattSecond TotalEngineWorkPositive(this IModalDataContainer data)
 		{
@@ -414,7 +407,7 @@ namespace TUGraz.VectoCore.OutputData
 			return data.TimeIntegral<Kilogram>(ModalResultField.FCMap) / distance;
 		}
 
-		
+
 		public static Watt TotalPowerEnginePositiveAverage(this IModalDataContainer data)
 		{
 			var simulationIntervals = data.GetValues<Second>(ModalResultField.simulationInterval);
@@ -450,6 +443,107 @@ namespace TUGraz.VectoCore.OutputData
 			return sum;
 		}
 
-		
+
+		public static MeterPerSecond MaxSpeed(this ModalDataContainer data)
+		{
+			return data.Max<MeterPerSecond>(ModalResultField.v_act).DefaultIfNull(0);
+		}
+
+		public static MeterPerSquareSecond MaxAcceleration(this ModalDataContainer data)
+		{
+			return data.Max<MeterPerSquareSecond>(ModalResultField.acc);
+		}
+
+		public static MeterPerSquareSecond MaxDeceleration(this ModalDataContainer data)
+		{
+			return -data.Min<MeterPerSquareSecond>(ModalResultField.acc);
+		}
+
+		public static PerSecond AvgEngineSpeed(this ModalDataContainer data)
+		{
+			var integral = data.Data.Rows.Cast<DataRow>()
+				.Sum(x => x.Field<PerSecond>((int)ModalResultField.n_eng_avg).Value() *
+						x.Field<Second>((int)ModalResultField.simulationInterval).Value());
+			return (integral / Duration(data).Value()).SI<PerSecond>();
+		}
+
+		public static PerSecond MaxEngineSpeed(this IModalDataContainer data)
+		{
+			return data.Max<PerSecond>(ModalResultField.n_eng_avg);
+		}
+
+		public static Scalar EngineMaxLoadTimeShare(this ModalDataContainer data)
+		{
+			var sum = data.Data.Rows.Cast<DataRow>()
+				.Select(x => new {
+					tMax = x.Field<NewtonMeter>((int)ModalResultField.Tq_full),
+					tEng = x.Field<NewtonMeter>((int)ModalResultField.T_eng_fcmap),
+					dt = x.Field<Second>((int)ModalResultField.simulationInterval)
+				})
+				.Sum(x => x.tMax.IsEqual(x.tEng, 5.SI<NewtonMeter>()) ? x.dt : 0.SI<Second>()) ?? 0.SI<Second>();
+			return 100 * sum / Duration(data);
+		}
+
+		public static Scalar GearshiftCount(this ModalDataContainer data)
+		{
+			var prevGear = data.GetValues<uint>(ModalResultField.Gear).First();
+			var gearCount = 0;
+			foreach (DataRow row in data.Data.Rows) {
+				var gear = row.Field<uint>((int)ModalResultField.Gear);
+				var speed = row.Field<MeterPerSecond>((int)ModalResultField.v_act);
+				if (speed.IsSmallerOrEqual(0.1)) {
+					prevGear = 0;
+					gearCount++;
+					continue;
+				}
+				if (gear == 0 || gear == prevGear) {
+					continue;
+				}
+				gearCount++;
+				prevGear = gear;
+			}
+			return gearCount.SI<Scalar>();
+		}
+
+		public static Scalar CoastingTimeShare(this ModalDataContainer data)
+		{
+			var sum = data.Data.Rows.Cast<DataRow>()
+				.Select(x => new {
+					DrivingBehavior = x.Field<DrivingBehavior>((int)ModalResultField.drivingBehavior),
+					dt = x.Field<Second>((int)ModalResultField.simulationInterval)
+				})
+				.Sum(x => x.DrivingBehavior == DrivingBehavior.Coasting ? x.dt : 0.SI<Second>()) ?? 0.SI<Second>();
+			return 100 * sum / Duration(data);
+		}
+
+		public static Scalar BrakingTimeShare(this ModalDataContainer data)
+		{
+			var sum = data.Data.Rows.Cast<DataRow>()
+				.Select(x => new {
+					DrivingBehavior = x.Field<DrivingBehavior>((int)ModalResultField.drivingBehavior),
+					dt = x.Field<Second>((int)ModalResultField.simulationInterval)
+				})
+				.Sum(x => x.DrivingBehavior == DrivingBehavior.Braking ? x.dt : 0.SI<Second>()) ?? 0.SI<Second>();
+			return 100 * sum / Duration(data);
+		}
+
+		public static Dictionary<uint, Scalar> TimeSharePerGear(this ModalDataContainer data, uint gearCount)
+		{
+			var retVal = new Dictionary<uint, Scalar>();
+			for (uint i = 0; i <= gearCount; i++) {
+				retVal[i] = 0.SI<Scalar>();
+			}
+
+			foreach (var dataRow in data.Data.Rows.Cast<DataRow>()) {
+				var gear = dataRow.Field<uint>((int)ModalResultField.Gear);
+				retVal[gear] += dataRow.Field<Second>((int)ModalResultField.simulationInterval).Value();
+			}
+
+			var duration = Duration(data).Value();
+			for (uint i = 0; i <= gearCount; i++) {
+				retVal[i] = 100 * retVal[i] / duration;
+			}
+			return retVal;
+		}
 	}
 }
\ No newline at end of file
diff --git a/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs b/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs
index 0a6c6a7367..f4f27c5f82 100644
--- a/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs
+++ b/VectoCore/VectoCore/OutputData/SummaryDataContainer.cs
@@ -33,6 +33,7 @@ using System;
 using System.Data;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using Org.BouncyCastle.Asn1;
 using TUGraz.VectoCommon.Models;
 using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.Configuration;
@@ -42,7 +43,7 @@ using TUGraz.VectoCore.Configuration;
 namespace TUGraz.VectoCore.OutputData
 {
 	public delegate void WriteSumData(
-		IModalDataContainer data, Kilogram vehicleMass, Kilogram loading, CubicMeter cargoVolume);
+		IModalDataContainer data, Kilogram vehicleMass, Kilogram loading, CubicMeter cargoVolume, uint gearCount);
 
 	/// <summary>
 	/// Class for the sum file in vecto.
@@ -112,6 +113,19 @@ namespace TUGraz.VectoCore.OutputData
 		public const string DEC_TIMESHARE = "DecelerationTimeShare [%]";
 		public const string CRUISE_TIMESHARE = "CruiseTimeShare [%]";
 		public const string STOP_TIMESHARE = "StopTimeShare [%]";
+
+		public const string MAX_SPEED = "max. speed [km/h";
+		public const string MAX_ACCELERATION = "max. acc [m/s²]";
+		public const string MAX_DECELERATION = "max. dec [m/s²]";
+		public const string AVG_ENGINE_SPEED = "n_eng_avg [rpm]";
+		public const string MAX_ENGINE_SPEED = "n_eng_max [rpm]";
+		public const string NUM_GEARSHIFTS = "gear shifts [-]";
+		public const string ENGINE_FULL_LOAD_TIME_SHARE = "Engine max. Load time share [%]";
+		public const string COASTING_TIME_SHARE = "CoastingTimeShare [%]";
+		public const string BRAKING_TIME_SHARE = "BrakingTImeShare [%]";
+
+		public const string TIME_SHARE_PER_GEAR_FORMAT = "Gear {0} TimeShare [%]";
+
 		// ReSharper restore InconsistentNaming
 
 		internal readonly DataTable _table;
@@ -141,7 +155,9 @@ namespace TUGraz.VectoCore.OutputData
 				P_WHEEL_POS, P_FCMAP_POS,
 				E_FCMAP_POS, E_FCMAP_NEG, E_POWERTRAIN_INERTIA, E_AUX, E_CLUTCH_LOSS, E_TC_LOSS, E_SHIFT_LOSS, E_GBX_LOSS,
 				E_RET_LOSS, E_ANGLE_LOSS, E_AXL_LOSS, E_BRAKE, E_VEHICLE_INERTIA, E_AIR, E_ROLL, E_GRAD,
-				ACC, ACC_POS, ACC_NEG, ACC_TIMESHARE, DEC_TIMESHARE, CRUISE_TIMESHARE, STOP_TIMESHARE
+				ACC, ACC_POS, ACC_NEG, ACC_TIMESHARE, DEC_TIMESHARE, CRUISE_TIMESHARE, STOP_TIMESHARE,
+				MAX_SPEED, MAX_ACCELERATION, MAX_DECELERATION, AVG_ENGINE_SPEED, MAX_ENGINE_SPEED, NUM_GEARSHIFTS,
+				ENGINE_FULL_LOAD_TIME_SHARE, COASTING_TIME_SHARE, BRAKING_TIME_SHARE
 			}.Select(x => new DataColumn(x, typeof(SI))).ToArray());
 		}
 
@@ -160,7 +176,7 @@ namespace TUGraz.VectoCore.OutputData
 		/// </summary>
 		[MethodImpl(MethodImplOptions.Synchronized)]
 		public virtual void Write(IModalDataContainer modData, string jobFileName, string jobName, string cycleFileName,
-			Kilogram vehicleMass, Kilogram vehicleLoading, CubicMeter cargoVolume)
+			Kilogram vehicleMass, Kilogram vehicleLoading, CubicMeter cargoVolume, uint gearCount)
 		{
 			var row = _table.NewRow();
 			_table.Rows.Add(row);
@@ -239,11 +255,6 @@ namespace TUGraz.VectoCore.OutputData
 			}
 
 			row[P_WHEEL_POS] = modData.PowerWheelPositive().ConvertTo().Kilo.Watt;
-			//row[P_BRAKE_LOSS] = modData.PowerBrake().ConvertTo().Kilo.Watt;
-			//row[P_ANGLE_LOSS] = modData.PowerAngle().ConvertTo().Kilo.Watt;
-			//row[P_TC_LOSS] = modData.PowerTorqueConverter().ConvertTo().Kilo.Watt;
-			//row[P_CLUTCH_POS] = modData.EnginePowerPositiveAverage().ConvertTo().Kilo.Watt;
-			//row[P_CLUTCH_NEG] = modData.EnginePowerNegativeAverage().ConvertTo().Kilo.Watt;
 
 			row[P_FCMAP_POS] = modData.TotalPowerEnginePositiveAverage().ConvertTo().Kilo.Watt;
 
@@ -264,8 +275,6 @@ namespace TUGraz.VectoCore.OutputData
 				row[colName] = modData.AuxiliaryWork(aux.Value).ConvertTo().Kilo.Watt.Hour;
 			}
 
-			//row[E_CLUTCH_POS] = modData.EngineWorkPositive().ConvertTo().Kilo.Watt.Hour;
-			//row[E_CLUTCH_NEG] = modData.EngineWorkNegative().ConvertTo().Kilo.Watt.Hour;
 			row[E_FCMAP_POS] = modData.TotalEngineWorkPositive().ConvertTo().Kilo.Watt.Hour;
 			row[E_FCMAP_NEG] = -modData.TotalEngineWorkNegative().ConvertTo().Kilo.Watt.Hour;
 			row[E_POWERTRAIN_INERTIA] = modData.PowerAccelerations().ConvertTo().Kilo.Watt.Hour;
@@ -303,6 +312,25 @@ namespace TUGraz.VectoCore.OutputData
 			var stopTimeShare = modal.StopTimeShare();
 			row[STOP_TIMESHARE] = stopTimeShare;
 
+			row[MAX_SPEED] = modal.MaxSpeed().AsKmph.SI<Scalar>();
+			row[MAX_ACCELERATION] = modal.MaxAcceleration();
+			row[MAX_DECELERATION] = modal.MaxDeceleration();
+			row[AVG_ENGINE_SPEED] = modal.AvgEngineSpeed().AsRPM.SI<Scalar>();
+			row[MAX_ENGINE_SPEED] = modData.MaxEngineSpeed().AsRPM.SI<Scalar>();
+			row[NUM_GEARSHIFTS] = modal.GearshiftCount();
+			row[ENGINE_FULL_LOAD_TIME_SHARE] = modal.EngineMaxLoadTimeShare();
+			row[COASTING_TIME_SHARE] = modal.CoastingTimeShare();
+			row[BRAKING_TIME_SHARE] = modal.BrakingTimeShare();
+
+			var timeSharePerGear = modal.TimeSharePerGear(gearCount);
+
+			for (uint i = 0; i <= gearCount; i++) {
+				var colName = string.Format(TIME_SHARE_PER_GEAR_FORMAT, i);
+				if (!_table.Columns.Contains(colName)) {
+					_table.Columns.Add(colName, typeof(SI));
+				}
+				row[colName] = timeSharePerGear[i];
+			}
 			if (accTimeShare != null && decTimeShare != null && cruiseTimeShare != null) {
 				var shareSum = accTimeShare + decTimeShare + cruiseTimeShare + stopTimeShare;
 				if (!shareSum.IsEqual(100)) {
-- 
GitLab