From 3579ba08b388933729eef678be5c737edbe19599 Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Fri, 3 Nov 2017 12:34:52 +0100
Subject: [PATCH] started working on EPTP powertrain creation

---
 VECTO.sln.DotSettings                         |   1 +
 .../Reader/DrivingCycleDataReader.cs          |  61 ++-
 .../EngineeringEPTPModeVectoRunDataFactory.cs |  78 ++++
 .../Simulation/Impl/PowertrainBuilder.cs      |  39 +-
 .../Simulation/Impl/SimulatorFactory.cs       |  35 +-
 .../Data/DrivingCycleData.cs                  | 415 +++++++++---------
 .../Impl/EngineAuxiliary.cs                   | 318 +++++++-------
 VectoCore/VectoCore/VectoCore.csproj          |   1 +
 .../Integration/EPTP/EPTPTest.cs              |  37 ++
 VectoCore/VectoCoreTest/VectoCoreTest.csproj  |   1 +
 10 files changed, 602 insertions(+), 384 deletions(-)
 create mode 100644 VectoCore/VectoCore/InputData/Reader/Impl/EngineeringEPTPModeVectoRunDataFactory.cs
 create mode 100644 VectoCore/VectoCoreTest/Integration/EPTP/EPTPTest.cs

diff --git a/VECTO.sln.DotSettings b/VECTO.sln.DotSettings
index 9ba0cdb648..1cd409260f 100644
--- a/VECTO.sln.DotSettings
+++ b/VECTO.sln.DotSettings
@@ -24,6 +24,7 @@
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AMT/@EntryIndexedValue">AMT</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AT/@EntryIndexedValue">AT</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CSV/@EntryIndexedValue">CSV</s:String>
+	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EPTP/@EntryIndexedValue">EPTP</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HVAC/@EntryIndexedValue">HVAC</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JSON/@EntryIndexedValue">JSON</s:String>
 	<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MT/@EntryIndexedValue">MT</s:String>
diff --git a/VectoCore/VectoCore/InputData/Reader/DrivingCycleDataReader.cs b/VectoCore/VectoCore/InputData/Reader/DrivingCycleDataReader.cs
index ba0e80a3f8..a1722ff80c 100644
--- a/VectoCore/VectoCore/InputData/Reader/DrivingCycleDataReader.cs
+++ b/VectoCore/VectoCore/InputData/Reader/DrivingCycleDataReader.cs
@@ -74,6 +74,9 @@ namespace TUGraz.VectoCore.InputData.Reader
 			if (DistanceBasedCycleDataParser.ValidateHeader(cols, false)) {
 				return CycleType.DistanceBased;
 			}
+            if (EPTPCycleDataParser.ValidateHeader(cols, false)) {
+                return CycleType.EPTP;
+            }
 			throw new VectoException("CycleFile format is unknown.");
 		}
 
@@ -92,7 +95,10 @@ namespace TUGraz.VectoCore.InputData.Reader
 					return new MeasuredSpeedDataParser();
 				case CycleType.PTO:
 					return new PTOCycleDataParser();
-				default:
+                case CycleType.EPTP:
+                    return new EPTPCycleDataParser();
+
+                default:
 					throw new ArgumentOutOfRangeException("Cycle Type", type.ToString());
 			}
 		}
@@ -311,6 +317,8 @@ namespace TUGraz.VectoCore.InputData.Reader
 			public const string RoadGradient = "grad";
 			public const string StoppingTime = "stop";
 			public const string EngineSpeed = "n";
+            public const string EngineSpeedSuffix = "n_eng";
+            public const string FanSpeed = "n_fan";
 			public const string Gear = "gear";
 			public const string AdditionalAuxPowerDemand = "Padd";
 			public const string AirSpeedRelativeToVehicle = "vair_res";
@@ -698,7 +706,56 @@ namespace TUGraz.VectoCore.InputData.Reader
 				return CheckColumns(header, allowedCols, requiredCols, throwExceptions, allowAux);
 			}
 		}
-	}
+
+        /// <summary>
+        /// Parser for PTO Cycles.
+        /// </summary>
+        // <t>,<v> [km/h],<Pwheel> [kW],<n_eng> [rpm],<n_fan> [rpm], <Padd> [kW]
+        private class EPTPCycleDataParser : AbstractCycleDataParser
+        {
+            public override IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table, bool crossWindRequired)
+            {
+                ValidateHeader(table.Columns);
+
+                var entries = table.Rows.Cast<DataRow>().Select(row => new DrivingCycleData.DrivingCycleEntry {
+                    Time = row.ParseDouble(Fields.Time).SI<Second>(),
+                    VehicleTargetSpeed = row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(),
+                    AdditionalAuxPowerDemand = row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
+                   PWheel = row.ParseDouble(Fields.PWheel).SI().Kilo.Watt.Cast<Watt>(),
+                   EngineSpeed = row.ParseDouble(Fields.EngineSpeedSuffix).RPMtoRad(),
+                   FanSpeed = row.ParseDouble(Fields.FanSpeed).RPMtoRad()
+                }).ToArray();
+
+                return entries;
+            }
+
+            public static bool ValidateHeader(DataColumnCollection header, bool throwExceptions = true)
+            {
+                var requiredCols = new[] {
+                    Fields.Time,
+                    Fields.VehicleSpeed,
+                    Fields.PWheel,
+                    Fields.EngineSpeedSuffix,
+                    Fields.FanSpeed
+                };
+
+                var allowedCols = new[] {
+                    Fields.Time,
+                    Fields.VehicleSpeed,
+                    Fields.AdditionalAuxPowerDemand,
+                    Fields.PWheel,
+                    Fields.EngineSpeedSuffix,
+                    Fields.FanSpeed
+                };
+
+                const bool allowAux = true;
+
+                return CheckColumns(header, allowedCols, requiredCols, throwExceptions, allowAux) &&
+                       CheckComboColumns(header, new[] { Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle }, throwExceptions);
+            }
+        }
+
+    }
 
 	#endregion
 }
