From c97f818949efc9b28c50d17fafc06409a622cf02 Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Tue, 4 Sep 2018 14:12:54 +0200
Subject: [PATCH] Set model name and digest value for torque converter data
 (missing in manufacturer report)

---
 ...LDeclarationTorqueConverterDataProvider.cs |  56 +-
 .../TorqueConverterDataReader.cs              | 180 +++---
 .../DeclarationDataAdapter.cs                 |   2 +
 .../Data/Gearbox/TorqueConverterData.cs       | 520 +++++++++---------
 4 files changed, 380 insertions(+), 378 deletions(-)

diff --git a/VectoCore/VectoCore/InputData/FileIO/XML/Declaration/XMLDeclarationTorqueConverterDataProvider.cs b/VectoCore/VectoCore/InputData/FileIO/XML/Declaration/XMLDeclarationTorqueConverterDataProvider.cs
index 0a2e3bf652..71b7780912 100644
--- a/VectoCore/VectoCore/InputData/FileIO/XML/Declaration/XMLDeclarationTorqueConverterDataProvider.cs
+++ b/VectoCore/VectoCore/InputData/FileIO/XML/Declaration/XMLDeclarationTorqueConverterDataProvider.cs
@@ -29,32 +29,32 @@
 *   Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
 */
 
-using TUGraz.IVT.VectoXML;
-using TUGraz.VectoCommon.InputData;
-using TUGraz.VectoCommon.Resources;
-
-namespace TUGraz.VectoCore.InputData.FileIO.XML.Declaration
-{
-	public class XMLDeclarationTorqueConverterDataProvider : AbstractDeclarationXMLComponentDataProvider,
-		ITorqueConverterDeclarationInputData
-	{
-		public XMLDeclarationTorqueConverterDataProvider(XMLDeclarationInputDataProvider xmlInputDataProvider)
-			: base(xmlInputDataProvider)
-		{
-			XBasePath = Helper.Query(VehiclePath,
-				XMLNames.Vehicle_Components,
-				XMLNames.Component_Gearbox,
-				XMLNames.Component_TorqueConverter,
-				XMLNames.ComponentDataWrapper);
-		}
-
-		public TableData TCData
-		{
-			get
-			{
-				return ReadTableData(AttributeMappings.TorqueConverterDataMapping,
-					Helper.Query(XMLNames.TorqueConverter_Characteristics, XMLNames.TorqueConverter_Characteristics_Entry));
-			}
-		}
-	}
+using TUGraz.IVT.VectoXML;
+using TUGraz.VectoCommon.InputData;
+using TUGraz.VectoCommon.Resources;
+
+namespace TUGraz.VectoCore.InputData.FileIO.XML.Declaration
+{
+	public class XMLDeclarationTorqueConverterDataProvider : AbstractDeclarationXMLComponentDataProvider,
+		ITorqueConverterDeclarationInputData
+	{
+		public XMLDeclarationTorqueConverterDataProvider(XMLDeclarationInputDataProvider xmlInputDataProvider)
+			: base(xmlInputDataProvider)
+		{
+			XBasePath = Helper.Query(VehiclePath,
+				XMLNames.Vehicle_Components,
+				XMLNames.Component_Gearbox,
+				XMLNames.Component_TorqueConverter,
+				XMLNames.ComponentDataWrapper);
+		}
+
+		public TableData TCData
+		{
+			get
+			{
+				return ReadTableData(AttributeMappings.TorqueConverterDataMapping,
+					Helper.Query(XMLNames.TorqueConverter_Characteristics, XMLNames.TorqueConverter_Characteristics_Entry));
+			}
+		}
+	}
 }
\ No newline at end of file
diff --git a/VectoCore/VectoCore/InputData/Reader/ComponentData/TorqueConverterDataReader.cs b/VectoCore/VectoCore/InputData/Reader/ComponentData/TorqueConverterDataReader.cs
index d08185fda8..992f3d477b 100644
--- a/VectoCore/VectoCore/InputData/Reader/ComponentData/TorqueConverterDataReader.cs
+++ b/VectoCore/VectoCore/InputData/Reader/ComponentData/TorqueConverterDataReader.cs
@@ -29,94 +29,94 @@
 *   Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
 */
 
-using System.Collections.Generic;
-using System.Data;
-using System.IO;
-using System.Linq;
-using TUGraz.VectoCommon.Exceptions;
-using TUGraz.VectoCommon.Models;
-using TUGraz.VectoCommon.Utils;
-using TUGraz.VectoCore.Models.Declaration;
-using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox;
-using TUGraz.VectoCore.Utils;
-
-namespace TUGraz.VectoCore.InputData.Reader.ComponentData
-{
-	public static class TorqueConverterDataReader
-	{
-		public static TorqueConverterData ReadFromFile(string filename, PerSecond referenceRpm, PerSecond maxRpm,
-			ExecutionMode mode, double ratio, MeterPerSquareSecond lcMinAcceleration, MeterPerSquareSecond ccMinAcceleration)
-		{
-			return Create(VectoCSVFile.Read(filename), referenceRpm, maxRpm, mode, ratio, lcMinAcceleration, ccMinAcceleration);
-		}
-
-		public static TorqueConverterData ReadFromStream(Stream stream, PerSecond referenceRpm, PerSecond maxRpm,
-			ExecutionMode mode, double ratio, MeterPerSquareSecond lcMinAcceleration, MeterPerSquareSecond ccMinAcceleration)
-		{
-			return Create(VectoCSVFile.ReadStream(stream), referenceRpm, maxRpm, mode, ratio, lcMinAcceleration,
-				ccMinAcceleration);
-		}
-
-		public static TorqueConverterData Create(DataTable data, PerSecond referenceRpm, PerSecond maxRpm, ExecutionMode mode,
-			double ratio, MeterPerSquareSecond lcMinAcceleration, MeterPerSquareSecond ccMinAcceleration)
-		{
-			if (data == null) {
-				throw new VectoException("TorqueConverter Characteristics data is missing.");
-			}
-
-			if (data.Columns.Count != 3) {
-				throw new VectoException("TorqueConverter Characteristics data must consist of 3 columns");
-			}
-			if (data.Rows.Count < 2) {
-				throw new VectoException("TorqueConverter Characteristics data must contain at least 2 lines with numeric values");
-			}
-
-			IEnumerable<TorqueConverterEntry> characteristicTorque;
-			if (HeaderIsValid(data.Columns)) {
-				characteristicTorque = (from DataRow row in data.Rows
-					select
-						new TorqueConverterEntry() {
-							SpeedRatio = row.ParseDouble(Fields.SpeedRatio),
-							Torque = row.ParseDouble(Fields.CharacteristicTorque).SI<NewtonMeter>(),
-							TorqueRatio = row.ParseDouble(Fields.TorqueRatio)
-						}).ToArray();
-			} else {
-				characteristicTorque = (from DataRow row in data.Rows
-					select
-						new TorqueConverterEntry() {
-							SpeedRatio = row.ParseDouble(0),
-							Torque = row.ParseDouble(2).SI<NewtonMeter>(),
-							TorqueRatio = row.ParseDouble(1)
-						}).ToArray();
-			}
-			if (mode == ExecutionMode.Declaration) {
-				var tcDrag = DeclarationData.TorqueConverter.GetTorqueConverterDragCurve(ratio).Last();
-				characteristicTorque =
-					characteristicTorque.Where(x => x.SpeedRatio < tcDrag.SpeedRatio)
-						.Concat(new[] { tcDrag })
-						.ToArray();
-			} else {
-				if (!characteristicTorque.Any(x => x.SpeedRatio > ratio)) {
-					characteristicTorque =
-						characteristicTorque.Where(x => x.SpeedRatio < ratio)
-							.Concat(DeclarationData.TorqueConverter.GetTorqueConverterDragCurve(ratio))
-							.ToArray();
-				}
-			}
-			return new TorqueConverterData(characteristicTorque, referenceRpm, maxRpm, lcMinAcceleration, ccMinAcceleration);
-		}
-
-		private static bool HeaderIsValid(DataColumnCollection columns)
-		{
-			return columns.Contains(Fields.SpeedRatio) && columns.Contains(Fields.TorqueRatio) &&
-					columns.Contains(Fields.CharacteristicTorque);
-		}
-
-		public static class Fields
-		{
-			public const string SpeedRatio = "Speed Ratio";
-			public const string TorqueRatio = "Torque Ratio";
-			public const string CharacteristicTorque = "MP1000";
-		}
-	}
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using System.Linq;
+using TUGraz.VectoCommon.Exceptions;
+using TUGraz.VectoCommon.Models;
+using TUGraz.VectoCommon.Utils;
+using TUGraz.VectoCore.Models.Declaration;
+using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox;
+using TUGraz.VectoCore.Utils;
+
+namespace TUGraz.VectoCore.InputData.Reader.ComponentData
+{
+	public static class TorqueConverterDataReader
+	{
+		public static TorqueConverterData ReadFromFile(string filename, PerSecond referenceRpm, PerSecond maxRpm,
+			ExecutionMode mode, double ratio, MeterPerSquareSecond lcMinAcceleration, MeterPerSquareSecond ccMinAcceleration)
+		{
+			return Create(VectoCSVFile.Read(filename), referenceRpm, maxRpm, mode, ratio, lcMinAcceleration, ccMinAcceleration);
+		}
+
+		public static TorqueConverterData ReadFromStream(Stream stream, PerSecond referenceRpm, PerSecond maxRpm,
+			ExecutionMode mode, double ratio, MeterPerSquareSecond lcMinAcceleration, MeterPerSquareSecond ccMinAcceleration)
+		{
+			return Create(VectoCSVFile.ReadStream(stream), referenceRpm, maxRpm, mode, ratio, lcMinAcceleration,
+				ccMinAcceleration);
+		}
+
+		public static TorqueConverterData Create(DataTable data, PerSecond referenceRpm, PerSecond maxRpm, ExecutionMode mode,
+			double ratio, MeterPerSquareSecond lcMinAcceleration, MeterPerSquareSecond ccMinAcceleration)
+		{
+			if (data == null) {
+				throw new VectoException("TorqueConverter Characteristics data is missing.");
+			}
+
+			if (data.Columns.Count != 3) {
+				throw new VectoException("TorqueConverter Characteristics data must consist of 3 columns");
+			}
+			if (data.Rows.Count < 2) {
+				throw new VectoException("TorqueConverter Characteristics data must contain at least 2 lines with numeric values");
+			}
+
+			IEnumerable<TorqueConverterEntry> characteristicTorque;
+			if (HeaderIsValid(data.Columns)) {
+				characteristicTorque = (from DataRow row in data.Rows
+					select
+						new TorqueConverterEntry() {
+							SpeedRatio = row.ParseDouble(Fields.SpeedRatio),
+							Torque = row.ParseDouble(Fields.CharacteristicTorque).SI<NewtonMeter>(),
+							TorqueRatio = row.ParseDouble(Fields.TorqueRatio)
+						}).ToArray();
+			} else {
+				characteristicTorque = (from DataRow row in data.Rows
+					select
+						new TorqueConverterEntry() {
+							SpeedRatio = row.ParseDouble(0),
+							Torque = row.ParseDouble(2).SI<NewtonMeter>(),
+							TorqueRatio = row.ParseDouble(1)
+						}).ToArray();
+			}
+			if (mode == ExecutionMode.Declaration) {
+				var tcDrag = DeclarationData.TorqueConverter.GetTorqueConverterDragCurve(ratio).Last();
+				characteristicTorque =
+					characteristicTorque.Where(x => x.SpeedRatio < tcDrag.SpeedRatio)
+						.Concat(new[] { tcDrag })
+						.ToArray();
+			} else {
+				if (!characteristicTorque.Any(x => x.SpeedRatio > ratio)) {
+					characteristicTorque =
+						characteristicTorque.Where(x => x.SpeedRatio < ratio)
+							.Concat(DeclarationData.TorqueConverter.GetTorqueConverterDragCurve(ratio))
+							.ToArray();
+				}
+			}
+			return new TorqueConverterData(characteristicTorque, referenceRpm, maxRpm, lcMinAcceleration, ccMinAcceleration);
+		}
+
+		private static bool HeaderIsValid(DataColumnCollection columns)
+		{
+			return columns.Contains(Fields.SpeedRatio) && columns.Contains(Fields.TorqueRatio) &&
+					columns.Contains(Fields.CharacteristicTorque);
+		}
+
+		public static class Fields
+		{
+			public const string SpeedRatio = "Speed Ratio";
+			public const string TorqueRatio = "Torque Ratio";
+			public const string CharacteristicTorque = "MP1000";
+		}
+	}
 }
\ No newline at end of file
diff --git a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/DeclarationDataAdapter.cs b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/DeclarationDataAdapter.cs
index ea986e9a0f..6226e3e6c8 100644
--- a/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/DeclarationDataAdapter.cs
+++ b/VectoCore/VectoCore/InputData/Reader/DataObjectAdapter/DeclarationDataAdapter.cs
@@ -255,6 +255,8 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdapter
 					DeclarationData.TorqueConverter.ReferenceRPM, DeclarationData.TorqueConverter.MaxInputSpeed,
 					ExecutionMode.Declaration, ratio,
 					DeclarationData.TorqueConverter.CLUpshiftMinAcceleration, DeclarationData.TorqueConverter.CCUpshiftMinAcceleration);
+				retVal.TorqueConverterData.ModelName = gearbox.TorqueConverter.Model;
+				retVal.TorqueConverterData.DigestValueInput = gearbox.TorqueConverter.DigestValue.DigestValue;
 			}
 
 			return retVal;
diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TorqueConverterData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TorqueConverterData.cs
index 18b7a47d7e..dc1f7bc368 100644
--- a/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TorqueConverterData.cs
+++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TorqueConverterData.cs
@@ -29,264 +29,264 @@
 *   Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
 */
 
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Linq;
-using TUGraz.VectoCommon.Exceptions;
-using TUGraz.VectoCommon.Utils;
-
-namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox
-{
-	[CustomValidation(typeof(TorqueConverterData), "ValidateData")]
-	public class TorqueConverterData : SimulationComponentData
-	{
-		protected internal readonly TorqueConverterEntry[] TorqueConverterEntries;
-
-		[Required, SIRange(0, double.MaxValue)]
-		public PerSecond ReferenceSpeed { get; protected internal set; }
-
-		[Required, SIRange(0, double.MaxValue)]
-		public MeterPerSquareSecond CLUpshiftMinAcceleration { get; internal set; }
-
-		[Required, SIRange(0, double.MaxValue)]
-		public MeterPerSquareSecond CCUpshiftMinAcceleration { get; internal set; }
-
-		[Required, SIRange(0, double.MaxValue)]
-		public PerSecond TorqueConverterSpeedLimit { get; protected internal set; }
-
-		internal double RequiredSpeedRatio; // only used for validation!
-
-		protected internal TorqueConverterData(IEnumerable<TorqueConverterEntry> torqueConverterEntries,
-			PerSecond referenceSpeed, PerSecond maxRpm, MeterPerSquareSecond clUpshiftMinAcceleration,
-			MeterPerSquareSecond ccUpshiftMinAcceleration)
-		{
-			TorqueConverterEntries = torqueConverterEntries.ToArray();
-			ReferenceSpeed = referenceSpeed;
-			TorqueConverterSpeedLimit = maxRpm;
-			CLUpshiftMinAcceleration = clUpshiftMinAcceleration;
-			CCUpshiftMinAcceleration = ccUpshiftMinAcceleration;
-		}
-
-		/// <summary>
-		/// find an operating point for the torque converter
-		/// 
-		/// find the input speed and input torque for the given output torque and output speed. 
-		/// </summary>
-		/// <param name="torqueOut">torque provided at the TC output</param>
-		/// <param name="angularSpeedOut">angular speed at the TC output</param>
-		/// <param name="minSpeed"></param>
-		/// <returns></returns>
-		public IList<TorqueConverterOperatingPoint> FindOperatingPoint(NewtonMeter torqueOut, PerSecond angularSpeedOut,
-			PerSecond minSpeed)
-		{
-			var solutions = new List<double>();
-			var mpNorm = ReferenceSpeed.Value();
-
-			var min = minSpeed == null ? 0 : minSpeed.Value();
-
-			// Find analytic solution for torque converter operating point
-			// mu = f(nu) = f(n_out / n_in) = T_out / T_in
-			// MP1000 = f(nu) = f(n_out / n_in)
-			// Tq_in = MP1000(nu) * (n_in/1000)^2 = T_out / mu
-			//
-			// mu(nu) and MP1000(nu) are provided as piecewise linear functions (y = k*x+d)
-			// solving the equation above for n_in results in a quadratic equation
-			foreach (var segment in TorqueConverterEntries.Pairwise(Tuple.Create)) {
-				var mpEdge = Edge.Create(new Point(segment.Item1.SpeedRatio, segment.Item1.Torque.Value()),
-					new Point(segment.Item2.SpeedRatio, segment.Item2.Torque.Value()));
-				var muEdge = Edge.Create(new Point(segment.Item1.SpeedRatio, segment.Item1.TorqueRatio),
-					new Point(segment.Item2.SpeedRatio, segment.Item2.TorqueRatio));
-
-				var a = muEdge.OffsetXY * mpEdge.OffsetXY / (mpNorm * mpNorm);
-				var b = angularSpeedOut.Value() * (muEdge.SlopeXY * mpEdge.OffsetXY + mpEdge.SlopeXY * muEdge.OffsetXY) /
-						(mpNorm * mpNorm);
-				var c = angularSpeedOut.Value() * angularSpeedOut.Value() * mpEdge.SlopeXY * muEdge.SlopeXY / (mpNorm * mpNorm) -
-						torqueOut.Value();
-
-				solutions.AddRange(VectoMath.QuadraticEquationSolver(a, b, c).Where(x => {
-					var ratio = angularSpeedOut.Value() / x;
-					return x > min && muEdge.P1.X <= ratio && ratio < muEdge.P2.X;
-				}));
-			}
-
-			if (solutions.Count == 0) {
-				Log.Debug(
-					"TorqueConverterData::FindOperatingPoint No solution for input torque/input speed found! n_out: {0}, tq_out: {1}",
-					angularSpeedOut, torqueOut);
-			}
-
-			return solutions.Select(sol => {
-				var s = sol.SI<PerSecond>();
-				var mu = MuLookup(angularSpeedOut / s);
-				return new TorqueConverterOperatingPoint {
-					OutTorque = torqueOut,
-					OutAngularVelocity = angularSpeedOut,
-					InAngularVelocity = s,
-					SpeedRatio = angularSpeedOut / s,
-					TorqueRatio = mu,
-					InTorque = torqueOut / mu,
-				};
-			}).ToList();
-		}
-
-		/// <summary>
-		/// find an operating point for the torque converter
-		/// 
-		/// find the input torque and output torque for the given input and output speeds.
-		/// Computes the speed ratio nu of input and output. Interpolates MP1000 and mu, Computes input torque and output torque
-		/// </summary>
-		/// <param name="inAngularVelocity">speed at the input of the TC</param>
-		/// <param name="outAngularVelocity">speed at the output of the TC</param>
-		/// <returns></returns>
-		public TorqueConverterOperatingPoint FindOperatingPoint(PerSecond inAngularVelocity, PerSecond outAngularVelocity)
-		{
-			var retVal = new TorqueConverterOperatingPoint {
-				InAngularVelocity = inAngularVelocity,
-				OutAngularVelocity = outAngularVelocity,
-				SpeedRatio = outAngularVelocity.Value() / inAngularVelocity.Value(),
-			};
-
-			foreach (var segment in TorqueConverterEntries.Pairwise()) {
-				if (retVal.SpeedRatio.IsBetween(segment.Item1.SpeedRatio, segment.Item2.SpeedRatio)) {
-					var mpTorque = segment.Interpolate(x => x.SpeedRatio, y => y.Torque, retVal.SpeedRatio);
-					retVal.TorqueRatio = segment.Interpolate(x => x.SpeedRatio, y => y.TorqueRatio, retVal.SpeedRatio);
-					retVal.InTorque = mpTorque * (inAngularVelocity * inAngularVelocity / ReferenceSpeed / ReferenceSpeed).Value();
-					retVal.OutTorque = retVal.InTorque * retVal.TorqueRatio;
-					return retVal;
-				}
-			}
-
-			// No solution found. Throw Error
-			var nu = outAngularVelocity / inAngularVelocity;
-			var nuMax = TorqueConverterEntries.Last().SpeedRatio;
-
-			if (nu.IsGreater(nuMax)) {
-				throw new VectoException(
-					"Torque Converter: Range of torque converter data is not sufficient. Required nu: {0}, Got nu_max: {1}", nu, nuMax);
-			}
-
-			throw new VectoException(
-				"Torque Converter: No solution for output speed/input speed found! n_out: {0}, n_in: {1}, nu: {2}, nu_max: {3}",
-				outAngularVelocity, inAngularVelocity, nu, nuMax);
-		}
-
-		/// <summary>
-		/// find an operating point for the torque converter
-		/// 
-		/// find the output torque and output speed for the given input torque and input speed
-		/// </summary>
-		/// <param name="inTorque"></param>
-		/// <param name="inAngularVelocity"></param>
-		/// <param name="outAngularSpeedEstimated"></param>
-		/// <returns></returns>
-		public TorqueConverterOperatingPoint FindOperatingPointForward(NewtonMeter inTorque, PerSecond inAngularVelocity,
-			PerSecond outAngularSpeedEstimated)
-		{
-			var referenceTorque = inTorque.Value() / inAngularVelocity.Value() / inAngularVelocity.Value() *
-								ReferenceSpeed.Value() * ReferenceSpeed.Value();
-			var maxTorque = TorqueConverterEntries.Max(x => x.Torque.Value());
-			if (referenceTorque.IsGreaterOrEqual(maxTorque)) {
-				referenceTorque = outAngularSpeedEstimated != null
-					? ReferenceTorqueLookup(outAngularSpeedEstimated / inAngularVelocity).Value()
-					: 0.9 * maxTorque;
-			}
-
-			var solutions = new List<double>();
-			foreach (var edge in TorqueConverterEntries.Pairwise(
-				(p1, p2) => Edge.Create(new Point(p1.SpeedRatio, p1.Torque.Value()), new Point(p2.SpeedRatio, p2.Torque.Value())))) {
-				var x = (referenceTorque - edge.OffsetXY) / edge.SlopeXY;
-				if (x >= edge.P1.X && x < edge.P2.X) {
-					solutions.Add(x * inAngularVelocity.Value());
-				}
-			}
-			if (solutions.Count == 0) {
-				throw new VectoSimulationException(
-					"Torque Converter: Failed to find operating point for inputTorque/inputSpeed! n_in: {0}, tq_in: {1}",
-					inAngularVelocity, inTorque);
-			}
-			return FindOperatingPoint(inAngularVelocity, solutions.Max().SI<PerSecond>());
-		}
-
-		public TorqueConverterOperatingPoint FindOperatingPointForPowerDemand(Watt power, PerSecond prevInputSpeed,
-			PerSecond nextOutputSpeed, KilogramSquareMeter inertia, Second dt, Watt previousPower)
-		{
-			var solutions = new List<double>();
-			var mpNorm = ReferenceSpeed.Value();
-
-			foreach (var segment in TorqueConverterEntries.Pairwise(Tuple.Create)) {
-				var mpEdge = Edge.Create(new Point(segment.Item1.SpeedRatio, segment.Item1.Torque.Value()),
-					new Point(segment.Item2.SpeedRatio, segment.Item2.Torque.Value()));
-
-				var a = mpEdge.OffsetXY / (2 * mpNorm * mpNorm);
-				var b = inertia.Value() / (2 * dt.Value()) + mpEdge.SlopeXY * nextOutputSpeed.Value() / (2 * mpNorm * mpNorm);
-				var c = 0;
-				var d = -inertia.Value() * prevInputSpeed.Value() * prevInputSpeed.Value() / (2 * dt.Value()) - power.Value() +
-						previousPower.Value() / 2;
-				var sol = VectoMath.CubicEquationSolver(a, b, c, d);
-
-				var selected = sol.Where(x => x > 0 && nextOutputSpeed / x >= mpEdge.P1.X && nextOutputSpeed / x < mpEdge.P2.X);
-				solutions.AddRange(selected);
-			}
-
-			if (solutions.Count == 0) {
-				throw new VectoException(
-					"Failed to find operating point for power {0}, prevInputSpeed {1}, nextOutputSpeed {2}", power,
-					prevInputSpeed, nextOutputSpeed);
-			}
-			solutions.Sort();
-
-			return FindOperatingPoint(solutions.First().SI<PerSecond>(), nextOutputSpeed);
-		}
-
-		private double MuLookup(double speedRatio)
-		{
-			return TorqueConverterEntries.Interpolate(x => x.SpeedRatio, y => y.TorqueRatio, speedRatio);
-		}
-
-		private NewtonMeter ReferenceTorqueLookup(double speedRatio)
-		{
-			return TorqueConverterEntries.Interpolate(x => x.SpeedRatio, y => y.Torque, speedRatio);
-		}
-
-		// ReSharper disable once UnusedMember.Global -- used by validation
-		public static ValidationResult ValidateData(TorqueConverterData data, ValidationContext validationContext)
-		{
-			var min = data.TorqueConverterEntries.Min(e => e.SpeedRatio);
-			var max = data.TorqueConverterEntries.Max(e => e.SpeedRatio);
-			if (min > 0 || max < data.RequiredSpeedRatio) {
-				return new ValidationResult(string.Format(
-					"Torque Converter Data invalid - Speedratio has to cover the range from 0.0 to {2}: given data only goes from {0} to {1}",
-					min, max, data.RequiredSpeedRatio));
-			}
-
-			return ValidationResult.Success;
-		}
-	}
-
-	public class TorqueConverterOperatingPoint
-	{
-		public PerSecond OutAngularVelocity;
-		public NewtonMeter OutTorque;
-
-		public PerSecond InAngularVelocity;
-		public NewtonMeter InTorque;
-
-		public double SpeedRatio;
-		public double TorqueRatio;
-		public bool Creeping;
-
-		public override string ToString()
-		{
-			return string.Format("n_out: {0}, n_in: {1}, tq_out: {2}, tq_in {3}, nu: {4}, my: {5}", OutAngularVelocity,
-				InAngularVelocity, OutTorque, InTorque, SpeedRatio, TorqueRatio);
-		}
-	}
-
-	public class TorqueConverterEntry
-	{
-		public double SpeedRatio;
-		public NewtonMeter Torque;
-		public double TorqueRatio;
-	}
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using TUGraz.VectoCommon.Exceptions;
+using TUGraz.VectoCommon.Utils;
+
+namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox
+{
+	[CustomValidation(typeof(TorqueConverterData), "ValidateData")]
+	public class TorqueConverterData : SimulationComponentData
+	{
+		protected internal readonly TorqueConverterEntry[] TorqueConverterEntries;
+
+		[Required, SIRange(0, double.MaxValue)]
+		public PerSecond ReferenceSpeed { get; protected internal set; }
+
+		[Required, SIRange(0, double.MaxValue)]
+		public MeterPerSquareSecond CLUpshiftMinAcceleration { get; internal set; }
+
+		[Required, SIRange(0, double.MaxValue)]
+		public MeterPerSquareSecond CCUpshiftMinAcceleration { get; internal set; }
+
+		[Required, SIRange(0, double.MaxValue)]
+		public PerSecond TorqueConverterSpeedLimit { get; protected internal set; }
+
+		internal double RequiredSpeedRatio; // only used for validation!
+
+		protected internal TorqueConverterData(IEnumerable<TorqueConverterEntry> torqueConverterEntries,
+			PerSecond referenceSpeed, PerSecond maxRpm, MeterPerSquareSecond clUpshiftMinAcceleration,
+			MeterPerSquareSecond ccUpshiftMinAcceleration)
+		{
+			TorqueConverterEntries = torqueConverterEntries.ToArray();
+			ReferenceSpeed = referenceSpeed;
+			TorqueConverterSpeedLimit = maxRpm;
+			CLUpshiftMinAcceleration = clUpshiftMinAcceleration;
+			CCUpshiftMinAcceleration = ccUpshiftMinAcceleration;
+		}
+
+		/// <summary>
+		/// find an operating point for the torque converter
+		/// 
+		/// find the input speed and input torque for the given output torque and output speed. 
+		/// </summary>
+		/// <param name="torqueOut">torque provided at the TC output</param>
+		/// <param name="angularSpeedOut">angular speed at the TC output</param>
+		/// <param name="minSpeed"></param>
+		/// <returns></returns>
+		public IList<TorqueConverterOperatingPoint> FindOperatingPoint(NewtonMeter torqueOut, PerSecond angularSpeedOut,
+			PerSecond minSpeed)
+		{
+			var solutions = new List<double>();
+			var mpNorm = ReferenceSpeed.Value();
+
+			var min = minSpeed == null ? 0 : minSpeed.Value();
+
+			// Find analytic solution for torque converter operating point
+			// mu = f(nu) = f(n_out / n_in) = T_out / T_in
+			// MP1000 = f(nu) = f(n_out / n_in)
+			// Tq_in = MP1000(nu) * (n_in/1000)^2 = T_out / mu
+			//
+			// mu(nu) and MP1000(nu) are provided as piecewise linear functions (y = k*x+d)
+			// solving the equation above for n_in results in a quadratic equation
+			foreach (var segment in TorqueConverterEntries.Pairwise(Tuple.Create)) {
+				var mpEdge = Edge.Create(new Point(segment.Item1.SpeedRatio, segment.Item1.Torque.Value()),
+					new Point(segment.Item2.SpeedRatio, segment.Item2.Torque.Value()));
+				var muEdge = Edge.Create(new Point(segment.Item1.SpeedRatio, segment.Item1.TorqueRatio),
+					new Point(segment.Item2.SpeedRatio, segment.Item2.TorqueRatio));
+
+				var a = muEdge.OffsetXY * mpEdge.OffsetXY / (mpNorm * mpNorm);
+				var b = angularSpeedOut.Value() * (muEdge.SlopeXY * mpEdge.OffsetXY + mpEdge.SlopeXY * muEdge.OffsetXY) /
+						(mpNorm * mpNorm);
+				var c = angularSpeedOut.Value() * angularSpeedOut.Value() * mpEdge.SlopeXY * muEdge.SlopeXY / (mpNorm * mpNorm) -
+						torqueOut.Value();
+
+				solutions.AddRange(VectoMath.QuadraticEquationSolver(a, b, c).Where(x => {
+					var ratio = angularSpeedOut.Value() / x;
+					return x > min && muEdge.P1.X <= ratio && ratio < muEdge.P2.X;
+				}));
+			}
+
+			if (solutions.Count == 0) {
+				Log.Debug(
+					"TorqueConverterData::FindOperatingPoint No solution for input torque/input speed found! n_out: {0}, tq_out: {1}",
+					angularSpeedOut, torqueOut);
+			}
+
+			return solutions.Select(sol => {
+				var s = sol.SI<PerSecond>();
+				var mu = MuLookup(angularSpeedOut / s);
+				return new TorqueConverterOperatingPoint {
+					OutTorque = torqueOut,
+					OutAngularVelocity = angularSpeedOut,
+					InAngularVelocity = s,
+					SpeedRatio = angularSpeedOut / s,
+					TorqueRatio = mu,
+					InTorque = torqueOut / mu,
+				};
+			}).ToList();
+		}
+
+		/// <summary>
+		/// find an operating point for the torque converter
+		/// 
+		/// find the input torque and output torque for the given input and output speeds.
+		/// Computes the speed ratio nu of input and output. Interpolates MP1000 and mu, Computes input torque and output torque
+		/// </summary>
+		/// <param name="inAngularVelocity">speed at the input of the TC</param>
+		/// <param name="outAngularVelocity">speed at the output of the TC</param>
+		/// <returns></returns>
+		public TorqueConverterOperatingPoint FindOperatingPoint(PerSecond inAngularVelocity, PerSecond outAngularVelocity)
+		{
+			var retVal = new TorqueConverterOperatingPoint {
+				InAngularVelocity = inAngularVelocity,
+				OutAngularVelocity = outAngularVelocity,
+				SpeedRatio = outAngularVelocity.Value() / inAngularVelocity.Value(),
+			};
+
+			foreach (var segment in TorqueConverterEntries.Pairwise()) {
+				if (retVal.SpeedRatio.IsBetween(segment.Item1.SpeedRatio, segment.Item2.SpeedRatio)) {
+					var mpTorque = segment.Interpolate(x => x.SpeedRatio, y => y.Torque, retVal.SpeedRatio);
+					retVal.TorqueRatio = segment.Interpolate(x => x.SpeedRatio, y => y.TorqueRatio, retVal.SpeedRatio);
+					retVal.InTorque = mpTorque * (inAngularVelocity * inAngularVelocity / ReferenceSpeed / ReferenceSpeed).Value();
+					retVal.OutTorque = retVal.InTorque * retVal.TorqueRatio;
+					return retVal;
+				}
+			}
+
+			// No solution found. Throw Error
+			var nu = outAngularVelocity / inAngularVelocity;
+			var nuMax = TorqueConverterEntries.Last().SpeedRatio;
+
+			if (nu.IsGreater(nuMax)) {
+				throw new VectoException(
+					"Torque Converter: Range of torque converter data is not sufficient. Required nu: {0}, Got nu_max: {1}", nu, nuMax);
+			}
+
+			throw new VectoException(
+				"Torque Converter: No solution for output speed/input speed found! n_out: {0}, n_in: {1}, nu: {2}, nu_max: {3}",
+				outAngularVelocity, inAngularVelocity, nu, nuMax);
+		}
+
+		/// <summary>
+		/// find an operating point for the torque converter
+		/// 
+		/// find the output torque and output speed for the given input torque and input speed
+		/// </summary>
+		/// <param name="inTorque"></param>
+		/// <param name="inAngularVelocity"></param>
+		/// <param name="outAngularSpeedEstimated"></param>
+		/// <returns></returns>
+		public TorqueConverterOperatingPoint FindOperatingPointForward(NewtonMeter inTorque, PerSecond inAngularVelocity,
+			PerSecond outAngularSpeedEstimated)
+		{
+			var referenceTorque = inTorque.Value() / inAngularVelocity.Value() / inAngularVelocity.Value() *
+								ReferenceSpeed.Value() * ReferenceSpeed.Value();
+			var maxTorque = TorqueConverterEntries.Max(x => x.Torque.Value());
+			if (referenceTorque.IsGreaterOrEqual(maxTorque)) {
+				referenceTorque = outAngularSpeedEstimated != null
+					? ReferenceTorqueLookup(outAngularSpeedEstimated / inAngularVelocity).Value()
+					: 0.9 * maxTorque;
+			}
+
+			var solutions = new List<double>();
+			foreach (var edge in TorqueConverterEntries.Pairwise(
+				(p1, p2) => Edge.Create(new Point(p1.SpeedRatio, p1.Torque.Value()), new Point(p2.SpeedRatio, p2.Torque.Value())))) {
+				var x = (referenceTorque - edge.OffsetXY) / edge.SlopeXY;
+				if (x >= edge.P1.X && x < edge.P2.X) {
+					solutions.Add(x * inAngularVelocity.Value());
+				}
+			}
+			if (solutions.Count == 0) {
+				throw new VectoSimulationException(
+					"Torque Converter: Failed to find operating point for inputTorque/inputSpeed! n_in: {0}, tq_in: {1}",
+					inAngularVelocity, inTorque);
+			}
+			return FindOperatingPoint(inAngularVelocity, solutions.Max().SI<PerSecond>());
+		}
+
+		public TorqueConverterOperatingPoint FindOperatingPointForPowerDemand(Watt power, PerSecond prevInputSpeed,
+			PerSecond nextOutputSpeed, KilogramSquareMeter inertia, Second dt, Watt previousPower)
+		{
+			var solutions = new List<double>();
+			var mpNorm = ReferenceSpeed.Value();
+
+			foreach (var segment in TorqueConverterEntries.Pairwise(Tuple.Create)) {
+				var mpEdge = Edge.Create(new Point(segment.Item1.SpeedRatio, segment.Item1.Torque.Value()),
+					new Point(segment.Item2.SpeedRatio, segment.Item2.Torque.Value()));
+
+				var a = mpEdge.OffsetXY / (2 * mpNorm * mpNorm);
+				var b = inertia.Value() / (2 * dt.Value()) + mpEdge.SlopeXY * nextOutputSpeed.Value() / (2 * mpNorm * mpNorm);
+				var c = 0;
+				var d = -inertia.Value() * prevInputSpeed.Value() * prevInputSpeed.Value() / (2 * dt.Value()) - power.Value() +
+						previousPower.Value() / 2;
+				var sol = VectoMath.CubicEquationSolver(a, b, c, d);
+
+				var selected = sol.Where(x => x > 0 && nextOutputSpeed / x >= mpEdge.P1.X && nextOutputSpeed / x < mpEdge.P2.X);
+				solutions.AddRange(selected);
+			}
+
+			if (solutions.Count == 0) {
+				throw new VectoException(
+					"Failed to find operating point for power {0}, prevInputSpeed {1}, nextOutputSpeed {2}", power,
+					prevInputSpeed, nextOutputSpeed);
+			}
+			solutions.Sort();
+
+			return FindOperatingPoint(solutions.First().SI<PerSecond>(), nextOutputSpeed);
+		}
+
+		private double MuLookup(double speedRatio)
+		{
+			return TorqueConverterEntries.Interpolate(x => x.SpeedRatio, y => y.TorqueRatio, speedRatio);
+		}
+
+		private NewtonMeter ReferenceTorqueLookup(double speedRatio)
+		{
+			return TorqueConverterEntries.Interpolate(x => x.SpeedRatio, y => y.Torque, speedRatio);
+		}
+
+		// ReSharper disable once UnusedMember.Global -- used by validation
+		public static ValidationResult ValidateData(TorqueConverterData data, ValidationContext validationContext)
+		{
+			var min = data.TorqueConverterEntries.Min(e => e.SpeedRatio);
+			var max = data.TorqueConverterEntries.Max(e => e.SpeedRatio);
+			if (min > 0 || max < data.RequiredSpeedRatio) {
+				return new ValidationResult(string.Format(
+					"Torque Converter Data invalid - Speedratio has to cover the range from 0.0 to {2}: given data only goes from {0} to {1}",
+					min, max, data.RequiredSpeedRatio));
+			}
+
+			return ValidationResult.Success;
+		}
+	}
+
+	public class TorqueConverterOperatingPoint
+	{
+		public PerSecond OutAngularVelocity;
+		public NewtonMeter OutTorque;
+
+		public PerSecond InAngularVelocity;
+		public NewtonMeter InTorque;
+
+		public double SpeedRatio;
+		public double TorqueRatio;
+		public bool Creeping;
+
+		public override string ToString()
+		{
+			return string.Format("n_out: {0}, n_in: {1}, tq_out: {2}, tq_in {3}, nu: {4}, my: {5}", OutAngularVelocity,
+				InAngularVelocity, OutTorque, InTorque, SpeedRatio, TorqueRatio);
+		}
+	}
+
+	public class TorqueConverterEntry
+	{
+		public double SpeedRatio;
+		public NewtonMeter Torque;
+		public double TorqueRatio;
+	}
 }
\ No newline at end of file
-- 
GitLab