LCOV - code coverage report
Current view: top level - src/microsim/trigger - MSChargingStation.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 88.7 % 213 189
Test Date: 2026-04-16 16:39:47 Functions: 84.0 % 25 21

            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              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1