Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2016-2024 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 33 : 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 33 : InitializeInstanceFields();
35 33 : _resistanceF0 = Vehicle->getRollingResData()->getFr0();
36 33 : _resistanceF1 = Vehicle->getRollingResData()->getFr1();
37 33 : _resistanceF2 = Vehicle->getRollingResData()->getFr2();
38 33 : _resistanceF3 = Vehicle->getRollingResData()->getFr3();
39 33 : _resistanceF4 = Vehicle->getRollingResData()->getFr4();
40 33 : _cWValue = Vehicle->getVehicleData()->getCw();
41 33 : _crossSectionalArea = Vehicle->getVehicleData()->getA();
42 33 : _massVehicle = Vehicle->getVehicleData()->getMass();
43 33 : _vehicleLoading = Vehicle->getVehicleData()->getLoading();
44 33 : _vehicleMassRot = Vehicle->getVehicleData()->getRedMassWheel();
45 33 : 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 33 : if (getCalcType() == "Conv") {
50 33 : setRatedPower(Vehicle->getEngineData()->getICEData()->getPrated());
51 33 : _engineRatedSpeed = Vehicle->getEngineData()->getICEData()->getnrated();
52 33 : _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 33 : _effectiveWheelDiameter = Vehicle->getVehicleData()->getWheelDiameter();
69 33 : privateHeavyVehicle = Vehicle->getVehicleData()->getMassType() == Constants::HeavyVehicle;
70 33 : setFuelType(Vehicle->getVehicleData()->getFuelType());
71 33 : _axleRatio = Vehicle->getTransmissionData()->getAxelRatio();
72 33 : _auxPower = Vehicle->getAuxiliariesData()->getPauxnorm();
73 :
74 33 : _pNormV0 = Vehicle->getFLDData()->getP_n_max_v0() / 3.6;
75 33 : _pNormP0 = Vehicle->getFLDData()->getP_n_max_p0();
76 33 : _pNormV1 = Vehicle->getFLDData()->getP_n_max_v1() / 3.6;
77 33 : _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 132 : for (int i = 0; i < (int)headerLineFCvalues.size(); i++) {
83 99 : FCvaluesIdentifier.push_back(headerLineFCvalues[i]);
84 99 : 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 198 : for (int i = 0; i < (int)headerLinePollutants.size(); i++) {
91 165 : pollutantIdentifier.push_back(headerLinePollutants[i]);
92 165 : normalizedPollutantMeasures.push_back(std::vector<double>());
93 : }
94 :
95 : // Assigning values for speed rotational table
96 33 : _speedPatternRotational = std::vector<double>();
97 275 : for (int i = 0; i < (int)Vehicle->getTransmissionData()->getTransm()["Speed"].size(); i++) {
98 484 : _speedPatternRotational.push_back(Vehicle->getTransmissionData()->getTransm()["Speed"][i] / 3.6);
99 : }
100 :
101 33 : _gearTransmissionCurve = Vehicle->getTransmissionData()->getTransm()["GearRatio"];
102 33 : _speedCurveRotational = Vehicle->getTransmissionData()->getTransm()["RotMassF"];
103 :
104 : // Assigning values for drag table
105 33 : _nNormTable = Vehicle->getFLDData()->getDragCurve()["n_norm"];
106 66 : _dragNormTable = Vehicle->getFLDData()->getDragCurve()["pe_drag_norm"];
107 :
108 : // Looping through matrix and assigning values for FC values
109 33 : _normalizedPowerPatternFCvalues = std::vector<double>();
110 :
111 33 : int headerFCCount = (int)headerLineFCvalues.size();
112 462 : for (int i = 0; i < (int)matrixFCvalues.size(); i++) {
113 2145 : for (int j = 0; j < (int)matrixFCvalues[i].size(); j++) {
114 1716 : if ((int)matrixFCvalues[i].size() != headerFCCount + 1) {
115 : return;
116 : }
117 :
118 1716 : if (j == 0) {
119 429 : _normalizedPowerPatternFCvalues.push_back(matrixFCvalues[i][j]);
120 : }
121 : else {
122 1287 : normalizedFCvaluesMeasures[j - 1].push_back(matrixFCvalues[i][j]);
123 : }
124 : }
125 : }
126 :
127 33 : _idlingValueFCvalues = std::map<std::string, double>();
128 33 : _normedCepCurveFCvalues = std::map<std::string, std::vector<double> >();
129 :
130 132 : for (int i = 0; i < (int)headerLineFCvalues.size(); i++) {
131 198 : _normedCepCurveFCvalues.insert(std::make_pair(FCvaluesIdentifier[i], normalizedFCvaluesMeasures[i]));
132 99 : _idlingValueFCvalues.insert(std::make_pair(FCvaluesIdentifier[i], idlingFCvalues[i]));
133 : }
134 :
135 33 : _normalizedPowerPatternPollutants = std::vector<double>();
136 33 : _cepNormalizedCurvePollutants = std::map<std::string, std::vector<double> >();
137 :
138 33 : int headerCount = (int)headerLinePollutants.size();
139 1056 : for (int i = 0; i < (int)matrixPollutants.size(); i++) {
140 7161 : for (int j = 0; j < (int)matrixPollutants[i].size(); j++) {
141 6138 : if ((int)matrixPollutants[i].size() != headerCount + 1) {
142 : return;
143 : }
144 :
145 6138 : if (j == 0) {
146 1023 : _normalizedPowerPatternPollutants.push_back(matrixPollutants[i][j]);
147 : }
148 : else {
149 5115 : normalizedPollutantMeasures[j - 1].push_back(matrixPollutants[i][j]);
150 : }
151 : }
152 : }
153 :
154 33 : _idlingValuesPollutants = std::map<std::string, double>();
155 :
156 198 : for (int i = 0; i < (int)headerLinePollutants.size(); i++) {
157 330 : _cepNormalizedCurvePollutants.insert(std::make_pair(pollutantIdentifier[i], normalizedPollutantMeasures[i]));
158 165 : _idlingValuesPollutants.insert(std::make_pair(pollutantIdentifier[i], idlingPollutants[i]));
159 : }
160 :
161 33 : _FleetMix = std::map<std::string, double>();
162 33 : _FleetMix.insert(std::make_pair(Constants::strGasoline, 0));
163 33 : _FleetMix.insert(std::make_pair(Constants::strDiesel, 0));
164 33 : _FleetMix.insert(std::make_pair(Constants::strCNG, 0));
165 33 : _FleetMix.insert(std::make_pair(Constants::strLPG, 0));
166 33 : }
167 :
168 118514 : const bool& CEP::getHeavyVehicle() const {
169 118514 : return privateHeavyVehicle;
170 : }
171 :
172 95167 : const std::string& CEP::getFuelType() const {
173 95167 : return privateFuelType;
174 : }
175 :
176 33 : void CEP::setFuelType(const std::string& value) {
177 33 : privateFuelType = value;
178 33 : }
179 :
180 482858 : const std::string& CEP::getCalcType() const {
181 482858 : return privateCalcType;
182 : }
183 :
184 33 : void CEP::setCalcType(const std::string& value) {
185 33 : privateCalcType = value;
186 33 : }
187 :
188 425566 : const double& CEP::getRatedPower() const {
189 425566 : return privateRatedPower;
190 : }
191 :
192 33 : void CEP::setRatedPower(const double& value) {
193 33 : privateRatedPower = value;
194 33 : }
195 :
196 76211 : double CEP::CalcEngPower(double power, const double ratedPower) {
197 76211 : if (power < _normalizedPowerPatternFCvalues.front() * ratedPower) {
198 : return _normalizedPowerPatternFCvalues.front() * ratedPower;
199 : }
200 74727 : if (power > _normalizedPowerPatternFCvalues.back() * ratedPower) {
201 924 : return _normalizedPowerPatternFCvalues.back() * ratedPower;
202 : }
203 :
204 : return power;
205 : }
206 :
207 76169 : 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 76169 : double emissionMultiplier = getHeavyVehicle() ? ratedPower : 1.;
217 76169 : if (std::abs(speed) <= Constants::ZERO_SPEED_ACCURACY) {
218 19408 : 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 19408 : if (_normedCepCurveFCvalues.find(pollutant) != _normedCepCurveFCvalues.end()) {
224 4852 : return _idlingValueFCvalues[pollutant] * ratedPower;
225 : }
226 14556 : else if (_cepNormalizedCurvePollutants.find(pollutant) != _cepNormalizedCurvePollutants.end()) {
227 14556 : return _idlingValuesPollutants[pollutant] * emissionMultiplier;
228 : }
229 : }
230 :
231 :
232 56761 : 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 56761 : if (_normedCepCurveFCvalues.find(pollutant) != _normedCepCurveFCvalues.end()) {
239 14449 : emissionCurve = &_normedCepCurveFCvalues[pollutant];
240 14449 : powerPattern = &_normalizedPowerPatternFCvalues;
241 : emissionMultiplier = ratedPower;
242 : }
243 42312 : else if (_cepNormalizedCurvePollutants.find(pollutant) != _cepNormalizedCurvePollutants.end()) {
244 42312 : emissionCurve = &_cepNormalizedCurvePollutants[pollutant];
245 42312 : powerPattern = &_normalizedPowerPatternPollutants;
246 42312 : if (!getHeavyVehicle()) {
247 : normalizingPower = drivingPower;
248 : }
249 : }
250 :
251 56761 : 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 56761 : 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 56761 : 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 56761 : if (power >= powerPattern->back() * normalizingPower) {
266 264 : return emissionCurve->back() * emissionMultiplier;
267 : }
268 :
269 56497 : FindLowerUpperInPattern(lowerIndex, upperIndex, *powerPattern, power, normalizingPower);
270 56497 : return Interpolate(power, (*powerPattern)[lowerIndex] * normalizingPower, (*powerPattern)[upperIndex] * normalizingPower, (*emissionCurve)[lowerIndex], (*emissionCurve)[upperIndex]) * emissionMultiplier;
271 : }
272 :
273 9478 : double CEP::GetCO2Emission(double _FC, double _CO, double _HC, Helpers* VehicleClass) {
274 : //Declaration
275 : double fCBr, fCHC, fCCO, fCCO2;
276 :
277 9478 : fCBr = 0;
278 9478 : fCHC = 0;
279 9478 : fCCO = 0;
280 9478 : fCCO2 = 0;
281 :
282 9478 : if (getFuelType() != "Mix") {
283 9478 : 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 9478 : 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 9478 : bool CEP::GetfcVals(const std::string& _fuelTypex, double& _fCBr, double& _fCHC, double& _fCCO, double& _fCCO2, Helpers* VehicleClass) {
339 9478 : _fCHC = 0.866;
340 9478 : _fCCO = 0.429;
341 9478 : _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 9478 : if (_fuelTypex == Constants::strGasoline) {
347 5141 : _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 85643 : double CEP::getFMot(const double speed, const double ratedPower, const double wheelRadius) {
371 85643 : if (speed < 10e-2) {
372 : return 0.;
373 : }
374 : //Declaration
375 : int upperIndex;
376 : int lowerIndex;
377 :
378 85643 : FindLowerUpperInPattern(lowerIndex, upperIndex, _speedPatternRotational, speed);
379 85643 : double iGear = Interpolate(speed, _speedPatternRotational[lowerIndex], _speedPatternRotational[upperIndex], _gearTransmissionCurve[lowerIndex], _gearTransmissionCurve[upperIndex]);
380 :
381 85643 : double iTot = iGear * _axleRatio;
382 :
383 85643 : double n = (30 * speed * iTot) / (wheelRadius * M_PI);
384 85643 : double nNorm = (n - _engineIdlingSpeed) / (_engineRatedSpeed - _engineIdlingSpeed);
385 :
386 85643 : FindLowerUpperInPattern(lowerIndex, upperIndex, _nNormTable, nNorm);
387 85643 : return (-Interpolate(nNorm, _nNormTable[lowerIndex], _nNormTable[upperIndex], _dragNormTable[lowerIndex], _dragNormTable[upperIndex]) * ratedPower * 1000 / speed) / Constants::getDRIVE_TRAIN_EFFICIENCY();
388 : }
389 :
390 349355 : double CEP::GetRotationalCoeffecient(double speed) {
391 : //Declaration
392 : int upperIndex;
393 : int lowerIndex;
394 :
395 349355 : FindLowerUpperInPattern(lowerIndex, upperIndex, _speedPatternRotational, speed);
396 349355 : return Interpolate(speed, _speedPatternRotational[lowerIndex], _speedPatternRotational[upperIndex], _speedCurveRotational[lowerIndex], _speedCurveRotational[upperIndex]);
397 : }
398 :
399 577138 : void CEP::FindLowerUpperInPattern(int& lowerIndex, int& upperIndex, const std::vector<double>& pattern, double value, double scale) {
400 577138 : lowerIndex = 0;
401 577138 : upperIndex = 0;
402 :
403 577138 : if (value <= pattern.front() * scale) {
404 : lowerIndex = 0;
405 : upperIndex = 0;
406 : return;
407 : }
408 :
409 561332 : 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 561332 : int middleIndex = ((int)pattern.size() - 1) / 2;
417 561332 : upperIndex = (int)pattern.size() - 1;
418 561332 : lowerIndex = 0;
419 :
420 2327243 : while (upperIndex - lowerIndex > 1) {
421 1765911 : if (pattern[middleIndex] * scale == value) {
422 0 : lowerIndex = middleIndex;
423 0 : upperIndex = middleIndex;
424 0 : return;
425 : }
426 1765911 : else if (pattern[middleIndex] * scale < value) {
427 851549 : lowerIndex = middleIndex;
428 851549 : middleIndex = (upperIndex - lowerIndex) / 2 + lowerIndex;
429 : }
430 : else {
431 914362 : upperIndex = middleIndex;
432 914362 : middleIndex = (upperIndex - lowerIndex) / 2 + lowerIndex;
433 : }
434 : }
435 : }
436 :
437 592020 : double CEP::Interpolate(double px, double p1, double p2, double e1, double e2) {
438 592020 : if (p2 == p1) {
439 : return e1;
440 : }
441 :
442 576214 : return e1 + (px - p1) / (p2 - p1) * (e2 - e1);
443 : }
444 :
445 60405 : double CEP::GetPMaxNorm(double speed) {
446 : // Linear function between v0 and v1, constant elsewhere
447 60405 : if (speed <= _pNormV0) {
448 2632 : return _pNormP0;
449 : }
450 57773 : else if (speed >= _pNormV1) {
451 42891 : return _pNormP1;
452 : }
453 : else {
454 14882 : return Interpolate(speed, _pNormV0, _pNormV1, _pNormP0, _pNormP1);
455 : }
456 : }
457 :
458 33 : void CEP::InitializeInstanceFields() {
459 33 : _massVehicle = 0;
460 33 : _vehicleLoading = 0;
461 33 : _vehicleMassRot = 0;
462 33 : _crossSectionalArea = 0;
463 33 : _cWValue = 0;
464 33 : _resistanceF0 = 0;
465 33 : _resistanceF1 = 0;
466 33 : _resistanceF2 = 0;
467 33 : _resistanceF3 = 0;
468 33 : _resistanceF4 = 0;
469 33 : _axleRatio = 0;
470 33 : _auxPower = 0;
471 33 : _pNormV0 = 0;
472 33 : _pNormP0 = 0;
473 33 : _pNormV1 = 0;
474 33 : _pNormP1 = 0;
475 33 : _engineRatedSpeed = 0;
476 33 : _engineIdlingSpeed = 0;
477 33 : _effectiveWheelDiameter = 0;
478 33 : }
479 : }
|