Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 MSChargingStation.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Tamas Kurczveil
17 : /// @author Pablo Alvarez Lopez
18 : /// @author Mirko Barthauer
19 : /// @date 20-12-13
20 : ///
21 : // Charging Station for Electric vehicles
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <cassert>
26 : #include <utils/common/StringUtils.h>
27 : #include <utils/common/WrappingCommand.h>
28 : #include <utils/vehicle/SUMOVehicle.h>
29 : #include <microsim/MSEventControl.h>
30 : #include <microsim/MSParkingArea.h>
31 : #include <microsim/MSVehicleType.h>
32 : #include <microsim/MSStoppingPlace.h>
33 : #include <microsim/devices/MSDevice_Battery.h>
34 : #include <microsim/MSNet.h>
35 : #include "MSChargingStation.h"
36 :
37 :
38 : // ===========================================================================
39 : // member method definitions
40 : // ===========================================================================
41 :
42 15237 : MSChargingStation::MSChargingStation(const std::string& chargingStationID, MSLane& lane, double startPos, double endPos,
43 : const std::string& name, double chargingPower, double totalPower, double efficency, bool chargeInTransit,
44 15237 : SUMOTime chargeDelay, const std::string& chargeType, SUMOTime waitingTime) :
45 15237 : MSStoppingPlace(chargingStationID, SUMO_TAG_CHARGING_STATION, std::vector<std::string>(), lane, startPos, endPos, name),
46 30474 : myChargeInTransit(chargeInTransit), myChargeType(stringToChargeType(chargeType)), myTotalPowerCheckEvent(nullptr) {
47 15237 : if (chargingPower < 0) {
48 0 : WRITE_WARNING(TLF("Attribute % for chargingStation with ID='%' is invalid (%).", toString(SUMO_ATTR_CHARGINGPOWER), getID(), toString(chargingPower)))
49 : } else {
50 15237 : myNominalChargingPower = chargingPower;
51 15237 : myTotalChargingPower = totalPower;
52 : }
53 15237 : if (efficency < 0 || efficency > 1) {
54 6 : WRITE_WARNING(TLF("Attribute % for chargingStation with ID='%' is invalid (%).", toString(SUMO_ATTR_EFFICIENCY), getID(), toString(efficency)))
55 : } else {
56 15235 : myEfficiency = efficency;
57 : }
58 15237 : if (chargeDelay < 0) {
59 0 : WRITE_WARNING(TLF("Attribute % for chargingStation with ID='%' is invalid (%).", toString(SUMO_ATTR_CHARGEDELAY), getID(), toString(chargeDelay)))
60 : } else {
61 15237 : myChargeDelay = chargeDelay;
62 : }
63 15237 : if (waitingTime < 0) {
64 0 : WRITE_WARNING(TLF("Attribute % for chargingStation with ID='%' is invalid (%).", toString(SUMO_ATTR_WAITINGTIME), getID(), toString(waitingTime)))
65 : } else {
66 15237 : myWaitingTime = waitingTime;
67 : }
68 15237 : if (getBeginLanePosition() > getEndLanePosition()) {
69 0 : WRITE_WARNING(TLF("ChargingStation with ID='%' doesn't have a valid position (% < %).", getID(), toString(getBeginLanePosition()), toString(getEndLanePosition())));
70 : }
71 15237 : }
72 :
73 :
74 17 : MSChargingStation::MSChargingStation(const std::string& chargingStationID, const MSParkingArea* parkingArea, const std::string& name, double chargingPower,
75 17 : double totalPower, double efficency, bool chargeInTransit, SUMOTime chargeDelay, const std::string& chargeType, SUMOTime waitingTime) :
76 17 : MSChargingStation(chargingStationID, const_cast<MSLane&>(parkingArea->getLane()), parkingArea->getBeginLanePosition(), parkingArea->getEndLanePosition(),
77 34 : name, chargingPower, totalPower, efficency, chargeInTransit, chargeDelay, chargeType, waitingTime) {
78 17 : myParkingArea = parkingArea;
79 17 : }
80 :
81 :
82 30080 : MSChargingStation::~MSChargingStation() {
83 45270 : }
84 :
85 :
86 : double
87 102206 : MSChargingStation::getChargingPower(bool usingFuel) const {
88 102206 : if (usingFuel) {
89 2333 : return myNominalChargingPower;
90 : } else {
91 : // Convert from [Ws] to [Wh] (3600s / 1h):
92 99873 : return myNominalChargingPower / 3600;
93 : }
94 : }
95 :
96 :
97 : double
98 101761 : MSChargingStation::getEfficency() const {
99 101761 : return myEfficiency;
100 : }
101 :
102 :
103 : bool
104 104909 : MSChargingStation::getChargeInTransit() const {
105 104909 : return myChargeInTransit;
106 : }
107 :
108 :
109 : SUMOTime
110 101908 : MSChargingStation::getChargeDelay() const {
111 101908 : return myChargeDelay;
112 : }
113 :
114 :
115 : MSChargingStation::ChargeType
116 101840 : MSChargingStation::getChargeType() const {
117 101840 : return myChargeType;
118 : }
119 :
120 :
121 : SUMOTime
122 0 : MSChargingStation::getWaitingTime() const {
123 0 : return myWaitingTime;
124 : }
125 :
126 :
127 : const MSParkingArea*
128 108372 : MSChargingStation::getParkingArea() const {
129 108372 : return myParkingArea;
130 : }
131 :
132 :
133 : double
134 8 : MSChargingStation::getTotalChargingPower() const {
135 8 : return myTotalChargingPower;
136 : }
137 :
138 :
139 : void
140 511 : MSChargingStation::setChargingPower(double chargingPower) {
141 511 : myNominalChargingPower = chargingPower;
142 511 : }
143 :
144 :
145 : void
146 511 : MSChargingStation::setEfficiency(double efficiency) {
147 511 : myEfficiency = efficiency;
148 511 : }
149 :
150 :
151 : void
152 10 : MSChargingStation::setChargeDelay(SUMOTime delay) {
153 10 : myChargeDelay = delay;
154 10 : }
155 :
156 :
157 : void
158 10 : MSChargingStation::setChargeInTransit(bool value) {
159 10 : myChargeInTransit = value;
160 10 : if (myTotalChargingPower > 0 && myChargeInTransit && myTotalPowerCheckEvent == nullptr) {
161 0 : myTotalPowerCheckEvent = new WrappingCommand<MSChargingStation>(this, &MSChargingStation::checkTotalPower);
162 0 : MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myTotalPowerCheckEvent);
163 : }
164 10 : }
165 :
166 :
167 : void
168 103636 : MSChargingStation::setChargingVehicle(bool value) {
169 103636 : myChargingVehicle = value;
170 103636 : if (myTotalChargingPower > 0 && myChargingVehicle && myTotalPowerCheckEvent == nullptr) {
171 8 : myTotalPowerCheckEvent = new WrappingCommand<MSChargingStation>(this, &MSChargingStation::checkTotalPower);
172 8 : MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myTotalPowerCheckEvent);
173 : }
174 103636 : }
175 :
176 :
177 : void
178 0 : MSChargingStation::setTotalChargingPower(double totalPower) {
179 0 : if (totalPower <= 0) {
180 : return;
181 : }
182 0 : const bool hadLimit = myTotalChargingPower > 0;
183 0 : myTotalChargingPower = totalPower;
184 0 : if (!hadLimit && (myChargeInTransit || myChargingVehicle) && myTotalPowerCheckEvent == nullptr) {
185 0 : myTotalPowerCheckEvent = new WrappingCommand<MSChargingStation>(this, &MSChargingStation::checkTotalPower);
186 0 : MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myTotalPowerCheckEvent);
187 : }
188 : }
189 :
190 :
191 : SUMOTime
192 1282 : MSChargingStation::checkTotalPower(SUMOTime currentTime) {
193 1282 : if (!myChargeInTransit && !myChargingVehicle) {
194 4 : myTotalPowerCheckEvent = nullptr;
195 : myChargedBatteries.clear();
196 4 : return 0;
197 : }
198 : double sumReqWh = 0;
199 : std::vector<Charge*> thisStepCharges;
200 3784 : for (auto& kv : myChargeValues) {
201 2506 : if (MSNet::getInstance()->getVehicleControl().getVehicle(kv.first) == nullptr) {
202 17 : continue;
203 : }
204 : Charge& lastcharge = kv.second.back();
205 2489 : if (lastcharge.timeStep == currentTime) {
206 296 : sumReqWh += lastcharge.WCharged;
207 296 : thisStepCharges.push_back(&lastcharge);
208 : }
209 : }
210 1278 : if (thisStepCharges.size() < 2) {
211 1174 : return DELTA_T;
212 : }
213 104 : const double capWh = myTotalChargingPower * myEfficiency /*W*/ * TS /*s*/ / 3600.0; // convert to Wh
214 : #ifdef DEBUG_SIMSTEP
215 : std::cout << "checkTotalPower: CS="
216 : << this->myID << " currentTime=" << currentTime << " myTotalChargingPower=" << myTotalChargingPower;
217 : if (sumReqWh > capWh && sumReqWh > 0) {
218 : std::cout << " exceeded, needs rebalancing!";
219 : }
220 : std::cout << std::endl;
221 : #endif
222 104 : if (sumReqWh > capWh && sumReqWh > 0) {
223 100 : const double ratio = capWh / sumReqWh;
224 300 : for (auto* charge : thisStepCharges) {
225 200 : MSDevice_Battery* battery = myChargedBatteries[charge->vehicleID];
226 200 : double abc = battery->getActualBatteryCapacity();
227 :
228 200 : const double deliveredWh = charge->WCharged * ratio;
229 200 : const double excessWh = charge->WCharged - deliveredWh;
230 200 : charge->WCharged = deliveredWh;
231 200 : if (charge->chargingEfficiency > 0 && TS > 0) {
232 : // derive power [W] from energy [Wh]: Power = (Energy [Wh] * 3600) [Ws] / (efficiency * TS [s])
233 : // ergo we are doing [Ws/s -> W]
234 200 : charge->chargingPower = (deliveredWh * 3600.0) / (charge->chargingEfficiency * TS);
235 : }
236 200 : charge->actualBatteryCapacity = abc - excessWh;
237 200 : charge->totalEnergyCharged -= excessWh;
238 :
239 : // inform also battery device
240 200 : battery->setActualBatteryCapacity(abc - excessWh);
241 200 : battery->setEnergyCharged(deliveredWh);
242 200 : myTotalCharge -= excessWh;
243 :
244 : #ifdef DEBUG_SIMSTEP
245 : std::cout << "time=" << time2string(currentTime)
246 : << " vehID=" << charge->vehicleID
247 : << " requestedWh=" << (deliveredWh + excessWh)
248 : << " deliveredWh=" << deliveredWh
249 : << " deliveredW=" << charge->chargingPower
250 : << " ratio=" << ratio << std::endl;
251 : #endif
252 : }
253 : #ifdef DEBUG_SIMSTEP
254 : std::cout << "===============\n\n";
255 : #endif
256 : }
257 104 : return DELTA_T;
258 1278 : }
259 :
260 :
261 : bool
262 0 : MSChargingStation::vehicleIsInside(const double position) const {
263 0 : if ((position >= getBeginLanePosition()) && (position <= getEndLanePosition())) {
264 : return true;
265 : } else {
266 0 : return false;
267 : }
268 : }
269 :
270 :
271 : bool
272 0 : MSChargingStation::isCharging() const {
273 0 : return myChargingVehicle;
274 : }
275 :
276 :
277 : void
278 101336 : MSChargingStation::addChargeValueForOutput(double WCharged, MSDevice_Battery* battery) {
279 202672 : if (!OptionsCont::getOptions().isSet("chargingstations-output")) {
280 1727 : return;
281 : }
282 99609 : std::string status = "";
283 99609 : if (battery->getChargingStartTime() > myChargeDelay) {
284 98897 : if (battery->getHolder().getSpeed() < battery->getStoppingThreshold()) {
285 : status = "chargingStopped";
286 845 : } else if (myChargeInTransit) {
287 : status = "chargingInTransit";
288 : } else {
289 : status = "noCharging";
290 : }
291 : } else {
292 712 : if (myChargeInTransit) {
293 : status = "waitingChargeInTransit";
294 344 : } else if (battery->getHolder().getSpeed() < battery->getStoppingThreshold()) {
295 : status = "waitingChargeStopped";
296 : } else {
297 : status = "noWaitingCharge";
298 : }
299 : }
300 : // update total charge
301 99609 : myTotalCharge += WCharged;
302 : // create charge row and insert it in myChargeValues
303 : const std::string vehID = battery->getHolder().getID();
304 : if (myChargeValues.count(vehID) == 0) {
305 391 : myChargedVehicles.push_back(vehID);
306 391 : myChargedBatteries[vehID] = battery;
307 : }
308 99609 : Charge C(MSNet::getInstance()->getCurrentTimeStep(), vehID, battery->getHolder().getVehicleType().getID(),
309 : status, WCharged, battery->getActualBatteryCapacity(), battery->getMaximumBatteryCapacity(),
310 298827 : myNominalChargingPower, myEfficiency, myTotalCharge);
311 99609 : myChargeValues[vehID].push_back(C);
312 99609 : }
313 :
314 :
315 : void
316 448 : MSChargingStation::writeChargingStationOutput(OutputDevice& output) {
317 448 : int chargingSteps = 0;
318 731 : for (const auto& item : myChargeValues) {
319 283 : chargingSteps += (int)item.second.size();
320 : }
321 448 : output.openTag(SUMO_TAG_CHARGING_STATION);
322 448 : output.writeAttr(SUMO_ATTR_ID, myID);
323 448 : output.writeAttr(SUMO_ATTR_TOTALENERGYCHARGED, myTotalCharge);
324 448 : output.writeAttr(SUMO_ATTR_CHARGINGSTEPS, chargingSteps);
325 : // start writing
326 448 : if (myChargeValues.size() > 0) {
327 431 : for (const std::string& vehID : myChargedVehicles) {
328 : int iStart = 0;
329 283 : const auto& chargeSteps = myChargeValues[vehID];
330 566 : while (iStart < (int)chargeSteps.size()) {
331 283 : int iEnd = iStart + 1;
332 283 : double charged = chargeSteps[iStart].WCharged;
333 94591 : while (iEnd < (int)chargeSteps.size() && chargeSteps[iEnd].timeStep == chargeSteps[iEnd - 1].timeStep + DELTA_T) {
334 94308 : charged += chargeSteps[iEnd].WCharged;
335 94308 : iEnd++;
336 : }
337 283 : writeVehicle(output, chargeSteps, iStart, iEnd, charged);
338 : iStart = iEnd;
339 : }
340 : }
341 : }
342 : // close charging station tag
343 448 : output.closeTag();
344 448 : }
345 :
346 :
347 : void
348 52094 : MSChargingStation::writeAggregatedChargingStationOutput(OutputDevice& output, bool includeUnfinished) {
349 : std::vector<std::string> terminatedChargers;
350 57324 : for (const auto& item : myChargeValues) {
351 : const Charge& lastCharge = item.second.back();
352 : // no charge during the last time step == has stopped charging
353 5230 : bool finished = lastCharge.timeStep < SIMSTEP - DELTA_T;
354 5230 : if (finished || includeUnfinished) {
355 108 : if (finished) {
356 104 : terminatedChargers.push_back(item.first);
357 : }
358 : // aggregate values
359 108 : double charged = 0.;
360 108 : double minPower = lastCharge.chargingPower;
361 108 : double maxPower = lastCharge.chargingPower;
362 108 : double minCharge = lastCharge.WCharged;
363 108 : double maxCharge = lastCharge.WCharged;
364 108 : double minEfficiency = lastCharge.chargingEfficiency;
365 108 : double maxEfficiency = lastCharge.chargingEfficiency;
366 5126 : for (const auto& charge : item.second) {
367 5018 : charged += charge.WCharged;
368 5018 : if (charge.chargingPower < minPower) {
369 0 : minPower = charge.chargingPower;
370 : }
371 5018 : if (charge.chargingPower > maxPower) {
372 0 : maxPower = charge.chargingPower;
373 : }
374 5018 : if (charge.WCharged < minCharge) {
375 32 : minCharge = charge.WCharged;
376 : }
377 5018 : if (charge.WCharged > maxCharge) {
378 4 : maxCharge = charge.WCharged;
379 : }
380 5018 : if (charge.chargingEfficiency < minEfficiency) {
381 0 : minEfficiency = charge.chargingEfficiency;
382 : }
383 5018 : if (charge.chargingEfficiency > maxEfficiency) {
384 0 : maxEfficiency = charge.chargingEfficiency;
385 : }
386 : }
387 : // actually write the data
388 108 : output.openTag(SUMO_TAG_CHARGING_EVENT);
389 108 : output.writeAttr(SUMO_ATTR_CHARGINGSTATIONID, myID);
390 108 : output.writeAttr(SUMO_ATTR_VEHICLE, lastCharge.vehicleID);
391 108 : output.writeAttr(SUMO_ATTR_TYPE, lastCharge.vehicleType);
392 108 : output.writeAttr(SUMO_ATTR_TOTALENERGYCHARGED_VEHICLE, charged);
393 108 : output.writeAttr(SUMO_ATTR_CHARGINGBEGIN, time2string(item.second.at(0).timeStep));
394 108 : if (finished) {
395 104 : output.writeAttr(SUMO_ATTR_CHARGINGEND, time2string(lastCharge.timeStep));
396 : }
397 108 : output.writeAttr(SUMO_ATTR_ACTUALBATTERYCAPACITY, lastCharge.actualBatteryCapacity);
398 108 : output.writeAttr(SUMO_ATTR_MAXIMUMBATTERYCAPACITY, lastCharge.maxBatteryCapacity);
399 108 : output.writeAttr(SUMO_ATTR_MINPOWER, minPower);
400 108 : output.writeAttr(SUMO_ATTR_MAXPOWER, maxPower);
401 108 : output.writeAttr(SUMO_ATTR_MINCHARGE, minCharge);
402 108 : output.writeAttr(SUMO_ATTR_MAXCHARGE, maxCharge);
403 108 : output.writeAttr(SUMO_ATTR_MINEFFICIENCY, minEfficiency);
404 108 : output.writeAttr(SUMO_ATTR_MAXEFFICIENCY, maxEfficiency);
405 216 : output.closeTag();
406 : }
407 : }
408 :
409 : // clear charging data of vehicles which terminated charging
410 52198 : for (auto vehID : terminatedChargers) {
411 : myChargeValues.erase(vehID);
412 : }
413 52094 : }
414 :
415 :
416 : void
417 283 : MSChargingStation::writeVehicle(OutputDevice& out, const std::vector<Charge>& chargeSteps, int iStart, int iEnd, double charged) {
418 283 : const Charge& first = chargeSteps[iStart];
419 283 : out.openTag(SUMO_TAG_VEHICLE);
420 283 : out.writeAttr(SUMO_ATTR_ID, first.vehicleID);
421 283 : out.writeAttr(SUMO_ATTR_TYPE, first.vehicleType);
422 283 : out.writeAttr(SUMO_ATTR_TOTALENERGYCHARGED_VEHICLE, charged);
423 283 : out.writeAttr(SUMO_ATTR_CHARGINGBEGIN, time2string(first.timeStep));
424 283 : out.writeAttr(SUMO_ATTR_CHARGINGEND, time2string(chargeSteps[iEnd - 1].timeStep));
425 94874 : for (int i = iStart; i < iEnd; i++) {
426 94591 : const Charge& c = chargeSteps[i];
427 94591 : out.openTag(SUMO_TAG_STEP);
428 94591 : out.writeAttr(SUMO_ATTR_TIME, time2string(c.timeStep));
429 : // charge values
430 94591 : out.writeAttr(SUMO_ATTR_CHARGING_STATUS, c.status);
431 94591 : out.writeAttr(SUMO_ATTR_ENERGYCHARGED, c.WCharged);
432 94591 : out.writeAttr(SUMO_ATTR_PARTIALCHARGE, c.totalEnergyCharged);
433 : // charging values of charging station in this timestep
434 94591 : out.writeAttr(SUMO_ATTR_CHARGINGPOWER, c.chargingPower);
435 94591 : out.writeAttr(SUMO_ATTR_EFFICIENCY, c.chargingEfficiency);
436 : // battery status of vehicle
437 94591 : out.writeAttr(SUMO_ATTR_ACTUALBATTERYCAPACITY, c.actualBatteryCapacity);
438 94591 : out.writeAttr(SUMO_ATTR_MAXIMUMBATTERYCAPACITY, c.maxBatteryCapacity);
439 : // close tag timestep
440 189182 : out.closeTag();
441 : }
442 283 : out.closeTag();
443 283 : }
444 :
445 :
446 : /****************************************************************************/
|