\ No newline at end of file
diff --git a/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringEPTPModeVectoRunDataFactory.cs b/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringEPTPModeVectoRunDataFactory.cs
new file mode 100644
index 0000000000..bf955d0242
--- /dev/null
+++ b/VectoCore/VectoCore/InputData/Reader/Impl/EngineeringEPTPModeVectoRunDataFactory.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using TUGraz.VectoCommon.InputData;
+using TUGraz.VectoCommon.Models;
+using TUGraz.VectoCommon.Utils;
+using TUGraz.VectoCore.Configuration;
+using TUGraz.VectoCore.InputData.Reader.ComponentData;
+using TUGraz.VectoCore.InputData.Reader.DataObjectAdapter;
+using TUGraz.VectoCore.Models.Declaration;
+using TUGraz.VectoCore.Models.Simulation.Data;
+
+namespace TUGraz.VectoCore.InputData.Reader.Impl {
+    internal class EngineeringEPTPModeVectoRunDataFactory : IVectoRunDataFactory
+    {
+        protected IEPTPInputDataProvider InputDataProvider;
+
+        public EngineeringEPTPModeVectoRunDataFactory(IEPTPInputDataProvider eptpProvider)
+        {
+            InputDataProvider = eptpProvider;
+        }
+
+        public IEnumerable<VectoRunData> NextRun()
+        {
+            var dao = new DeclarationDataAdapter();
+            var segment = DeclarationData.Segments.Lookup(InputDataProvider.JobInputData.Vehicle.VehicleCategory,
+                InputDataProvider.JobInputData.Vehicle.AxleConfiguration,
+                InputDataProvider.JobInputData.Vehicle.GrossVehicleMassRating,
+                InputDataProvider.JobInputData.Vehicle.CurbMassChassis);
+            var driverdata = dao.CreateDriverData();
+            driverdata.AccelerationCurve = AccelerationCurveReader.ReadFromStream(segment.AccelerationFile);
+            var tempVehicle = dao.CreateVehicleData(InputDataProvider.JobInputData.Vehicle, segment.Missions.First(),
+                segment.Missions.First().Loadings.First().Value, segment.MunicipalBodyWeight);
+            var airdragData = dao.CreateAirdragData(InputDataProvider.JobInputData.Vehicle.AirdragInputData,
+                segment.Missions.First(), segment);
+            var engineData = dao.CreateEngineData(InputDataProvider.JobInputData.Vehicle.EngineInputData,
+                InputDataProvider.JobInputData.Vehicle.EngineIdleSpeed,
+                InputDataProvider.JobInputData.Vehicle.GearboxInputData, InputDataProvider.JobInputData.Vehicle.TorqueLimits);
+            var axlegearData = dao.CreateAxleGearData(InputDataProvider.JobInputData.Vehicle.AxleGearInputData, false);
+            var angledriveData = dao.CreateAngledriveData(InputDataProvider.JobInputData.Vehicle.AngledriveInputData, false);
+            var gearboxData = dao.CreateGearboxData(InputDataProvider.JobInputData.Vehicle.GearboxInputData, engineData,
+                axlegearData.AxleGear.Ratio,
+                tempVehicle.DynamicTyreRadius, tempVehicle.VehicleCategory, false);
+            var retarderData = dao.CreateRetarderData(InputDataProvider.JobInputData.Vehicle.RetarderInputData);
+
+            var ptoTransmissionData = dao.CreatePTOTransmissionData(InputDataProvider.JobInputData.Vehicle.PTOTransmissionInputData);
+
+
+            var aux = dao.CreateAuxiliaryData(InputDataProvider.JobInputData.Vehicle.AuxiliaryInputData(), MissionType.RegionalDelivery, segment.VehicleClass).ToList();
+            aux.RemoveAll(x => x.ID == Constants.Auxiliaries.IDs.Fan);
+            aux.Add(new VectoRunData.AuxData {
+                DemandType = AuxiliaryDemandType.Direct,
+                ID = DrivingCycleDataReader.Fields.AdditionalAuxPowerDemand
+            });
+
+            return InputDataProvider.JobInputData.Cycles.Select(cycle => {
+                var drivingCycle = DrivingCycleDataReader.ReadFromDataTable(cycle.CycleData, cycle.Name, false);
+                return new VectoRunData {
+                    JobName = InputDataProvider.JobInputData.Vehicle.VIN,
+                    EngineData = engineData,
+                    GearboxData = gearboxData,
+                    AxleGearData = axlegearData,
+                    AngledriveData = angledriveData,
+                    VehicleData = dao.CreateVehicleData(InputDataProvider.JobInputData.Vehicle, segment.Missions.First(),
+                        0.SI<Kilogram>(), segment.MunicipalBodyWeight),
+                    AirdragData =airdragData,
+                    DriverData = null,
+                    Aux = aux,
+                    AdvancedAux = null,
+                    Retarder = dao.CreateRetarderData(InputDataProvider.JobInputData.Vehicle.RetarderInputData),
+                    PTO = ptoTransmissionData,
+                    Cycle = new DrivingCycleProxy(drivingCycle, cycle.Name),
+                    ExecutionMode = ExecutionMode.Engineering
+                };
+            });
+        }
+    }
+}
\ No newline at end of file
diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs b/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs
index f677315e4e..88a5c904a9 100644
--- a/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs
+++ b/VectoCore/VectoCore/Models/Simulation/Impl/PowertrainBuilder.cs
@@ -33,6 +33,7 @@ using System;
 using System.Linq;
 using TUGraz.VectoCommon.Exceptions;
 using TUGraz.VectoCommon.Models;
+using TUGraz.VectoCommon.Utils;
 using TUGraz.VectoCore.Configuration;
 using TUGraz.VectoCore.Models.Declaration;
 using TUGraz.VectoCore.Models.Simulation.Data;
@@ -69,6 +70,8 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 					return BuildEngineOnly(data);
 				case CycleType.PWheel:
 					return BuildPWheel(data);
