Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2013-2024 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 0 : 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 0 : const std::vector<double>& idlingValuesPollutants) {
51 0 : _emissionClass = emissionClass;
52 0 : _resistanceF0 = f0;
53 0 : _resistanceF1 = f1;
54 0 : _resistanceF2 = f2;
55 0 : _resistanceF3 = f3;
56 0 : _resistanceF4 = f4;
57 0 : _cdValue = cdValue;
58 0 : _crossSectionalArea = crossArea;
59 0 : _massVehicle = vehicleMass;
60 0 : _vehicleLoading = vehicleLoading;
61 0 : _massRot = vehicleMassRot;
62 0 : _ratedPower = ratedPower;
63 0 : _vehicleFuelType = vehicleFuelType;
64 :
65 0 : _pNormV0 = pNormV0 / 3.6;
66 0 : _pNormP0 = pNormP0;
67 0 : _pNormV1 = pNormV1 / 3.6;
68 0 : _pNormP1 = pNormP1;
69 :
70 0 : _axleRatio = axleRatio;
71 0 : _engineIdlingSpeed = engineIdlingSpeed;
72 0 : _engineRatedSpeed = engineRatedSpeed;
73 0 : _effictiveWheelDiameter = effectiveWheelDiameter;
74 :
75 0 : _heavyVehicle = heavyVehicle;
76 0 : _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 0 : for (int i = 0; i < (int)headerLinePollutants.size(); i++) {
84 0 : pollutantIdentifier.push_back(headerLinePollutants[i]);
85 : } // end for
86 :
87 : // get size of powerPatterns
88 0 : _sizeOfPatternFC = (int)matrixFC.size();
89 0 : _sizeOfPatternPollutants = (int)matrixPollutants.size();
90 :
91 : // initialize measures
92 0 : for (int i = 0; i < (int)headerLinePollutants.size(); i++) {
93 0 : pollutantMeasures.push_back(std::vector<double>());
94 0 : 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 0 : for (int i = 0; i < (int)matrixSpeedRotational.size(); i++) {
102 0 : if (matrixSpeedRotational[i].size() != 3) {
103 0 : throw InvalidArgument("Error loading vehicle file for: " + emissionClassIdentifier);
104 : }
105 :
106 0 : _speedPatternRotational.push_back(matrixSpeedRotational[i][0] / 3.6);
107 0 : _speedCurveRotational.push_back(matrixSpeedRotational[i][1]);
108 0 : _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 0 : for (int i = 0; i < (int) normedDragTable.size(); i++) {
115 0 : if (normedDragTable[i].size() != 2) {
116 : return;
117 : }
118 :
119 0 : _nNormTable.push_back(normedDragTable[i][0]);
120 0 : _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 0 : for (int i = 0; i < (int)matrixFC.size(); i++) {
130 0 : if (matrixFC[i].size() != 2) {
131 0 : throw InvalidArgument("Error loading vehicle file for: " + emissionClassIdentifier);
132 : }
133 :
134 0 : _powerPatternFC.push_back(matrixFC[i][0] * _ratedPower);
135 0 : _normalizedPowerPatternFC.push_back(matrixFC[i][0]);
136 0 : _cepCurveFC.push_back(matrixFC[i][1] * _ratedPower);
137 0 : _normedCepCurveFC.push_back(matrixFC[i][1]);
138 :
139 : } // end for
140 :
141 : _powerPatternPollutants.clear();
142 : double pollutantMultiplyer = 1;
143 :
144 0 : _drivingPower = _normalizingPower = CalcPower(NORMALIZING_SPEED, NORMALIZING_ACCELARATION, 0, vehicleLoading);
145 :
146 : // looping through matrix and assigning values for pollutants
147 :
148 0 : if (heavyVehicle) {
149 0 : _normalizingPower = _ratedPower;
150 : pollutantMultiplyer = _ratedPower;
151 0 : _normalizingType = RatedPower;
152 : } else {
153 : _normalizingPower = _drivingPower;
154 0 : _normalizingType = DrivingPower;
155 : } // end if
156 :
157 0 : const int headerCount = (int)headerLinePollutants.size();
158 0 : for (int i = 0; i < (int)matrixPollutants.size(); i++) {
159 0 : for (int j = 0; j < (int)matrixPollutants[i].size(); j++) {
160 0 : if ((int)matrixPollutants[i].size() != headerCount + 1) {
161 : return;
162 : }
163 :
164 0 : if (j == 0) {
165 0 : _normailzedPowerPatternPollutants.push_back(matrixPollutants[i][j]);
166 0 : _powerPatternPollutants.push_back(matrixPollutants[i][j] * _normalizingPower);
167 : } else {
168 0 : pollutantMeasures[j - 1].push_back(matrixPollutants[i][j] * pollutantMultiplyer);
169 0 : normalizedPollutantMeasures[j - 1].push_back(matrixPollutants[i][j]);
170 : } // end if
171 : } // end for
172 : } // end for
173 :
174 0 : for (int i = 0; i < (int) headerLinePollutants.size(); i++) {
175 0 : _cepCurvePollutants.insert(pollutantIdentifier[i], pollutantMeasures[i]);
176 0 : _normalizedCepCurvePollutants.insert(pollutantIdentifier[i], normalizedPollutantMeasures[i]);
177 0 : _idlingValuesPollutants.insert(pollutantIdentifier[i], idlingValuesPollutants[i] * pollutantMultiplyer);
178 : } // end for
179 :
180 0 : _idlingFC = idlingFC * _ratedPower;
181 :
182 0 : } // end of Cep
183 :
184 :
185 0 : PHEMCEP::~PHEMCEP() {
186 : // free power pattern
187 : _powerPatternFC.clear();
188 : _powerPatternPollutants.clear();
189 : _cepCurveFC.clear();
190 : _speedCurveRotational.clear();
191 : _speedPatternRotational.clear();
192 0 : } // end of ~Cep
193 :
194 :
195 : double
196 0 : 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 0 : if (!normalized && fabs(speed) <= ZERO_SPEED_ACCURACY) {
201 0 : if (pollutant == "FC") {
202 0 : return _idlingFC;
203 : } else {
204 0 : return _idlingValuesPollutants.get(pollutant);
205 : }
206 : } // end if
207 :
208 0 : if (pollutant == "FC") {
209 0 : if (normalized) {
210 0 : emissionCurve = _normedCepCurveFC;
211 0 : powerPattern = _normalizedPowerPatternFC;
212 : } else {
213 0 : emissionCurve = _cepCurveFC;
214 0 : powerPattern = _powerPatternFC;
215 : }
216 : } else {
217 0 : if (!_cepCurvePollutants.hasString(pollutant)) {
218 0 : throw InvalidArgument("Emission pollutant " + pollutant + " not found!");
219 : }
220 :
221 0 : if (normalized) {
222 0 : emissionCurve = _normalizedCepCurvePollutants.get(pollutant);
223 0 : powerPattern = _normailzedPowerPatternPollutants;
224 : } else {
225 0 : emissionCurve = _cepCurvePollutants.get(pollutant);
226 0 : powerPattern = _powerPatternPollutants;
227 : }
228 :
229 : } // end if
230 :
231 :
232 :
233 0 : if (emissionCurve.size() == 0) {
234 0 : throw InvalidArgument("Empty emission curve for " + pollutant + " found!");
235 : }
236 :
237 0 : if (emissionCurve.size() == 1) {
238 0 : 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 0 : if (power <= powerPattern.front()) {
243 0 : double calcEmission = PHEMCEP::Interpolate(power, powerPattern[0], powerPattern[1], emissionCurve[0], emissionCurve[1]);
244 :
245 0 : if (calcEmission < 0) {
246 : return 0;
247 : } else {
248 0 : 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 0 : if (power >= powerPattern.back()) {
255 0 : 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 0 : PHEMCEP::FindLowerUpperInPattern(lowerIndex, upperIndex, powerPattern, power);
263 :
264 0 : return PHEMCEP::Interpolate(power, powerPattern[lowerIndex], powerPattern[upperIndex], emissionCurve[lowerIndex], emissionCurve[upperIndex]);
265 :
266 0 : } // end of GetEmission
267 :
268 :
269 : double
270 0 : PHEMCEP::Interpolate(double px, double p1, double p2, double e1, double e2) const {
271 0 : if (p2 == p1) {
272 : return e1;
273 : }
274 0 : return e1 + (px - p1) / (p2 - p1) * (e2 - e1);
275 : } // end of Interpolate
276 :
277 :
278 0 : double PHEMCEP::GetDecelCoast(double speed, double acc, double gradient, double /* vehicleLoading */) const {
279 0 : if (speed < SPEED_DCEL_MIN) {
280 0 : return speed / SPEED_DCEL_MIN * GetDecelCoast(SPEED_DCEL_MIN, acc, gradient, _vehicleLoading); // !!!vehicleLoading
281 : } // end if
282 :
283 0 : double rotCoeff = GetRotationalCoeffecient(speed);
284 :
285 : int upperIndex;
286 : int lowerIndex;
287 :
288 0 : double iGear = GetGearCoeffecient(speed);
289 :
290 0 : double iTot = iGear * _axleRatio;
291 :
292 0 : double n = (30 * speed * iTot) / ((_effictiveWheelDiameter / 2) * M_PI2);
293 0 : double nNorm = (n - _engineIdlingSpeed) / (_engineRatedSpeed - _engineIdlingSpeed);
294 :
295 0 : FindLowerUpperInPattern(lowerIndex, upperIndex, _nNormTable, nNorm);
296 :
297 : double fMot = 0;
298 :
299 0 : if (speed >= 10e-2) {
300 0 : fMot = (-GetDragCoeffecient(nNorm) * _ratedPower * 1000 / speed) / 0.9;
301 : } // end if
302 :
303 0 : double fRoll = (_resistanceF0
304 0 : + _resistanceF1 * speed
305 0 : + pow(_resistanceF2 * speed, 2)
306 0 : + pow(_resistanceF3 * speed, 3)
307 0 : + pow(_resistanceF4 * speed, 4)) * (_massVehicle + _vehicleLoading) * GRAVITY_CONST; // !!!vehicleLoading
308 :
309 0 : double fAir = _cdValue * _crossSectionalArea * 1.2 * 0.5 * pow(speed, 2);
310 :
311 0 : double fGrad = (_massVehicle + _vehicleLoading) * GRAVITY_CONST * gradient / 100; // !!!vehicleLoading
312 :
313 0 : return -(fMot + fRoll + fAir + fGrad) / ((_massVehicle + _vehicleLoading) * rotCoeff); // !!!vehicleLoading
314 : } // end of GetDecelCoast
315 :
316 :
317 : double
318 0 : PHEMCEP::GetRotationalCoeffecient(double speed) const {
319 : int upperIndex;
320 : int lowerIndex;
321 :
322 0 : PHEMCEP::FindLowerUpperInPattern(lowerIndex, upperIndex, _speedPatternRotational, speed);
323 :
324 0 : return PHEMCEP::Interpolate(speed,
325 : _speedPatternRotational[lowerIndex],
326 : _speedPatternRotational[upperIndex],
327 0 : _speedCurveRotational[lowerIndex],
328 0 : _speedCurveRotational[upperIndex]);
329 : } // end of GetRotationalCoeffecient
330 :
331 0 : double PHEMCEP::GetGearCoeffecient(double speed) const {
332 : int upperIndex;
333 : int lowerIndex;
334 :
335 0 : FindLowerUpperInPattern(lowerIndex, upperIndex, _gearTransmissionCurve, speed);
336 :
337 0 : return Interpolate(speed,
338 : _speedPatternRotational[lowerIndex],
339 : _speedPatternRotational[upperIndex],
340 0 : _gearTransmissionCurve[lowerIndex],
341 0 : _gearTransmissionCurve[upperIndex]);
342 : } // end of GetGearCoefficient
343 :
344 0 : double PHEMCEP::GetDragCoeffecient(double nNorm) const {
345 : int upperIndex;
346 : int lowerIndex;
347 :
348 0 : FindLowerUpperInPattern(lowerIndex, upperIndex, _dragNormTable, nNorm);
349 :
350 0 : return Interpolate(nNorm,
351 : _nNormTable[lowerIndex],
352 : _nNormTable[upperIndex],
353 0 : _dragNormTable[lowerIndex],
354 0 : _dragNormTable[upperIndex]);
355 : } // end of GetGearCoefficient
356 :
357 0 : void PHEMCEP::FindLowerUpperInPattern(int& lowerIndex, int& upperIndex, const std::vector<double>& pattern, double value) const {
358 0 : if (value <= pattern.front()) {
359 0 : lowerIndex = 0;
360 0 : upperIndex = 0;
361 0 : return;
362 :
363 : } // end if
364 :
365 0 : if (value >= pattern.back()) {
366 0 : lowerIndex = (int)pattern.size() - 1;
367 0 : upperIndex = (int)pattern.size() - 1;
368 0 : return;
369 : } // end if
370 :
371 : // bisection search to find correct position in power pattern
372 0 : int middleIndex = ((int)pattern.size() - 1) / 2;
373 0 : upperIndex = (int)pattern.size() - 1;
374 0 : lowerIndex = 0;
375 :
376 0 : while (upperIndex - lowerIndex > 1) {
377 0 : if (pattern[middleIndex] == value) {
378 0 : lowerIndex = middleIndex;
379 0 : upperIndex = middleIndex;
380 0 : return;
381 0 : } else if (pattern[middleIndex] < value) {
382 0 : lowerIndex = middleIndex;
383 0 : middleIndex = (upperIndex - lowerIndex) / 2 + lowerIndex;
384 : } else {
385 0 : upperIndex = middleIndex;
386 0 : middleIndex = (upperIndex - lowerIndex) / 2 + lowerIndex;
387 : } // end if
388 : } // end while
389 :
390 0 : if (pattern[lowerIndex] <= value && value < pattern[upperIndex]) {
391 : return;
392 : } else {
393 0 : throw ProcessError("Error during calculation of position in pattern!");
394 : }
395 : } // end of FindLowerUpperInPattern
396 :
397 :
398 : double
399 0 : PHEMCEP::CalcPower(double v, double a, double slope, double /* vehicleLoading */) const {
400 0 : const double rotFactor = GetRotationalCoeffecient(v);
401 0 : double power = (_massVehicle + _vehicleLoading) * GRAVITY_CONST * (_resistanceF0 + _resistanceF1 * v + _resistanceF4 * pow(v, 4)) * v;
402 0 : power += (_crossSectionalArea * _cdValue * AIR_DENSITY_CONST / 2) * pow(v, 3);
403 0 : power += (_massVehicle * rotFactor + _massRot + _vehicleLoading) * a * v;
404 0 : power += (_massVehicle + _vehicleLoading) * slope * 0.01 * v;
405 0 : return power / 950.;
406 : }
407 :
408 :
409 : double
410 0 : PHEMCEP::GetMaxAccel(double v, double a, double gradient, double /* vehicleLoading */) const {
411 : UNUSED_PARAMETER(a);
412 0 : double rotFactor = GetRotationalCoeffecient(v);
413 0 : const double pMaxForAcc = GetPMaxNorm(v) * _ratedPower - PHEMCEP::CalcPower(v, 0, gradient, _vehicleLoading); // !!!vehicleLoading
414 0 : return (pMaxForAcc * 1000) / ((_massVehicle * rotFactor + _massRot + _vehicleLoading) * v); // !!!vehicleLoading
415 : }
416 :
417 :
418 :
419 0 : double PHEMCEP::GetPMaxNorm(double speed) const {
420 : // Linear function between v0 and v1, constant elsewhere
421 0 : if (speed <= _pNormV0) {
422 0 : return _pNormP0;
423 0 : } else if (speed >= _pNormV1) {
424 0 : return _pNormP1;
425 : } else {
426 0 : return PHEMCEP::Interpolate(speed, _pNormV0, _pNormV1, _pNormP0, _pNormP1);
427 : }
428 : } // end of GetPMaxNorm
429 :
430 :
431 : /****************************************************************************/
|