Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS has been phased out. To see alternatives please check here

Skip to content
Snippets Groups Projects
Select Git revision
  • 367d878450f6692edd0c9924770dec99190df893
  • stable default
  • feat-fchv-bus
  • fix-h2-ice-bus
  • powertrains-multiple-axles
  • amdm3/develop
  • issue-1039
  • amdm3/main
  • test/nuget_publish
  • IEPC-experiments
  • amdm2/main
  • amdm2/develop
  • aptngearbox-not-auto
  • playground
  • official/main
  • official/develop
  • issue-templates
  • pdf-reports
  • HEV-timeruns-dev
  • timerun-empower-hybrids
  • timerun-pwheel-hybrids
  • Release/v5.0.3
  • Release/v5.0.1
  • Release/5.0.0-RC
  • Nuget/v0.11.4-DEV
  • Release/v0.11.4-DEV
  • Release/4.3.4-DEV
  • Release/4.3.3
  • Release/4.3.2-RC
  • Release/v4.3.0-DEV
  • Release/4.2.7
  • XMLConverterTool/4.2.6.0
  • Release/4.2.6-RC
  • Release/v4.2.5
  • Release/v4.2.3
  • Release/v4.2.2.3539-RC
  • Release/v4.2.1.3469
  • Release/v0.11.2.3456-DEV
  • Release/v4.2.0.3448-RC
  • Release/v4.1.3.3415
  • Release/v4.1.1.3413
41 results

FuelConsumptionMap.cs

