diff --git a/VectoCommon/VectoCommon/Utils/SI.cs b/VectoCommon/VectoCommon/Utils/SI.cs index 1845b4948cc88832d6179dde3e99f38f5406a1de..46b38bd467573ad815fbe524527d83c8f5a1a758 100644 --- a/VectoCommon/VectoCommon/Utils/SI.cs +++ b/VectoCommon/VectoCommon/Utils/SI.cs @@ -1672,6 +1672,7 @@ namespace TUGraz.VectoCommon.Utils } } + [DebuggerStepThrough] public int CompareTo(object obj) { var si = obj as SI; diff --git a/VectoCommon/VectoCommon/Utils/VectoMath.cs b/VectoCommon/VectoCommon/Utils/VectoMath.cs index f488301deff3abe1ba54ea3a72f3d846e2576ff8..42982e3bbcd06b10c1403224d50fe6b992903076 100644 --- a/VectoCommon/VectoCommon/Utils/VectoMath.cs +++ b/VectoCommon/VectoCommon/Utils/VectoMath.cs @@ -223,24 +223,7 @@ namespace TUGraz.VectoCommon.Utils return Math.Atan(inclinationPercent).SI<Radian>(); } - public static double[] QuadraticEquationSolver(double a, double b, double c) - { - var d = b * b - 4 * a * c; - - // no real solution - if (d < 0) { - return new double[0]; - } - - if (d > 0) { - // two solutions - return new[] { (-b + Math.Sqrt(d)) / (2 * a), (-b - Math.Sqrt(d)) / (2 * a) }; - } - - // one real solution - return new[] { -b / (2 * a) }; - } - + public static Point Intersect(Edge line1, Edge line2) { var s10X = line1.P2.X - line1.P1.X; @@ -347,7 +330,77 @@ namespace TUGraz.VectoCommon.Utils return Math.Ceiling(si.Value()).SI<T>(); } + + private static double Cbrt(double x) + { + return x < 0 ? -Math.Pow(-x, 1.0 / 3.0) : Math.Pow(x, 1.0 / 3.0); + } + + + public static void LeastSquaresFitting<T>(IEnumerable<T> entries, Func<T, double> getX, Func<T, double> getY, + out double k, out double d, out double r) + { + // algoritm taken from http://mathworld.wolfram.com/LeastSquaresFitting.html (eqn. 27 & 28) + var count = 0; + var sumX = 0.0; + var sumY = 0.0; + var sumXSquare = 0.0; + var sumYSquare = 0.0; + var sumXY = 0.0; + foreach (var entry in entries) { + var x = getX(entry); + var y = getY(entry); + sumX += x; + sumY += y; + sumXSquare += x * x; + sumYSquare += y * y; + sumXY += x * y; + count++; + } + if (count == 0) { + k = 0; + d = 0; + r = 0; + return; + } + var ssxx = sumXSquare - sumX * sumX / count; + var ssxy = sumXY - sumX * sumY / count; + var ssyy = sumYSquare - sumY * sumY / count; + k = ssxy / ssxx; + d = (sumY - k * sumX) / count; + r = ssxy * ssxy / ssxx / ssyy; + } + + public static double[] QuadraticEquationSolver(double a, double b, double c) + { + return Polynom2Solver(a, b, c); + } + public static double[] CubicEquationSolver(double a, double b, double c, double d) + { + return Polynom3Solver(a, b, c, d); + } + + public static double[] Polynom2Solver(double a, double b, double c) + { + var d = b * b - 4 * a * c; + + // no real solution + if (d < 0) { + return new double[0]; + } + + if (d > 0) { + // two solutions + return new[] { (-b + Math.Sqrt(d)) / (2 * a), (-b - Math.Sqrt(d)) / (2 * a) }; + } + + // one real solution + return new[] { -b / (2 * a) }; + } + + + public static double[] Polynom3Solver(double a, double b, double c, double d) { var solutions = new List<double>(); if (a.IsEqual(0, 1e-12)) { @@ -382,44 +435,48 @@ namespace TUGraz.VectoCommon.Utils return solutions.ToArray(); } - private static double Cbrt(double x) + public static double[] Polynom4Solver(double A, double B, double C, double D, double E) { - return x < 0 ? -Math.Pow(-x, 1.0 / 3.0) : Math.Pow(x, 1.0 / 3.0); - } + // see http://www.mathe.tu-freiberg.de/~hebisch/cafe/viertergrad.pdf + var a = B / A; + var b = C / A; + var c = D / A; + var d = E / A; + var p = -3.0 / 8.0 * a * a + b; + var q = 1.0 / 8.0 * a * a * a - a * b / 2.0 + c; + var r = -3.0 / 256.0 * a * a * a * a + a * a * b / 16.0 - a * c / 4.0 + d; + if (q.IsEqual(0, 1e-12)) { + var solY = VectoMath.QuadraticEquationSolver(1, b, d); + var retVal = new List<double>(); + foreach (var s in solY) { + if (s < 0) { + continue; + } - public static void LeastSquaresFitting<T>(IEnumerable<T> entries, Func<T, double> getX, Func<T, double> getY, - out double k, out double d, out double r) - { - // algoritm taken from http://mathworld.wolfram.com/LeastSquaresFitting.html (eqn. 27 & 28) - var count = 0; - var sumX = 0.0; - var sumY = 0.0; - var sumXSquare = 0.0; - var sumYSquare = 0.0; - var sumXY = 0.0; - foreach (var entry in entries) { - var x = getX(entry); - var y = getY(entry); - sumX += x; - sumY += y; - sumXSquare += x * x; - sumYSquare += y * y; - sumXY += x * y; - count++; + retVal.Add(Math.Sqrt(s)); + retVal.Add(-Math.Sqrt(s)); + } + + return retVal.ToArray(); } - if (count == 0) { - k = 0; - d = 0; - r = 0; - return; + + var solZ = VectoMath.Polynom3Solver(8.0, 20.0 * p, 16.0 * p * p - 8.0 * r, 4.0 * p * p * p - 4.0 * p * r - q * q); + if (solZ.Length == 0) { + return new double[0]; + + //throw new VectoException("no solution for polynom grade 4 found"); } - var ssxx = sumXSquare - sumX * sumX / count; - var ssxy = sumXY - sumX * sumY / count; - var ssyy = sumYSquare - sumY * sumY / count; - k = ssxy / ssxx; - d = (sumY - k * sumX) / count; - r = ssxy * ssxy / ssxx / ssyy; + + var z = solZ.First(); + var u = p + 2.0 * z; + if (u < 0) { + // no real-valued solution + return new double[0]; + } + var solY1 = VectoMath.QuadraticEquationSolver(1, -Math.Sqrt(u), q / (2.0 * Math.Sqrt(u)) + p + z); + var solY2 = VectoMath.QuadraticEquationSolver(1, Math.Sqrt(u), -q / (2.0 * Math.Sqrt(u)) + p + z); + return solY1.Select(s => s - a / 4.0).Concat(solY2.Select(s => s - a / 4.0)).ToArray(); } } diff --git a/VectoCore/VectoCore/InputData/Reader/Impl/DeclarationModeVectoRunDataFactory.cs b/VectoCore/VectoCore/InputData/Reader/Impl/DeclarationModeVectoRunDataFactory.cs index ce0ae82ea1491f5d5ca1ea08bfe8e65913a15c25..215fbd7bcd1920d3fd7e4753456f80043e12c722 100644 --- a/VectoCore/VectoCore/InputData/Reader/Impl/DeclarationModeVectoRunDataFactory.cs +++ b/VectoCore/VectoCore/InputData/Reader/Impl/DeclarationModeVectoRunDataFactory.cs @@ -112,8 +112,8 @@ namespace TUGraz.VectoCore.InputData.Reader.Impl _engineData = _dao.CreateEngineData(vehicle.EngineInputData, vehicle.EngineIdleSpeed, vehicle.GearboxInputData, vehicle.TorqueLimits, vehicle.TankSystem); - _axlegearData = _dao.CreateAxleGearData(vehicle.AxleGearInputData); - _angledriveData = _dao.CreateAngledriveData(vehicle.AngledriveInputData); + _axlegearData = _dao.CreateAxleGearData(InputDataProvider.JobInputData.Vehicle.AxleGearInputData); + _angledriveData = _dao.CreateAngledriveData(InputDataProvider.JobInputData.Vehicle.AngledriveInputData); _gearboxData = _dao.CreateGearboxData(vehicle.GearboxInputData, _engineData, _axlegearData.AxleGear.Ratio, tempVehicle.DynamicTyreRadius, tempVehicle.VehicleCategory); diff --git a/VectoCore/VectoCore/Models/Simulation/Impl/DistanceRun.cs b/VectoCore/VectoCore/Models/Simulation/Impl/DistanceRun.cs index 9daf59e0c110bc4ebf1ff5902c7c49da6f088ba1..50fe805263b782ef8035f7be9548dd3485a56a90 100644 --- a/VectoCore/VectoCore/Models/Simulation/Impl/DistanceRun.cs +++ b/VectoCore/VectoCore/Models/Simulation/Impl/DistanceRun.cs @@ -53,6 +53,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl var loopCount = 0; IResponse response; + var debug = new DebugData(); do { IterationStatistics.Increment(this, "Iterations"); @@ -74,6 +75,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl if (loopCount++ > Constants.SimulationSettings.MaximumIterationCountForSimulationStep) { throw new VectoSimulationException("Maximum iteration count for a single simulation interval reached! Aborting!"); } + debug.Add(new {Response = response}); } while (!(response is ResponseSuccess || response is ResponseCycleFinished)); IterationStatistics.Increment(this, "Distance", Container.Distance.Value()); diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TorqueConverterData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TorqueConverterData.cs index 8b38730ef69a83f9abaa0a260ca1023c1e7d814d..b840aefffd1c576c300c44d6c317aa54c9ea1e2d 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TorqueConverterData.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TorqueConverterData.cs @@ -216,7 +216,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox foreach (var edge in TorqueConverterEntries.Pairwise((p1, p2) => Edge.Create(new Point(p1.SpeedRatio, p1.TorqueRatio), new Point(p2.SpeedRatio, p2.TorqueRatio)))) { if (nu >= edge.P1.X && nu < edge.P2.X) { var my = VectoMath.Interpolate(edge, nu); - return new TorqueConverterOperatingPoint() { + return new TorqueConverterOperatingPoint { InAngularVelocity = inAngularVelocity, OutAngularVelocity = outAngularVelocity, OutTorque = outTorque, @@ -231,22 +231,80 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox outAngularVelocity, inAngularVelocity, outTorque); } - public TorqueConverterOperatingPoint FindOperatingPointForPowerDemand(Watt power, PerSecond prevInputSpeed, - PerSecond nextOutputSpeed, KilogramSquareMeter inertia, Second dt, Watt previousPower) + public TorqueConverterOperatingPoint LookupOperatingPointOut(PerSecond outAngularVelocity, PerSecond inAngularVelocity, NewtonMeter inTorque) + { + var nu = outAngularVelocity / inAngularVelocity; + foreach (var edge in TorqueConverterEntries.Pairwise((p1, p2) => Edge.Create(new Point(p1.SpeedRatio, p1.TorqueRatio), new Point(p2.SpeedRatio, p2.TorqueRatio)))) { + if (nu >= edge.P1.X && nu < edge.P2.X) { + var my = VectoMath.Interpolate(edge, nu); + return new TorqueConverterOperatingPoint { + InAngularVelocity = inAngularVelocity, + OutAngularVelocity = outAngularVelocity, + OutTorque = inTorque * my, + InTorque = inTorque, + SpeedRatio = nu, + TorqueRatio = my, + }; + } + } + throw new VectoSimulationException( + "Torque Converter: Failed to find operating point for outputSpeed/outputTorque/inputSpeed! n_out: {0}, n_in: {1}, tq_in: {2}", + outAngularVelocity, inAngularVelocity, inTorque); + } + + public TorqueConverterOperatingPoint FindOperatingPointForPowerDemand(Watt enginePower, PerSecond prevInputSpeed, + PerSecond nextOutputSpeed, KilogramSquareMeter inertia, Second dt, Watt previousPowerTC) { var solutions = new List<double>(); - var mpNorm = ReferenceSpeed.Value(); + 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); + + // Torque Converter: M_P1000 = k * n_out / n_in + d + // T_out = M_P1000 * (n_in / 1000rpm)^2 = (k * n_out / n_in + d) * (n_in / c)^2 + // P_eng_out = P_eng_inertia + P_TC_in_avg + // P_eng_inertia = I_eng * (n_2_eng^2 - n_1_eng^2) / (2 * dt) + // P_TC_in_avg = (T_in_1 * n_in_1 + T_in_2 * n_in_2) / 2 + // => solve for n_in + + var a = mpEdge.OffsetXY.SI<NewtonMeter>() / (2 * mpNorm * mpNorm); + var b = inertia / (2 * dt) + mpEdge.SlopeXY.SI<NewtonMeter>() * nextOutputSpeed / (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 d = -inertia * prevInputSpeed * prevInputSpeed / (2 * dt) - enginePower + + previousPowerTC / 2; + var sol = VectoMath.CubicEquationSolver(a.Value(), b.Value(), c, d.Value()); + //============================================================================ + + + /* + // Torque Converter: M_P1000 = k * n_out / n_in + d + // T_out = M_P1000 * (n_in / 1000rpm)^2 = (k * n_out / n_in + d) * (n_in / c)^2 + // P_eng_out = P_eng_inertia + P_TC_in_avg + // P_eng_inertia = I_eng * (n_2_eng^2 - n_1_eng^2) / (2 * dt) + // P_TC_in_avg = n_in_2 (T_in_1 * n_in_1 + T_in_2 * n_in_2) / (n_in_1 + n_in_2) + // (index _1: beginning of simulation interval, index _2: end of simulation interval) + // => solve for n_in + + var a = 2 * mpEdge.OffsetXY.SI<NewtonMeter>() * dt / (mpNorm * mpNorm); + var b = inertia + 2 * dt * nextOutputSpeed * mpEdge.SlopeXY.SI<NewtonMeter>() / (mpNorm * mpNorm); + var c = prevInputSpeed * inertia; + var d = 2 * dt * previousPowerTC - inertia * prevInputSpeed * prevInputSpeed - 2 * dt * enginePower; + var e = - inertia * prevInputSpeed * prevInputSpeed * prevInputSpeed - 2 * dt * prevInputSpeed * enginePower; + + var sol = VectoMath.Polynom4Solver(a.Value(), b.Value(), c.Value(), d.Value(), e.Value()); + //============================================================================ + */ + + // T_eng_o_2 + T_eng_I + T_aux - T_max) (n_in_1 + n_in_2) / 2 = 0 + //var a = dt * mpEdge.OffsetXY.SI<NewtonMeter>() / (mpNorm * mpNorm); + //var b = inertia + dt * mpEdge.SlopeXY.SI<NewtonMeter>() * nextOutputSpeed / (mpNorm * mpNorm); + //var c = dt * mpEdge.SlopeXY.SI<NewtonMeter>() * nextOutputSpeed * prevInputSpeed / (mpNorm * mpNorm) - inertia * prevInputSpeed * prevInputSpeed; + //var d = 2 * dt * enginePower; + + //var sol = VectoMath.CubicEquationSolver(a.Value(), b.Value(), c.Value(), d.Value()); var selected = sol.Where(x => x > 0 && nextOutputSpeed / x >= mpEdge.P1.X && nextOutputSpeed / x < mpEdge.P2.X); solutions.AddRange(selected); @@ -254,7 +312,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox if (solutions.Count == 0) { throw new VectoException( - "Failed to find operating point for power {0}, prevInputSpeed {1}, nextOutputSpeed {2}", power, + "Failed to find operating point for power {0}, prevInputSpeed {1}, nextOutputSpeed {2}", enginePower, prevInputSpeed, nextOutputSpeed); } solutions.Sort(); @@ -285,6 +343,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox return ValidationResult.Success; } + + } public class TorqueConverterOperatingPoint diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATGearbox.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATGearbox.cs index 76871676193a8cc905cb8b2e6b8e42f7a03acd4e..7811d57c540ecc4c54fdddf6839fec2bdce374c7 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATGearbox.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATGearbox.cs @@ -51,6 +51,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl private IIdleController _idleController; protected bool RequestAfterGearshift; + private WattSecond _powershiftLossEnergy; + public bool TorqueConverterLocked { get { return CurrentState.TorqueConverterLocked; } @@ -94,6 +96,16 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl get { return _strategy.NextGear; } } + #region Overrides of AbstractGearbox<ATGearboxState> + + public override uint Gear { get { return _gear; } protected internal set { _gear = value; + //if (PreviousState.Gear == value) { + // RequestAfterGearshift = false; + //} + } } + + #endregion + public override bool ClutchClosed(Second absTime) { return absTime.IsGreater(DataBus.AbsTime) || @@ -144,7 +156,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl return response; } - public override bool TCLocked { get { return PreviousState.TorqueConverterLocked; } } + public override bool TCLocked { get { return CurrentState.TorqueConverterLocked; } } internal ResponseDryRun Initialize(uint gear, bool torqueConverterLocked, NewtonMeter outTorque, PerSecond outAngularVelocity) @@ -195,7 +207,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Log.Debug("AT-Gearbox Power Request: torque: {0}, angularVelocity: {1}", outTorque, outAngularVelocity); var driveOffSpeed = DataBus.VehicleStopped && outAngularVelocity > 0; - var driveOffTorque = CurrentState.Disengaged && outTorque.IsGreater(0, 1e-2); + var driveOffTorque = CurrentState.Disengaged && outTorque.IsGreater(0, 1e-1); if (!dryRun && (driveOffSpeed || driveOffTorque)) { Gear = 1; CurrentState.TorqueConverterLocked = false; @@ -219,7 +231,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl if (!(retVal is ResponseGearShift)) { continue; } - if (ConsiderShiftLosses(_strategy.NextGear, outTorque)) { + if (ConsiderShiftLosses(_strategy.NextGear, outTorque) && !RequestAfterGearshift) { retVal = new ResponseFailTimeInterval { Source = this, DeltaT = ModelData.PowershiftShiftTime, @@ -241,13 +253,15 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl private void SetPowershiftLossEnergy(Second absTime, Second dt, NewtonMeter outTorque, PerSecond outAngularVelocity) { - if (RequestAfterGearshift) { + if (RequestAfterGearshift /*&& Gear != PreviousState.Gear*/) { LastShift = absTime; Gear = _strategy.Engage(absTime, dt, outTorque, outAngularVelocity); - CurrentState.PowershiftLossEnergy = ComputeShiftLosses(outTorque, outAngularVelocity); + _powershiftLossEnergy = ComputeShiftLosses(outTorque, outAngularVelocity); } else { if (PreviousState.PowershiftLossEnergy != null && PreviousState.PowershiftLossEnergy.IsGreater(0)) { - CurrentState.PowershiftLossEnergy = PreviousState.PowershiftLossEnergy; + _powershiftLossEnergy = PreviousState.PowershiftLossEnergy; + } else { + _powershiftLossEnergy = null; } } } @@ -278,15 +292,16 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl avgOutAngularVelocity : 0.SI<NewtonMeter>(); inTorque += inertiaTorqueLossOut / effectiveRatio; - - if (CurrentState.PowershiftLossEnergy != null) { + var powershiftLoss = 0.SI<NewtonMeter>(); + var aliquotEnergyLoss = 0.SI<WattSecond>(); + if (_powershiftLossEnergy != null) { var remainingShiftLossLime = ModelData.PowershiftShiftTime - (absTime - LastShift); if (remainingShiftLossLime.IsGreater(0)) { - var aliquotEnergyLoss = CurrentState.PowershiftLossEnergy * VectoMath.Min(1.0, dt / remainingShiftLossLime); - var avgEngineSpeed = (DataBus.EngineSpeed + outAngularVelocity * ModelData.Gears[Gear].Ratio) / 2; - CurrentState.PowershiftLoss = aliquotEnergyLoss / dt / avgEngineSpeed; - inTorque += CurrentState.PowershiftLoss; - CurrentState.PowershiftLossEnergy -= aliquotEnergyLoss; + aliquotEnergyLoss = _powershiftLossEnergy * VectoMath.Min(1.0, dt / remainingShiftLossLime); + var avgEngineSpeed = (DataBus.EngineSpeed + outAngularVelocity * effectiveRatio) / 2; + powershiftLoss = aliquotEnergyLoss / dt / avgEngineSpeed; + inTorque += powershiftLoss; + //inTorque += CurrentState.PowershiftLossEnergy; } } @@ -297,6 +312,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl CurrentState.SetState(inTorque, inAngularVelocity, outTorque, outAngularVelocity); CurrentState.Gear = Gear; CurrentState.TransmissionTorqueLoss = inTorque * effectiveRatio - outTorque; + CurrentState.PowershiftLoss = powershiftLoss; + CurrentState.PowershiftLossEnergy = _powershiftLossEnergy ?? 0.SI<WattSecond>() - aliquotEnergyLoss; TorqueConverter.Locked(CurrentState.InTorque, CurrentState.InAngularVelocity, CurrentState.InTorque, CurrentState.InAngularVelocity); } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATShiftStrategy.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATShiftStrategy.cs index 92af6aeb2f46cb1d49307533f9de0668bbb3fc1c..fff47730f30f6e5f913791adfac0e4864a1c5f00 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATShiftStrategy.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ATShiftStrategy.cs @@ -34,6 +34,7 @@ using System.Linq; using TUGraz.VectoCommon.Exceptions; using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.Configuration; +using TUGraz.VectoCore.Models.Connector.Ports.Impl; using TUGraz.VectoCore.Models.Simulation.DataBus; using TUGraz.VectoCore.Models.SimulationComponent.Data; using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox; @@ -149,7 +150,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } // EMERGENCY SHIFTS --------------------------------------- - if (CheckEmergencyShift(absTime, inAngularVelocity, gear)) { + if (CheckEmergencyShift(absTime, outTorque, outAngularVelocity, inAngularVelocity, gear)) { return true; } @@ -168,9 +169,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl return false; } - private bool CheckEmergencyShift(Second absTime, PerSecond inAngularVelocity, uint gear) + private bool CheckEmergencyShift(Second absTime, NewtonMeter outTorque, PerSecond outAngularVelocity, PerSecond inAngularVelocity, uint gear) { -// Emergency Downshift: if lower than engine idle speed + // Emergency Downshift: if lower than engine idle speed if (inAngularVelocity.IsSmaller(DataBus.EngineIdleSpeed)) { Log.Debug("engine speed would fall below idle speed - shift down"); Downshift(absTime, gear); @@ -182,9 +183,21 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl if (!ModelData.Gears.ContainsKey(gear + 1)) { return false; } - Log.Debug("engine speed would be above max speed / rated speed - shift up"); - Upshift(absTime, gear); - return true; + + PerSecond nextInAngularSpeed; + NewtonMeter nextInTorque; + if (ModelData.Gears[gear].HasLockedGear) { + nextInAngularSpeed = outAngularVelocity * ModelData.Gears[gear].Ratio; + nextInTorque = outTorque / ModelData.Gears[gear].Ratio; + } else { + nextInAngularSpeed = outAngularVelocity * ModelData.Gears[gear + 1].Ratio; + nextInTorque = outTorque / ModelData.Gears[gear + 1].Ratio; + } + if (!IsBelowDownShiftCurve(gear + 1, nextInTorque, nextInAngularSpeed)) { + Log.Debug("engine speed would be above max speed / rated speed - shift up"); + Upshift(absTime, gear); + return true; + } } return false; } @@ -270,7 +283,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var minAcceleration = _gearbox.TorqueConverterLocked ? ModelData.UpshiftMinAcceleration : ModelData.TorqueConverterData.CLUpshiftMinAcceleration; - minAcceleration = VectoMath.Min(minAcceleration, DataBus.DriverAcceleration); + minAcceleration = VectoMath.Min(minAcceleration, VectoMath.Max(0.SI<MeterPerSquareSecond>(), DataBus.DriverAcceleration)); minAccelerationReachable = reachableAcceleration.IsGreaterOrEqual(minAcceleration); } @@ -330,9 +343,36 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl return true; } + if (shiftTimeReached && DataBus.DrivingAction == DrivingAction.Accelerate) { + if (DataBus.VehicleSpeed < DataBus.CycleData.LeftSample.VehicleTargetSpeed - 10.KMPHtoMeterPerSecond() && DataBus.DriverAcceleration < 0.SI<MeterPerSquareSecond>()) { + var tmpResponseCurr = (ResponseDryRun)_gearbox.Request(absTime, dt, outTorque, outAngularVelocity, true); + if (_gearbox.Gear > 1 || _gearbox.Gear == 1 && _gearbox.TorqueConverterLocked) { + var tmpCurr = _nextGear.Clone(); + var tmpGbxState = new NextGearState(absTime, _gearbox); + + Downshift(absTime, gear); + SetGear(_nextGear); + var tmpResponseDs = (ResponseDryRun)_gearbox.Request(absTime, dt, outTorque, outAngularVelocity, true); + _nextGear.SetState(tmpCurr); + SetGear(tmpGbxState); + if (tmpResponseDs.DeltaFullLoad < tmpResponseCurr.DeltaFullLoad) { + Downshift(absTime, gear); + return true; + } + } + } + } + return false; } + private void SetGear(NextGearState gbxState) + { + _gearbox.Gear = gbxState.Gear; + _gearbox.TorqueConverterLocked = gbxState.TorqueConverterLocked; + _gearbox.Disengaged = gbxState.Disengaged; + } + /// <summary> /// Tests if the operating point is below (left of) the down-shift curve. /// </summary> @@ -372,6 +412,21 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl public uint Gear; public bool TorqueConverterLocked; + public NextGearState() { } + + private NextGearState(NextGearState nextGearState) + { + AbsTime = nextGearState.AbsTime; + Disengaged = nextGearState.Disengaged; + Gear = nextGearState.Gear; + TorqueConverterLocked = nextGearState.TorqueConverterLocked; + } + + public NextGearState(Second absTime, ATGearbox gearbox) + { + SetState(absTime, gearbox); + } + public void SetState(Second absTime, bool disengaged, uint gear, bool tcLocked) { AbsTime = absTime; @@ -379,6 +434,27 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Gear = gear; TorqueConverterLocked = tcLocked; } + + public void SetState(NextGearState state) + { + AbsTime = state.AbsTime; + Disengaged = state.Disengaged; + Gear = state.Gear; + TorqueConverterLocked = state.TorqueConverterLocked; + } + + public void SetState(Second absTime, ATGearbox gearbox) + { + AbsTime = absTime; + Disengaged = gearbox.Disengaged; + Gear = gearbox.Gear; + TorqueConverterLocked = gearbox.TorqueConverterLocked; + } + + public NextGearState Clone() + { + return new NextGearState(this); + } } } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/AbstractGearbox.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/AbstractGearbox.cs index 1feacbaf805dd0b08821796f2e9e093c7d56e8ae..56463b1622cc7fd700d31371cea1105086c2b35c 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/AbstractGearbox.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/AbstractGearbox.cs @@ -52,6 +52,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl /// </summary> [Required, ValidateObject] internal readonly GearboxData ModelData; + protected uint _gear; + protected AbstractGearbox(IVehicleContainer container, VectoRunData runData) : base(container) { ModelData = runData.GearboxData; @@ -76,7 +78,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl /// <summary> /// The current gear. /// </summary> - public uint Gear { get; protected internal set; } + public virtual uint Gear + { + get { return _gear; } + protected internal set { _gear = value; } + } public abstract bool TCLocked { get; } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs index 4ca611871051c3f26d46030911e3a9c430c0ef5f..09c9ba2e97e72ff144db6df86516f33db8169173 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/DefaultDriverStrategy.cs @@ -402,7 +402,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl velocity += DriverData.OverSpeedEcoRoll.OverSpeed; } if (DataBus.GearboxType.AutomaticTransmission() || DataBus.ClutchClosed(absTime)) { - return HandleRequestEngaged(absTime, ds, targetVelocity, gradient, prohibitOverspeed, velocity, debug); + for (var i = 0; i < 3; i++) { + var retVal = HandleRequestEngaged(absTime, ds, targetVelocity, gradient, prohibitOverspeed, velocity, debug); + if (retVal != null) { + return retVal; + } + } + throw new VectoException("HandleRequestEngaged found no operating point."); } else { return HandleRequestDisengaged(absTime, ds, gradient, velocity, debug); } @@ -459,6 +465,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl second = Driver.DrivingActionBrake(absTime, ds, targetVelocity, gradient, r); }); + if (second == null) { + return null; + } var third = second; second.Switch(). @@ -585,7 +594,14 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl bool prohibitOverspeed = false) { if (DataBus.VehicleSpeed <= DriverStrategy.BrakeTrigger.NextTargetSpeed) { - return HandleTargetspeedReached(absTime, ds, targetVelocity, gradient); + var retVal = HandleTargetspeedReached(absTime, ds, targetVelocity, gradient); + for (var i = 0; i < 3 && retVal == null; i++) { + retVal = HandleTargetspeedReached(absTime, ds, targetVelocity, gradient); + } + + if (retVal == null) { + throw new VectoException("Failed to find operating point!"); + } } var currentDistance = DataBus.Distance; @@ -628,6 +644,17 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Driver.DriverBehavior = DrivingBehavior.Braking; response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient, targetDistance: targetDistance); + + if (DataBus.GearboxType.AutomaticTransmission() && response == null) { + for (var i = 0; i < 3 && response == null; i++) { + response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient, targetDistance: targetDistance); + } + + if (response == null) { + throw new VectoException("No valid operating point found"); + } + } response.Switch(). Case<ResponseOverload>(r => { Log.Info( @@ -780,30 +807,34 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl // gradient, r); response = Driver.DrivingActionBrake(absTime, ds, DataBus.VehicleSpeed + r.Acceleration * r.SimulationInterval, gradient, r); - response.Switch(). - Case<ResponseGearShift>(() => { - DataBus.BrakePower = 0.SI<Watt>(); - response = Driver.DrivingActionBrake(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, - gradient, r); - }). - Case<ResponseOverload>(() => { - DataBus.BrakePower = 0.SI<Watt>(); - if (DataBus.GearboxType.AutomaticTransmission() || DataBus.ClutchClosed(absTime)) { - if (DataBus.VehicleSpeed.IsGreater(0)) { - response = Driver.DrivingActionAccelerate(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); - } else { - if (RetryDistanceExceeded) { - response = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); + if (response != null) { + response.Switch().Case<ResponseGearShift>( + () => { + DataBus.BrakePower = 0.SI<Watt>(); + response = Driver.DrivingActionBrake( + absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, + gradient, r); + }).Case<ResponseOverload>( + () => { + DataBus.BrakePower = 0.SI<Watt>(); + if (DataBus.GearboxType.AutomaticTransmission() || DataBus.ClutchClosed(absTime)) { + if (DataBus.VehicleSpeed.IsGreater(0)) { + response = Driver.DrivingActionAccelerate( + absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); } else { - RetryDistanceExceeded = true; - response = new ResponseDrivingCycleDistanceExceeded() { MaxDistance = ds / 2 }; + if (RetryDistanceExceeded) { + response = Driver.DrivingActionAccelerate(absTime, ds, targetVelocity, gradient); + } else { + RetryDistanceExceeded = true; + response = new ResponseDrivingCycleDistanceExceeded() { MaxDistance = ds / 2 }; + } } + } else { + response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); } - } else { - response = Driver.DrivingActionRoll(absTime, ds, DriverStrategy.BrakeTrigger.NextTargetSpeed, gradient); - } - }); - }); + }); + } + }); //} while (!(response is ResponseSuccess) && i++ < 3); return response; } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/Driver.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/Driver.cs index 0b123606072091d6db2aea310991c3d2feba7fff..34bfd9b2a0b0fd7f05b59f4b23d6c63927da96b3 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/Driver.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/Driver.cs @@ -141,7 +141,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Radian gradient, IResponse previousResponse = null) { - DrivingAction = DrivingAction.Accelerate; + DrivingAction = DrivingAction.Accelerate; IterationStatistics.Increment(this, "Accelerate"); Log.Debug("DrivingAction Accelerate"); var operatingPoint = ComputeAcceleration(ds, targetVelocity); @@ -214,6 +214,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl Log.Debug("Found operating point for Drive/Accelerate. dt: {0}, acceleration: {1}", limitedOperatingPoint.SimulationInterval, limitedOperatingPoint.Acceleration); } + if (limitedOperatingPoint == null) { + throw new VectoException("DrivingActionAccelerate: Failed to find operating point"); + } DriverAcceleration = limitedOperatingPoint.Acceleration; retVal = NextComponent.Request(absTime, limitedOperatingPoint.SimulationInterval, limitedOperatingPoint.Acceleration, @@ -235,6 +238,14 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl DriverAcceleration = nextOperatingPoint.Acceleration; retVal = NextComponent.Request(absTime, nextOperatingPoint.SimulationInterval, nextOperatingPoint.Acceleration, gradient); + retVal.Switch().Case<ResponseFailTimeInterval>( + rt => { + // occurs only with AT gearboxes - extend time interval after gearshift! + retVal = new ResponseDrivingCycleDistanceExceeded { + Source = this, + MaxDistance = DriverAcceleration / 2 * rt.DeltaT * rt.DeltaT + DataBus.VehicleSpeed * rt.DeltaT + }; + }); } else { if (absTime > 0 && DataBus.VehicleStopped) { Log.Info( @@ -264,7 +275,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } } } + retVal.Acceleration = operatingPoint.Acceleration; retVal.Switch(). + Case<ResponseDrivingCycleDistanceExceeded>(). Case<ResponseSuccess>(() => operatingPoint = nextOperatingPoint). Case<ResponseGearShift>(() => operatingPoint = nextOperatingPoint). Case<ResponseOverload>( @@ -274,7 +287,22 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl DriverAcceleration = nextOperatingPoint.Acceleration; retVal = NextComponent.Request(absTime, nextOperatingPoint.SimulationInterval, nextOperatingPoint.Acceleration, gradient); + retVal.Switch().Case<ResponseFailTimeInterval>( + rt => { + // occurs only with AT gearboxes - extend time interval after gearshift! + retVal = new ResponseDrivingCycleDistanceExceeded { + Source = this, + MaxDistance = DriverAcceleration / 2 * rt.DeltaT * rt.DeltaT + DataBus.VehicleSpeed * rt.DeltaT + }; + }); }). + Case<ResponseFailTimeInterval>(r => { + // occurs only with AT gearboxes - extend time interval after gearshift! + retVal = new ResponseDrivingCycleDistanceExceeded { + Source = this, + MaxDistance = r.Acceleration / 2 * r.DeltaT * r.DeltaT + DataBus.VehicleSpeed * r.DeltaT + }; + }). Default( r => { throw new UnexpectedResponseException("DrivingAction Accelerate after Overload", r); @@ -532,9 +560,15 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } DriverAcceleration = operatingPoint.Acceleration; + var gear = DataBus.Gear; + var tcLocked = DataBus.TCLocked; retVal = NextComponent.Request(absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, gradient); - + var gearChanged = !(DataBus.Gear == gear && DataBus.TCLocked == tcLocked); + if (DataBus.GearboxType.AutomaticTransmission() && gearChanged && retVal is ResponseOverload) { + Log.Debug("Gear changed after a valid operating point was found - braking is no longer applicable due to overload"); + return null; + } retVal.Switch(). Case<ResponseSuccess>(). Case<ResponseGearShift>(). @@ -559,8 +593,16 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var i = 5; while (i-- > 0 && !(retVal is ResponseSuccess)) { DataBus.BrakePower = 0.SI<Watt>(); + + retVal = NextComponent.Request( + absTime, operatingPoint.SimulationInterval, operatingPoint.Acceleration, + gradient); + if (retVal is ResponseSuccess) { + break; + } + operatingPoint = SearchBrakingPower(absTime, operatingPoint.SimulationDistance, gradient, - operatingPoint.Acceleration, response); + operatingPoint.Acceleration, retVal); DriverAcceleration = operatingPoint.Acceleration; if (DataBus.BrakePower.IsSmaller(0)) { DataBus.BrakePower = 0.SI<Watt>(); @@ -580,6 +622,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl throw new UnexpectedResponseException( "DrivingAction Brake: request failed after braking power was found.", r); }); + CurrentState.Acceleration = operatingPoint.Acceleration; CurrentState.dt = operatingPoint.SimulationInterval; CurrentState.Response = retVal; @@ -587,7 +630,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl retVal.SimulationInterval = operatingPoint.SimulationInterval; retVal.SimulationDistance = ds; retVal.OperatingPoint = operatingPoint; - + return retVal; } @@ -830,6 +873,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl if (actionRoll) { initialResponse.Switch(). Case<ResponseDryRun>(r => origDelta = r.GearboxPowerRequest). + Case<ResponseOverload>(r => origDelta = r.Delta). Case<ResponseFailTimeInterval>(r => origDelta = r.GearboxPowerRequest). Default(r => { throw new UnexpectedResponseException("SearchOperatingPoint: Unknown response type.", r); diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/TorqueConverter.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/TorqueConverter.cs index 0e5eb487fad515148cd93b1824c8e383d72179ac..320a6c98de08b789a5bf725ef9908250a784577f 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/TorqueConverter.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/TorqueConverter.cs @@ -164,10 +164,21 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var avgOutSpeedMin = (PreviousState.OutAngularVelocity + dryOperatingPointMin.OutAngularVelocity) / 2.0; var deltaMin = (outTorque - dryOperatingPointMin.OutTorque) * avgOutSpeedMin; + var inTorqueMin = + (PreviousState.InAngularVelocity * PreviousState.InTorque + + dryOperatingPointMin.InAngularVelocity * dryOperatingPointMin.InTorque) / + (PreviousState.InAngularVelocity + dryOperatingPointMin.InAngularVelocity); + var engRespMin = (ResponseDryRun) + NextComponent.Request(absTime, dt, inTorqueMin, dryOperatingPointMin.InAngularVelocity, true); + + //var tqMin = 2.0 * engineResponse.DeltaDragLoad / (operatingPoint.InAngularVelocity + DataBus.EngineSpeed); + //var operatingPointMin = ModelData.LookupOperatingPointOut( + // outAngularVelocity, operatingPoint.InAngularVelocity, tqMin); + return new ResponseDryRun { Source = this, DeltaFullLoad = 2 * deltaMax, - DeltaDragLoad = 2 * deltaMin, + DeltaDragLoad = 2*deltaMin, TorqueConverterOperatingPoint = dryOperatingPointMax }; } @@ -179,7 +190,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var operatingPoint = ModelData.FindOperatingPointForPowerDemand( engineResponse.DragPower - engineResponse.AuxiliariesPowerDemand, DataBus.EngineSpeed, outAngularVelocity, _engineInertia, dt, previousPower); - var maxInputSpeed = VectoMath.Min(ModelData.TorqueConverterSpeedLimit, DataBus.EngineRatedSpeed); + var maxInputSpeed = VectoMath.Min(ModelData.TorqueConverterSpeedLimit, DataBus.EngineN95hSpeed); if (operatingPoint.InAngularVelocity.IsGreater(maxInputSpeed)) { operatingPoint = ModelData.FindOperatingPoint(maxInputSpeed, outAngularVelocity); } @@ -202,7 +213,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var operatingPoint = ModelData.FindOperatingPointForPowerDemand( engineResponse.DynamicFullLoadPower - engineResponse.AuxiliariesPowerDemand, DataBus.EngineSpeed, outAngularVelocity, _engineInertia, dt, previousPower); - var maxInputSpeed = VectoMath.Min(ModelData.TorqueConverterSpeedLimit, DataBus.EngineRatedSpeed); + var maxInputSpeed = VectoMath.Min(ModelData.TorqueConverterSpeedLimit, DataBus.EngineN95hSpeed); if (operatingPoint.InAngularVelocity.IsGreater(maxInputSpeed)) { operatingPoint = ModelData.FindOperatingPoint(maxInputSpeed, outAngularVelocity); } @@ -233,7 +244,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl "TorqueConverter: Invalid operating point, inAngularVelocity would be below engine's idle speed: {0}", operatingPoint.InAngularVelocity); } - var maxInputSpeed = VectoMath.Min(ModelData.TorqueConverterSpeedLimit, DataBus.EngineRatedSpeed); + var maxInputSpeed = VectoMath.Min(ModelData.TorqueConverterSpeedLimit, DataBus.EngineN95hSpeed); if (operatingPoint.InAngularVelocity.IsGreater(maxInputSpeed)) { operatingPoint = ModelData.FindOperatingPoint(maxInputSpeed, outAngularVelocity); } diff --git a/VectoCore/VectoCore/Utils/SwitchExtension.cs b/VectoCore/VectoCore/Utils/SwitchExtension.cs index d4582b9456c7da2811271496d879c25ae67f7871..1e58df7453b65df440c575bb4f381160f1ee54c5 100644 --- a/VectoCore/VectoCore/Utils/SwitchExtension.cs +++ b/VectoCore/VectoCore/Utils/SwitchExtension.cs @@ -29,96 +29,96 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; -using System.Diagnostics; - -namespace TUGraz.VectoCore.Utils -{ - /// <summary> - /// Extension Methods for Creating a Switch-Case Construct on Types. - /// </summary> - /// <remarks> - /// Adapted for VECTO. Created by Virtlink. Original source code on GitHub: <see href="https://gist.github.com/Virtlink/8722649"/>. - /// </remarks> - public static class SwitchExtension - { - /// <summary> - /// Switches on the type. - /// With ".Case" you can define single alternatives (only the first suitable case will be executed). - /// With ".If" you can define multiple type-conditionals (every suitable if will be executed) - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="self">The self.</param> - /// <returns></returns> - [DebuggerHidden] - public static Switch<T> Switch<T>(this T self) - { - return new Switch<T>(self); - } - } - - public class Switch<T> - { - private readonly T _value; - private bool _handled; - - [DebuggerHidden] - internal Switch(T value) - { - _value = value; - _handled = false; - } - - [DebuggerHidden] - public Switch<T> Case<TFilter>(Action action) where TFilter : T - { - return Case<TFilter>(_ => action()); - } - - [DebuggerHidden] - public Switch<T> Case<TFilter>() where TFilter : T - { - return Case<TFilter>(() => { }); - } - - - [DebuggerHidden] - public Switch<T> Case<TFilter>(Action<TFilter> action) where TFilter : T - { - if (!_handled && _value.GetType() == typeof(TFilter)) { - action((TFilter)_value); - _handled = true; - } - return this; - } - - /// <summary> - /// Does the action if the type is fullfilled and continues the evaluation. - /// </summary> - /// <typeparam name="TFilter">The type of the filter.</typeparam> - /// <param name="action">The action.</param> - /// <returns></returns> - [DebuggerHidden] - public Switch<T> If<TFilter>(Action<TFilter> action) where TFilter : class - { - if (_value is TFilter) { - action(_value as TFilter); - } - return this; - } - - [DebuggerHidden] - public void Default(Action action) - { - Default(_ => action()); - } - - [DebuggerHidden] - public void Default(Action<T> action) - { - if (!_handled) { - action(_value); - } - } - } +using System; +using System.Diagnostics; + +namespace TUGraz.VectoCore.Utils +{ + /// <summary> + /// Extension Methods for Creating a Switch-Case Construct on Types. + /// </summary> + /// <remarks> + /// Adapted for VECTO. Created by Virtlink. Original source code on GitHub: <see href="https://gist.github.com/Virtlink/8722649"/>. + /// </remarks> + public static class SwitchExtension + { + /// <summary> + /// Switches on the type. + /// With ".Case" you can define single alternatives (only the first suitable case will be executed). + /// With ".If" you can define multiple type-conditionals (every suitable if will be executed) + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="self">The self.</param> + /// <returns></returns> + [DebuggerHidden] + public static Switch<T> Switch<T>(this T self) + { + return new Switch<T>(self); + } + } + + public class Switch<T> + { + private readonly T _value; + private bool _handled; + + [DebuggerHidden] + internal Switch(T value) + { + _value = value; + _handled = false; + } + + [DebuggerHidden] + public Switch<T> Case<TFilter>(Action action) where TFilter : T + { + return Case<TFilter>(_ => action()); + } + + [DebuggerHidden] + public Switch<T> Case<TFilter>() where TFilter : T + { + return Case<TFilter>(() => { }); + } + + + [DebuggerHidden] + public Switch<T> Case<TFilter>(Action<TFilter> action) where TFilter : T + { + if (!_handled && _value.GetType() == typeof(TFilter)) { + action((TFilter)_value); + _handled = true; + } + return this; + } + + /// <summary> + /// Does the action if the type is fullfilled and continues the evaluation. + /// </summary> + /// <typeparam name="TFilter">The type of the filter.</typeparam> + /// <param name="action">The action.</param> + /// <returns></returns> + [DebuggerHidden] + public Switch<T> If<TFilter>(Action<TFilter> action) where TFilter : class + { + if (_value is TFilter) { + action(_value as TFilter); + } + return this; + } + + [DebuggerHidden] + public void Default(Action action) + { + Default(_ => action()); + } + + [DebuggerHidden] + public void Default(Action<T> action) + { + if (!_handled) { + action(_value); + } + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCoreTest/Models/SimulationComponentData/TorqueConverterDataTest.cs b/VectoCore/VectoCoreTest/Models/SimulationComponentData/TorqueConverterDataTest.cs index 140b237fd199cd1d23da94a5335ad4158a41c746..28e2000b99f2520b67aae12235e6364592eb6b84 100644 --- a/VectoCore/VectoCoreTest/Models/SimulationComponentData/TorqueConverterDataTest.cs +++ b/VectoCore/VectoCoreTest/Models/SimulationComponentData/TorqueConverterDataTest.cs @@ -461,11 +461,11 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData var tmp = tqData.FindOperatingPoint(operatingPoint.OutTorque, operatingPoint.OutAngularVelocity, 0.RPMtoRad()); var backward = tmp.First(); - Debug.WriteLine(operatingPoint); - Debug.WriteLine(operatingPoint.InAngularVelocity * operatingPoint.InTorque); - - Debug.WriteLine(backward); - Debug.WriteLine(backward.InAngularVelocity * backward.InTorque); + Console.WriteLine(operatingPoint); + Console.WriteLine(operatingPoint.InAngularVelocity * operatingPoint.InTorque); + + Console.WriteLine(backward); + Console.WriteLine(backward.InAngularVelocity * backward.InTorque); Assert.AreEqual(backward.OutAngularVelocity.Value(), operatingPoint.OutAngularVelocity.Value(), 1e-9); Assert.AreEqual(backward.OutTorque.Value(), operatingPoint.OutTorque.Value(), 1e-9); diff --git a/VectoCore/VectoCoreTest/Reports/ModDataTest.cs b/VectoCore/VectoCoreTest/Reports/ModDataTest.cs index 1336ab4f911072183ce0cbc5504d0af1f67a691f..8487f426b3eaf4d6601e59115e30015c3e900aa7 100644 --- a/VectoCore/VectoCoreTest/Reports/ModDataTest.cs +++ b/VectoCore/VectoCoreTest/Reports/ModDataTest.cs @@ -527,14 +527,14 @@ namespace TUGraz.VectoCore.Tests.Reports foreach (var modalResults in modData) { AssertModDataIntegrityAT(modalResults.Item1, auxKeys, modalResults.Item2, - FuelConsumptionMapReader.Create(((IEngineeringInputDataProvider)inputData).JobInputData.Vehicle.EngineInputData.FuelConsumptionMap)); + FuelConsumptionMapReader.Create(((IEngineeringInputDataProvider)inputData).JobInputData.Vehicle.EngineInputData.FuelConsumptionMap), true); } AssertSumDataIntegrity(sumData, ExecutionMode.Engineering, true); } private static void AssertModDataIntegrityAT(ModalResults modData, Dictionary<string, DataColumn> auxKeys, - Meter totalDistance, FuelConsumptionMap consumptionMap) + Meter totalDistance, FuelConsumptionMap consumptionMap, bool atGbx) { Assert.IsTrue(modData.Rows.Count > 0); @@ -623,7 +623,7 @@ namespace TUGraz.VectoCore.Tests.Reports "time: {0} distance: {1}", time, distance); // P_eng_fcmap = P_eng_out + P_AUX + P_eng_inertia ( + P_PTO_Transm + P_PTO_Consumer ) - Assert.AreEqual(pEngFcmap.Value(), (pEngOut + pAux + pEngInertia + pPTOtransm + pPTOconsumer).Value(), 1E-3, + Assert.AreEqual(pEngFcmap.Value(), (pEngOut + pAux + pEngInertia + pPTOtransm + pPTOconsumer).Value(), atGbx ? 1E-1 : 1e-3, "time: {0} distance: {1}", time, distance); @@ -631,7 +631,7 @@ namespace TUGraz.VectoCore.Tests.Reports var pLossTot = pTcLoss + pLossGbx + pLossRet + pGbxInertia + pLossAngle + pLossAxle + pBrakeLoss + pWheelInertia + pAir + pRoll + pGrad + pVehInertia + pPTOconsumer + pPTOtransm; - Assert.AreEqual(pEngFcmap.Value(), (pLossTot + pEngInertia + pAux).Value(), 1E-3, "time: {0} distance: {1}", time, + Assert.AreEqual(pEngFcmap.Value(), (pLossTot + pEngInertia + pAux).Value(), atGbx ? 1E-1 : 1e-3, "time: {0} distance: {1}", time, distance); Assert.IsTrue(pLossGbx.IsGreaterOrEqual(pShiftLoss + pGbxInertia, 0.5.SI<Watt>()), "time: {0} distance: {1}", time, diff --git a/VectoCore/VectoCoreTest/Utils/VectoMathTest.cs b/VectoCore/VectoCoreTest/Utils/VectoMathTest.cs index 4940aba3de9513255d04dfb2fded45aa314050aa..8c8870a8869d716d37efb47c3ca73ccdd14859c9 100644 --- a/VectoCore/VectoCoreTest/Utils/VectoMathTest.cs +++ b/VectoCore/VectoCoreTest/Utils/VectoMathTest.cs @@ -104,6 +104,32 @@ namespace TUGraz.VectoCore.Tests.Utils } } + [TestCase(1, 6, 18, 30, 25, new double[] { }), // only complex-valued solutions + TestCase(1, 6, -18, 30, -25, new[] { 1.31684387048749, -8.55416218029519 }), // two complex, two real-valued + TestCase(1, 11, 41, 61, 30, new double[] {-5, -3, -2, -1}), + TestCase(1, 0, -4, 0, 3, new[] { 1.73205080756888, 1, -1.73205080756888, -1 }), // biquadratic + TestCase(5, 0, -20, 0, 15, new[] { 1.73205080756888, 1, -1.73205080756888, -1 }), + TestCase(1, 1, 1, 1, 1, new double[] { }), // only complex solutions + TestCase(1, 2, -14, 2, 1, new[] { 2.76090563295441601 , 0.362199992663244539, -0.203258341626567109 , -4.91984728399109344 }), + TestCase(16, 8,-16,-8,1, new[] { 0.1045284632676534713998341548025, 0.9781476007338056379285667478696, -0.91354545764260089550212757198532, -0.66913060635885821382627333068678 }) + ] + public void Polynom4SolverTest(double a, double b, double c, double d, double e, double[] expected) + { + var results = VectoMath.Polynom4Solver(a, b, c, d, e); + + Console.WriteLine(string.Join(", ", results)); + + Assert.AreEqual(expected.Length, results.Length); + + Array.Sort(expected); + Array.Sort(results); + var comparison = expected.Zip(results, (exp, result) => exp - result); + foreach (var comp in comparison) { + Assert.AreEqual(0, comp, 1e-12); + } + } + + [TestCase()] public void TestLeastSquaresFittingExact() {