sumo icon indicating copy to clipboard operation
sumo copied to clipboard

Feature request: per-chargingstation max output power (MAXOUTPOWER)

Open lorenzo-ghiro opened this issue 2 months ago • 19 comments

Hi all,

I would like to ask for support for a new feature regarding chargingStations. Specifically, I propose adding an attribute (MAXOUTPOWER) as shown below, to avoid the need for using the TraCI routine suggested in https://sumo.dlr.de/docs/Models/Electric.html#reducing_the_power_of_a_charging_station to limit the power of a charging station at runtime.

<myCS>-------------------------------------------------------------------------------------</myCS>
                   [v0>]       [v1>]        [v2>]        [v3>]
<myCS>-------------------------------------------------------------------------------------</myCS>
<additional>
    <chargingStation chargeInTransit="1" power="100000" efficiency="1" startPos="0" endPos="30" id="myCS"
    MAXOUTPOWER="200000"/>
</additional>

In this example, I would like to model a chargingStation that delivers a nominal power of 100 kW to vehicles in transit, and that is able to provide an overall maximum of MAXOUTPOWER = 200 kW. If the number of vehicles on the chargingStation implies a power request higher than MAXOUTPOWER (as in this case, with 4 vehicles requiring 400 kW > 200 kW), the chargingStation should automatically distribute the available power uniformly among all connected vehicles, without external workarounds via TraCI.

The expected battery output for this scenario would be:

<battery-export>
    <timestep time="0.00">
        <vehicle id="v0" chargingStationId="myCS" energyChargedInTransit="50000" .../>
        <vehicle id="v1" chargingStationId="myCS" energyChargedInTransit="50000" .../>
        <vehicle id="v2" chargingStationId="myCS" energyChargedInTransit="50000" .../>
        <vehicle id="v3" chargingStationId="myCS" energyChargedInTransit="50000" .../>
    </timestep>
    <timestep time="1.00"> ... </timestep> ...
</battery-export>

Can you please suggest me possible strategies to implement the MAXOUTPOWER feature?

Thank you very much,

Lorenzo

lorenzo-ghiro avatar Oct 06 '25 15:10 lorenzo-ghiro

Some quick and dirty solution could be to implement this for charging facilities on dedicated off-road parkings only. Then MSChargingStation::getChargingPower could include a check for the parking occupancy and return a reduced power value in some case. However this neglects some things like...

  • delay before starting to charge
  • two or more vehicles starting to park & charge at the same time (will be recognised only from the next time step on)

m-kro avatar Oct 06 '25 19:10 m-kro

Thank you very much, Mirko, for this suggestion. I have one further question related to a scenario where, at a given step, one vehicle (v0) already on the charging station cs moves forward but does not yet exit cs. At the same time, another vehicle (v1) enters cs for the first time.

I assume that batteries are updated in some order related to the driving direction, so I expect that v0 would be the first to execute this line of code: MSDevice_Battery.cpp:L263

When v0 executes that line, is myActChargingStation already aware that v1 will also be charging during the same step?

I am considering customizing MSChargingStation::getChargingPower to return myChargingPower / num_of_chargingVehs but I am not sure about the full execution flow, so I would like to confirm whether num_of_chargingVehs can be correctly computed when the first vehicle in this set updates its own battery capacity.

It would be great to know whether you think this approach could work or not. Thank you very much for your time and help.

Best regards, Lorenzo

lorenzo-ghiro avatar Oct 07 '25 09:10 lorenzo-ghiro

Vehicles are updated in reverse order (as you assumed). There is no further iteration whether the occupancy of the chargingStation in the same timestep. So as long vehicles arrive or leave within the timestep, it may happen that the occupancy changes as well while iterating through the vehicles.

Just (re)discovered a better way to get the number of vehicles: MSStoppingPlace::getStoppedVehicleNumber() (MSChargingStation is derived from MSStoppingPlace).

m-kro avatar Oct 07 '25 11:10 m-kro

Ah, I see. Thank you very much for the explanation and for pointing me to getStoppedVehicleNumber(). That clarifies what I can do for now. Thanks again for your help!

Best, Lorenzo

lorenzo-ghiro avatar Oct 07 '25 12:10 lorenzo-ghiro

If the feature is no longer needed, it's fine to close this issue. If it's something we should still include in our code base, the ticket should stay open.

namdre avatar Oct 08 '25 07:10 namdre

