Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2016-2025 German Aerospace Center (DLR) and others.
4 : // PHEMlight module
5 : // Copyright (C) 2016-2023 Technische Universitaet Graz, https://www.tugraz.at/
6 : // This program and the accompanying materials are made available under the
7 : // terms of the Eclipse Public License 2.0 which is available at
8 : // https://www.eclipse.org/legal/epl-2.0/
9 : // This Source Code may also be made available under the following Secondary
10 : // Licenses when the conditions for such availability set forth in the Eclipse
11 : // Public License 2.0 are satisfied: GNU General Public License, version 2
12 : // or later which is available at
13 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
14 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
15 : /****************************************************************************/
16 : /// @file CEP.cpp
17 : /// @author Martin Dippold
18 : /// @author Michael Behrisch
19 : /// @date July 2016
20 : ///
21 : //
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include "CEP.h"
26 : #include "CEPHandler.h"
27 : #include "Constants.h"
28 : #include "Helpers.h"
29 :
30 :
31 : namespace PHEMlightdllV5 {
32 :
33 34 : CEP::CEP(VEHPHEMLightJSON::VEH* Vehicle, std::vector<std::string>& headerLineFCvalues, std::vector<std::vector<double> >& matrixFCvalues, std::vector<std::string>& headerLinePollutants, std::vector<std::vector<double> >& matrixPollutants, std::vector<double>& idlingFCvalues, std::vector<double>& idlingPollutants) {
34 34 : InitializeInstanceFields();
35 34 : _resistanceF0 = Vehicle->getRollingResData()->getFr0();
36 34 : _resistanceF1 = Vehicle->getRollingResData()->getFr1();
37 34 : _resistanceF2 = Vehicle->getRollingResData()->getFr2();
38 34 : _resistanceF3 = Vehicle->getRollingResData()->getFr3();
39 34 : _resistanceF4 = Vehicle->getRollingResData()->getFr4();
40 34 : _cWValue = Vehicle->getVehicleData()->getCw();
41 34 : _crossSectionalArea = Vehicle->getVehicleData()->getA();
42 34 : _massVehicle = Vehicle->getVehicleData()->getMass();
43 34 : _vehicleLoading = Vehicle->getVehicleData()->getLoading();
44 34 : _vehicleMassRot = Vehicle->getVehicleData()->getRedMassWheel();
45 34 : setCalcType(Vehicle->getVehicleData()->getCalcType());
46 : //C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string variable and was converted to C++ 'if-else' logic:
47 : // switch (CalcType)
48 : //ORIGINAL LINE: case "Conv":
49 34 : if (getCalcType() == "Conv") {
50 34 : setRatedPower(Vehicle->getEngineData()->getICEData()->getPrated());
51 34 : _engineRatedSpeed = Vehicle->getEngineData()->getICEData()->getnrated();
52 34 : _engineIdlingSpeed = Vehicle->getEngineData()->getICEData()->getIdling();
53 : }
54 : //ORIGINAL LINE: case "HEV":
55 0 : else if (getCalcType() == "HEV") {
56 : // Power von beiden zusammen Rest bezogen auf ICE
57 0 : setRatedPower(Vehicle->getEngineData()->getICEData()->getPrated() + Vehicle->getEngineData()->getEMData()->getPrated());
58 0 : _engineRatedSpeed = Vehicle->getEngineData()->getICEData()->getnrated();
59 0 : _engineIdlingSpeed = Vehicle->getEngineData()->getICEData()->getIdling();
60 : }
61 : //ORIGINAL LINE: case "BEV":
62 0 : else if (getCalcType() == "BEV") {
63 0 : setRatedPower(Vehicle->getEngineData()->getEMData()->getPrated());
64 0 : _engineRatedSpeed = Vehicle->getEngineData()->getEMData()->getnrated();
65 0 : _engineIdlingSpeed = 0;
66 : }
67 :
68 34 : _effectiveWheelDiameter = Vehicle->getVehicleData()->getWheelDiameter();
69 34 : privateHeavyVehicle = Vehicle->getVehicleData()->getMassType() == Constants::HeavyVehicle;
70 34 : setFuelType(Vehicle->getVehicleData()->getFuelType());
71 34 : _axleRatio = Vehicle->getTransmissionData()->getAxelRatio();
72 34 : _auxPower = Vehicle->getAuxiliariesData()->getPauxnorm();
73 :
74 34 : _pNormV0 = Vehicle->getFLDData()->getP_n_max_v0() / 3.6;
75 34 : _pNormP0 = Vehicle->getFLDData()->getP_n_max_p0();
76 34 : _pNormV1 = Vehicle->getFLDData()->getP_n_max_v1() / 3.6;
77 34 : _pNormP1 = Vehicle->getFLDData()->getP_n_max_p1();
78 :
79 : // Init pollutant identifiers, unit and measures
80 : std::vector<std::string> FCvaluesIdentifier;
81 : std::vector<std::vector<double> > normalizedFCvaluesMeasures;
82 136 : for (int i = 0; i < (int)headerLineFCvalues.size(); i++) {
83 102 : FCvaluesIdentifier.push_back(headerLineFCvalues[i]);
84 102 : normalizedFCvaluesMeasures.push_back(std::vector<double>());
85 : }
86 :
87 : // Init pollutant identifiers, unit and measures
88 : std::vector<std::string> pollutantIdentifier;
89 : std::vector<std::vector<double> > normalizedPollutantMeasures;
90 204 : for (int i = 0; i < (int)headerLinePollutants.size(); i++) {
91 170 : pollutantIdentifier.push_back(headerLinePollutants[i]);
92 170 : normalizedPollutantMeasures.push_back(std::vector<double>());
93 : }
94 :
95 : // Assigning values for speed rotational table
96 34 : _speedPatternRotational = std::vector<double>();
97 283 : for (int i = 0; i < (int)Vehicle->getTransmissionData()->getTransm()["Speed"].size(); i++) {
98 498 : _speedPatternRotational.push_back(Vehicle->getTransmissionData()->getTransm()["Speed"][i] / 3.6);
99 : }
100 :
101 34 : _gearTransmissionCurve = Vehicle->getTransmissionData()->getTransm()["GearRatio"];
102 34 : _speedCurveRotational = Vehicle->getTransmissionData()->getTransm()["RotMassF"];
103 :
104 : // Assigning values for drag table
105 34 : _nNormTable = Vehicle->getFLDData()->getDragCurve()["n_norm"];
106 68 : _dragNormTable = Vehicle->getFLDData()->getDragCurve()["pe_drag_norm"];
107 :
108 : // Looping through matrix and assigning values for FC values
109 34 : _normalizedPowerPatternFCvalues = std::vector<double>();
110 :
111 34 : int headerFCCount = (int)headerLineFCvalues.size();
112 476 : for (int i = 0; i < (int)matrixFCvalues.size(); i++) {
113 2210 : for (int j = 0; j < (int)matrixFCvalues[i].size(); j++) {
114 1768 : if ((int)matrixFCvalues[i].size() != headerFCCount + 1) {
115 : return;
116 : }
117 :
118 1768 : if (j == 0) {
119 442 : _normalizedPowerPatternFCvalues.push_back(matrixFCvalues[i][j]);
120 : }
121 : else {
122 1326 : normalizedFCvaluesMeasures[j - 1].push_back(matrixFCvalues[i][j]);
123 : }
124 : }
125 : }
126 :
127 34 : _idlingValueFCvalues = std::map<std::string, double>();
128 34 : _normedCepCurveFCvalues = std::map<std::string, std::vector<double> >();
129 :
130 136 : for (int i = 0; i < (int)headerLineFCvalues.size(); i++) {
131 102 : _normedCepCurveFCvalues.insert(std::make_pair(FCvaluesIdentifier[i], normalizedFCvaluesMeasures[i]));
132 204 : _idlingValueFCvalues.insert(std::make_pair(FCvaluesIdentifier[i], idlingFCvalues[i]));
133 : }
134 :
135 34 : _normalizedPowerPatternPollutants = std::vector<double>();
136 34 : _cepNormalizedCurvePollutants = std::map<std::string, std::vector<double> >();
137 :
138 34 : int headerCount = (int)headerLinePollutants.size();
139 1088 : for (int i = 0; i < (int)matrixPollutants.size(); i++) {
140 7378 : for (int j = 0; j < (int)matrixPollutants[i].size(); j++) {
141 6324 : if ((int)matrixPollutants[i].size() != headerCount + 1) {
142 : return;
143 : }
144 :
145 6324 : if (j == 0) {
146 1054 : _normalizedPowerPatternPollutants.push_back(matrixPollutants[i][j]);
147 : }
148 : else {
149 5270 : normalizedPollutantMeasures[j - 1].push_back(matrixPollutants[i][j]);
150 : }
151 : }
152 : }
153 :
154 34 : _idlingValuesPollutants = std::map<std::string, double>();
155 :
156 204 : for (int i = 0; i < (int)headerLinePollutants.size(); i++) {
157 170 : _cepNormalizedCurvePollutants.insert(std::make_pair(pollutantIdentifier[i], normalizedPollutantMeasures[i]));
158 340 : _idlingValuesPollutants.insert(std::make_pair(pollutantIdentifier[i], idlingPollutants[i]));
159 : }
160 :
161 34 : _FleetMix = std::map<std::string, double>();
162 68 : _FleetMix.insert(std::make_pair(Constants::strGasoline, 0));
163 68 : _FleetMix.insert(std::make_pair(Constants::strDiesel, 0));
164 68 : _FleetMix.insert(std::make_pair(Constants::strCNG, 0));
165 34 : _FleetMix.insert(std::make_pair(Constants::strLPG, 0));
166 34 : }
167 :
168 118779 : const bool& CEP::getHeavyVehicle() const {
169 118779 : return privateHeavyVehicle;
170 : }
171 :
172 95411 : const std::string& CEP::getFuelType() const {
173 95411 : return privateFuelType;
174 : }
175 :
176 34 : void CEP::setFuelType(const std::string& value) {
177 34 : privateFuelType = value;
178 34 : }
179 :
180 484021 : const std::string& CEP::getCalcType() const {
181 484021 : return privateCalcType;
182 : }
183 :
184 34 : void CEP::setCalcType(const std::string& value) {
185 34 : privateCalcType = value;
186 34 : }
187 :
188 426588 : const double& CEP::getRatedPower() const {
189 426588 : return privateRatedPower;
190 : }
191 :
192 34 : void CEP::setRatedPower(const double& value) {
193 34 : privateRatedPower = value;
194 34 : }
195 :
196 76407 : double CEP::CalcEngPower(double power, const double ratedPower) {
197 76407 : if (power < _normalizedPowerPatternFCvalues.front() * ratedPower) {
198 : return _normalizedPowerPatternFCvalues.front() * ratedPower;
199 : }
200 74923 : if (power > _normalizedPowerPatternFCvalues.back() * ratedPower) {
201 924 : return _normalizedPowerPatternFCvalues.back() * ratedPower;
202 : }
203 :
204 : return power;
205 : }
206 :
207 76361 : double CEP::GetEmission(const std::string& pollutant, double power, double speed, Helpers* VehicleClass, const double drivingPower, const double ratedPower) {
208 : //Declaration
209 : std::vector<double>* emissionCurve = nullptr;
210 : std::vector<double>* powerPattern = nullptr;
211 :
212 : // bisection search to find correct position in power pattern
213 : int upperIndex;
214 : int lowerIndex;
215 :
216 76361 : double emissionMultiplier = getHeavyVehicle() ? ratedPower : 1.;
217 76361 : if (std::abs(speed) <= Constants::ZERO_SPEED_ACCURACY) {
218 19504 : if (_cepNormalizedCurvePollutants.find(pollutant) == _cepNormalizedCurvePollutants.end() && _normedCepCurveFCvalues.find(pollutant) == _normedCepCurveFCvalues.end()) {
219 0 : VehicleClass->setErrMsg(std::string("Emission pollutant or fuel value ") + pollutant + std::string(" not found!"));
220 0 : return 0;
221 : }
222 :
223 19504 : if (_normedCepCurveFCvalues.find(pollutant) != _normedCepCurveFCvalues.end()) {
224 4876 : return _idlingValueFCvalues[pollutant] * ratedPower;
225 : }
226 14628 : else if (_cepNormalizedCurvePollutants.find(pollutant) != _cepNormalizedCurvePollutants.end()) {
227 14628 : return _idlingValuesPollutants[pollutant] * emissionMultiplier;
228 : }
229 : }
230 :
231 :
232 56857 : if (_cepNormalizedCurvePollutants.find(pollutant) == _cepNormalizedCurvePollutants.end() && _normedCepCurveFCvalues.find(pollutant) == _normedCepCurveFCvalues.end()) {
233 0 : VehicleClass->setErrMsg(std::string("Emission pollutant or fuel value ") + pollutant + std::string(" not found!"));
234 0 : return 0;
235 : }
236 :
237 : double normalizingPower = ratedPower;
238 56857 : if (_normedCepCurveFCvalues.find(pollutant) != _normedCepCurveFCvalues.end()) {
239 14473 : emissionCurve = &_normedCepCurveFCvalues[pollutant];
240 14473 : powerPattern = &_normalizedPowerPatternFCvalues;
241 : emissionMultiplier = ratedPower;
242 : }
243 42384 : else if (_cepNormalizedCurvePollutants.find(pollutant) != _cepNormalizedCurvePollutants.end()) {
244 42384 : emissionCurve = &_cepNormalizedCurvePollutants[pollutant];
245 42384 : powerPattern = &_normalizedPowerPatternPollutants;
246 42384 : if (!getHeavyVehicle()) {
247 : normalizingPower = drivingPower;
248 : }
249 : }
250 :
251 56857 : if (emissionCurve == nullptr || emissionCurve->empty()) {
252 0 : VehicleClass->setErrMsg(std::string("Empty emission curve for ") + pollutant + std::string(" found!"));
253 0 : return 0;
254 : }
255 56857 : if (emissionCurve->size() == 1) {
256 0 : return emissionCurve->front() * emissionMultiplier;
257 : }
258 :
259 : // in case that the demanded power is smaller than the first entry (smallest) in the power pattern the first is returned (should never happen)
260 56857 : if (power <= powerPattern->front() * normalizingPower) {
261 0 : return emissionCurve->front() * emissionMultiplier;
262 : }
263 :
264 : // if power bigger than all entries in power pattern return the last (should never happen)
265 56857 : if (power >= powerPattern->back() * normalizingPower) {
266 264 : return emissionCurve->back() * emissionMultiplier;
267 : }
268 :
269 56593 : FindLowerUpperInPattern(lowerIndex, upperIndex, *powerPattern, power, normalizingPower);
270 56593 : return Interpolate(power, (*powerPattern)[lowerIndex] * normalizingPower, (*powerPattern)[upperIndex] * normalizingPower, (*emissionCurve)[lowerIndex], (*emissionCurve)[upperIndex]) * emissionMultiplier;
271 : }
272 :
273 9502 : double CEP::GetCO2Emission(double _FC, double _CO, double _HC, Helpers* VehicleClass) {
274 : //Declaration
275 : double fCBr, fCHC, fCCO, fCCO2;
276 :
277 9502 : fCBr = 0;
278 9502 : fCHC = 0;
279 9502 : fCCO = 0;
280 9502 : fCCO2 = 0;
281 :
282 9502 : if (getFuelType() != "Mix") {
283 9502 : if (!GetfcVals(getFuelType(), fCBr, fCHC, fCCO, fCCO2, VehicleClass)) {
284 : return 0;
285 : }
286 : }
287 : else {
288 0 : if (!CalcfCValMix(fCBr, fCHC, fCCO, fCCO2, VehicleClass)) {
289 : return 0;
290 : }
291 : }
292 :
293 9502 : return (_FC * fCBr - _CO * fCCO - _HC * fCHC) / fCCO2;
294 : }
295 :
296 0 : bool CEP::CalcfCValMix(double& _fCBr, double& _fCHC, double& _fCCO, double& _fCCO2, Helpers* VehicleClass) {
297 : //Declaration
298 : double Sum = 0;
299 : double sumfCBr, sumfCHC, sumfCCO, sumfCCO2;
300 :
301 : //Initialise
302 : sumfCBr = 0;
303 : sumfCHC = 0;
304 : sumfCCO = 0;
305 : sumfCCO2 = 0;
306 :
307 : //calculate the sum
308 0 : for (std::map<std::string, double>::const_iterator id = _FleetMix.begin(); id != _FleetMix.end(); ++id) {
309 0 : Sum += _FleetMix[id->first];
310 : }
311 :
312 : //Calculate the weighted fuel factors
313 0 : if (Sum <= 0) {
314 0 : VehicleClass->setErrMsg("All propolsion types in the fleetshares file are not known!");
315 0 : return false;
316 : }
317 : else {
318 0 : for (std::map<std::string, double>::const_iterator id = _FleetMix.begin(); id != _FleetMix.end(); ++id) {
319 0 : if (!GetfcVals(id->first, _fCBr, _fCHC, _fCCO, _fCCO2, VehicleClass)) {
320 : return false;
321 : }
322 : else {
323 0 : sumfCBr += _fCBr * _FleetMix[id->first] / Sum;
324 0 : sumfCHC += _fCHC * _FleetMix[id->first] / Sum;
325 0 : sumfCCO += _fCCO * _FleetMix[id->first] / Sum;
326 0 : sumfCCO2 += _fCCO2 * _FleetMix[id->first] / Sum;
327 : }
328 : }
329 : }
330 : //Result values
331 0 : _fCBr = sumfCBr;
332 0 : _fCHC = sumfCHC;
333 0 : _fCCO = sumfCCO;
334 0 : _fCCO2 = sumfCCO2;
335 0 : return true;
336 : }
337 :
338 9502 : bool CEP::GetfcVals(const std::string& _fuelTypex, double& _fCBr, double& _fCHC, double& _fCCO, double& _fCCO2, Helpers* VehicleClass) {
339 9502 : _fCHC = 0.866;
340 9502 : _fCCO = 0.429;
341 9502 : _fCCO2 = 0.273;
342 :
343 : //C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string variable and was converted to C++ 'if-else' logic:
344 : // switch (_fuelTypex)
345 : //ORIGINAL LINE: case Constants.strGasoline:
346 9502 : if (_fuelTypex == Constants::strGasoline) {
347 5165 : _fCBr = 0.865;
348 : }
349 : //ORIGINAL LINE: case Constants.strDiesel:
350 4337 : else if (_fuelTypex == Constants::strDiesel) {
351 4337 : _fCBr = 0.863;
352 : }
353 : //ORIGINAL LINE: case Constants.strCNG:
354 0 : else if (_fuelTypex == Constants::strCNG) {
355 0 : _fCBr = 0.693;
356 0 : _fCHC = 0.803;
357 : }
358 : //ORIGINAL LINE: case Constants.strLPG:
359 0 : else if (_fuelTypex == Constants::strLPG) {
360 0 : _fCBr = 0.825;
361 0 : _fCHC = 0.825;
362 : }
363 : else {
364 0 : VehicleClass->setErrMsg(std::string("The propulsion type is not known! (") + getFuelType() + std::string(")"));
365 0 : return false;
366 : }
367 : return true;
368 : }
369 :
370 85867 : double CEP::getFMot(const double speed, const double ratedPower, const double wheelRadius) {
371 85867 : if (speed < 10e-2) {
372 : return 0.;
373 : }
374 : //Declaration
375 : int upperIndex;
376 : int lowerIndex;
377 :
378 85867 : FindLowerUpperInPattern(lowerIndex, upperIndex, _speedPatternRotational, speed);
379 85867 : double iGear = Interpolate(speed, _speedPatternRotational[lowerIndex], _speedPatternRotational[upperIndex], _gearTransmissionCurve[lowerIndex], _gearTransmissionCurve[upperIndex]);
380 :
381 85867 : double iTot = iGear * _axleRatio;
382 :
383 85867 : double n = (30 * speed * iTot) / (wheelRadius * M_PI);
384 85867 : double nNorm = (n - _engineIdlingSpeed) / (_engineRatedSpeed - _engineIdlingSpeed);
385 :
386 85867 : FindLowerUpperInPattern(lowerIndex, upperIndex, _nNormTable, nNorm);
387 85867 : return (-Interpolate(nNorm, _nNormTable[lowerIndex], _nNormTable[upperIndex], _dragNormTable[lowerIndex], _dragNormTable[upperIndex]) * ratedPower * 1000 / speed) / Constants::getDRIVE_TRAIN_EFFICIENCY();
388 : }
389 :
390 350181 : double CEP::GetRotationalCoeffecient(double speed) {
391 : //Declaration
392 : int upperIndex;
393 : int lowerIndex;
394 :
395 350181 : FindLowerUpperInPattern(lowerIndex, upperIndex, _speedPatternRotational, speed);
396 350181 : return Interpolate(speed, _speedPatternRotational[lowerIndex], _speedPatternRotational[upperIndex], _speedCurveRotational[lowerIndex], _speedCurveRotational[upperIndex]);
397 : }
398 :
399 578508 : void CEP::FindLowerUpperInPattern(int& lowerIndex, int& upperIndex, const std::vector<double>& pattern, double value, double scale) {
400 578508 : lowerIndex = 0;
401 578508 : upperIndex = 0;
402 :
403 578508 : if (value <= pattern.front() * scale) {
404 : lowerIndex = 0;
405 : upperIndex = 0;
406 : return;
407 : }
408 :
409 562625 : if (value >= pattern.back() * scale) {
410 0 : lowerIndex = (int)pattern.size() - 1;
411 0 : upperIndex = (int)pattern.size() - 1;
412 0 : return;
413 : }
414 :
415 : // bisection search to find correct position in power pattern
416 562625 : int middleIndex = ((int)pattern.size() - 1) / 2;
417 562625 : upperIndex = (int)pattern.size() - 1;
418 562625 : lowerIndex = 0;
419 :
420 2332309 : while (upperIndex - lowerIndex > 1) {
421 1769684 : if (pattern[middleIndex] * scale == value) {
422 0 : lowerIndex = middleIndex;
423 0 : upperIndex = middleIndex;
424 0 : return;
425 : }
426 1769684 : else if (pattern[middleIndex] * scale < value) {
427 852638 : lowerIndex = middleIndex;
428 852638 : middleIndex = (upperIndex - lowerIndex) / 2 + lowerIndex;
429 : }
430 : else {
431 917046 : upperIndex = middleIndex;
432 917046 : middleIndex = (upperIndex - lowerIndex) / 2 + lowerIndex;
433 : }
434 : }
435 : }
436 :
437 593488 : double CEP::Interpolate(double px, double p1, double p2, double e1, double e2) {
438 593488 : if (p2 == p1) {
439 : return e1;
440 : }
441 :
442 577605 : return e1 + (px - p1) / (p2 - p1) * (e2 - e1);
443 : }
444 :
445 60524 : double CEP::GetPMaxNorm(double speed) {
446 : // Linear function between v0 and v1, constant elsewhere
447 60524 : if (speed <= _pNormV0) {
448 2653 : return _pNormP0;
449 : }
450 57871 : else if (speed >= _pNormV1) {
451 42891 : return _pNormP1;
452 : }
453 : else {
454 14980 : return Interpolate(speed, _pNormV0, _pNormV1, _pNormP0, _pNormP1);
455 : }
456 : }
457 :
458 34 : void CEP::InitializeInstanceFields() {
459 34 : _massVehicle = 0;
460 34 : _vehicleLoading = 0;
461 34 : _vehicleMassRot = 0;
462 34 : _crossSectionalArea = 0;
463 34 : _cWValue = 0;
464 34 : _resistanceF0 = 0;
465 34 : _resistanceF1 = 0;
466 34 : _resistanceF2 = 0;
467 34 : _resistanceF3 = 0;
468 34 : _resistanceF4 = 0;
469 34 : _axleRatio = 0;
470 34 : _auxPower = 0;
471 34 : _pNormV0 = 0;
472 34 : _pNormP0 = 0;
473 34 : _pNormV1 = 0;
474 34 : _pNormP1 = 0;
475 34 : _engineRatedSpeed = 0;
476 34 : _engineIdlingSpeed = 0;
477 34 : _effectiveWheelDiameter = 0;
478 34 : }
479 : }
|