magu5026 / ElectricTrain

Mod for Factorio
GNU General Public License v3.0
9 stars 11 forks source link

Make power providers work as intended #4

Closed nybble41 closed 3 years ago

nybble41 commented 5 years ago

Fixes the issue that a single power provider is enough to operate any number of trains at full capacity, regardless of the amount of energy "burned" by the trains.

The expectation is that trains should burn alternative fuel, if available, or else operate at reduced capacity when there are not enough power providers to keep up with the power the consumed by the trains. In practice, however, while an insufficient number of power providers does limit the amount of "electric fuel" placed in the trains at each tick, the trains continue to operate at full capacity so long as there is any fuel remaining.

The change proposed here is to assign each train a 1MJ buffer (one "electric fuel"—reduced from the original 10GJ fuel value). These buffers, which are initially fully discharged, are filled from the power providers in proportion to the amount of energy needed to top off each train[1], up to a maximum of 10% of the total provider energy per tick. Whenever the buffer is less than 10% charged the "electric fuel" is removed from the burner. The removed energy is stored in a separate field until the next tick and accumulates until it reaches 10%, which allows the trains to operate at reduced capacity; with 1/3 of the needed providers, for example, each train will be supplied with "electric fuel" one tick out of every three, on average—assuming constant power draw—and spend the other two ticks either burning normal fuel or drifting while the buffer charges.

Testing the changes with my own base shows about 60MW total power draw from the 30-40 power providers needed to keep my trains supplied (a few dozen MK3s). Before this change I was running the same trains from a single power provider drawing a constant 2.1MW with no noticeable difference in train performance, which means the system was generating about 48MW of free energy.

Overall there is more code in the OnTick function, but since I also extracted some of the common subexpressions to local variables (e.g. global.Fuel.fuel_value & train.entity.burner) the CPU utilization has actually decreased compared to the original version. It's still fairly high—3ms per tick for my base, which is much higher than any other mods I have enabled—but as I recall the original code was using 10ms. I experimented with only running the OnTick handler one tick out of 10; while that did appear to work well enough, and increased my UPS by about 30%, it also caused the power draw to pulsate, so in the end I reverted back to processing on every tick.

[1] Originally the total provider energy was divided equally among all trains, but this can cause the energy remaining in the burner to exceed the maximum energy in one unit of "fuel". For example, consider the case where there is one train with a full burner and other trains in need of energy; if the same amount of energy is added to all the trains then the first one will be overfilled. For the same reason, energy is now drawn from power providers in proportion to each provider's stored energy rather than taking the same average energy from all providers, which could potentially result in negative energy.