Ok, then I'll reopen this issue because I actually still need the feature. I closed it since I had no clear implementation strategy and wasn't sure how to contribute to solving it.

At this point, I'm wondering whether it could be feasible to implement a mechanism that, after all vehicle movements are completed, inspects the list of power values "absorbed" by each vehicle, those stored as output values in the charging stations here MSDevice_Battery.cpp:L269 Then, a posteriori, I would check whether any chargingStation exceeded its MAXOUTPOWER in this step. If that happens, I would compute actualDeliveredPower as MAXOUTPOWER / numVehiclesOnStation, and powerToRemoveFromBatteries as the difference between the station's power and this adjusted value. At that point, I would call something like setActualBatteryCapacity(getActualBatteryCapacity() - powerToRemoveFromBatteries); on all affected batteries.

I'm not sure whether there is a suitable point in the execution flow where this adjustment could actually be applied, and I haven't yet explored the possible consequences on the consistency of the battery-output, but I hope this could be a viable direction.

Any feedback or hints are very welcome. Thanks again for your help, Lorenzo

lorenzo-ghiro avatar Oct 08 '25 12:10 lorenzo-ghiro

It should be possible to find spot in the execution to do this (i.e. MSNet myEndOfTimestepEvents->execute(myStep) ).

An alternative (approximate) solution would be to have the chargingStation record the ratio of requested power to MAXOUTPOWER and adjust delivered power by that ratio in the next step. By avoiding posterior manipulation of drawn power, output consistency is guaranteed with the downside that delivered power may spike above MAXOUTPOWER for a single step and may be depressed for one step once a vehicle finishes charging.

namdre avatar Oct 08 '25 12:10 namdre

Thank you very much for the suggestion. As soon as I can, I’ll try to implement the post-simulation-step version, and if that turns out to be too complex, I’ll also consider the alternative approach you mentioned. I hope to make some concrete attempts soon. Thanks again, and talk to you soon!

Best, Lorenzo

lorenzo-ghiro avatar Oct 09 '25 15:10 lorenzo-ghiro

@lorenzo-ghiro I have added a solution proposal to my fork. I am a bit hesitant to merge this directly here as we are close to the next release. If you want to try it out, please use totalPower attribute to limit the output power, e.g.

<chargingStation id="CS2" lane="D2_1" startPos="100" endPos="120" power="11000" totalPower="22000" efficiency="0.9" chargeInTransit="true" chargeDelay="0"/>

Looking forward to your remarks. If everything is fine, I'll merge the code within approx. two weeks.

m-kro avatar Oct 17 '25 11:10 m-kro

Excellent! Thank you very much, Mirko! I'll prepare a small test scenario as soon as possible to verify that everything works as expected. I should be able to do this sometime next week, have a great weekend in the meantime!

Best, Lorenzo

lorenzo-ghiro avatar Oct 17 '25 13:10 lorenzo-ghiro

Image

Unfortunately, I don’t get the expected output from the quick test I created.

There are two vehicles; when they travel together, their combined demand should exceed totalPower, so they should not both be able to charge at the full power rate. When traveling one by one, each should be able to receive power at each time step (1s).

<additional>
    <chargingStation id="cs_0" lane="E0_0" startPos="0.1" endPos="-0.1" 
        power="1000" totalPower="1500" efficiency="1.0" chargeDelay="0" chargeInTransit="1"/>
</additional>

However, inspecting the battery-output, I notice that both vehicles consistently show vehicle_energyChargedInTransit = 0.2778 Wh, which matches the station power (0.2778 × 3600 ~ 1000). It seems that the totalPower attribute is currently being ignored.

I attach here the few files necessary to replicate the shown test

<configuration>
    <input>
        <net-file value="onelane_withCS.net.xml"/>
        <route-files value="vtypes.xml,trips.xml"/>
        <additional-files value="onetestcs.add.xml"/>
    </input>
    <time>
        <begin value="0"/>
        <end value="33"/>
    </time>
    <output>
        <battery-output value="battery.out.xml"/>
        <battery-output.precision value="4"/>
        <device.battery.probability value="1"/>
        <summary-output value="summary_battery.xml"/>
        <chargingstations-output value="chargingstations.xml"/>
        <device.emissions.period value="5"/>
    </output>
    <gui_only>
        <start value="false"/>
        <gui-settings-file value="gui.xml"/>
    </gui_only>
</configuration>

gui.xml onelane_withCS.net.xml onetestcs.add.xml trips.xml vtypes.xml battery.out.csv

