Select Git revision
ValidationTest.cs
Forked from
VECTO / VECTO Sim
Source project has a limited visibility.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
DeclarationData.cs 16.53 KiB
/*
* This file is part of VECTO.
*
* Copyright © 2012-2016 European Union
*
* Developed by Graz University of Technology,
* Institute of Internal Combustion Engines and Thermodynamics,
* Institute of Technical Informatics
*
* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use VECTO except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* https://joinup.ec.europa.eu/community/eupl/og_page/eupl
*
* Unless required by applicable law or agreed to in writing, VECTO
* 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.
*
* Authors:
* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology
* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology
* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology
* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology
* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology
* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
*/
using System.Collections.Generic;
using System.Data;
using System.Linq;
using TUGraz.VectoCommon.Exceptions;
using TUGraz.VectoCommon.InputData;
using TUGraz.VectoCommon.Models;
using TUGraz.VectoCommon.Utils;
using TUGraz.VectoCore.InputData.Reader.ComponentData;
using TUGraz.VectoCore.Models.SimulationComponent.Data;
using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine;
using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox;
using TUGraz.VectoCore.Utils;
namespace TUGraz.VectoCore.Models.Declaration
{
public static class DeclarationData
{
public const string DeclarationDataResourcePrefix = "TUGraz.VectoCore.Resources.Declaration";
public static readonly Watt MinEnginePowerForEMS = 300e3.SI<Watt>();
public static readonly Segments Segments = new Segments();
public static readonly Wheels Wheels = new Wheels();
public static readonly PT1 PT1 = new PT1();
public static readonly FuelData FuelData = FuelData.Instance();
public static readonly ElectricSystem ElectricSystem = new ElectricSystem();
public static readonly Fan Fan = new Fan();
public static readonly HeatingVentilationAirConditioning HeatingVentilationAirConditioning =
new HeatingVentilationAirConditioning();
public static readonly PneumaticSystem PneumaticSystem = new PneumaticSystem();
public static readonly SteeringPump SteeringPump = new SteeringPump();
public static readonly WHTCCorrection WHTCCorrection = new WHTCCorrection();
public static readonly AirDrag AirDrag = new AirDrag();
public static readonly StandardBodies StandardBodies = new StandardBodies();
public static readonly Payloads Payloads = new Payloads();
public static readonly PTOTransmission PTOTransmission = new PTOTransmission();
/// <summary>
/// Formula for calculating the payload for a given gross vehicle weight.
/// (so called "pc-formula", Whitebook Apr 2016, Part 1, p.187)
/// </summary>
public static Kilogram GetPayloadForGrossVehicleWeight(Kilogram grossVehicleWeight, string equationName)
{
if (equationName.ToLower().StartsWith("pc10")) {
return Payloads.Lookup10Percent(grossVehicleWeight);
}
if (equationName.ToLower().StartsWith("pc75")) {
return Payloads.Lookup75Percent(grossVehicleWeight);
}
return Payloads.Lookup50Percent(grossVehicleWeight);
}
/// <summary>
/// Returns the payload for a trailer. This is 75% of (GVW-CurbWeight).
/// </summary>
public static Kilogram GetPayloadForTrailerWeight(Kilogram grossVehicleWeight, Kilogram curbWeight, bool lowLoading)
{
return Payloads.LookupTrailer(grossVehicleWeight, curbWeight) / (lowLoading ? 7.5 : 1);
}
public static int PoweredAxle()
{
return 1;
}
public static class Driver
{
public static class LookAhead
{
public const bool Enabled = true;
public const double DecisionFactorCoastingOffset = 2.5;
public const double DecisionFactorCoastingScaling = 1.5;
public const double LookAheadDistanceFactor = 10;
public static readonly MeterPerSecond MinimumSpeed = 50.KMPHtoMeterPerSecond();
}
public static class OverSpeedEcoRoll
{
public static readonly IList<DriverMode> AllowedModes = new List<DriverMode> {
DriverMode.EcoRoll,
DriverMode.Overspeed
};
public static readonly MeterPerSecond MinSpeed = 50.KMPHtoMeterPerSecond();
public static readonly MeterPerSecond OverSpeed = 5.KMPHtoMeterPerSecond();
public static readonly MeterPerSecond UnderSpeed = 5.KMPHtoMeterPerSecond();
}
//public static class StartStop
//{
// public static readonly MeterPerSecond MaxSpeed = 5.KMPHtoMeterPerSecond();
// public static readonly Second Delay = 5.SI<Second>();
// public static readonly Second MinTime = 5.SI<Second>();
//}
}
public static class Trailer
{
public const double RollResistanceCoefficient = 0.0055;
public const double TyreTestLoad = 37500;
public const bool TwinTyres = false;
//public const string WheelsType = "385/65 R 22.5";
}
public static class Engine
{
public static readonly KilogramSquareMeter ClutchInertia = 1.3.SI<KilogramSquareMeter>();
public static readonly KilogramSquareMeter TorqueConverterInertia = 1.2.SI<KilogramSquareMeter>();
public static readonly KilogramSquareMeter EngineBaseInertia = 0.41.SI<KilogramSquareMeter>();
public static readonly SI EngineDisplacementInertia = (0.27 * 1000).SI().Kilo.Gramm.Per.Meter; // [kg/m]
public const double TorqueLimitGearboxFactor = 0.9;
public const double TorqueLimitVehicleFactor = 0.95;
public static KilogramSquareMeter EngineInertia(CubicMeter displacement, GearboxType gbxType)
{
// VB Code: Return 1.3 + 0.41 + 0.27 * (Displ / 1000)
return (gbxType.AutomaticTransmission() ? TorqueConverterInertia : ClutchInertia) + EngineBaseInertia +
EngineDisplacementInertia * displacement;
}
}
public static class Gearbox
{
public const double TorqueReserve = 0.2;
public const double TorqueReserveStart = 0.2;
public static readonly MeterPerSecond StartSpeed = 1.3.SI<MeterPerSecond>();
public static readonly MeterPerSquareSecond StartAcceleration = 0.6.SI<MeterPerSquareSecond>();
public static readonly KilogramSquareMeter Inertia = 0.SI<KilogramSquareMeter>();
public static readonly MeterPerSecond TruckMaxAllowedSpeed = 85.KMPHtoMeterPerSecond();
public const double ShiftPolygonRPMMargin = 7; // %
private const double ShiftPolygonEngineFldMargin = 0.98;
public static readonly Second MinTimeBetweenGearshifts = 1.5.SI<Second>();
public static readonly Second DownshiftAfterUpshiftDelay = 10.SI<Second>();
public static readonly Second UpshiftAfterDownshiftDelay = 10.SI<Second>();
public static readonly MeterPerSquareSecond UpshiftMinAcceleration = 0.1.SI<MeterPerSquareSecond>();
//public static readonly PerSecond TorqueConverterSpeedLimit = 1600.RPMtoRad();
public static readonly double TorqueConverterSecondGearThreshold = 1.8;
/// <summary>
/// computes the shift polygons for a single gear according to the whitebook 2016
/// </summary>
/// <param name="gearIdx">index of the gear to compute the shift polygons for -- gear number - 1!</param>
/// <param name="fullLoadCurve">engine full load curve, potentially limited by the gearbox</param>
/// <param name="gears">list of gears</param>
/// <param name="engine">engine data</param>
/// <param name="axlegearRatio"></param>
/// <param name="dynamicTyreRadius"></param>
/// <returns></returns>
public static ShiftPolygon ComputeShiftPolygon(int gearIdx, EngineFullLoadCurve fullLoadCurve,
IList<ITransmissionInputData> gears, CombustionEngineData engine, double axlegearRatio, Meter dynamicTyreRadius)
{
if (gears.Count < 2) {
throw new VectoException("ComputeShiftPolygon needs at least 2 gears. {0} gears given.", gears.Count);
}
// ReSharper disable once InconsistentNaming
var engineSpeed85kmhLastGear = ComputeEngineSpeed85kmh(gears[gears.Count - 1], axlegearRatio, dynamicTyreRadius);
var nVHigh = VectoMath.Min(engineSpeed85kmhLastGear, engine.FullLoadCurves[0].RatedSpeed);
var diffRatio = gears[gears.Count - 2].Ratio / gears[gears.Count - 1].Ratio - 1;
var maxDragTorque = fullLoadCurve.MaxDragTorque * 1.1;
var p1 = new Point(engine.IdleSpeed.Value() / 2, 0);
var p2 = new Point(engine.IdleSpeed.Value() * 1.1, 0);
var p3 = new Point(nVHigh.Value() * 0.9,
fullLoadCurve.FullLoadStationaryTorque(nVHigh * 0.9).Value());
var p4 =
new Point((nVHigh * (1 + diffRatio / 3)).Value(), 0);
var p5 = new Point(fullLoadCurve.N95hSpeed.Value(), fullLoadCurve.MaxTorque.Value());
var p6 = new Point(p2.X, VectoMath.Interpolate(p1, p3, p2.X));
var p7 = new Point(p4.X, VectoMath.Interpolate(p2, p5, p4.X));
var fldMargin = ShiftPolygonFldMargin(fullLoadCurve.FullLoadEntries, (p3.X * 0.95).SI<PerSecond>());
var downshiftCorr = MoveDownshiftBelowFld(Edge.Create(p6, p3), fldMargin, 1.1 * fullLoadCurve.MaxTorque);
var downShift = new List<ShiftPolygon.ShiftPolygonEntry>();
if (gearIdx > 0) {
downShift =
new[] { p2, downshiftCorr.P1, downshiftCorr.P2 }.Select(
point => new ShiftPolygon.ShiftPolygonEntry() {
AngularSpeed = point.X.SI<PerSecond>(),
Torque = point.Y.SI<NewtonMeter>()
})
.ToList();
downShift[0].Torque = maxDragTorque;
}
var upShift = new List<ShiftPolygon.ShiftPolygonEntry>();
if (gearIdx >= gears.Count - 1) {
return new ShiftPolygon(downShift, upShift);
}
var gearRatio = gears[gearIdx].Ratio / gears[gearIdx + 1].Ratio;
var rpmMarginFactor = 1 + ShiftPolygonRPMMargin / 100.0;
// ReSharper disable InconsistentNaming
var p2p = new Point(p2.X * gearRatio * rpmMarginFactor, p2.Y / gearRatio);
var p3p = new Point(p3.X * gearRatio * rpmMarginFactor, p3.Y / gearRatio);
var p6p = new Point(p6.X * gearRatio * rpmMarginFactor, p6.Y / gearRatio);
var edgeP6pP3p = new Edge(p6p, p3p);
var p3pExt = new Point((1.1 * p5.Y - edgeP6pP3p.OffsetXY) / edgeP6pP3p.SlopeXY, 1.1 * p5.Y);
// ReSharper restore InconsistentNaming
upShift = IntersectShiftPolygon(new[] { p4, p7, p5 }.ToList(), new[] { p2p, p6p, p3pExt }.ToList())
.Select(point => new ShiftPolygon.ShiftPolygonEntry() {
AngularSpeed = point.X.SI<PerSecond>(),
Torque = point.Y.SI<NewtonMeter>()
})
.ToList();
upShift[0].Torque = maxDragTorque;
return new ShiftPolygon(downShift, upShift);
}
/// <summary>
/// ensures the original downshift line is below the (already reduced) full-load curve
/// </summary>
/// <param name="shiftLine">second part of the shift polygon (slope)</param>
/// <param name="fldMargin">reduced full-load curve</param>
/// <param name="maxTorque">max torque</param>
/// <returns>returns a corrected shift polygon segment (slope) that is below the full load curve and reaches given maxTorque. the returned segment has the same slope</returns>
internal static Edge MoveDownshiftBelowFld(Edge shiftLine, IEnumerable<Point> fldMargin, NewtonMeter maxTorque)
{
var slope = shiftLine.SlopeXY;
var d = shiftLine.P2.Y - slope * shiftLine.P2.X;
d = fldMargin.Select(point => point.Y - slope * point.X).Concat(new[] { d }).Min();
var p6Corr = new Point(shiftLine.P1.X, shiftLine.P1.X * slope + d);
var p3Corr = new Point((maxTorque.Value() - d) / slope, maxTorque.Value());
return Edge.Create(p6Corr, p3Corr);
}
/// <summary>
/// reduce the torque of the full load curve up to the given rpms
/// </summary>
/// <param name="fullLoadCurve"></param>
/// <param name="rpmLimit"></param>
/// <returns></returns>
internal static IEnumerable<Point> ShiftPolygonFldMargin(List<FullLoadCurve.FullLoadCurveEntry> fullLoadCurve,
PerSecond rpmLimit)
{
return
fullLoadCurve.TakeWhile(fldEntry => fldEntry.EngineSpeed < rpmLimit)
.Select(
fldEntry =>
new Point(fldEntry.EngineSpeed.Value(), fldEntry.TorqueFullLoad.Value() * ShiftPolygonEngineFldMargin))
.ToList();
}
// ReSharper disable once InconsistentNaming
private static PerSecond ComputeEngineSpeed85kmh(ITransmissionInputData gear, double axleRatio,
Meter dynamicTyreRadius)
{
var engineSpeed = TruckMaxAllowedSpeed / dynamicTyreRadius * axleRatio * gear.Ratio;
return engineSpeed;
}
internal static List<Point> IntersectShiftPolygon(List<Point> orig, List<Point> transformedDownshift)
{
var intersections = new List<Point>();
// compute all intersection points between both line segments
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var origLine in orig.Pairwise(Edge.Create)) {
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var transformedLine in transformedDownshift.Pairwise(Edge.Create)) {
var isect = VectoMath.Intersect(origLine, transformedLine);
if (isect != null) {
intersections.Add(isect);
}
}
}
// add all points (i.e. intersecting points and both line segments) to a single list
var pointSet = new List<Point>(orig);
pointSet.AddRange(transformedDownshift);
pointSet.AddRange(intersections);
pointSet.AddRange(ProjectPointsToLineSegments(orig, transformedDownshift));
pointSet.AddRange(ProjectPointsToLineSegments(transformedDownshift, orig));
// line sweeping from max_X to 0: select point with lowest Y coordinate, abort if a point has Y = 0
var shiftPolygon = new List<Point>();
foreach (var xCoord in pointSet.Select(pt => pt.X).Distinct().OrderBy(x => x).Reverse()) {
var coord = xCoord;
var xPoints = pointSet.Where(pt => pt.X.IsEqual(coord) && !pt.Y.IsEqual(0)).ToList();
shiftPolygon.Add(xPoints.MinBy(pt => pt.Y));
var tmp = pointSet.Where(pt => pt.X.IsEqual(coord)).Where(pt => pt.Y.IsEqual(0)).ToList();
if (!tmp.Any()) {
continue;
}
shiftPolygon.Add(tmp.First());
break;
}
// find and remove colinear points
var toRemove = new List<Point>();
for (var i = 0; i < shiftPolygon.Count - 2; i++) {
var edge = new Edge(shiftPolygon[i], shiftPolygon[i + 2]);
if (edge.ContainsXY(shiftPolygon[i + 1])) {
toRemove.Add(shiftPolygon[i + 1]);
}
}
foreach (var point in toRemove) {
shiftPolygon.Remove(point);
}
// order points first by x coordinate and the by Y coordinate ASC
return shiftPolygon.OrderBy(pt => pt.X).ThenBy(pt => pt.Y).ToList();
}
private static List<Point> ProjectPointsToLineSegments(List<Point> lineSegments, List<Point> points)
{
var pointSet = new List<Point>();
foreach (var segment in lineSegments.Pairwise(Edge.Create)) {
if (segment.P1.X.IsEqual(segment.P2.X)) {
continue;
}
var k = segment.SlopeXY;
var d = segment.P1.Y - segment.P1.X * k;
pointSet.AddRange(points.Select(point => new Point(point.X, point.X * k + d)));
}
return pointSet;
}
public static IEnumerable<TorqueConverterEntry> GetTorqueConverterDragCurve(double ratio)
{
var resourceId = DeclarationData.DeclarationDataResourcePrefix + ".TorqueConverter.csv";
var data = VectoCSVFile.ReadStream(RessourceHelper.ReadStream(resourceId), source: resourceId);
var characteristicTorque = (from DataRow row in data.Rows
select
new TorqueConverterEntry() {
SpeedRatio = row.ParseDouble(TorqueConverterDataReader.Fields.SpeedRatio),
Torque = row.ParseDouble(TorqueConverterDataReader.Fields.CharacteristicTorque).SI<NewtonMeter>(),
TorqueRatio = row.ParseDouble(TorqueConverterDataReader.Fields.TorqueRatio)
}).ToArray();
foreach (var torqueConverterEntry in characteristicTorque) {
torqueConverterEntry.SpeedRatio = torqueConverterEntry.SpeedRatio * ratio;
torqueConverterEntry.TorqueRatio = torqueConverterEntry.TorqueRatio / ratio;
}
return characteristicTorque.Where(x => x.SpeedRatio >= ratio).ToArray();
}
}
public static class PTO
{
public const string DefaultPTOTechnology =
"only the drive shaft of the PTO - shift claw, synchronizer, sliding gearwheel";
public const string DefaultPTOIdleLosses =
DeclarationDataResourcePrefix + ".MissionCycles.MunicipalUtility_PTO_generic.vptol";
public const string DefaultPTOActivationCycle =
DeclarationDataResourcePrefix + ".MissionCycles.MunicipalUtility_PTO_generic.vptoc";
}
}
}