From a62cdb396685949cb0d38b8035239ec178449403 Mon Sep 17 00:00:00 2001
From: Markus Quaritsch <markus.quaritsch@tugraz.at>
Date: Wed, 15 Nov 2017 17:57:39 +0100
Subject: [PATCH] additional SI classes for 'special' units, extensive tests
 for all conversion methods

---
 VectoCommon/VectoCommon/Utils/SI.cs           |  69 +++++-
 .../Utils/SIConvertExtensionMethods.cs        |  29 +--
 .../OutputData/IModalDataContainer.cs         |   4 +-
 VectoCore/VectoCoreTest/Utils/SITest.cs       | 213 +++++++++++++++++-
 4 files changed, 277 insertions(+), 38 deletions(-)

diff --git a/VectoCommon/VectoCommon/Utils/SI.cs b/VectoCommon/VectoCommon/Utils/SI.cs
index 1cf8a45a81..344db00e28 100644
--- a/VectoCommon/VectoCommon/Utils/SI.cs
+++ b/VectoCommon/VectoCommon/Utils/SI.cs
@@ -223,7 +223,17 @@ namespace TUGraz.VectoCommon.Utils
 
         [DebuggerHidden]
         private KilogramPerMeter(double val) : base(val, Units) { }
-    }
+
+		public static KilogramPerMeterMass operator /(KilogramPerMeter kpm, Kilogram kg)
+		{
+			return SIBase<KilogramPerMeterMass>.Create(kpm.Val / kg.Value());
+		}
+
+		public static KilogramPerMeterCubicMeter operator /(KilogramPerMeter kpm, CubicMeter vol)
+		{
+			return SIBase<KilogramPerMeterCubicMeter>.Create(kpm.Val / vol.Value());
+		}
+	}
 
     /// <summary>
     /// SI Class for Liter per Second [l/s].
@@ -787,12 +797,57 @@ namespace TUGraz.VectoCommon.Utils
         }
     }
 