Blame
  • Forked from VECTO / VECTO Sim
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    DrivingCycleDataReader.cs 15.35 KiB
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.IO;
    using System.Linq;
    using TUGraz.VectoCore.Configuration;
    using TUGraz.VectoCore.Exceptions;
    using TUGraz.VectoCore.Models;
    using TUGraz.VectoCore.Models.SimulationComponent.Data;
    using TUGraz.VectoCore.Utils;
    
    namespace TUGraz.VectoCore.FileIO.Reader
    {
    	public class DrivingCycleDataReader : LoggingObject
    	{
    		// --- Factory Methods
    		public static DrivingCycleData ReadFromStream(Stream stream, CycleType type)
    		{
    			return DoReadCycleData(type, VectoCSVFile.ReadStream(stream));
    		}
    
    		public static DrivingCycleData ReadFromFileEngineOnly(string fileName)
    		{
    			return ReadFromFile(fileName, CycleType.EngineOnly);
    		}
    
    		public static DrivingCycleData ReadFromFileDistanceBased(string fileName)
    		{
    			return ReadFromFile(fileName, CycleType.DistanceBased);
    		}
    
    		public static DrivingCycleData ReadFromFileTimeBased(string fileName)
    		{
    			return ReadFromFile(fileName, CycleType.TimeBased);
    		}
    
    		public static DrivingCycleData ReadFromFile(string fileName, CycleType type)
    		{
    			DataTable data;
    			try {
    				data = VectoCSVFile.Read(fileName);
    			} catch (Exception ex) {
    				throw new VectoException("ERROR while reading DrivingCycle File: " + ex.Message);
    			}
    			var retVal = DoReadCycleData(type, data);
    			retVal.Name = Path.GetFileNameWithoutExtension(fileName);
    			return retVal;
    		}
    
    		private static DrivingCycleData DoReadCycleData(CycleType type, DataTable data)
    		{
    			var parser = CreateDataParser(type);
    			var entries = parser.Parse(data).ToList();
    
    			if (type == CycleType.DistanceBased) {
    				entries = FilterDrivingCycleEntries(entries);
    			}
    			var cycle = new DrivingCycleData {
    				Entries = entries,
    				CycleType = type
    			};
    			return cycle;
    		}
    
    
    		// ----
    
    		private static List<DrivingCycleData.DrivingCycleEntry> FilterDrivingCycleEntries(
    			List<DrivingCycleData.DrivingCycleEntry> entries)
    		{
    			var filtered = new List<DrivingCycleData.DrivingCycleEntry>();
    			var current = entries.First();
    			current.Altitude = 0.SI<Meter>();
    			filtered.Add(current);
    			var distance = current.Distance;
    			var altitude = current.Altitude;
    			//foreach (var entry in entries) {
    			for (var i = 0; i < entries.Count; i++) {
    				var entry = entries[i];
    				if (!entry.StoppingTime.IsEqual(0) && !entry.VehicleTargetSpeed.IsEqual(0)) {
    					throw new VectoException(
    						"Error in DrivingCycle: stop time specified but target-speed > 0! Distance: {0}, stop-time: {1}, target speed: {2}",
    						entry.Distance, entry.StoppingTime, entry.VehicleTargetSpeed);
    				}
    				if (entry.Distance < distance) {
    					throw new VectoException(
    						"Error in DrivingCycle: distance entry is smaller than last distance! last distance: {0}, current distance: {1} ",
    						distance, entry.Distance);
    				}
    				if (i > 0) {
    					altitude += (entry.Distance - distance) * entries[i - 1].RoadGradientPercent / 100.0;
    				}
    				entry.Altitude = altitude;
    				// if something changes in the cycle, add it to the filtered cycle but always add last entry
    				if (!CycleEntriesAreEqual(current, entry) || i == entries.Count - 1) {
    					entry.Altitude = altitude;
    					filtered.Add(entry);
    					current = entry;
    				}
    				if (!entry.StoppingTime.IsEqual(0) && entry.VehicleTargetSpeed.IsEqual(0)) {
    					// vehicle stops. duplicate current distance entry with 0 waiting time
    					var tmp = new DrivingCycleData.DrivingCycleEntry(entry) {
    						StoppingTime = 0.SI<Second>(),
    						VehicleTargetSpeed = i < entries.Count - 1 ? entries[i + 1].VehicleTargetSpeed : 0.SI<MeterPerSecond>()
    					};
    					filtered.Add(tmp);
    					current = tmp;
    				}
    
    				distance = entry.Distance;
    			}
    			Logger<DrivingCycleDataReader>()
    				.Info("Data loaded. Number of Entries: {0}, filtered Entries: {1}", entries.Count, filtered.Count);
    			entries = filtered;
    
    			AdjustDistanceAfterStop(entries);
    
    			return entries;
    		}
    
    		private static void AdjustDistanceAfterStop(List<DrivingCycleData.DrivingCycleEntry> entries)
    		{
    			var currentIt = entries.GetEnumerator();
    			var nextIt = entries.GetEnumerator();
    			nextIt.MoveNext();
    			while (currentIt.MoveNext() && nextIt.MoveNext()) {
    				if (currentIt.Current != null && !currentIt.Current.StoppingTime.IsEqual(0)) {
    					if (nextIt.Current != null) {
    						nextIt.Current.Distance = currentIt.Current.Distance;
    					}
    				}
    			}
    		}
    
    		// -----
    
    		private static bool CycleEntriesAreEqual(DrivingCycleData.DrivingCycleEntry first,
    			DrivingCycleData.DrivingCycleEntry second)
    		{
    			if (first.Distance.IsEqual(second.Distance)) {
    				return true;
    			}
    			var retVal = first.VehicleTargetSpeed == second.VehicleTargetSpeed;
    			retVal = retVal &&
    					first.RoadGradient.IsEqual(second.RoadGradient, Constants.SimulationSettings.DrivingCycleRoadGradientTolerance);
    			retVal = retVal && first.StoppingTime.IsEqual(0) && second.StoppingTime.IsEqual(0);
    			retVal = retVal && first.AdditionalAuxPowerDemand == second.AdditionalAuxPowerDemand;
    			retVal = retVal && first.AuxiliarySupplyPower.Count == second.AuxiliarySupplyPower.Count;
    
    			foreach (var key in first.AuxiliarySupplyPower.Keys) {
    				if (!second.AuxiliarySupplyPower.ContainsKey(key)) {
    					return false;
    				}
    				retVal = retVal && first.AuxiliarySupplyPower[key] == second.AuxiliarySupplyPower[key];
    			}
    
    			return retVal;
    		}
    
    		private static IDataParser CreateDataParser(CycleType type)
    		{
    			switch (type) {
    				case CycleType.EngineOnly:
    					return new EngineOnlyDataParser();
    				case CycleType.TimeBased:
    					return new TimeBasedDataParser();
    				case CycleType.DistanceBased:
    					return new DistanceBasedDataParser();
    				default:
    					throw new ArgumentOutOfRangeException("type");
    			}
    		}
    
    		private static class Fields
    		{
    			/// <summary>
    			///     [m]	Travelled distance used for distance-based cycles. If t is also defined this column will be ignored.
    			/// </summary>
    			public const string Distance = "s";
    
    			/// <summary>
    			///     [s]	Used for time-based cycles. If neither this nor the distance s is defined the data will be interpreted as 1Hz.
    			/// </summary>
    			public const string Time = "t";
    
    			/// <summary>
    			///     [km/h]	Required except for Engine Only Mode calculations.
    			/// </summary>
    			public const string VehicleSpeed = "v";
    
    			/// <summary>
    			///     [%]	Optional.
    			/// </summary>
    			public const string RoadGradient = "grad";
    
    			/// <summary>
    			///     [s]	Required for distance-based cycles. Not used in time based cycles. stop defines the time the vehicle spends in
    			///     stop phases.
    			/// </summary>
    			public const string StoppingTime = "stop";
    
    			/// <summary>
    			///     [kW]	"Aux_xxx" Supply Power input for each auxiliary defined in the .vecto file , where xxx matches the ID of the
    			///     corresponding Auxiliary. ID's are not case sensitive and must not contain space or special characters.
    			/// </summary>
    			public const string AuxiliarySupplyPower = "Aux_";
    
    			/// <summary>
    			///     [rpm]	If n is defined VECTO uses that instead of the calculated engine speed value.
    			/// </summary>
    			public const string EngineSpeed = "n";
    
    			/// <summary>
    			///     [-]	Gear input. Overwrites the gear shift model.
    			/// </summary>
    			public const string Gear = "gear";
    
    			/// <summary>
    			///     [kW]	This power input will be directly added to the engine power in addition to possible other auxiliaries. Also
    			///     used in Engine Only Mode.
    			/// </summary>
    			public const string AdditionalAuxPowerDemand = "Padd";
    
    			/// <summary>
    			///     [km/h]	Only required if Cross Wind Correction is set to Vair and Beta Input.
    			/// </summary>
    			public const string AirSpeedRelativeToVehicle = "vair_res";
    
    			/// <summary>
    			///     [°]	Only required if Cross Wind Correction is set to Vair and Beta Input.
    			/// </summary>
    			public const string WindYawAngle = "vair_beta";
    
    			/// <summary>
    			///     [kW]	Effective engine power at clutch. Only required in Engine Only Mode. Alternatively torque Me can be defined.
    			///     Use DRAG to define motoring operation.
    			/// </summary>
    			public const string EnginePower = "Pe";
    
    			/// <summary>
    			///     [Nm]	Effective engine torque at clutch. Only required in Engine Only Mode. Alternatively power Pe can be defined.
    			///     Use DRAG to define motoring operation.
    			/// </summary>
    			public const string EngineTorque = "Me";
    		}
    
    		#region DataParser
    
    		private interface IDataParser
    		{
    			IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table);
    		}
    
    		internal class DistanceBasedDataParser : IDataParser
    		{
    			public IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table)
    			{
    				ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray());
    
    				return table.Rows.Cast<DataRow>().Select(row => new DrivingCycleData.DrivingCycleEntry {
    					Distance = row.ParseDouble(Fields.Distance).SI<Meter>(),
    					VehicleTargetSpeed =
    						row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(),
    					RoadGradientPercent = row.ParseDoubleOrGetDefault(Fields.RoadGradient),
    					RoadGradient =
    						VectoMath.InclinationToAngle(row.ParseDoubleOrGetDefault(Fields.RoadGradient) / 100.0),
    					StoppingTime =
    						(row.ParseDouble(Fields.StoppingTime)).SI<Second>(),
    					AdditionalAuxPowerDemand =
    						row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
    					EngineSpeed =
    						row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(),
    					Gear = row.ParseDoubleOrGetDefault(Fields.Gear),
    					AirSpeedRelativeToVehicle =
    						row.ParseDoubleOrGetDefault(Fields.AirSpeedRelativeToVehicle).KMPHtoMeterPerSecond(),
    					WindYawAngle = row.ParseDoubleOrGetDefault(Fields.WindYawAngle),
    					AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row)
    				});
    			}
    
    			private static void ValidateHeader(string[] header)
    			{
    				var allowedCols = new[] {
    					Fields.Distance,
    					Fields.VehicleSpeed,
    					Fields.RoadGradient,
    					Fields.StoppingTime,
    					Fields.EngineSpeed,
    					Fields.Gear,
    					Fields.AdditionalAuxPowerDemand,
    					Fields.AirSpeedRelativeToVehicle,
    					Fields.WindYawAngle
    				};
    
    				foreach (
    					var col in
    						header.Where(
    							col => !(allowedCols.Contains(col) || col.StartsWith(Fields.AuxiliarySupplyPower)))
    					) {
    					throw new VectoException(string.Format("Column '{0}' is not allowed.", col));
    				}
    
    				if (!header.Contains(Fields.VehicleSpeed)) {
    					throw new VectoException(string.Format("Column '{0}' is missing.",
    						Fields.VehicleSpeed));
    				}
    
    				if (!header.Contains(Fields.Distance)) {
    					throw new VectoException(string.Format("Column '{0}' is missing.", Fields.Distance));
    				}
    
    				if (header.Contains(Fields.AirSpeedRelativeToVehicle) ^
    					header.Contains(Fields.WindYawAngle)) {
    					throw new VectoException(
    						string.Format("Both Columns '{0}' and '{1}' must be defined, or none of them.",
    							Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle));
    				}
    			}
    		}
    
    		private class TimeBasedDataParser : IDataParser
    		{
    			public IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table)
    			{
    				ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray());
    
    				var entries = table.Rows.Cast<DataRow>().Select((row, index) => new DrivingCycleData.DrivingCycleEntry {
    					Time = row.ParseDoubleOrGetDefault(Fields.Time, index).SI<Second>(),
    					VehicleTargetSpeed =
    						row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(),
    					RoadGradientPercent = row.ParseDoubleOrGetDefault(Fields.RoadGradient),
    					RoadGradient =
    						VectoMath.InclinationToAngle(row.ParseDoubleOrGetDefault(Fields.RoadGradient) / 100.0),
    					AdditionalAuxPowerDemand =
    						row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
    					Gear = row.ParseDoubleOrGetDefault(Fields.Gear),
    					EngineSpeed =
    						row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(),
    					AirSpeedRelativeToVehicle =
    						row.ParseDoubleOrGetDefault(Fields.AirSpeedRelativeToVehicle).KMPHtoMeterPerSecond(),
    					WindYawAngle = row.ParseDoubleOrGetDefault(Fields.WindYawAngle),
    					AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row)
    				}).ToArray();
    
    				return entries;
    			}
    
    			private static void ValidateHeader(string[] header)
    			{
    				var allowedCols = new[] {
    					Fields.Time,
    					Fields.VehicleSpeed,
    					Fields.RoadGradient,
    					Fields.EngineSpeed,
    					Fields.Gear,
    					Fields.AdditionalAuxPowerDemand,
    					Fields.AirSpeedRelativeToVehicle,
    					Fields.WindYawAngle
    				};
    
    				foreach (
    					var col in
    						header.Where(
    							col => !(allowedCols.Contains(col) || col.StartsWith(Fields.AuxiliarySupplyPower)))
    					) {
    					throw new VectoException(string.Format("Column '{0}' is not allowed.", col));
    				}
    
    				if (!header.Contains(Fields.VehicleSpeed)) {
    					throw new VectoException(string.Format("Column '{0}' is missing.",
    						Fields.VehicleSpeed));
    				}
    
    				if (header.Contains(Fields.AirSpeedRelativeToVehicle) ^
    					header.Contains(Fields.WindYawAngle)) {
    					throw new VectoException(
    						string.Format("Both Columns '{0}' and '{1}' must be defined, or none of them.",
    							Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle));
    				}
    			}
    		}
    
    		private class EngineOnlyDataParser : IDataParser
    		{
    			public IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table)
    			{
    				ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray());
    				var absTime = 0.SI<Second>();
    
    				foreach (DataRow row in table.Rows) {
    					var entry = new DrivingCycleData.DrivingCycleEntry {
    						EngineSpeed =
    							row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(),
    						AdditionalAuxPowerDemand =
    							row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
    						AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row)
    					};
    					if (row.Table.Columns.Contains(Fields.EngineTorque)) {
    						if (row.Field<string>(Fields.EngineTorque).Equals("<DRAG>")) {
    							entry.Drag = true;
    						} else {
    							entry.EngineTorque =
    								row.ParseDouble(Fields.EngineTorque).SI<NewtonMeter>();
    						}
    					} else {
    						if (row.Field<string>(Fields.EnginePower).Equals("<DRAG>")) {
    							entry.Drag = true;
    						} else {
    							entry.EngineTorque = row.ParseDouble(Fields.EnginePower).SI().Kilo.Watt.Cast<Watt>() / entry.EngineSpeed;
    						}
    					}
    					entry.Time = absTime;
    					absTime += 1.SI<Second>();
    
    					yield return entry;
    				}
    			}
    
    			private static void ValidateHeader(string[] header)
    			{
    				var allowedCols = new[] {
    					Fields.EngineTorque,
    					Fields.EnginePower,
    					Fields.EngineSpeed,
    					Fields.AdditionalAuxPowerDemand
    				};
    
    				foreach (var col in header.Where(col => !allowedCols.Contains(col))) {
    					throw new VectoException(string.Format("Column '{0}' is not allowed.", col));
    				}
    
    				if (!header.Contains(Fields.EngineSpeed)) {
    					throw new VectoException(string.Format("Column '{0}' is missing.",
    						Fields.EngineSpeed));
    				}
    
    				if (!(header.Contains(Fields.EngineTorque) || header.Contains(Fields.EnginePower))) {
    					throw new VectoException(
    						string.Format("Columns missing: Either column '{0}' or column '{1}' must be defined.",
    							Fields.EngineTorque, Fields.EnginePower));
    				}
    
    				if (header.Contains(Fields.EngineTorque) && header.Contains(Fields.EnginePower)) {
    					Logger<DrivingCycleDataReader>().Warn("Found column '{0}' and column '{1}': only column '{0}' will be used.",
    						Fields.EngineTorque, Fields.EnginePower);
    				}
    			}
    		}
    
    		#endregion
    	}
    }