Apologies in advance if I made any configuration mistakes; please let me know if there’s something I should correct. Thanks a lot for your time and support!

PS: It seems to me that netedit target does not compile correctly with ticket17173

lorenzo-ghiro avatar Oct 20 '25 09:10 lorenzo-ghiro

@lorenzo-ghiro The netedit part is not ready yet, that is true. You should see an effect if the cars actually stop on the charging station (define a stop). That is due to the internal structure of charging stations as a specific "stopping place" (others are bus stops and parkings), which registers arriving and leaving vehicles. Charging in transit requires more internal changes and isn't available yet.

m-kro avatar Oct 20 '25 17:10 m-kro

I see, so it should already work for stopped vehicles, but there's still some work needed for vehicles in transit. When possible, I'll try to experiment in the direction suggested by @namdre here, although due to my current commitments I can’t say when I’ll be able to give it proper attention. In the meantime, thank you very much for your help!

Best regards, Lorenzo

lorenzo-ghiro avatar Oct 21 '25 09:10 lorenzo-ghiro

I came up with a solution (on this branch) that I've tested so far only with the small scenario shared earlier and, at least in that case, it produces the expected output.

In particular, in both the battery and charging-stations outputs, from time 11s until 20s (when both vehicles are on the cs) the delivered energy is for both vehicles the correct share of the totalPower attribute.

battery.out.csv chargingstations.csv battery.out.xml chargingstations.xml

The totalPower was set to 1500W with a 1s time-step. Both vehicle_energyChargedInTransit in the battery output and step_energyCharged in the charging-stations output is equal to 1500 / 2 / 3600 ~ 0.2083 Wh, as expected.

In theory, I believe to have handled also other attributes such as the vehicle_actualBatteryCapacity (see MSChargingStation.cpp#L214-L216), but I haven't yet checked the rest, so please let me know if you think further adjustments are needed.

Thanks again for the previous guidance, I'd be very happy if you could review this approach or suggest additional tests to ensure it's consistent with the intended behavior.

Best, Lorenzo

lorenzo-ghiro avatar Oct 28 '25 10:10 lorenzo-ghiro

@lorenzo-ghiro How do you want to proceed when integrating your proposal? You could do a pull request (preferably without the code from my fork, as your approach is separated) or just let me integrate your code directly.

I would still prefer executing the event only when there are actually vehicles charging. Your current implementation would run the checks at all charging stations and every timestep. One can infer the on/off times e.g. from the final state of the member vars MSChargingStation::myChargeInTransit / MSChargingStation::myChargingVehicle .

m-kro avatar Oct 31 '25 13:10 m-kro

Thank you so much @m-kro! I really like your idea of running the check only when there are actually vehicles charging, that would definitely be better.

As for the pull request, feel free to handle the integration directly; since my commit already builds on your implementation of the totalPower attribute (XML definition, parsing, and class integration...), separating it cleanly wouldn't be very practical anyway.

Thanks again for all your help! Best, Lorenzo

lorenzo-ghiro avatar Nov 03 '25 09:11 lorenzo-ghiro

@lorenzo-ghiro The work has been integrated into the main branch.

m-kro avatar Nov 13 '25 18:11 m-kro

Excellent, thank you so much!

lorenzo-ghiro avatar Nov 14 '25 10:11 lorenzo-ghiro

Hi all,

unfortunately, I came across a small issue while testing the new feature in a scenario where some chargingStations extend over edges that are also the end of the trip for certain vehicles.

It seems that some vehicles reach their destination and are deleted, yet still receive a small amount of energy during their final movement. When the execution reaches MSChargingStation.cpp#L207, the battery object no longer exists because the vehicle has already been deleted before MSChargingStation::checkTotalPower is called.

I’m currently working around this issue with the following patch, although it does not properly model the energy recharged by vehicles in their last step before disappearing.

Hope this helps, All the best, Lorenzo

P.S. One last curiosity: why does the check for thisStepCharges.size() < 2 in MSChargingStation.cpp#L188-L190 cause an immediate return?

lorenzo-ghiro avatar Dec 09 '25 14:12 lorenzo-ghiro

@lorenzo-ghiro I will look at this corner case within the next days. The separate return command is some small optimisation as the whole thing does not make much sense if there is only one vehicle charging. I think contradictory values between totalChargePower and chargePower are patched somewhere in the constructor but I'd need to check.

m-kro avatar Dec 10 '25 19:12 m-kro