-    /// <summary>
-    /// Base Class for all special SI Classes. Not intended to be used directly.
-    /// Implements templated operators for type safety and convenience.
-    /// </summary>
-    /// <typeparam name="T"></typeparam>
-    public abstract class SIBase<T> : SI where T : SIBase<T>
+	public class VolumePerMeter : SIBase<VolumePerMeter>
+	{
+		private static readonly int[] Units = { 0, 2, 0, 0, 0, 0, 0 };
+		private VolumePerMeter(double val) : base(val, Units) { }
+
+		public static VolumePerMeterMass operator /(VolumePerMeter vpm, Kilogram kg)
+		{
+			return SIBase<VolumePerMeterMass>.Create(vpm.Val / kg.Value());
+		}
+
+		public static VolumePerMeterVolume operator /(VolumePerMeter vpm, CubicMeter vol)
+		{
+			return SIBase<VolumePerMeterVolume>.Create(vpm.Val / vol.Value());
+		}
+
+	}
+
+	public class VolumePerMeterMass : SIBase<VolumePerMeterMass>
+	{
+		private static readonly int[] Units = { -1, 2, 0, 0, 0, 0, 0 };
+
+		private VolumePerMeterMass(double val) : base (val, Units) { }
+	}
+
+	public class VolumePerMeterVolume : SIBase<VolumePerMeterVolume>
+	{
+		private static readonly int[] Units = { 0, -1, 0, 0, 0, 0, 0 };
+
+		private VolumePerMeterVolume(double val) : base (val, Units) { }
+	}
+
+	public class KilogramPerMeterCubicMeter : SIBase<KilogramPerMeterCubicMeter>
+	{
+		private static readonly int[] Units = { 1, -4, 0, 0, 0, 0, 0 };
+
+		private KilogramPerMeterCubicMeter(double val) : base(val, Units) { }
+	}
+
+	public class KilogramPerMeterMass : SIBase<KilogramPerMeterMass>
+	{
+		private static readonly int[] Units = { 0, -1, 0, 0, 0, 0, 0 };
+
+		private KilogramPerMeterMass(double val) : base(val, Units) { }
+	}
+
+	/// <summary>
+	/// Base Class for all special SI Classes. Not intended to be used directly.
+	/// Implements templated operators for type safety and convenience.
+	/// </summary>
+	/// <typeparam name="T"></typeparam>
+	public abstract class SIBase<T> : SI where T : SIBase<T>
     {
         private static readonly T ZeroPrototype;
 
diff --git a/VectoCommon/VectoCommon/Utils/SIConvertExtensionMethods.cs b/VectoCommon/VectoCommon/Utils/SIConvertExtensionMethods.cs
index 7a910a2ac6..3f7a0d65c0 100644
--- a/VectoCommon/VectoCommon/Utils/SIConvertExtensionMethods.cs
+++ b/VectoCommon/VectoCommon/Utils/SIConvertExtensionMethods.cs
@@ -6,17 +6,17 @@ namespace TUGraz.VectoCommon.Utils
     public class ConvertedSI
     {
         private readonly double _value;
-        private readonly string _units;
+        public string Units { get; }
 
         public ConvertedSI(double value, string units)
         {
             _value = value;
-            _units = units;
+            Units = units;
         }
 
 		protected bool Equals(ConvertedSI other)
 		{
-			return _value.Equals(other._value) && string.Equals(_units, other._units);
+			return _value.Equals(other._value) && string.Equals(Units, other.Units);
 		}
 
 		public override bool Equals(object obj)
@@ -33,7 +33,7 @@ namespace TUGraz.VectoCommon.Utils
 		public override int GetHashCode()
 		{
 			unchecked {
-				return (_value.GetHashCode() * 397) ^ (_units != null ? _units.GetHashCode() : 0);
+				return (_value.GetHashCode() * 397) ^ (Units != null ? Units.GetHashCode() : 0);
 			}
 		}
 
@@ -66,7 +66,7 @@ namespace TUGraz.VectoCommon.Utils
 
 			if (showUnit.Value) {
 				return (_value * outputFactor.Value).ToString("F" + decimals.Value, CultureInfo.InvariantCulture) + " [" +
-						_units + "]";
+						Units + "]";
 			}
 
 			return (_value * outputFactor.Value).ToString("F" + decimals.Value, CultureInfo.InvariantCulture);
@@ -84,7 +84,7 @@ namespace TUGraz.VectoCommon.Utils
         }
         public static ConvertedSI ConvertToTon(this Kilogram value)
         {
-            return new ConvertedSI(value.Value() / Kilo, "Ton");
+            return new ConvertedSI(value.Value() / Kilo, "t");
         }
         public static ConvertedSI ConvertToKiloMeterPerHour(this MeterPerSecond value)
         {
@@ -95,12 +95,12 @@ namespace TUGraz.VectoCommon.Utils
             return value == null ? null : new ConvertedSI(value.Value() * Kilo * Kilo, "g/km");
         }
 
-        public static ConvertedSI ConvertToLiterPer100Kilometer(this SI value)
+        public static ConvertedSI ConvertToLiterPer100Kilometer(this VolumePerMeter value)
         {
             return value == null ? null : new ConvertedSI(value.Value() * (10*10*10) * (100*1000), "l/100km");
         }
         
-        public static ConvertedSI ConvertToLiterPer100TonKiloMeter(this SI value)
+        public static ConvertedSI ConvertToLiterPer100TonKiloMeter(this VolumePerMeterMass value)
         {
             const int CubicMeterToLiter = 10 * 10 * 10;
             const int MeterTo100KiloMeter = 100 * Kilo;
@@ -109,7 +109,7 @@ namespace TUGraz.VectoCommon.Utils
             return value == null ? null  : new ConvertedSI(value.Value() * CubicMeterToLiter * (MeterTo100KiloMeter * KilogrammToTon), "l/100tkm");
         }
 
-		public static ConvertedSI ConvertToLiterPerCubicMeter100KiloMeter(this SI value)
+		public static ConvertedSI ConvertToLiterPerCubicMeter100KiloMeter(this VolumePerMeterVolume value)
         {
             const int CubicMeterToLiter = 10 * 10 * 10;
             const int MeterTo100KiloMeter = 100 * Kilo;
@@ -131,20 +131,15 @@ namespace TUGraz.VectoCommon.Utils
             return new ConvertedSI(value.Value() * 100 * 100 * 100, "cm^3");
         }
 
-        public static ConvertedSI ConvertToGrammPerCubicMeterKiloMeter(this SI value)
+        public static ConvertedSI ConvertToGrammPerCubicMeterKiloMeter(this KilogramPerMeterCubicMeter value)
         {
             return new ConvertedSI(value.Value()  * Kilo * Kilo, "g/m^3km");
         }
 
-        public static ConvertedSI ConvertToGrammPerTonKilometer(this SI value)
+        public static ConvertedSI ConvertToGrammPerTonKilometer(this KilogramPerMeterMass value)
         {
             return new ConvertedSI(value.Value() * Kilo * Kilo * Kilo, "g/tkm");
         }
-		
-        public static ConvertedSI ConvertToLiterPer100KiloMeter(this SI value)
-        {
-            return new ConvertedSI(value.Value() * 10 * 10 * 10 * 100 * Kilo, "l/100km");
-        }
 
         public static ConvertedSI ConvertToKiloWattHour(this WattSecond value)
         {
@@ -157,7 +152,7 @@ namespace TUGraz.VectoCommon.Utils
 
         public static ConvertedSI ConvertToRoundsPerMinute(this PerSecond value)
         {
-            return new ConvertedSI(value.Value() * 2 * Math.PI / 60, "rpm");
+            return new ConvertedSI(value.AsRPM, "rpm");
         }
         public static ConvertedSI ConvertToCubicDeziMeter(this CubicMeter value)
         {
diff --git a/VectoCore/VectoCore/OutputData/IModalDataContainer.cs b/VectoCore/VectoCore/OutputData/IModalDataContainer.cs
index 6bdc2259ea..43f16c5b77 100644
--- a/VectoCore/VectoCore/OutputData/IModalDataContainer.cs
+++ b/VectoCore/VectoCore/OutputData/IModalDataContainer.cs
@@ -365,7 +365,7 @@ namespace TUGraz.VectoCore.OutputData
 			return data.TimeIntegral<Kilogram>(ModalResultField.FCFinal) / distance;
 		}
 
-        public static SquareMeter FuelConsumptionFinalVolumePerMeter(this IModalDataContainer data)
+        public static VolumePerMeter FuelConsumptionFinalVolumePerMeter(this IModalDataContainer data)
 		{
 			var fuelConsumptionFinal = data.FuelConsumptionFinal();
 			if (fuelConsumptionFinal == null || data.FuelData.FuelDensity == null) {
@@ -373,7 +373,7 @@ namespace TUGraz.VectoCore.OutputData
 			}
 
 			var fcVolumePerMeter = fuelConsumptionFinal / data.FuelData.FuelDensity;
-            return fcVolumePerMeter.Cast<SquareMeter>();
+            return fcVolumePerMeter.Cast<VolumePerMeter>();
 		}
 
 		public static KilogramPerMeter CO2PerMeter(this IModalDataContainer data)
diff --git a/VectoCore/VectoCoreTest/Utils/SITest.cs b/VectoCore/VectoCoreTest/Utils/SITest.cs
index 31c01ccfcf..5d384d4bef 100644
--- a/VectoCore/VectoCoreTest/Utils/SITest.cs
+++ b/VectoCore/VectoCoreTest/Utils/SITest.cs
@@ -571,22 +571,211 @@ namespace TUGraz.VectoCore.Tests.Utils
 			Assert.AreEqual("m^3", 2.13093.SI(Unit.SI.Liter).GetUnitString());
 		}
 
-		[TestCase]
-        public void SI_ConvertValues()
-        {
-            var sig1 = 5.SI(Unit.SI.Gramm);
-            Assert.AreEqual(0.005, sig1.Value());
+		//[TestCase]
+  //      public void SI_ConvertValues()
+  //      {
+  //          var sig1 = 5.SI(Unit.SI.Gramm);
+  //          Assert.AreEqual(0.005, sig1.Value());
+
+		//	var val2 = 7.SI(Unit.SI.Cubic.Dezi.Meter).Cast<CubicMeter>().ConvertToCubicDeziMeter();
+		//	AssertHelper.AreRelativeEqual(7, val2);
+
+		//	var val3 = 5.SI(Unit.SI.Cubic.Dezi.Meter).Cast<CubicMeter>().ConvertToCubicCentiMeter();
+		//	AssertHelper.AreRelativeEqual(5000, val3); // 5000 cm^3
+
+		//	var val4 = 5.SI(Unit.SI.Cubic.Centi.Meter).Cast<CubicMeter>().ConvertToCubicDeziMeter();
+		//	AssertHelper.AreRelativeEqual(0.005, val4); // 0.005 dm^3
+		//}
+
+		[TestCase(1, 1000),
+		TestCase(1e-3, 1),
+		TestCase(2.65344, 2653.44)]
+		public void SI_Convert_ConvertToGramm(double val, double converted)
+		{
+			var siVal = val.SI<Kilogram>();
+			var siConv = siVal.ConvertToGramm();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("g", siConv.Units);
+		}
 
-			var val2 = 7.SI(Unit.SI.Cubic.Dezi.Meter).Cast<CubicMeter>().ConvertToCubicDeziMeter();
-			AssertHelper.AreRelativeEqual(7, val2);
+		[TestCase(1000, 1),
+		TestCase(1, 1e-3),
+		TestCase(5243, 5.243)]
+		public void SI_Convert_ConvertToTon(double val, double converted)
+		{
+			var siVal = val.SI<Kilogram>();
+			var siConv = siVal.ConvertToTon();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("t", siConv.Units);
+		}
 
-			var val3 = 5.SI(Unit.SI.Cubic.Dezi.Meter).Cast<CubicMeter>().ConvertToCubicCentiMeter();
-			AssertHelper.AreRelativeEqual(5000, val3); // 5000 cm^3
+		[TestCase(1, 3.6),
+		TestCase(0.2777777777777777, 1),
+		TestCase(13.7603, 49.53708)]
+		public void SI_Convert_ConvertToKiloMeterPerHour(double val, double converted)
+		{
+			var siVal = val.SI<MeterPerSecond>();
+			var siConv = siVal.ConvertToKiloMeterPerHour();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("km/h", siConv.Units);
+		}
+
+		[TestCase(1, 1e6),
+		TestCase(1e-6, 1),
+		TestCase(7.54214451, 7542144.51)]
+		public void SI_Convert_ConvertToGrammPerKiloMeter(double val, double converted)
+		{
+			var siVal = val.SI<KilogramPerMeter>();
+			var siConv = siVal.ConvertToGrammPerKiloMeter();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("g/km", siConv.Units);
+		}
 
-			var val4 = 5.SI(Unit.SI.Cubic.Centi.Meter).Cast<CubicMeter>().ConvertToCubicDeziMeter();
-			AssertHelper.AreRelativeEqual(0.005, val4); // 0.005 dm^3
+		[TestCase(1, 1e8),
+		TestCase(1e-8, 1),
+		TestCase(0.00935934235, 935934.235)]
+		public void SI_Convert_ConvertToLiterPer100Kilometer(double val, double converted)
+		{
+			var siVal = val.SI<VolumePerMeter>();
+			var siConv = siVal.ConvertToLiterPer100Kilometer();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("l/100km", siConv.Units);
 		}
 
-    }
+		[TestCase(1, 1e11),
+		TestCase(1e-11, 1),
+		TestCase(0.00013243241234, 13243241.234)]
+		public void SI_Convert_ConvertToLiterPer100TonKiloMeter(double val, double converted)
+		{
+			var siVal = val.SI<VolumePerMeterMass>();
+			var siConv = siVal.ConvertToLiterPer100TonKiloMeter();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("l/100tkm", siConv.Units);
+		}
+
+		[TestCase(1, 1e8),
+		TestCase(1e-8, 1),
+		TestCase(0.13243241234, 13243241.234)]
+		public void SI_Convert_ConvertToLiterPerCubicMeter100KiloMeter(double val, double converted)
+		{
+			var siVal = val.SI<VolumePerMeterVolume>();
+			var siConv = siVal.ConvertToLiterPerCubicMeter100KiloMeter();
+			Assert.AreEqual(converted, siConv, 1e-6);
+			Assert.AreEqual("l/100m^3km", siConv.Units);
+		}
+
+		[TestCase(1,3.6e6),
+		TestCase(0.277777777777777777e-6,1),
+		TestCase(0.0135897845, 13589.7845*3.6)]
+		public void SI_Convert_ConvertToGrammPerHour(double val, double converted)
+		{
+			var siVal = val.SI<KilogramPerSecond>();
+			var siConv = siVal.ConvertToGrammPerHour();
+			Assert.AreEqual(converted, siConv, 1e-6);
+			Assert.AreEqual("g/h", siConv.Units);
+		}
+
+		[TestCase(1, 1e-3),
+		TestCase(1e3, 1),
+		TestCase(4353.32, 4.35332)]
+		public void SI_Convert_ConvertToKiloMeter(double val, double converted)
+		{
+			var siVal = val.SI<Meter>();
+			var siConv = siVal.ConvertToKiloMeter();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("km", siConv.Units);
+		}
+
+		[TestCase(1, 1e6),
+		TestCase(1e-6, 1),
+		TestCase(0.053798513789, 53798.513789)]
+		public void SI_Convert_ConvertToCubicCentiMeter(double val, double converted)
+		{
+			var siVal = val.SI<CubicMeter>();
+			var siConv = siVal.ConvertToCubicCentiMeter();
+			Assert.AreEqual(converted, siConv, 1e-6);
+			Assert.AreEqual("cm^3", siConv.Units);
+		}
+
+		[TestCase(1, 1e6),
+		TestCase(1e-6, 1),
+		TestCase(7.54214451, 7542144.51)]
+		public void SI_Convert_ConvertToGrammPerCubicMeterKiloMeter(double val, double converted)
+		{
+			var siVal = val.SI<KilogramPerMeterCubicMeter>();
+			var siConv = siVal.ConvertToGrammPerCubicMeterKiloMeter();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("g/m^3km", siConv.Units);
+		}
+
+		[TestCase(1, 1e9),
+		TestCase(1e-9, 1),
+		TestCase(7.54214451, 7542144510)]
+		public void SI_Convert_ConvertToGrammPerTonKilometer(double val, double converted)
+		{
+			var siVal = val.SI<KilogramPerMeterMass>();
+			var siConv = siVal.ConvertToGrammPerTonKilometer();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("g/tkm", siConv.Units);
+		}
+
+		[TestCase(1, 0.277777777777e-6),
+		TestCase(3600e3, 1),
+		TestCase(135890, 0.0377472222)]
+		public void SI_Convert_ConvertToKiloWattHour(double val, double converted)
+		{
+			var siVal = val.SI<WattSecond>();
+			var siConv = siVal.ConvertToKiloWattHour();
+			Assert.AreEqual(converted, siConv, 1e-6);
+			Assert.AreEqual("kWh", siConv.Units);
+		}
+
+		[TestCase(1, 1e-3),
+		TestCase(1e3, 1),
+		TestCase(23453, 23.453)]
+		public void SI_Convert_ConvertToKiloWatt(double val, double converted)
+		{
+			var siVal = val.SI<Watt>();
+			var siConv = siVal.ConvertToKiloWatt();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("kW", siConv.Units);
+		}
+
+		[TestCase(1, 9.549296586),
+		TestCase(0.104719755, 1),
+		TestCase(62.83185307, 600)]
+		public void SI_Convert_ConvertToRoundsPerMinute(double val, double converted)
+		{
+			var siVal = val.SI<PerSecond>();
+			var siConv = siVal.ConvertToRoundsPerMinute();
+			Assert.AreEqual(converted, siConv, 1e-6);
+			Assert.AreEqual(siVal.Value(), ((double)siConv).RPMtoRad().Value());
+			Assert.AreEqual("rpm", siConv.Units);
+		}
+
+		[TestCase(1, 1e3),
+		TestCase(1e-3, 1),
+		TestCase(123.780, 123780)]
+		public void SI_Convert_ConvertToCubicDeziMeter(double val, double converted)
+		{
+			var siVal = val.SI<CubicMeter>();
+			var siConv = siVal.ConvertToCubicDeziMeter();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("dm^3", siConv.Units);
+		}
+
+		[TestCase(1, 1e3),
+		TestCase(1e-3, 1),
+		TestCase(0.255, 255)]
+		public void SI_Convert_ConvertToMilliMeter(double val, double converted)
+		{
+			var siVal = val.SI<Meter>();
+			var siConv = siVal.ConvertToMilliMeter();
+			Assert.AreEqual(converted, siConv, 1e-12);
+			Assert.AreEqual("mm", siConv.Units);
+		}
+
+
+	}
 
 }
\ No newline at end of file
-- 
GitLab