From 123aeb323ba700119f23ddbeee62eb6c50464209 Mon Sep 17 00:00:00 2001 From: Markus Quaritsch <markus.quaritsch@tugraz.at> Date: Thu, 25 Feb 2016 13:08:11 +0100 Subject: [PATCH] refactoring crosswind correction classes: - separate reader and crosswind airdrag losses - renaming files - adapting tests --- .../DeclarationDataAdapter.cs | 16 +- .../EngineeringDataAdapter.cs | 20 +- .../Data/CrossWindCorrectionCurve.cs | 171 ----------------- .../Data/CrossWindCorrectionCurveReader.cs | 179 ++++++++++++++++++ .../Data/CrosswindCorrectionCdxALookup.cs | 57 ++++++ .../Data/CrosswindCorrectionVAirBeta.cs | 73 +++++++ .../Data/ICrossWindCorrection.cs | 2 + .../Data/VAirBetaCrosswindCorrection.cs | 116 ------------ VectoCore/VectoCore.csproj | 5 +- VectoCoreTest/Integration/CoachPowerTrain.cs | 4 +- .../SimulationRuns/FullPowertrain.cs | 2 +- .../SimulationRuns/MinimalPowertrain.cs | 4 +- .../Integration/Truck40tPowerTrain.cs | 2 +- .../Models/Declaration/DeclarationDataTest.cs | 4 +- .../Simulation/MeasuredSpeedModeTest.cs | 3 +- .../Models/SimulationComponent/DriverTest.cs | 2 +- .../Models/SimulationComponent/VehicleTest.cs | 31 +-- 17 files changed, 367 insertions(+), 324 deletions(-) delete mode 100644 VectoCore/Models/SimulationComponent/Data/CrossWindCorrectionCurve.cs create mode 100644 VectoCore/Models/SimulationComponent/Data/CrossWindCorrectionCurveReader.cs create mode 100644 VectoCore/Models/SimulationComponent/Data/CrosswindCorrectionCdxALookup.cs create mode 100644 VectoCore/Models/SimulationComponent/Data/CrosswindCorrectionVAirBeta.cs delete mode 100644 VectoCore/Models/SimulationComponent/Data/VAirBetaCrosswindCorrection.cs diff --git a/VectoCore/InputData/Reader/DataObjectAdaper/DeclarationDataAdapter.cs b/VectoCore/InputData/Reader/DataObjectAdaper/DeclarationDataAdapter.cs index 9d694fb158..f18d2a24d7 100644 --- a/VectoCore/InputData/Reader/DataObjectAdaper/DeclarationDataAdapter.cs +++ b/VectoCore/InputData/Reader/DataObjectAdaper/DeclarationDataAdapter.cs @@ -87,7 +87,9 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdaper ? data.AirDragAreaRigidTruck : data.AirDragArea; - retVal.CrossWindCorrectionCurve = GetDeclarationAirResistanceCurve(retVal.VehicleCategory, aerodynamicDragAera); + retVal.CrossWindCorrectionCurve = + new CrosswindCorrectionCdxALookup(GetDeclarationAirResistanceCurve(retVal.VehicleCategory, aerodynamicDragAera), + CrossWindCorrectionMode.DeclarationModeCorrection); var axles = data.Axles; if (axles.Count < mission.AxleWeightDistribution.Length) { throw new VectoException("Vehicle does not contain sufficient axles. {0} axles defined, {1} axles required", @@ -253,12 +255,12 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdaper } - public static CrossWindCorrectionCurve GetDeclarationAirResistanceCurve(VehicleCategory vehicleCategory, - SquareMeter aerodynamicDragAera) + public static List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> GetDeclarationAirResistanceCurve( + VehicleCategory vehicleCategory, SquareMeter aerodynamicDragAera) { var values = DeclarationData.AirDrag.Lookup(vehicleCategory); - var points = new List<CrossWindCorrectionCurve.CrossWindCorrectionEntry> { - new CrossWindCorrectionCurve.CrossWindCorrectionEntry { + var points = new List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> { + new CrossWindCorrectionCurveReader.CrossWindCorrectionEntry { Velocity = 0.SI<MeterPerSecond>(), EffectiveCrossSectionArea = 0.SI<SquareMeter>() } @@ -281,14 +283,14 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdaper // cdASum += degreeShare * cdA * (vAir * vAir / (vVeh * vVeh)).Cast<Scalar>(); cdASum += degreeShare * cdA * ((vAirX * vAirX + vAirY * vAirY) / (vVeh * vVeh)).Cast<Scalar>(); } - points.Add(new CrossWindCorrectionCurve.CrossWindCorrectionEntry { + points.Add(new CrossWindCorrectionCurveReader.CrossWindCorrectionEntry { Velocity = vVeh, EffectiveCrossSectionArea = cdASum }); } points[0].EffectiveCrossSectionArea = points[1].EffectiveCrossSectionArea; - return new CrossWindCorrectionCurve(points, CrossWindCorrectionMode.DeclarationModeCorrection); + return points; } protected static SquareMeter ComputeDeltaCd(double beta, AirDrag.AirDragEntry values) diff --git a/VectoCore/InputData/Reader/DataObjectAdaper/EngineeringDataAdapter.cs b/VectoCore/InputData/Reader/DataObjectAdaper/EngineeringDataAdapter.cs index 808ab0cda8..3f033f1550 100644 --- a/VectoCore/InputData/Reader/DataObjectAdaper/EngineeringDataAdapter.cs +++ b/VectoCore/InputData/Reader/DataObjectAdaper/EngineeringDataAdapter.cs @@ -47,21 +47,23 @@ namespace TUGraz.VectoCore.InputData.Reader.DataObjectAdaper retVal.DynamicTyreRadius = data.DynamicTyreRadius; switch (data.CrossWindCorrectionMode) { case CrossWindCorrectionMode.NoCorrection: - retVal.CrossWindCorrectionCurve = CrossWindCorrectionCurve.GetNoCorrectionCurve(data.AirDragArea); + retVal.CrossWindCorrectionCurve = + new CrosswindCorrectionCdxALookup(CrossWindCorrectionCurveReader.GetNoCorrectionCurve(data.AirDragArea), + CrossWindCorrectionMode.NoCorrection); break; case CrossWindCorrectionMode.SpeedDependentCorrectionFactor: - retVal.CrossWindCorrectionCurve = - CrossWindCorrectionCurve.ReadSpeedDependentCorrectionCurve(data.CrosswindCorrectionMap, - data.AirDragArea); + retVal.CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup( + CrossWindCorrectionCurveReader.ReadSpeedDependentCorrectionCurve(data.CrosswindCorrectionMap, + data.AirDragArea), CrossWindCorrectionMode.SpeedDependentCorrectionFactor); break; case CrossWindCorrectionMode.VAirBetaLookupTable: - var delcEntries = DeclarationDataAdapter.GetDeclarationAirResistanceCurve(retVal.VehicleCategory, - data.AirDragArea); - retVal.CrossWindCorrectionCurve = new VAirBetaCrosswindCorrection(data.AirDragArea, data.CrosswindCorrectionMap); + retVal.CrossWindCorrectionCurve = new CrosswindCorrectionVAirBeta(data.AirDragArea, + CrossWindCorrectionCurveReader.ReadCdxABetaTable(data.CrosswindCorrectionMap)); break; case CrossWindCorrectionMode.DeclarationModeCorrection: - retVal.CrossWindCorrectionCurve = DeclarationDataAdapter.GetDeclarationAirResistanceCurve(retVal.VehicleCategory, - data.AirDragArea); + retVal.CrossWindCorrectionCurve = + new CrosswindCorrectionCdxALookup(DeclarationDataAdapter.GetDeclarationAirResistanceCurve(retVal.VehicleCategory, + data.AirDragArea), CrossWindCorrectionMode.DeclarationModeCorrection); break; default: throw new ArgumentOutOfRangeException(); diff --git a/VectoCore/Models/SimulationComponent/Data/CrossWindCorrectionCurve.cs b/VectoCore/Models/SimulationComponent/Data/CrossWindCorrectionCurve.cs deleted file mode 100644 index 2f8751a005..0000000000 --- a/VectoCore/Models/SimulationComponent/Data/CrossWindCorrectionCurve.cs +++ /dev/null @@ -1,171 +0,0 @@ -/* -* Copyright 2015, 2016 Graz University of Technology, -* Institute of Internal Combustion Engines and Thermodynamics, -* Institute of Technical Informatics -* -* Licensed under the EUPL (the "Licence"); -* You may not use this work except in compliance with the Licence. -* You may obtain a copy of the Licence at: -* -* http://ec.europa.eu/idabc/eupl -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the Licence is distributed on an "AS IS" basis, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the Licence for the specific language governing permissions and -* limitations under the Licence. -*/ - -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Linq; -using TUGraz.VectoCore.Exceptions; -using TUGraz.VectoCore.Models.Simulation.DataBus; -using TUGraz.VectoCore.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Data -{ - public class CrossWindCorrectionCurve : LoggingObject, ICrossWindCorrection - { - protected List<CrossWindCorrectionEntry> Entries; - - public CrossWindCorrectionMode CorrectionMode { get; internal set; } - - public CrossWindCorrectionCurve(List<CrossWindCorrectionEntry> entries, CrossWindCorrectionMode correctionMode) - { - CorrectionMode = correctionMode; - Entries = entries; - } - - - public static CrossWindCorrectionCurve ReadSpeedDependentCorrectionCurve(DataTable data, - SquareMeter aerodynamicDragArea) - { - return ParseSpeedDependent(data, aerodynamicDragArea); - } - - public static CrossWindCorrectionCurve ReadSpeedDependentCorrectionCurveFromStream(Stream inputData, - SquareMeter aerodynamicDragArea) - { - var data = VectoCSVFile.ReadStream(inputData); - return ParseSpeedDependent(data, aerodynamicDragArea); - } - - public static CrossWindCorrectionCurve ReadSpeedDependentCorrectionFromFile(string fileName, - SquareMeter aerodynamicDragArea) - { - var data = VectoCSVFile.Read(fileName); - return ParseSpeedDependent(data, aerodynamicDragArea); - } - - protected static CrossWindCorrectionCurve ParseSpeedDependent(DataTable data, - SquareMeter aerodynamicDragArea) - { - if (data.Columns.Count != 2) { - throw new VectoException("Crosswind correction file must consist of 2 columns."); - } - if (data.Rows.Count < 2) { - throw new VectoException("Crosswind correction file must consist of at least two entries"); - } - - if (HeaderIsValid(data.Columns)) { - return new CrossWindCorrectionCurve(ReadSpeedDependentFromColumnNames(data, aerodynamicDragArea), - CrossWindCorrectionMode.SpeedDependentCorrectionFactor); - } - Logger<CrossWindCorrectionCurve>() - .Warn( - "Crosswind correction file: Header line is not valid. Expected: '{0}, {1}', Got: '{2}'. Falling back to column index.", - Fields.Velocity, Fields.Cd, ", ".Join(data.Columns.Cast<DataColumn>().Select(c => c.ColumnName).Reverse())); - return new CrossWindCorrectionCurve(ReadSpeedDependentFromColumnIndizes(data, aerodynamicDragArea), - CrossWindCorrectionMode.SpeedDependentCorrectionFactor); - } - - protected static List<CrossWindCorrectionEntry> ReadSpeedDependentFromColumnIndizes(DataTable data, - SquareMeter aerodynamicDragArea) - { - return (from DataRow row in data.Rows - select new CrossWindCorrectionEntry { - Velocity = row.ParseDouble(0).KMPHtoMeterPerSecond(), - EffectiveCrossSectionArea = row.ParseDouble(1) * aerodynamicDragArea - }).ToList(); - } - - protected static List<CrossWindCorrectionEntry> ReadSpeedDependentFromColumnNames(DataTable data, - SquareMeter aerodynamicDragArea) - { - return (from DataRow row in data.Rows - select new CrossWindCorrectionEntry { - Velocity = row.ParseDouble(Fields.Velocity).KMPHtoMeterPerSecond(), - EffectiveCrossSectionArea = row.ParseDouble(Fields.Cd) * aerodynamicDragArea - }).ToList(); - } - - private static bool HeaderIsValid(DataColumnCollection columns) - { - return columns.Contains(Fields.Velocity) && columns.Contains(Fields.Cd); - } - - public static CrossWindCorrectionCurve GetNoCorrectionCurve(SquareMeter aerodynamicDragArea) - { - return new CrossWindCorrectionCurve(new[] { - new CrossWindCorrectionEntry { - Velocity = 0.KMPHtoMeterPerSecond(), - EffectiveCrossSectionArea = aerodynamicDragArea - }, - new CrossWindCorrectionEntry { - Velocity = 100.KMPHtoMeterPerSecond(), - EffectiveCrossSectionArea = aerodynamicDragArea - } - }.ToList(), CrossWindCorrectionMode.NoCorrection); - } - - public void SetDataBus(IDataBus dataBus) {} - - public Watt AverageAirDragPowerLoss(MeterPerSecond v1, MeterPerSecond v2, Second dt) - { - var vAverage = (v1 + v2) / 2; - var CdA = EffectiveAirDragArea(vAverage); - Watt averageAirDragPower; - if (v1.IsEqual(v2)) { - averageAirDragPower = (Physics.AirDensity / 2.0 * CdA * vAverage * vAverage * vAverage).Cast<Watt>(); - } else { - // compute the average force within the current simulation interval - // P(t) = k * CdA * v(t)^3 , v(t) = v0 + a * t // a != 0, P_avg = 1/T * Integral P(t) dt - // => P_avg = (CdA * rho/2)/(4*a * dt) * (v2^4 - v1^4) - var acceleration = (v2 - v1) / dt; - averageAirDragPower = - (Physics.AirDensity / 2.0 * CdA * (v2 * v2 * v2 * v2 - v1 * v1 * v1 * v1) / (4 * acceleration * dt)) - .Cast<Watt>(); - } - return averageAirDragPower; - } - - protected internal SquareMeter EffectiveAirDragArea(MeterPerSecond x) - { - var p = Entries.GetSection(c => c.Velocity < x); - - if (x < p.Item1.Velocity || p.Item2.Velocity < x) { - //Log.Error(_data.CrossWindCorrectionMode == CrossWindCorrectionMode.VAirBetaLookupTable - // ? string.Format("CdExtrapol β = {0}", x) - // : string.Format("CdExtrapol v = {0}", x)); - Log.Error("CdExtrapol v = {0}", x); - } - - return VectoMath.Interpolate(p.Item1.Velocity, p.Item2.Velocity, - p.Item1.EffectiveCrossSectionArea, p.Item2.EffectiveCrossSectionArea, x); - } - - public class Fields - { - public static readonly string Velocity = "v"; - public static readonly string Cd = "Cd"; - } - - public class CrossWindCorrectionEntry - { - public SquareMeter EffectiveCrossSectionArea; - public MeterPerSecond Velocity; - } - } -} \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/CrossWindCorrectionCurveReader.cs b/VectoCore/Models/SimulationComponent/Data/CrossWindCorrectionCurveReader.cs new file mode 100644 index 0000000000..6d43d02d46 --- /dev/null +++ b/VectoCore/Models/SimulationComponent/Data/CrossWindCorrectionCurveReader.cs @@ -0,0 +1,179 @@ +/* +* Copyright 2015, 2016 Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* Licensed under the EUPL (the "Licence"); +* You may not use this work except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* http://ec.europa.eu/idabc/eupl +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +*/ + +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using TUGraz.VectoCore.Exceptions; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Data +{ + public class CrossWindCorrectionCurveReader : LoggingObject + { + public static List<CrossWindCorrectionEntry> GetNoCorrectionCurve(SquareMeter aerodynamicDragArea) + { + return new[] { + new CrossWindCorrectionEntry { + Velocity = 0.KMPHtoMeterPerSecond(), + EffectiveCrossSectionArea = aerodynamicDragArea + }, + new CrossWindCorrectionEntry { + Velocity = 100.KMPHtoMeterPerSecond(), + EffectiveCrossSectionArea = aerodynamicDragArea + } + }.ToList(); + } + + public static List<CrossWindCorrectionEntry> ReadSpeedDependentCorrectionFromFile(string fileName, + SquareMeter aerodynamicDragArea) + { + var data = VectoCSVFile.Read(fileName); + return ParseSpeedDependent(data, aerodynamicDragArea); + } + + public static List<CrossWindCorrectionEntry> ReadSpeedDependentCorrectionCurveFromStream(Stream inputData, + SquareMeter aerodynamicDragArea) + { + var data = VectoCSVFile.ReadStream(inputData); + return ParseSpeedDependent(data, aerodynamicDragArea); + } + + public static List<CrossWindCorrectionEntry> ReadSpeedDependentCorrectionCurve(DataTable data, + SquareMeter aerodynamicDragArea) + { + return ParseSpeedDependent(data, aerodynamicDragArea); + } + + public static List<AirDragBetaEntry> ReadCdxABetaTable(DataTable betaTable) + { + if (betaTable.Columns.Count != 2) { + throw new VectoException("VAir/Beta Crosswind Correction must consist of 2 columns"); + } + if (betaTable.Rows.Count < 2) { + throw new VectoException("VAir/Beta Crosswind Correction must consist of at least 2 rows"); + } + if (HeaderIsValid(betaTable.Columns)) { + return ParseCdxABetaFromColumnNames(betaTable); + } + Logger<CrossWindCorrectionCurveReader>().Warn("VAir/Beta Crosswind Correction header Line is not valid"); + return ParseCdxABetaFromColumnIndices(betaTable); + } + + + protected static List<CrossWindCorrectionEntry> ParseSpeedDependent(DataTable data, + SquareMeter aerodynamicDragArea) + { + if (data.Columns.Count != 2) { + throw new VectoException("Crosswind correction file must consist of 2 columns."); + } + if (data.Rows.Count < 2) { + throw new VectoException("Crosswind correction file must consist of at least two entries"); + } + + if (SpeedDependentHeaderIsValid(data.Columns)) { + return ParseSpeedDependentFromColumnNames(data, aerodynamicDragArea); + } + Logger<CrossWindCorrectionCurveReader>() + .Warn( + "Crosswind correction file: Header line is not valid. Expected: '{0}, {1}', Got: '{2}'. Falling back to column index.", + FieldsSpeedDependent.Velocity, FieldsSpeedDependent.Cd, + ", ".Join(data.Columns.Cast<DataColumn>().Select(c => c.ColumnName).Reverse())); + return ParseSpeedDependentFromColumnIndizes(data, aerodynamicDragArea); + } + + protected static List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> ParseSpeedDependentFromColumnIndizes( + DataTable data, + SquareMeter aerodynamicDragArea) + { + return (from DataRow row in data.Rows + select new CrossWindCorrectionEntry { + Velocity = row.ParseDouble(0).KMPHtoMeterPerSecond(), + EffectiveCrossSectionArea = row.ParseDouble(1) * aerodynamicDragArea + }).ToList(); + } + + protected static List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> ParseSpeedDependentFromColumnNames( + DataTable data, + SquareMeter aerodynamicDragArea) + { + return (from DataRow row in data.Rows + select new CrossWindCorrectionEntry { + Velocity = row.ParseDouble(FieldsSpeedDependent.Velocity).KMPHtoMeterPerSecond(), + EffectiveCrossSectionArea = row.ParseDouble(FieldsSpeedDependent.Cd) * aerodynamicDragArea + }).ToList(); + } + + private static bool SpeedDependentHeaderIsValid(DataColumnCollection columns) + { + return columns.Contains(FieldsSpeedDependent.Velocity) && columns.Contains(FieldsSpeedDependent.Cd); + } + + private static List<AirDragBetaEntry> ParseCdxABetaFromColumnIndices(DataTable betaTable) + { + return (from DataRow row in betaTable.Rows + select + new AirDragBetaEntry() { + Beta = row.ParseDouble(0), + DeltaCdA = row.ParseDouble(1).SI<SquareMeter>() + }).ToList(); + } + + private static List<AirDragBetaEntry> ParseCdxABetaFromColumnNames(DataTable betaTable) + { + return (from DataRow row in betaTable.Rows + select + new AirDragBetaEntry() { + Beta = row.ParseDouble(FieldsCdxABeta.Beta), + DeltaCdA = row.ParseDouble(FieldsCdxABeta.DeltaCdxA).SI<SquareMeter>() + }).ToList(); + } + + private static bool HeaderIsValid(DataColumnCollection columns) + { + return columns.Contains(FieldsCdxABeta.Beta) && columns.Contains(FieldsCdxABeta.DeltaCdxA); + } + + private static class FieldsCdxABeta + { + public const string Beta = "Beta"; + + public const string DeltaCdxA = "Delta CdA"; + } + + + public class AirDragBetaEntry + { + public double Beta; + public SquareMeter DeltaCdA; + } + + public class FieldsSpeedDependent + { + public static readonly string Velocity = "v"; + public static readonly string Cd = "Cd"; + } + + public class CrossWindCorrectionEntry + { + public SquareMeter EffectiveCrossSectionArea; + public MeterPerSecond Velocity; + } + } +} \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/CrosswindCorrectionCdxALookup.cs b/VectoCore/Models/SimulationComponent/Data/CrosswindCorrectionCdxALookup.cs new file mode 100644 index 0000000000..a2b95cf497 --- /dev/null +++ b/VectoCore/Models/SimulationComponent/Data/CrosswindCorrectionCdxALookup.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using TUGraz.VectoCore.Models.Simulation.DataBus; +using TUGraz.VectoCore.Utils; + + +namespace TUGraz.VectoCore.Models.SimulationComponent.Data +{ + public class CrosswindCorrectionCdxALookup : LoggingObject, ICrossWindCorrection + { + protected List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> Entries; + + public CrosswindCorrectionCdxALookup(List<CrossWindCorrectionCurveReader.CrossWindCorrectionEntry> entries, + CrossWindCorrectionMode correctionMode) + { + CorrectionMode = correctionMode; + Entries = entries; + } + + public CrossWindCorrectionMode CorrectionMode { get; internal set; } + + public void SetDataBus(IDataBus dataBus) {} + + public Watt AverageAirDragPowerLoss(MeterPerSecond v1, MeterPerSecond v2, Second dt) + { + var vAverage = (v1 + v2) / 2; + var CdA = EffectiveAirDragArea(vAverage); + Watt averageAirDragPower; + if (v1.IsEqual(v2)) { + averageAirDragPower = (Physics.AirDensity / 2.0 * CdA * vAverage * vAverage * vAverage).Cast<Watt>(); + } else { + // compute the average force within the current simulation interval + // P(t) = k * CdA * v(t)^3 , v(t) = v0 + a * t // a != 0, P_avg = 1/T * Integral P(t) dt + // => P_avg = (CdA * rho/2)/(4*a * dt) * (v2^4 - v1^4) + var acceleration = (v2 - v1) / dt; + averageAirDragPower = + (Physics.AirDensity / 2.0 * CdA * (v2 * v2 * v2 * v2 - v1 * v1 * v1 * v1) / (4 * acceleration * dt)) + .Cast<Watt>(); + } + return averageAirDragPower; + } + + protected internal SquareMeter EffectiveAirDragArea(MeterPerSecond x) + { + var p = Entries.GetSection(c => c.Velocity < x); + + if (x < p.Item1.Velocity || p.Item2.Velocity < x) { + //Log.Error(_data.CrossWindCorrectionMode == CrossWindCorrectionMode.VAirBetaLookupTable + // ? string.Format("CdExtrapol β = {0}", x) + // : string.Format("CdExtrapol v = {0}", x)); + Log.Error("CdExtrapol v = {0}", x); + } + + return VectoMath.Interpolate(p.Item1.Velocity, p.Item2.Velocity, + p.Item1.EffectiveCrossSectionArea, p.Item2.EffectiveCrossSectionArea, x); + } + } +} \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/CrosswindCorrectionVAirBeta.cs b/VectoCore/Models/SimulationComponent/Data/CrosswindCorrectionVAirBeta.cs new file mode 100644 index 0000000000..5d768d6e0a --- /dev/null +++ b/VectoCore/Models/SimulationComponent/Data/CrosswindCorrectionVAirBeta.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using TUGraz.VectoCore.Exceptions; +using TUGraz.VectoCore.Models.Simulation.DataBus; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Data +{ + internal class CrosswindCorrectionVAirBeta : LoggingObject, ICrossWindCorrection + { + protected SquareMeter AirDragArea { get; set; } + + protected List<CrossWindCorrectionCurveReader.AirDragBetaEntry> AirDragEntries; + protected IDataBus DataBus; + + public CrosswindCorrectionVAirBeta(SquareMeter airDragArea, + List<CrossWindCorrectionCurveReader.AirDragBetaEntry> entries) + { + AirDragArea = airDragArea; + AirDragEntries = entries; + } + + public void SetDataBus(IDataBus dataBus) + { + DataBus = dataBus; + } + + public CrossWindCorrectionMode CorrectionMode + { + get { return CrossWindCorrectionMode.VAirBetaLookupTable; } + } + + public Watt AverageAirDragPowerLoss(MeterPerSecond v1, MeterPerSecond v2, Second dt) + { + if (DataBus == null) { + throw new VectoException("Databus is not set - can't access vAir, beta!"); + } + var vAir = DataBus.CycleData.LeftSample.AirSpeedRelativeToVehicle; + var beta = DataBus.CycleData.LeftSample.WindYawAngle; + + // F_air(t) = k * CdA_korr * v_air^2 // assumption: v_air = const for the current interval + // P(t) = F_air(t) * v(t) , v(t) = v1 + a * t + // P_avg = 1/T * Integral P(t) dt + // P_avg = k * CdA_korr * v_air^2 * (v1 + v2) / 2 + var airDragForce = (AirDragArea + DeltaCdxA(Math.Abs(beta))) * Physics.AirDensity / 2.0 * vAir * vAir; + var vAverage = (v1 + v2) / 2; + + return (airDragForce * vAverage).Cast<Watt>(); + } + + protected SquareMeter DeltaCdxA(double beta) + { + var idx = FindIndex(beta); + return VectoMath.Interpolate(AirDragEntries[idx - 1].Beta, AirDragEntries[idx].Beta, AirDragEntries[idx - 1].DeltaCdA, + AirDragEntries[idx].DeltaCdA, beta); + } + + protected int FindIndex(double beta) + { + if (beta < AirDragEntries.First().Beta) { + throw new VectoSimulationException("Beta / CdxA Lookup table does not cover beta={0}", beta); + } + if (beta > AirDragEntries.Last().Beta) { + throw new VectoSimulationException("Beta / CdxA Lookup table does not cover beta={0}", beta); + } + int index; + AirDragEntries.GetSection(x => x.Beta < beta, out index); + return index + 1; + } + } +} \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/ICrossWindCorrection.cs b/VectoCore/Models/SimulationComponent/Data/ICrossWindCorrection.cs index 1417240503..e312f9e7ae 100644 --- a/VectoCore/Models/SimulationComponent/Data/ICrossWindCorrection.cs +++ b/VectoCore/Models/SimulationComponent/Data/ICrossWindCorrection.cs @@ -10,6 +10,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data void SetDataBus(IDataBus dataBus); + CrossWindCorrectionMode CorrectionMode { get; } + Watt AverageAirDragPowerLoss(MeterPerSecond v1, MeterPerSecond v2, Second dt); } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/VAirBetaCrosswindCorrection.cs b/VectoCore/Models/SimulationComponent/Data/VAirBetaCrosswindCorrection.cs deleted file mode 100644 index adac8e653e..0000000000 --- a/VectoCore/Models/SimulationComponent/Data/VAirBetaCrosswindCorrection.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System.Collections.Generic; -using System.Data; -using System.Linq; -using TUGraz.VectoCore.Exceptions; -using TUGraz.VectoCore.Models.Simulation.DataBus; -using TUGraz.VectoCore.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Data -{ - internal class VAirBetaCrosswindCorrection : LoggingObject, ICrossWindCorrection - { - protected SquareMeter AirDragArea { get; set; } - - protected List<AirDragBetaEntry> AirDragEntries; - protected IDataBus DataBus; - - public VAirBetaCrosswindCorrection(SquareMeter airDragArea, DataTable betaTable) - { - AirDragArea = airDragArea; - if (betaTable.Columns.Count != 2) { - throw new VectoException("VAir/Beta Crosswind Correction must consist of 2 columns"); - } - if (betaTable.Rows.Count < 2) { - throw new VectoException("VAir/Beta Crosswind Correction must consist of at least 2 rows"); - } - if (HeaderIsValid(betaTable.Columns)) { - AirDragEntries = CreateFromColumnNames(betaTable); - } else { - Log.Warn("VAir/Beta Crosswind Correction header Line is not valid"); - AirDragEntries = CreateFromColumnIndices(betaTable); - } - } - - private static List<AirDragBetaEntry> CreateFromColumnIndices(DataTable betaTable) - { - return (from DataRow row in betaTable.Rows - select - new AirDragBetaEntry() { - Beta = row.ParseDouble(0), - DeltaCdA = row.ParseDouble(1).SI<SquareMeter>() - }).ToList(); - } - - private static List<AirDragBetaEntry> CreateFromColumnNames(DataTable betaTable) - { - return (from DataRow row in betaTable.Rows - select - new AirDragBetaEntry() { - Beta = row.ParseDouble(Fields.Beta), - DeltaCdA = row.ParseDouble(Fields.DeltaCdxA).SI<SquareMeter>() - }).ToList(); - } - - private static bool HeaderIsValid(DataColumnCollection columns) - { - return columns.Contains(Fields.Beta) && columns.Contains(Fields.DeltaCdxA); - } - - - public void SetDataBus(IDataBus dataBus) - { - DataBus = dataBus; - } - - public Watt AverageAirDragPowerLoss(MeterPerSecond v1, MeterPerSecond v2, Second dt) - { - if (DataBus == null) { - throw new VectoException("Databus is not set - can't access vAir, beta!"); - } - var vAir = DataBus.CycleData.LeftSample.AirSpeedRelativeToVehicle; - var beta = DataBus.CycleData.LeftSample.WindYawAngle; - - // F_air(t) = k * CdA_korr * v_air^2 // assumption: v_air = const for the current interval - // P(t) = F_air(t) * v(t) , v(t) = v1 + a * t - // P_avg = 1/T * Integral P(t) dt - // P_avg = k * CdA_korr * v_air^2 * (v1 + v2) / 2 - var airDragForce = (AirDragArea + DeltaCdA(beta)) * Physics.AirDensity / 2.0 * vAir * vAir; - var vAverage = (v1 + v2) / 2; - - return (airDragForce * vAverage).Cast<Watt>(); - } - - protected SquareMeter DeltaCdA(double beta) - { - var idx = FindIndex(beta); - return VectoMath.Interpolate(AirDragEntries[idx - 1].Beta, AirDragEntries[idx].Beta, AirDragEntries[idx - 1].DeltaCdA, - AirDragEntries[idx].DeltaCdA, beta); - } - - protected int FindIndex(double beta) - { - if (beta < AirDragEntries.First().Beta) { - throw new VectoSimulationException("Beta / CdxA Lookup table does not cover beta={0}", beta); - } - if (beta > AirDragEntries.Last().Beta) { - throw new VectoSimulationException("Beta / CdxA Lookup table does not cover beta={0}", beta); - } - int index; - AirDragEntries.GetSection(x => x.Beta < beta, out index); - return index + 1; - } - - protected class AirDragBetaEntry - { - public double Beta; - public SquareMeter DeltaCdA; - } - - private static class Fields - { - public const string Beta = "Beta"; - - public const string DeltaCdxA = "Delta CdA"; - } - } -} \ No newline at end of file diff --git a/VectoCore/VectoCore.csproj b/VectoCore/VectoCore.csproj index 6077cd9b67..fce6d8eb75 100644 --- a/VectoCore/VectoCore.csproj +++ b/VectoCore/VectoCore.csproj @@ -147,7 +147,8 @@ <Compile Include="Models\Declaration\VehicleClass.cs" /> <Compile Include="Models\Declaration\Wheels.cs" /> <Compile Include="Models\Declaration\WHTCCorrection.cs" /> - <Compile Include="Models\SimulationComponent\Data\CrossWindCorrectionCurve.cs" /> + <Compile Include="Models\SimulationComponent\Data\CrosswindCorrectionCdxALookup.cs" /> + <Compile Include="Models\SimulationComponent\Data\CrossWindCorrectionCurveReader.cs" /> <Compile Include="Models\SimulationComponent\Data\AuxiliaryData.cs" /> <Compile Include="Models\SimulationComponent\Data\AuxSupplyPowerReader.cs" /> <Compile Include="Models\SimulationComponent\Data\AxleGearData.cs" /> @@ -163,7 +164,7 @@ <Compile Include="Models\LoggingObject.cs" /> <Compile Include="Models\SimulationComponent\Data\GearboxType.cs" /> <Compile Include="Models\SimulationComponent\Data\ICrossWindCorrection.cs" /> - <Compile Include="Models\SimulationComponent\Data\VAirBetaCrosswindCorrection.cs" /> + <Compile Include="Models\SimulationComponent\Data\CrosswindCorrectionVAirBeta.cs" /> <Compile Include="Models\SimulationComponent\IAuxiliary.cs" /> <Compile Include="Models\SimulationComponent\IBrakes.cs" /> <Compile Include="Models\SimulationComponent\ICombustionEngineIdleController.cs" /> diff --git a/VectoCoreTest/Integration/CoachPowerTrain.cs b/VectoCoreTest/Integration/CoachPowerTrain.cs index bb74ea31fe..6c4fe406e9 100644 --- a/VectoCoreTest/Integration/CoachPowerTrain.cs +++ b/VectoCoreTest/Integration/CoachPowerTrain.cs @@ -160,7 +160,9 @@ namespace TUGraz.VectoCore.Tests.Integration AxleConfiguration = AxleConfiguration.AxleConfig_6x2, //AerodynamicDragAera = 3.2634.SI<SquareMeter>(), //CrossWindCorrectionMode = CrossWindCorrectionMode.NoCorrection, - CrossWindCorrectionCurve = CrossWindCorrectionCurve.GetNoCorrectionCurve(3.2634.SI<SquareMeter>()), + CrossWindCorrectionCurve = + new CrosswindCorrectionCdxALookup(CrossWindCorrectionCurveReader.GetNoCorrectionCurve(3.2634.SI<SquareMeter>()), + CrossWindCorrectionMode.NoCorrection), CurbWeight = 15700.SI<Kilogram>(), CurbWeigthExtra = 0.SI<Kilogram>(), Loading = loading, diff --git a/VectoCoreTest/Integration/SimulationRuns/FullPowertrain.cs b/VectoCoreTest/Integration/SimulationRuns/FullPowertrain.cs index 946f28d8a5..a3370a74c1 100644 --- a/VectoCoreTest/Integration/SimulationRuns/FullPowertrain.cs +++ b/VectoCoreTest/Integration/SimulationRuns/FullPowertrain.cs @@ -372,7 +372,7 @@ namespace TUGraz.VectoCore.Tests.Integration.SimulationRuns }; return new VehicleData { AxleConfiguration = AxleConfiguration.AxleConfig_6x2, - CrossWindCorrectionCurve = CrossWindCorrectionCurve.GetNoCorrectionCurve(3.2634.SI<SquareMeter>()), + CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup(CrossWindCorrectionCurveReader.GetNoCorrectionCurve(3.2634.SI<SquareMeter>()),CrossWindCorrectionMode.NoCorrection), CurbWeight = 15700.SI<Kilogram>(), CurbWeigthExtra = 0.SI<Kilogram>(), Loading = loading, diff --git a/VectoCoreTest/Integration/SimulationRuns/MinimalPowertrain.cs b/VectoCoreTest/Integration/SimulationRuns/MinimalPowertrain.cs index 114824c837..81b954ece7 100644 --- a/VectoCoreTest/Integration/SimulationRuns/MinimalPowertrain.cs +++ b/VectoCoreTest/Integration/SimulationRuns/MinimalPowertrain.cs @@ -267,7 +267,9 @@ namespace TUGraz.VectoCore.Tests.Integration.SimulationRuns }; return new VehicleData { AxleConfiguration = AxleConfiguration.AxleConfig_6x2, - CrossWindCorrectionCurve = CrossWindCorrectionCurve.GetNoCorrectionCurve(3.2634.SI<SquareMeter>()), + CrossWindCorrectionCurve = + new CrosswindCorrectionCdxALookup(CrossWindCorrectionCurveReader.GetNoCorrectionCurve(3.2634.SI<SquareMeter>()), + CrossWindCorrectionMode.NoCorrection), CurbWeight = 15700.SI<Kilogram>(), CurbWeigthExtra = 0.SI<Kilogram>(), Loading = loading, diff --git a/VectoCoreTest/Integration/Truck40tPowerTrain.cs b/VectoCoreTest/Integration/Truck40tPowerTrain.cs index 28b9a3207f..d5512bf3a7 100644 --- a/VectoCoreTest/Integration/Truck40tPowerTrain.cs +++ b/VectoCoreTest/Integration/Truck40tPowerTrain.cs @@ -184,7 +184,7 @@ namespace TUGraz.VectoCore.Tests.Integration AxleConfiguration = AxleConfiguration.AxleConfig_4x2, //AerodynamicDragAera = 6.2985.SI<SquareMeter>(), //CrossWindCorrectionMode = CrossWindCorrectionMode.NoCorrection, - CrossWindCorrectionCurve = CrossWindCorrectionCurve.GetNoCorrectionCurve(6.2985.SI<SquareMeter>()), + CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup(CrossWindCorrectionCurveReader.GetNoCorrectionCurve(6.2985.SI<SquareMeter>()),CrossWindCorrectionMode.NoCorrection), CurbWeight = 7100.SI<Kilogram>(), CurbWeigthExtra = massExtra, Loading = loading, diff --git a/VectoCoreTest/Models/Declaration/DeclarationDataTest.cs b/VectoCoreTest/Models/Declaration/DeclarationDataTest.cs index 5c368bba3e..e85c555577 100644 --- a/VectoCoreTest/Models/Declaration/DeclarationDataTest.cs +++ b/VectoCoreTest/Models/Declaration/DeclarationDataTest.cs @@ -152,9 +152,9 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration [TestMethod] public void CrossWindCorrectionTest() { - var crossWindCorrectionCurve = + var crossWindCorrectionCurve = new CrosswindCorrectionCdxALookup( DeclarationDataAdapter.GetDeclarationAirResistanceCurve(VehicleCategory.Tractor, - 6.46.SI<SquareMeter>()); + 6.46.SI<SquareMeter>()), CrossWindCorrectionMode.DeclarationModeCorrection); var tmp = crossWindCorrectionCurve.EffectiveAirDragArea(0.KMPHtoMeterPerSecond()); Assert.AreEqual(8.12204, tmp.Value(), Tolerance); diff --git a/VectoCoreTest/Models/Simulation/MeasuredSpeedModeTest.cs b/VectoCoreTest/Models/Simulation/MeasuredSpeedModeTest.cs index c5657d9d8c..0f92d32960 100644 --- a/VectoCoreTest/Models/Simulation/MeasuredSpeedModeTest.cs +++ b/VectoCoreTest/Models/Simulation/MeasuredSpeedModeTest.cs @@ -382,7 +382,8 @@ namespace TUGraz.VectoCore.Tests.Models.Simulation var tbl = VectoCSVFile.Read(@"TestData/MeasuredSpeed/VairBeta.vcdb"); var dataBus = new MockVairVechicleContainer(); - var vairbeta = new VAirBetaCrosswindCorrection(5.SI<SquareMeter>(), tbl); + var vairbeta = new CrosswindCorrectionVAirBeta(5.SI<SquareMeter>(), + CrossWindCorrectionCurveReader.ReadCdxABetaTable(tbl)); vairbeta.SetDataBus(dataBus); var cycleEntry = new DrivingCycleData.DrivingCycleEntry() { diff --git a/VectoCoreTest/Models/SimulationComponent/DriverTest.cs b/VectoCoreTest/Models/SimulationComponent/DriverTest.cs index 5b072da8bc..439c2360a5 100644 --- a/VectoCoreTest/Models/SimulationComponent/DriverTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/DriverTest.cs @@ -376,7 +376,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent }; return new VehicleData { AxleConfiguration = AxleConfiguration.AxleConfig_4x2, - CrossWindCorrectionCurve = CrossWindCorrectionCurve.GetNoCorrectionCurve(3.2634.SI<SquareMeter>()), + CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup(CrossWindCorrectionCurveReader.GetNoCorrectionCurve(3.2634.SI<SquareMeter>()), CrossWindCorrectionMode.NoCorrection), CurbWeight = 15700.SI<Kilogram>(), CurbWeigthExtra = 0.SI<Kilogram>(), Loading = loading, diff --git a/VectoCoreTest/Models/SimulationComponent/VehicleTest.cs b/VectoCoreTest/Models/SimulationComponent/VehicleTest.cs index 5a05b1c888..65b28cdfdc 100644 --- a/VectoCoreTest/Models/SimulationComponent/VehicleTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/VehicleTest.cs @@ -74,9 +74,9 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var vehicleData = MockSimulationDataFactory.CreateVehicleDataFromFile(VehicleDataFileTruck); //vehicleData.AerodynamicDragAera = 6.46.SI<SquareMeter>(); - vehicleData.CrossWindCorrectionCurve = + vehicleData.CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup( DeclarationDataAdapter.GetDeclarationAirResistanceCurve(VehicleCategory.Tractor, - 6.46.SI<SquareMeter>()); + 6.46.SI<SquareMeter>()), CrossWindCorrectionMode.DeclarationModeCorrection); var vehicle = new Vehicle(container, vehicleData); var mockPort = new MockFvOutPort(); @@ -117,9 +117,9 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var container = new VehicleContainer(); var vehicleData = MockSimulationDataFactory.CreateVehicleDataFromFile(VehicleDataFileTruck); - vehicleData.CrossWindCorrectionCurve = + vehicleData.CrossWindCorrectionCurve = new CrosswindCorrectionCdxALookup( DeclarationDataAdapter.GetDeclarationAirResistanceCurve(VehicleCategory.Tractor, - 6.2985.SI<SquareMeter>()); + 6.2985.SI<SquareMeter>()), CrossWindCorrectionMode.DeclarationModeCorrection); var vehicle = new Vehicle(container, vehicleData); @@ -179,20 +179,29 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent correctionData.Seek(0, SeekOrigin.Begin); var crossSectionArea = 5.19.SI<SquareMeter>(); - var cwcc = CrossWindCorrectionCurve.ReadSpeedDependentCorrectionCurveFromStream(correctionData, crossSectionArea); + var cwcc = + new CrosswindCorrectionCdxALookup( + CrossWindCorrectionCurveReader.ReadSpeedDependentCorrectionCurveFromStream(correctionData, + crossSectionArea), CrossWindCorrectionMode.SpeedDependentCorrectionFactor); - Assert.AreEqual(crossSectionArea.Value() * 1.173, cwcc.EffectiveAirDragArea(0.KMPHtoMeterPerSecond()).Value(), + Assert.AreEqual(crossSectionArea.Value() * 1.173, + cwcc.EffectiveAirDragArea(0.KMPHtoMeterPerSecond()).Value(), Tolerance); - Assert.AreEqual(crossSectionArea.Value() * 1.173, cwcc.EffectiveAirDragArea(40.KMPHtoMeterPerSecond()).Value(), + Assert.AreEqual(crossSectionArea.Value() * 1.173, + cwcc.EffectiveAirDragArea(40.KMPHtoMeterPerSecond()).Value(), Tolerance); - Assert.AreEqual(crossSectionArea.Value() * 1.173, cwcc.EffectiveAirDragArea(60.KMPHtoMeterPerSecond()).Value(), + Assert.AreEqual(crossSectionArea.Value() * 1.173, + cwcc.EffectiveAirDragArea(60.KMPHtoMeterPerSecond()).Value(), Tolerance); - Assert.AreEqual(crossSectionArea.Value() * 1.109, cwcc.EffectiveAirDragArea(80.KMPHtoMeterPerSecond()).Value(), + Assert.AreEqual(crossSectionArea.Value() * 1.109, + cwcc.EffectiveAirDragArea(80.KMPHtoMeterPerSecond()).Value(), Tolerance); - Assert.AreEqual(crossSectionArea.Value() * 1.075, cwcc.EffectiveAirDragArea(100.KMPHtoMeterPerSecond()).Value(), + Assert.AreEqual(crossSectionArea.Value() * 1.075, + cwcc.EffectiveAirDragArea(100.KMPHtoMeterPerSecond()).Value(), Tolerance); - Assert.AreEqual(crossSectionArea.Value() * 1.163, cwcc.EffectiveAirDragArea(62.5.KMPHtoMeterPerSecond()).Value(), + Assert.AreEqual(crossSectionArea.Value() * 1.163, + cwcc.EffectiveAirDragArea(62.5.KMPHtoMeterPerSecond()).Value(), Tolerance); } } -- GitLab