+                case CycleType.EPTP:
+                    return BuildEPTP(data);
 				case CycleType.MeasuredSpeed:
 					return BuildMeasuredSpeed(data);
 				case CycleType.MeasuredSpeedGear:
@@ -124,7 +127,40 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 			return container;
 		}
 
-		private VehicleContainer BuildMeasuredSpeed(VectoRunData data)
+        private VehicleContainer BuildEPTP(VectoRunData data)
+        {
+            if (data.Cycle.CycleType != CycleType.PWheel) {
+                throw new VectoException("CycleType must be PWheel.");
+            }
+
+            var container = new VehicleContainer(ExecutionMode.Engineering, _modData, _sumWriter) { RunData = data };
+            var gearbox = new CycleGearbox(container, data);
+
+            // PWheelCycle --> AxleGear --> Clutch --> Engine <-- Aux
+            var powertrain = new EPTPCycle(container, data.Cycle, data.AxleGearData.AxleGear.Ratio, data.VehicleData,
+                    gearbox.ModelData.Gears.ToDictionary(g => g.Key, g => g.Value.Ratio))
+                .AddComponent(new AxleGear(container, data.AxleGearData))
+                .AddComponent(data.AngledriveData != null ? new Angledrive(container, data.AngledriveData) : null)
+                .AddComponent(gearbox, data.Retarder, container)
+                .AddComponent(new Clutch(container, data.EngineData));
+            var engine = new CombustionEngine(container, data.EngineData, pt1Disabled: true);
+
+            var aux = CreateAuxiliaries(data, container);
+            aux.AddCycle(Constants.Auxiliaries.IDs.Fan, cycleEntry => {
+                var fanSpeed = cycleEntry.FanSpeed.AsRPM;
+                return (c1 * Math.Pow(fanSpeed / c2, 3) * Math.Pow(fanSpeed / c3, 5)*1000).SI<Watt>();
+            });
+
+            engine.Connect(aux.Port());
+            var idleController = GetIdleController(data.PTO, engine, container);
+
+            powertrain.AddComponent(engine, idleController);
+                //.AddAuxiliaries(container, data);
+
+            return container;
+        }
+
+        private VehicleContainer BuildMeasuredSpeed(VectoRunData data)
 		{
 			if (data.Cycle.CycleType != CycleType.MeasuredSpeed) {
 				throw new VectoException("CycleType must be MeasuredSpeed.");
@@ -272,7 +308,6 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 				container.ModalData.AddAuxiliary(Constants.Auxiliaries.IDs.PTOConsumer,
 					Constants.Auxiliaries.PowerPrefix + Constants.Auxiliaries.IDs.PTOConsumer);
 			}
-
 			return aux;
 		}
 
diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs b/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
index fe23cb39d8..be8a7420b9 100644
--- a/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
+++ b/VectoCore/VectoCore/Models/Simulation/Impl/SimulatorFactory.cs
@@ -89,14 +89,23 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 
 		private void CreateEngineeringDataReader(IInputDataProvider dataProvider)
 		{
-			var engDataProvider = ToEngineeringInputDataProvider(dataProvider);
-			if (engDataProvider.JobInputData.EngineOnlyMode) {
-				DataReader = new EngineOnlyVectoRunDataFactory(engDataProvider);
-				_engineOnlyMode = true;
-			} else {
-				DataReader = new EngineeringModeVectoRunDataFactory(engDataProvider);
-			}
-		}
+            if (dataProvider is IEPTPInputDataProvider) {
+                var eptpProvider = dataProvider as IEPTPInputDataProvider;
+                DataReader = new EngineeringEPTPModeVectoRunDataFactory(eptpProvider);
+                return;
+            }
+            if (dataProvider is IEngineeringInputDataProvider) {
+                var engDataProvider = dataProvider as IEngineeringInputDataProvider;
+                if (engDataProvider.JobInputData.EngineOnlyMode) {
+                    DataReader = new EngineOnlyVectoRunDataFactory(engDataProvider);
+                    _engineOnlyMode = true;
+                } else {
+                    DataReader = new EngineeringModeVectoRunDataFactory(engDataProvider);
+                }
+                return;
+            }
+            throw  new VectoException("Unknown InputData for Engineering Mode!");
+        }
 
 		private static IDeclarationInputDataProvider ToDeclarationInputDataProvider(IInputDataProvider dataProvider)
 		{
@@ -107,15 +116,6 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 			return declDataProvider;
 		}
 
-		private static IEngineeringInputDataProvider ToEngineeringInputDataProvider(IInputDataProvider dataProvider)
-		{
-			var engDataProvider = dataProvider as IEngineeringInputDataProvider;
-			if (engDataProvider == null) {
-				throw new VectoException("InputDataProvider does not implement Engineering interface");
-			}
-			return engDataProvider;
-		}
-
 		public bool Validate { get; set; }
 
 		public IVectoRunDataFactory DataReader { get; private set; }
@@ -203,6 +203,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
 					break;
 				case CycleType.EngineOnly:
 				case CycleType.PWheel:
