Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
PHEMCEP.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2013-2025 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
22// Helper class for PHEM Light, holds a specific CEP for a PHEM emission class
23/****************************************************************************/
24#include <config.h>
25
26#include <cmath>
27#include <string>
31#include "PHEMCEP.h"
32
33
34// ===========================================================================
35// method definitions
36// ===========================================================================
37PHEMCEP::PHEMCEP(bool heavyVehicle, SUMOEmissionClass emissionClass, const std::string& emissionClassIdentifier,
38 double vehicleMass, double vehicleLoading, double vehicleMassRot,
39 double crossArea, double cdValue,
40 double f0, double f1, double f2, double f3, double f4,
41 double ratedPower, double pNormV0, double pNormP0, double pNormV1, double pNormP1,
42 double axleRatio, double engineIdlingSpeed, double engineRatedSpeed, double effectiveWheelDiameter,
43 double idlingFC,
44 const std::string& vehicleFuelType,
45 const std::vector< std::vector<double> >& matrixFC,
46 const std::vector<std::string>& headerLinePollutants,
47 const std::vector< std::vector<double> >& matrixPollutants,
48 const std::vector< std::vector<double> >& matrixSpeedRotational,
49 const std::vector< std::vector<double> >& normedDragTable,
50 const std::vector<double>& idlingValuesPollutants) {
51 _emissionClass = emissionClass;
52 _resistanceF0 = f0;
53 _resistanceF1 = f1;
54 _resistanceF2 = f2;
55 _resistanceF3 = f3;
56 _resistanceF4 = f4;
57 _cdValue = cdValue;
58 _crossSectionalArea = crossArea;
59 _massVehicle = vehicleMass;
60 _vehicleLoading = vehicleLoading;
61 _massRot = vehicleMassRot;
62 _ratedPower = ratedPower;
63 _vehicleFuelType = vehicleFuelType;
64
65 _pNormV0 = pNormV0 / 3.6;
66 _pNormP0 = pNormP0;
67 _pNormV1 = pNormV1 / 3.6;
68 _pNormP1 = pNormP1;
69
70 _axleRatio = axleRatio;
71 _engineIdlingSpeed = engineIdlingSpeed;
72 _engineRatedSpeed = engineRatedSpeed;
73 _effictiveWheelDiameter = effectiveWheelDiameter;
74
75 _heavyVehicle = heavyVehicle;
76 _idlingFC = idlingFC;
77
78 std::vector<std::string> pollutantIdentifier;
79 std::vector< std::vector<double> > pollutantMeasures;
80 std::vector<std::vector<double> > normalizedPollutantMeasures;
81
82 // init pollutant identifiers
83 for (int i = 0; i < (int)headerLinePollutants.size(); i++) {
84 pollutantIdentifier.push_back(headerLinePollutants[i]);
85 } // end for
86
87 // get size of powerPatterns
88 _sizeOfPatternFC = (int)matrixFC.size();
89 _sizeOfPatternPollutants = (int)matrixPollutants.size();
90
91 // initialize measures
92 for (int i = 0; i < (int)headerLinePollutants.size(); i++) {
93 pollutantMeasures.push_back(std::vector<double>());
94 normalizedPollutantMeasures.push_back(std::vector<double>());
95 } // end for
96
97 // looping through matrix and assigning values for speed rotational table
98 _speedCurveRotational.clear();
99 _speedPatternRotational.clear();
100 _gearTransmissionCurve.clear();
101 for (int i = 0; i < (int)matrixSpeedRotational.size(); i++) {
102 if (matrixSpeedRotational[i].size() != 3) {
103 throw InvalidArgument("Error loading vehicle file for: " + emissionClassIdentifier);
104 }
105
106 _speedPatternRotational.push_back(matrixSpeedRotational[i][0] / 3.6);
107 _speedCurveRotational.push_back(matrixSpeedRotational[i][1]);
108 _gearTransmissionCurve.push_back(matrixSpeedRotational[i][2]);
109 } // end for
110
111 // looping through matrix and assigning values for drag table
112 _nNormTable.clear();
113 _dragNormTable.clear();
114 for (int i = 0; i < (int) normedDragTable.size(); i++) {
115 if (normedDragTable[i].size() != 2) {
116 return;
117 }
118
119 _nNormTable.push_back(normedDragTable[i][0]);
120 _dragNormTable.push_back(normedDragTable[i][1]);
121
122 } // end for
123
124 // looping through matrix and assigning values for Fuel consumption
125 _cepCurveFC.clear();
126 _powerPatternFC.clear();
127 _normalizedPowerPatternFC.clear();
128 _normedCepCurveFC.clear();
129 for (int i = 0; i < (int)matrixFC.size(); i++) {
130 if (matrixFC[i].size() != 2) {
131 throw InvalidArgument("Error loading vehicle file for: " + emissionClassIdentifier);
132 }
133
134 _powerPatternFC.push_back(matrixFC[i][0] * _ratedPower);
135 _normalizedPowerPatternFC.push_back(matrixFC[i][0]);
136 _cepCurveFC.push_back(matrixFC[i][1] * _ratedPower);
137 _normedCepCurveFC.push_back(matrixFC[i][1]);
138
139 } // end for
140
141 _powerPatternPollutants.clear();
142 double pollutantMultiplyer = 1;
143
144 _drivingPower = _normalizingPower = CalcPower(NORMALIZING_SPEED, NORMALIZING_ACCELARATION, 0, vehicleLoading);
145
146 // looping through matrix and assigning values for pollutants
147
148 if (heavyVehicle) {
149 _normalizingPower = _ratedPower;
150 pollutantMultiplyer = _ratedPower;
151 _normalizingType = RatedPower;
152 } else {
153 _normalizingPower = _drivingPower;
154 _normalizingType = DrivingPower;
155 } // end if
156
157 const int headerCount = (int)headerLinePollutants.size();
158 for (int i = 0; i < (int)matrixPollutants.size(); i++) {
159 for (int j = 0; j < (int)matrixPollutants[i].size(); j++) {
160 if ((int)matrixPollutants[i].size() != headerCount + 1) {
161 return;
162 }
163
164 if (j == 0) {
165 _normailzedPowerPatternPollutants.push_back(matrixPollutants[i][j]);
166 _powerPatternPollutants.push_back(matrixPollutants[i][j] * _normalizingPower);
167 } else {
168 pollutantMeasures[j - 1].push_back(matrixPollutants[i][j] * pollutantMultiplyer);
169 normalizedPollutantMeasures[j - 1].push_back(matrixPollutants[i][j]);
170 } // end if
171 } // end for
172 } // end for
173
174 for (int i = 0; i < (int) headerLinePollutants.size(); i++) {
175 _cepCurvePollutants.insert(pollutantIdentifier[i], pollutantMeasures[i]);
176 _normalizedCepCurvePollutants.insert(pollutantIdentifier[i], normalizedPollutantMeasures[i]);
177 _idlingValuesPollutants.insert(pollutantIdentifier[i], idlingValuesPollutants[i] * pollutantMultiplyer);
178 } // end for
179
180 _idlingFC = idlingFC * _ratedPower;
181
182} // end of Cep
183
184
185PHEMCEP::~PHEMCEP() {
186 // free power pattern
187 _powerPatternFC.clear();
188 _powerPatternPollutants.clear();
189 _cepCurveFC.clear();
190 _speedCurveRotational.clear();
191 _speedPatternRotational.clear();
192} // end of ~Cep
193
194
195double
196PHEMCEP::GetEmission(const std::string& pollutant, double power, double speed, bool normalized) const {
197 std::vector<double> emissionCurve;
198 std::vector<double> powerPattern;
199
200 if (!normalized && fabs(speed) <= ZERO_SPEED_ACCURACY) {
201 if (pollutant == "FC") {
202 return _idlingFC;
203 } else {
204 return _idlingValuesPollutants.get(pollutant);
205 }
206 } // end if
207
208 if (pollutant == "FC") {
209 if (normalized) {
210 emissionCurve = _normedCepCurveFC;
211 powerPattern = _normalizedPowerPatternFC;
212 } else {
213 emissionCurve = _cepCurveFC;
214 powerPattern = _powerPatternFC;
215 }
216 } else {
217 if (!_cepCurvePollutants.hasString(pollutant)) {
218 throw InvalidArgument("Emission pollutant " + pollutant + " not found!");
219 }
220
221 if (normalized) {
222 emissionCurve = _normalizedCepCurvePollutants.get(pollutant);
223 powerPattern = _normailzedPowerPatternPollutants;
224 } else {
225 emissionCurve = _cepCurvePollutants.get(pollutant);
226 powerPattern = _powerPatternPollutants;
227 }
228
229 } // end if
230
231
232
233 if (emissionCurve.size() == 0) {
234 throw InvalidArgument("Empty emission curve for " + pollutant + " found!");
235 }
236
237 if (emissionCurve.size() == 1) {
238 return emissionCurve[0];
239 }
240
241 // in case that the demanded power is smaller than the first entry (smallest) in the power pattern the first two entries are extrapolated
242 if (power <= powerPattern.front()) {
243 double calcEmission = PHEMCEP::Interpolate(power, powerPattern[0], powerPattern[1], emissionCurve[0], emissionCurve[1]);
244
245 if (calcEmission < 0) {
246 return 0;
247 } else {
248 return calcEmission;
249 }
250
251 } // end if
252
253 // if power bigger than all entries in power pattern the last two values are linearly extrapolated
254 if (power >= powerPattern.back()) {
255 return PHEMCEP::Interpolate(power, powerPattern[powerPattern.size() - 2], powerPattern.back(), emissionCurve[emissionCurve.size() - 2], emissionCurve.back());
256 } // end if
257
258 // bisection search to find correct position in power pattern
259 int upperIndex;
260 int lowerIndex;
261
262 PHEMCEP::FindLowerUpperInPattern(lowerIndex, upperIndex, powerPattern, power);
263
264 return PHEMCEP::Interpolate(power, powerPattern[lowerIndex], powerPattern[upperIndex], emissionCurve[lowerIndex], emissionCurve[upperIndex]);
265
266} // end of GetEmission
267
268
269double
270PHEMCEP::Interpolate(double px, double p1, double p2, double e1, double e2) const {
271 if (p2 == p1) {
272 return e1;
273 }
274 return e1 + (px - p1) / (p2 - p1) * (e2 - e1);
275} // end of Interpolate
276
277
278double PHEMCEP::GetDecelCoast(double speed, double acc, double gradient, double /* vehicleLoading */) const {
279 if (speed < SPEED_DCEL_MIN) {
280 return speed / SPEED_DCEL_MIN * GetDecelCoast(SPEED_DCEL_MIN, acc, gradient, _vehicleLoading); // !!!vehicleLoading
281 } // end if
282
283 double rotCoeff = GetRotationalCoeffecient(speed);
284
285 int upperIndex;
286 int lowerIndex;
287
288 double iGear = GetGearCoeffecient(speed);
289
290 double iTot = iGear * _axleRatio;
291
292 double n = (30 * speed * iTot) / ((_effictiveWheelDiameter / 2) * M_PI2);
293 double nNorm = (n - _engineIdlingSpeed) / (_engineRatedSpeed - _engineIdlingSpeed);
294
295 FindLowerUpperInPattern(lowerIndex, upperIndex, _nNormTable, nNorm);
296
297 double fMot = 0;
298
299 if (speed >= 10e-2) {
300 fMot = (-GetDragCoeffecient(nNorm) * _ratedPower * 1000 / speed) / 0.9;
301 } // end if
302
303 double fRoll = (_resistanceF0
304 + _resistanceF1 * speed
305 + pow(_resistanceF2 * speed, 2)
306 + pow(_resistanceF3 * speed, 3)
307 + pow(_resistanceF4 * speed, 4)) * (_massVehicle + _vehicleLoading) * GRAVITY_CONST; // !!!vehicleLoading
308
309 double fAir = _cdValue * _crossSectionalArea * 1.2 * 0.5 * pow(speed, 2);
310
311 double fGrad = (_massVehicle + _vehicleLoading) * GRAVITY_CONST * gradient / 100; // !!!vehicleLoading
312
313 return -(fMot + fRoll + fAir + fGrad) / ((_massVehicle + _vehicleLoading) * rotCoeff); // !!!vehicleLoading
314} // end of GetDecelCoast
315
316
317double
318PHEMCEP::GetRotationalCoeffecient(double speed) const {
319 int upperIndex;
320 int lowerIndex;
321
322 PHEMCEP::FindLowerUpperInPattern(lowerIndex, upperIndex, _speedPatternRotational, speed);
323
324 return PHEMCEP::Interpolate(speed,
325 _speedPatternRotational[lowerIndex],
326 _speedPatternRotational[upperIndex],
327 _speedCurveRotational[lowerIndex],
328 _speedCurveRotational[upperIndex]);
329} // end of GetRotationalCoeffecient
330
331double PHEMCEP::GetGearCoeffecient(double speed) const {
332 int upperIndex;
333 int lowerIndex;
334
335 FindLowerUpperInPattern(lowerIndex, upperIndex, _gearTransmissionCurve, speed);
336
337 return Interpolate(speed,
338 _speedPatternRotational[lowerIndex],
339 _speedPatternRotational[upperIndex],
340 _gearTransmissionCurve[lowerIndex],
341 _gearTransmissionCurve[upperIndex]);
342} // end of GetGearCoefficient
343
344double PHEMCEP::GetDragCoeffecient(double nNorm) const {
345 int upperIndex;
346 int lowerIndex;
347
348 FindLowerUpperInPattern(lowerIndex, upperIndex, _dragNormTable, nNorm);
349
350 return Interpolate(nNorm,
351 _nNormTable[lowerIndex],
352 _nNormTable[upperIndex],
353 _dragNormTable[lowerIndex],
354 _dragNormTable[upperIndex]);
355} // end of GetGearCoefficient
356
357void PHEMCEP::FindLowerUpperInPattern(int& lowerIndex, int& upperIndex, const std::vector<double>& pattern, double value) const {
358 if (value <= pattern.front()) {
359 lowerIndex = 0;
360 upperIndex = 0;
361 return;
362
363 } // end if
364
365 if (value >= pattern.back()) {
366 lowerIndex = (int)pattern.size() - 1;
367 upperIndex = (int)pattern.size() - 1;
368 return;
369 } // end if
370
371 // bisection search to find correct position in power pattern
372 int middleIndex = ((int)pattern.size() - 1) / 2;
373 upperIndex = (int)pattern.size() - 1;
374 lowerIndex = 0;
375
376 while (upperIndex - lowerIndex > 1) {
377 if (pattern[middleIndex] == value) {
378 lowerIndex = middleIndex;
379 upperIndex = middleIndex;
380 return;
381 } else if (pattern[middleIndex] < value) {
382 lowerIndex = middleIndex;
383 middleIndex = (upperIndex - lowerIndex) / 2 + lowerIndex;
384 } else {
385 upperIndex = middleIndex;
386 middleIndex = (upperIndex - lowerIndex) / 2 + lowerIndex;
387 } // end if
388 } // end while
389
390 if (pattern[lowerIndex] <= value && value < pattern[upperIndex]) {
391 return;
392 } else {
393 throw ProcessError("Error during calculation of position in pattern!");
394 }
395} // end of FindLowerUpperInPattern
396
397
398double
399PHEMCEP::CalcPower(double v, double a, double slope, double /* vehicleLoading */) const {
400 const double rotFactor = GetRotationalCoeffecient(v);
401 double power = (_massVehicle + _vehicleLoading) * GRAVITY_CONST * (_resistanceF0 + _resistanceF1 * v + _resistanceF4 * pow(v, 4)) * v;
402 power += (_crossSectionalArea * _cdValue * AIR_DENSITY_CONST / 2) * pow(v, 3);
403 power += (_massVehicle * rotFactor + _massRot + _vehicleLoading) * a * v;
404 power += (_massVehicle + _vehicleLoading) * slope * 0.01 * v;
405 return power / 950.;
406}
407
408
409double
410PHEMCEP::GetMaxAccel(double v, double a, double gradient, double /* vehicleLoading */) const {
412 double rotFactor = GetRotationalCoeffecient(v);
413 const double pMaxForAcc = GetPMaxNorm(v) * _ratedPower - PHEMCEP::CalcPower(v, 0, gradient, _vehicleLoading); // !!!vehicleLoading
414 return (pMaxForAcc * 1000) / ((_massVehicle * rotFactor + _massRot + _vehicleLoading) * v); // !!!vehicleLoading
415}
416
417
418
419double PHEMCEP::GetPMaxNorm(double speed) const {
420 // Linear function between v0 and v1, constant elsewhere
421 if (speed <= _pNormV0) {
422 return _pNormP0;
423 } else if (speed >= _pNormV1) {
424 return _pNormP1;
425 } else {
426 return PHEMCEP::Interpolate(speed, _pNormV0, _pNormV1, _pNormP0, _pNormP1);
427 }
428} // end of GetPMaxNorm
429
430
431/****************************************************************************/
@ RatedPower
Definition PHEMCEP.h:37
@ DrivingPower
Definition PHEMCEP.h:38
const double M_PI2
const double AIR_DENSITY_CONST
const double ZERO_SPEED_ACCURACY
const double SPEED_DCEL_MIN
const double GRAVITY_CONST
const double NORMALIZING_ACCELARATION
const double NORMALIZING_SPEED
const std::string invalid_return< std::string >::value
int SUMOEmissionClass
#define UNUSED_PARAMETER(x)
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:21899