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