+                case CycleType.EPTP:
 				case CycleType.MeasuredSpeed:
 				case CycleType.MeasuredSpeedGear:
 					run = new TimeRun(builder.Build(data));
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs
index 97df6a5a1d..e9ea12ac4d 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs
@@ -29,208 +29,215 @@
 *   Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
 */
 
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Diagnostics;
-using System.Linq;
-using TUGraz.VectoCommon.Models;
-using TUGraz.VectoCommon.Utils;
-
-namespace TUGraz.VectoCore.Models.SimulationComponent.Data
-{
-	public enum CycleType
-	{
-		EngineOnly,
-		DistanceBased,
-		PWheel,
-		MeasuredSpeed,
-		MeasuredSpeedGear,
-		PTO
-	}
-
-	public static class CycleTypeHelper
-	{
-		public static bool IsDistanceBased(this CycleType type)
-		{
-			return type == CycleType.DistanceBased;
-		}
-	}
-
-	public interface IDrivingCycleData
-	{
-		List<DrivingCycleData.DrivingCycleEntry> Entries { get; }
-		string Name { get; }
-		CycleType CycleType { get; }
-		void Finish();
-	}
-
-	[CustomValidation(typeof(DrivingCycleData), "ValidateCycleData")]
-	public class DrivingCycleData : SimulationComponentData, IDrivingCycleData
-	{
-		internal DrivingCycleData() {}
-
-		public List<DrivingCycleEntry> Entries { get; internal set; }
-
-		public string Name { get; internal set; }
-
-		public CycleType CycleType { get; internal set; }
-
-		public void Finish() {}
-
-		// ReSharper disable once UnusedMember.Global -- used by Validation
-		public static ValidationResult ValidateCycleData(DrivingCycleData cycleData, ValidationContext validationContext)
-		{
-			var mode = GetExecutionMode(validationContext);
-			if (mode == ExecutionMode.Declaration) {
-				return ValidationResult.Success;
-			}
-
-			var result = new List<string>();
-			if (cycleData.CycleType.IsDistanceBased()) {
-				var cur = cycleData.Entries[0].Distance;
-				for (var i = 1; i < cycleData.Entries.Count; i++) {
-					if (cycleData.Entries[i].Distance < cur) {
-						result.Add(
-							string.Format("distance-based cycle is not increasing strictly monotonous. entry: {0}, s_{1}: {2} s_{0}: {3}", i,
-								i - 1, cycleData.Entries[i - 1].Distance, cycleData.Entries[i].Distance));
-					}
-					cur = cycleData.Entries[i].Distance;
-				}
-			} else {
-				var cur = cycleData.Entries[0].Time;
-				for (var i = 1; i < cycleData.Entries.Count; i++) {
-					if (cycleData.Entries[i].Time < cur) {
-						result.Add(
-							string.Format("time-based cycle is not increasing strictly monotonous. entry: {0}, t_{1}: {2} t_{0}: {3}", i,
-								i - 1, cycleData.Entries[i - 1].Time, cycleData.Entries[i].Time));
-					}
-					cur = cycleData.Entries[i].Time;
-				}
-			}
-			if (result.Any()) {
-				return new ValidationResult(string.Format("Validation of Cycle {0} failed", cycleData.Name), result);
-			}
-			return ValidationResult.Success;
-		}
-
-		[DebuggerDisplay(
-			"s:{Distance}, t:{Time}, v:{VehicleTargetSpeed}, grad:{RoadGradient}, n:{AngularVelocity}, gear:{Gear}")]
-		public class DrivingCycleEntry
-		{
-			public DrivingCycleEntry() {}
-
-			public DrivingCycleEntry(DrivingCycleEntry entry)
-			{
-				Distance = entry.Distance;
-				Time = entry.Time;
-				VehicleTargetSpeed = entry.VehicleTargetSpeed;
-				RoadGradient = entry.RoadGradient;
-				Altitude = entry.Altitude;
-				StoppingTime = entry.StoppingTime;
-				AngularVelocity = entry.AngularVelocity;
-				Gear = entry.Gear;
-				AdditionalAuxPowerDemand = entry.AdditionalAuxPowerDemand;
-				AirSpeedRelativeToVehicle = entry.AirSpeedRelativeToVehicle;
-				WindYawAngle = entry.WindYawAngle;
-				Torque = entry.Torque;
-				Drag = entry.Drag;
-				PTOActive = entry.PTOActive;
-				AuxiliarySupplyPower = new Dictionary<string, Watt>(entry.AuxiliarySupplyPower);
-			}
-
-			/// <summary>
-			/// Travelled distance used for distance-based cycles. If "t" is also defined this column will be ignored.
-			/// </summary>
-			public Meter Distance;
-
-			/// <summary>
-			/// Used for time-based cycles. If neither this nor the distance. "s" is defined the data will be interpreted as 1Hz.
-			/// </summary>
-			public Second Time;
-
-			/// <summary>
-			/// Required except for Engine Only Mode calculations.
-			/// </summary>
-			public MeterPerSecond VehicleTargetSpeed;
-
-			/// <summary>
-			/// Optional.
-			/// </summary>
-			public Radian RoadGradient;
-
-			/// <summary>
-			/// [%] Optional.
-			/// </summary>
-			public Scalar RoadGradientPercent
-			{
-				get { return (Math.Tan(RoadGradient.Value()) * 100).SI<Scalar>(); }
-			}
-
-			/// <summary>
-			/// relative altitude of the driving cycle over distance
-			/// </summary>
-			public Meter Altitude;
-
-			/// <summary>
-			/// Required for distance-based cycles. Not used in time based cycles. "stop" defines the time the vehicle spends in stop phases.
-			/// </summary>
-			public Second StoppingTime;
-
-			/// <summary>
-			/// 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 Dictionary<string, Watt> AuxiliarySupplyPower;
-
-			/// <summary>
-			/// If "n_eng_avg" is defined VECTO uses that instead of the calculated engine speed value.
-			/// </summary>
-			public PerSecond AngularVelocity;
-
-			/// <summary>
-			/// [-] Gear input. Overwrites the gear shift model.
-			/// </summary>
-			public uint Gear;
-
-			/// <summary>
-			/// 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 Watt AdditionalAuxPowerDemand;
-
-			/// <summary>
-			/// Only required if Cross Wind Correction is set to Vair and Beta Input.
-			/// </summary>
-			public MeterPerSecond AirSpeedRelativeToVehicle;
-
-			/// <summary>
-			/// [°] Only required if Cross Wind Correction is set to Vair and Beta Input.
-			/// </summary>
-			public double WindYawAngle;
-
-			/// <summary>
-			/// 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 NewtonMeter Torque;
-
-			public bool Drag;
-
-			/// <summary>
-			/// Power on the Wheels (only used in PWheel Mode).
-			/// </summary>
-			public Watt PWheel;
-
-			public bool? TorqueConverterActive { get; set; }
-
-			/// <summary>
-			/// The angular velocity at the wheel. only used in PWheelCycle.
-			/// </summary>
-			public PerSecond WheelAngularVelocity;
-
-			/// <summary>
-			/// Flag if PTO Cycle is active or not.
-			/// </summary>
-			public bool PTOActive;
-		}
-	}
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Diagnostics;
+using System.Linq;
+using TUGraz.VectoCommon.Models;
+using TUGraz.VectoCommon.Utils;
+
+namespace TUGraz.VectoCore.Models.SimulationComponent.Data
+{
+	public enum CycleType
+	{
+		EngineOnly,
+		DistanceBased,
+		PWheel,
+		MeasuredSpeed,
+		MeasuredSpeedGear,
+		PTO,
+        EPTP
+	}
+
+	public static class CycleTypeHelper
+	{
+		public static bool IsDistanceBased(this CycleType type)
+		{
+			return type == CycleType.DistanceBased;
+		}
+	}
+
+	public interface IDrivingCycleData
+	{
+		List<DrivingCycleData.DrivingCycleEntry> Entries { get; }
+		string Name { get; }
+		CycleType CycleType { get; }
+		void Finish();
+	}
+
+	[CustomValidation(typeof(DrivingCycleData), "ValidateCycleData")]
+	public class DrivingCycleData : SimulationComponentData, IDrivingCycleData
+	{
+		internal DrivingCycleData() {}
+
+		public List<DrivingCycleEntry> Entries { get; internal set; }
+
+		public string Name { get; internal set; }
+
+		public CycleType CycleType { get; internal set; }
+
+		public void Finish() {}
+
+		// ReSharper disable once UnusedMember.Global -- used by Validation
+		public static ValidationResult ValidateCycleData(DrivingCycleData cycleData, ValidationContext validationContext)
+		{
+			var mode = GetExecutionMode(validationContext);
+			if (mode == ExecutionMode.Declaration) {
+				return ValidationResult.Success;
+			}
+
+			var result = new List<string>();
+			if (cycleData.CycleType.IsDistanceBased()) {
+				var cur = cycleData.Entries[0].Distance;
+				for (var i = 1; i < cycleData.Entries.Count; i++) {
+					if (cycleData.Entries[i].Distance < cur) {
+						result.Add(
+							string.Format("distance-based cycle is not increasing strictly monotonous. entry: {0}, s_{1}: {2} s_{0}: {3}", i,
+								i - 1, cycleData.Entries[i - 1].Distance, cycleData.Entries[i].Distance));
+					}
+					cur = cycleData.Entries[i].Distance;
+				}
+			} else {
+				var cur = cycleData.Entries[0].Time;
+				for (var i = 1; i < cycleData.Entries.Count; i++) {
+					if (cycleData.Entries[i].Time < cur) {
+						result.Add(
+							string.Format("time-based cycle is not increasing strictly monotonous. entry: {0}, t_{1}: {2} t_{0}: {3}", i,
+								i - 1, cycleData.Entries[i - 1].Time, cycleData.Entries[i].Time));
+					}
+					cur = cycleData.Entries[i].Time;
+				}
+			}
+			if (result.Any()) {
+				return new ValidationResult(string.Format("Validation of Cycle {0} failed", cycleData.Name), result);
+			}
+			return ValidationResult.Success;
+		}
+
+		[DebuggerDisplay(
+			"s:{Distance}, t:{Time}, v:{VehicleTargetSpeed}, grad:{RoadGradient}, n:{AngularVelocity}, gear:{Gear}")]
+		public class DrivingCycleEntry
+		{
+			public DrivingCycleEntry() {}
+
+			public DrivingCycleEntry(DrivingCycleEntry entry)
+			{
+				Distance = entry.Distance;
+				Time = entry.Time;
+				VehicleTargetSpeed = entry.VehicleTargetSpeed;
+				RoadGradient = entry.RoadGradient;
+				Altitude = entry.Altitude;
+				StoppingTime = entry.StoppingTime;
+				AngularVelocity = entry.AngularVelocity;
+				Gear = entry.Gear;
+				AdditionalAuxPowerDemand = entry.AdditionalAuxPowerDemand;
+				AirSpeedRelativeToVehicle = entry.AirSpeedRelativeToVehicle;
+				WindYawAngle = entry.WindYawAngle;
+				Torque = entry.Torque;
+				Drag = entry.Drag;
+				PTOActive = entry.PTOActive;
+				AuxiliarySupplyPower = new Dictionary<string, Watt>(entry.AuxiliarySupplyPower);
+                EngineSpeed = entry.EngineSpeed;
+                FanSpeed = entry.FanSpeed;
+            }
+
+			/// <summary>
+			/// Travelled distance used for distance-based cycles. If "t" is also defined this column will be ignored.
+			/// </summary>
+			public Meter Distance;
+
+			/// <summary>
+			/// Used for time-based cycles. If neither this nor the distance. "s" is defined the data will be interpreted as 1Hz.
+			/// </summary>
+			public Second Time;
+
+			/// <summary>
+			/// Required except for Engine Only Mode calculations.
+			/// </summary>
+			public MeterPerSecond VehicleTargetSpeed;
+
+			/// <summary>
+			/// Optional.
+			/// </summary>
+			public Radian RoadGradient;
+
+			/// <summary>
+			/// [%] Optional.
+			/// </summary>
+			public Scalar RoadGradientPercent
+			{
+				get { return (Math.Tan(RoadGradient.Value()) * 100).SI<Scalar>(); }
+			}
+
+			/// <summary>
+			/// relative altitude of the driving cycle over distance
+			/// </summary>
+			public Meter Altitude;
+
+			/// <summary>
+			/// Required for distance-based cycles. Not used in time based cycles. "stop" defines the time the vehicle spends in stop phases.
+			/// </summary>
+			public Second StoppingTime;
+
+			/// <summary>
+			/// 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 Dictionary<string, Watt> AuxiliarySupplyPower;
+
+			/// <summary>
+			/// If "n_eng_avg" is defined VECTO uses that instead of the calculated engine speed value.
+			/// </summary>
+			public PerSecond AngularVelocity;
+
+			/// <summary>
+			/// [-] Gear input. Overwrites the gear shift model.
+			/// </summary>
+			public uint Gear;
+
+			/// <summary>
+			/// 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 Watt AdditionalAuxPowerDemand;
+
+			/// <summary>
+			/// Only required if Cross Wind Correction is set to Vair and Beta Input.
+			/// </summary>
+			public MeterPerSecond AirSpeedRelativeToVehicle;
+
+			/// <summary>
+			/// [°] Only required if Cross Wind Correction is set to Vair and Beta Input.
+			/// </summary>
+			public double WindYawAngle;
+
+			/// <summary>
+			/// 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 NewtonMeter Torque;
+
+			public bool Drag;
+
+			/// <summary>
+			/// Power on the Wheels (only used in PWheel Mode).
+			/// </summary>
+			public Watt PWheel;
+
+            public bool? TorqueConverterActive;
+
+			/// <summary>
+			/// The angular velocity at the wheel. only used in PWheelCycle.
+			/// </summary>
+			public PerSecond WheelAngularVelocity;
+
+			/// <summary>
+			/// Flag if PTO Cycle is active or not.
+			/// </summary>
+			public bool PTOActive;
+
+            public PerSecond EngineSpeed;
+
+            public PerSecond FanSpeed;
+        }
+	}
 }
\ No newline at end of file
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/EngineAuxiliary.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/EngineAuxiliary.cs
index 6d6af1d390..f3c84e2500 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/EngineAuxiliary.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/EngineAuxiliary.cs
@@ -29,163 +29,163 @@
 *   Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
 */
 
-using System;
-using System.Collections.Generic;
-using TUGraz.VectoCommon.Exceptions;
-using TUGraz.VectoCommon.Utils;
-using TUGraz.VectoCore.Configuration;
-using TUGraz.VectoCore.Models.Simulation;
-using TUGraz.VectoCore.Models.Simulation.Data;
-using TUGraz.VectoCore.Models.SimulationComponent.Data;
-using TUGraz.VectoCore.OutputData;
-
-namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
-{
-	/// <summary>
-	/// Container Class for Auxiliaries which are connected to the Engine.
-	/// </summary>
-	public class EngineAuxiliary : StatefulVectoSimulationComponent<EngineAuxiliary.State>, IAuxInProvider,
-		IAuxPort
-	{
-		protected readonly Dictionary<string, Func<PerSecond, Watt>> Auxiliaries =
-			new Dictionary<string, Func<PerSecond, Watt>>();
-
-		public EngineAuxiliary(IVehicleContainer container) : base(container) {}
-
-		public IAuxPort Port()
-		{
-			return this;
-		}
-
-		/// <summary>
-		/// Adds a constant power demand auxiliary.
-		/// </summary>
-		/// <param name="auxId"></param>
-		/// <param name="powerDemand"></param>
-		public void AddConstant(string auxId, Watt powerDemand)
-		{
-			Add(auxId, _ => powerDemand);
-		}
-
-		/// <summary>
-		/// Adds an auxiliary which gets its power demand from the driving cycle.
-		/// </summary>
-		/// <param name="auxId"></param>
-		public void AddCycle(string auxId)
-		{
-			Add(auxId, _ => DataBus.CycleData.LeftSample.AdditionalAuxPowerDemand);
-		}
-
-		/// <summary>
-		/// Adds an auxiliary which calculates the demand based on a aux-map and the engine speed.
-		/// </summary>
-		/// <param name="auxId"></param>
-		/// <param name="data"></param>
-		public void AddMapping(string auxId, AuxiliaryData data)
-		{
-			if (!DataBus.CycleData.LeftSample.AuxiliarySupplyPower.ContainsKey(auxId)) {
-				var error = string.Format("driving cycle does not contain column for auxiliary: {0}",
-					Constants.Auxiliaries.Prefix + auxId);
-				Log.Error(error);
-				throw new VectoException(error);
-			}
-
-			Add(auxId, speed => {
-				var powerSupply = DataBus.CycleData.LeftSample.AuxiliarySupplyPower[auxId];
-				var nAuxiliary = speed * data.TransmissionRatio;
-				var powerAuxOut = powerSupply / data.EfficiencyToSupply;
-				var powerAuxIn = data.GetPowerDemand(nAuxiliary, powerAuxOut);
-				return powerAuxIn / data.EfficiencyToEngine;
-			});
-		}
-
-		/// <summary>
-		/// Adds an auxiliary with a function returning the power demand based on the engine speed.
-		/// </summary>
-		/// <param name="auxId"></param>
-		/// <param name="powerLossFunction"></param>
-		public void Add(string auxId, Func<PerSecond, Watt> powerLossFunction)
-		{
-			Auxiliaries[auxId] = powerLossFunction;
-		}
-
-		public NewtonMeter Initialize(NewtonMeter torque, PerSecond angularSpeed)
-		{
-			PreviousState.AngularSpeed = angularSpeed;
-			if (angularSpeed.IsEqual(0)) {
-				return 0.SI<NewtonMeter>();
-			}
-
-			return ComputePowerDemand(angularSpeed, false) / angularSpeed;
-		}
-
-		/// <summary>
-		/// Calculates the torque demand for all registered auxiliaries for the the current engine 
-		/// </summary>
-		/// <param name="absTime"></param>
-		/// <param name="dt"></param>
-		/// <param name="torquePowerTrain"></param>
-		/// <param name="torqueEngine"></param>
-		/// <param name="angularSpeed"></param>
-		/// <param name="dryRun"></param>
-		/// <returns></returns>
-		public NewtonMeter TorqueDemand(Second absTime, Second dt, NewtonMeter torquePowerTrain, NewtonMeter torqueEngine,
-			PerSecond angularSpeed, bool dryRun = false)
-		{
-			var avgAngularSpeed = PreviousState.AngularSpeed != null
-				? (angularSpeed + PreviousState.AngularSpeed) / 2.0
-				: angularSpeed;
-			if (!dryRun) {
-				CurrentState.AngularSpeed = angularSpeed;
-			}
-			if (avgAngularSpeed.IsGreater(0)) {
-				return ComputePowerDemand(avgAngularSpeed, dryRun) / avgAngularSpeed;
-			}
-			return 0.SI<NewtonMeter>();
-		}
-
-		protected Watt ComputePowerDemand(PerSecond engineSpeed, bool dryRun)
-		{
-			var powerDemands = new Dictionary<string, Watt>(Auxiliaries.Count);
-			foreach (var item in Auxiliaries) {
-				var value = item.Value(engineSpeed);
-				if (value != null) {
-					powerDemands[item.Key] = value;
-				}
-			}
-			if (!dryRun) {
-				CurrentState.PowerDemands = powerDemands;
-			}
-			return powerDemands.Sum(kv => kv.Value);
-		}
-
-		protected override void DoWriteModalResults(IModalDataContainer container)
-		{
-			var auxPowerDemand = 0.SI<Watt>();
-			if (CurrentState.PowerDemands != null) {
-				foreach (var kv in CurrentState.PowerDemands) {
-					container[kv.Key] = kv.Value;
-					// mk 2016-10-11: pto's should not be counted in sum auxiliary power demand
-					if (kv.Key != Constants.Auxiliaries.IDs.PTOTransmission && kv.Key != Constants.Auxiliaries.IDs.PTOConsumer) {
-						auxPowerDemand += kv.Value;
-					}
-				}
-			}
-			if (container[ModalResultField.P_aux] == null || container[ModalResultField.P_aux] == DBNull.Value) {
-				// only overwrite if nobody else already wrote the total aux power
-				container[ModalResultField.P_aux] = auxPowerDemand;
-			}
-		}
-
-		protected override void DoCommitSimulationStep()
-		{
-			AdvanceState();
-		}
-
-		public class State
-		{
-			public PerSecond AngularSpeed;
-			public Dictionary<string, Watt> PowerDemands;
-		}
-	}
+using System;
+using System.Collections.Generic;
+using TUGraz.VectoCommon.Exceptions;
+using TUGraz.VectoCommon.Utils;
+using TUGraz.VectoCore.Configuration;
+using TUGraz.VectoCore.Models.Simulation;
+using TUGraz.VectoCore.Models.Simulation.Data;
+using TUGraz.VectoCore.Models.SimulationComponent.Data;
+using TUGraz.VectoCore.OutputData;
+
+namespace TUGraz.VectoCore.Models.SimulationComponent.Impl
+{
+	/// <summary>
+	/// Container Class for Auxiliaries which are connected to the Engine.
+	/// </summary>
+	public class EngineAuxiliary : StatefulVectoSimulationComponent<EngineAuxiliary.State>, IAuxInProvider,
+		IAuxPort
+	{
+		protected readonly Dictionary<string, Func<PerSecond, Watt>> Auxiliaries =
+			new Dictionary<string, Func<PerSecond, Watt>>();
+
+		public EngineAuxiliary(IVehicleContainer container) : base(container) {}
+
+		public IAuxPort Port()
+		{
+			return this;
+		}
+
+		/// <summary>
+		/// Adds a constant power demand auxiliary.
+		/// </summary>
+		/// <param name="auxId"></param>
+		/// <param name="powerDemand"></param>
+		public void AddConstant(string auxId, Watt powerDemand)
+		{
+			Add(auxId, _ => powerDemand);
+		}
+
+		/// <summary>
+		/// Adds an auxiliary which gets its power demand from the driving cycle.
+		/// </summary>
+		/// <param name="auxId"></param>
+		public void AddCycle(string auxId)
+		{
+			Add(auxId, _ => DataBus.CycleData.LeftSample.AdditionalAuxPowerDemand);
+		}
+
+		/// <summary>
+		/// Adds an auxiliary which calculates the demand based on a aux-map and the engine speed.
+		/// </summary>
+		/// <param name="auxId"></param>
+		/// <param name="data"></param>
+		public void AddMapping(string auxId, AuxiliaryData data)
+		{
+			if (!DataBus.CycleData.LeftSample.AuxiliarySupplyPower.ContainsKey(auxId)) {
+				var error = string.Format("driving cycle does not contain column for auxiliary: {0}",
+					Constants.Auxiliaries.Prefix + auxId);
+				Log.Error(error);
+				throw new VectoException(error);
+			}
+
+			Add(auxId, speed => {
+				var powerSupply = DataBus.CycleData.LeftSample.AuxiliarySupplyPower[auxId];
+				var nAuxiliary = speed * data.TransmissionRatio;
+				var powerAuxOut = powerSupply / data.EfficiencyToSupply;
+				var powerAuxIn = data.GetPowerDemand(nAuxiliary, powerAuxOut);
+				return powerAuxIn / data.EfficiencyToEngine;
+			});
+		}
+
+		/// <summary>
+		/// Adds an auxiliary with a function returning the power demand based on the engine speed.
+		/// </summary>
+		/// <param name="auxId"></param>
+		/// <param name="powerLossFunction"></param>
+		public void Add(string auxId, Func<PerSecond, Watt> powerLossFunction)
+		{
+			Auxiliaries[auxId] = powerLossFunction;
+		}
+
+		public NewtonMeter Initialize(NewtonMeter torque, PerSecond angularSpeed)
+		{
+			PreviousState.AngularSpeed = angularSpeed;
+			if (angularSpeed.IsEqual(0)) {
+				return 0.SI<NewtonMeter>();
+			}
+
+			return ComputePowerDemand(angularSpeed, false) / angularSpeed;
+		}
+
+		/// <summary>
+		/// Calculates the torque demand for all registered auxiliaries for the the current engine 
+		/// </summary>
+		/// <param name="absTime"></param>
+		/// <param name="dt"></param>
+		/// <param name="torquePowerTrain"></param>
+		/// <param name="torqueEngine"></param>
+		/// <param name="angularSpeed"></param>
+		/// <param name="dryRun"></param>
+		/// <returns></returns>
+		public NewtonMeter TorqueDemand(Second absTime, Second dt, NewtonMeter torquePowerTrain, NewtonMeter torqueEngine,
+			PerSecond angularSpeed, bool dryRun = false)
+		{
+			var avgAngularSpeed = PreviousState.AngularSpeed != null
+				? (angularSpeed + PreviousState.AngularSpeed) / 2.0
+				: angularSpeed;
+			if (!dryRun) {
+				CurrentState.AngularSpeed = angularSpeed;
+			}
+			if (avgAngularSpeed.IsGreater(0)) {
+				return ComputePowerDemand(avgAngularSpeed, dryRun) / avgAngularSpeed;
+			}
+			return 0.SI<NewtonMeter>();
+		}
+
+		protected Watt ComputePowerDemand(PerSecond engineSpeed, bool dryRun)
+		{
+			var powerDemands = new Dictionary<string, Watt>(Auxiliaries.Count);
+			foreach (var item in Auxiliaries) {
+				var value = item.Value(engineSpeed);
+				if (value != null) {
+					powerDemands[item.Key] = value;
+				}
+			}
+			if (!dryRun) {
+				CurrentState.PowerDemands = powerDemands;
+			}
+			return powerDemands.Sum(kv => kv.Value);
+		}
+
+		protected override void DoWriteModalResults(IModalDataContainer container)
+		{
+			var auxPowerDemand = 0.SI<Watt>();
+			if (CurrentState.PowerDemands != null) {
+				foreach (var kv in CurrentState.PowerDemands) {
+					container[kv.Key] = kv.Value;
+					// mk 2016-10-11: pto's should not be counted in sum auxiliary power demand
+					if (kv.Key != Constants.Auxiliaries.IDs.PTOTransmission && kv.Key != Constants.Auxiliaries.IDs.PTOConsumer) {
+						auxPowerDemand += kv.Value;
+					}
+				}
+			}
+			if (container[ModalResultField.P_aux] == null || container[ModalResultField.P_aux] == DBNull.Value) {
+				// only overwrite if nobody else already wrote the total aux power
+				container[ModalResultField.P_aux] = auxPowerDemand;
+			}
+		}
+
+		protected override void DoCommitSimulationStep()
+		{
+			AdvanceState();
+		}
+
+		public class State
+		{
+			public PerSecond AngularSpeed;
+			public Dictionary<string, Watt> PowerDemands;
+		}
+	}
 }
\ No newline at end of file
diff --git a/VectoCore/VectoCore/VectoCore.csproj b/VectoCore/VectoCore/VectoCore.csproj
index d705d3cab6..271776d82b 100644
--- a/VectoCore/VectoCore/VectoCore.csproj
+++ b/VectoCore/VectoCore/VectoCore.csproj
@@ -186,6 +186,7 @@
     <Compile Include="Models\SimulationComponent\Impl\TorqueConverter.cs" />
     <Compile Include="Models\SimulationComponent\Impl\IdleControllerSwitcher.cs" />
     <Compile Include="Models\Simulation\Data\ModalResultField.cs" />
+    <Compile Include="InputData\Reader\Impl\EngineeringEPTPModeVectoRunDataFactory.cs" />
     <Compile Include="OutputData\ModFilter\ActualModalDataFilter.cs" />
     <Compile Include="OutputData\ModFilter\ModalData1HzFilter.cs" />
     <Compile Include="OutputData\XML\AbstractXMLWriter.cs" />
diff --git a/VectoCore/VectoCoreTest/Integration/EPTP/EPTPTest.cs b/VectoCore/VectoCoreTest/Integration/EPTP/EPTPTest.cs
new file mode 100644
index 0000000000..67f0d53702
--- /dev/null
+++ b/VectoCore/VectoCoreTest/Integration/EPTP/EPTPTest.cs
@@ -0,0 +1,37 @@
+using NUnit.Framework;
+using TUGraz.VectoCommon.Models;
+using TUGraz.VectoCore.InputData.FileIO.JSON;
+using TUGraz.VectoCore.Models.Simulation.Impl;
+using TUGraz.VectoCore.OutputData;
+using TUGraz.VectoCore.OutputData.FileIO;
+
+namespace TUGraz.VectoCore.Tests.Integration.EPTP
+{
+    [TestFixture]
+    public class EPTPTest
+    {
+        [TestCase()]
+        public void RunEPTP()
+        {
+            var jobFile = @"E:\QUAM\Workspace\VECTO_quam\EPTP\MAN_EPTP.vecto";
+
+            var fileWriter = new FileOutputWriter(jobFile);
+            var sumWriter = new SummaryDataContainer(fileWriter);
+            var jobContainer = new JobContainer(sumWriter);
+            var dataProvider = JSONInputDataFactory.ReadJsonJob(jobFile);
+            var runsFactory = new SimulatorFactory(ExecutionMode.Engineering, dataProvider, fileWriter) {
+                ModalResults1Hz = false,
+                WriteModalResults = true,
+                ActualModalData = false,
+                Validate = false,
+            };
+            
+            jobContainer.AddRuns(runsFactory);
+            jobContainer.Execute();
+
+            Assert.AreEqual(true, jobContainer.AllCompleted);
+            
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/VectoCore/VectoCoreTest/VectoCoreTest.csproj b/VectoCore/VectoCoreTest/VectoCoreTest.csproj
index d68ab97396..06dec001cf 100644
--- a/VectoCore/VectoCoreTest/VectoCoreTest.csproj
+++ b/VectoCore/VectoCoreTest/VectoCoreTest.csproj
@@ -83,6 +83,7 @@
     <Compile Include="Integration\CoachAdvancedAuxPowertrain.cs" />
     <Compile Include="Integration\CoachPowerTrain.cs" />
     <Compile Include="Integration\DriverStrategy\SimpleCycles.cs" />
+    <Compile Include="Integration\EPTP\EPTPTest.cs" />
     <Compile Include="Integration\FuelTypesTest.cs" />
     <Compile Include="Integration\FullCycleDeclarationTest.cs">
       <SubType>Code</SubType>
-- 
GitLab