Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/emissions/PHEMCEP.cpp
169678 views
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
/****************************************************************************/
14
/// @file PHEMCEP.cpp
15
/// @author Nikolaus Furian
16
/// @author Daniel Krajzewicz
17
/// @author Jakob Erdmann
18
/// @author Michael Behrisch
19
/// @author Marek Heinrich
20
/// @date Thu, 13.06.2013
21
///
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>
28
#include <utils/common/StdDefs.h>
29
#include <utils/common/StringBijection.h>
30
#include <utils/common/UtilExceptions.h>
31
#include "PHEMCEP.h"
32
33
34
// ===========================================================================
35
// method definitions
36
// ===========================================================================
37
PHEMCEP::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
185
PHEMCEP::~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
195
double
196
PHEMCEP::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
269
double
270
PHEMCEP::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
278
double 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
317
double
318
PHEMCEP::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
331
double 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
344
double 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
357
void 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
398
double
399
PHEMCEP::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
409
double
410
PHEMCEP::GetMaxAccel(double v, double a, double gradient, double /* vehicleLoading */) const {
411
UNUSED_PARAMETER(a);
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
419
double 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
/****************************************************************************/
432
433