[1.1.91] Train air resistance is calculated incorrectly

Things that we don't consider worth fixing at this moment.
Post Reply
magneticflux-
Manual Inserter
Manual Inserter
Posts: 2
Joined: Mon Oct 09, 2023 6:21 pm
Contact:

[1.1.91] Train air resistance is calculated incorrectly

Post by magneticflux- »

A log: https://pastebin.com/9dmBKmSH
The Problem
Air resistance should be proportional to velocity squared, but it is currently only proportional to velocity.
Symptoms
In vanilla, trains accelerate more than they should at high speeds and don't accelerate as much as they should at low speeds. With the max_speed cap of ~300km/h this is less noticeable than it otherwise would be; but removing the hard cap and relying on air resistance to cap the speed (using a mod that changes properties of RollingStockPrototypes, see https://mods.factorio.com/mod/ConfigTra ... icationsMU) makes the problem much worse: a train with N locomotives goes N times faster than a single locomotive!
As it is now, since we're trying to approximate a quadratic function with a linear one, we can either 1. underestimate the air resistance and have trains be able to continue accelerating and reach much higher top speeds, or 2. overestimate the air resistance and have trains accelerate from a stop extremely slowly.
Corroborating quotes
These are some quotes indicating this is a real issue. Koub and Optera even seem to assume air resistance is correctly calculated proportional to velocity squared, when it actually isn't!
Koub wrote:
Tue Jun 18, 2019 5:40 am
the difference further increases, due to the fact that air drag is proportional to the squared velocity, while rolling resistance is independant from speed.
Optera wrote:
Tue Oct 17, 2017 5:24 pm
At some point air_resistance becomes more pronounced than weight resulting in a nice curve.

[...] setting max_speed to higher values is the correct test to find out drag induced speed limits.
BlueTemplar wrote:
Fri Jul 12, 2019 1:16 pm
mass has no (direct) effect on top speed, only drag has (aerodynamic and wheel friction*). And drag depends solely on the shape of the object and the density of the fluid it's moving through. An extreme case is vacuum, with no top speed in newtonian mechanics.
danatron1 wrote:
Sun Jul 07, 2019 3:24 pm
That would make sense for justifying a longer stopping distance, but not a top speed. The energy input (force) is the same, so surely each train would have the same kinetic energy, but not the same top speed (F=MA).
Dimensional analysis
Factorio clearly strives to get fundamental relations between units correct (energy vs power vs heat vs efficiency, velocity vs acceleration vs force vs weight, etc). However, looking at the air resistance component to the train speed equation we can see that it immediately fails under simple dimensional analysis! This means that the application of air resistance to the train speed each tick does not make physical or in-game sense.

Reference code from the wiki:

Code: Select all

train_speed = max(0, abs(train_speed) - train_friction_force ÷ train_weight)
train_speed = train_speed + (10 × number_of_locomotives_in_moving_direction × fuel_acceleration_bonus ÷ train_weight)
train_speed = train_speed × (1 - air_resistance_of_front_rolling_stock ÷ (train_weight ÷ 1000))
Let's assume the following units that I feel are unambiguous:
  • train_speed :: distance / time (speed)
  • train_weight :: mass
  • train_friction_force :: mass * distance / time^2 (force)
Remember, this runs every tick so there's an implicit delta-T coefficient! This means that in the first line train_friction_force ÷ train_weight represents acceleration (distance / time^2), but it also has an implicit dt which results in speed, letting us add it to train_speed under our dimensional analysis.

The more ambiguous units:
  • number_of_locomotives_in_moving_direction :: mass * distance / time^2 (force)
  • fuel_acceleration_bonus :: unitless
  • air_resistance_of_front_rolling_stock :: ???
For "10 * number_of_locomotives_in_moving_direction", I assume the authors meant each locomotive to contribute some fixed force from the engine, leading to the same units as the first line, including the delta-T.

For last, most important line in question, how can we algebraically manipulate this to match the previous lines (of the form speed = speed + acceleration * dt)? Distributing the multiplication by train_speed, we get:

Code: Select all

train_speed = train_speed - train_speed × air_resistance_of_front_rolling_stock ÷ (train_weight ÷ 1000)
Since we know the units for train_speed and train_weight, we can conclude that air_resistance_of_front_rolling_stock has units of "mass / time" (To convince yourself this is the case, substitute the units in, remembering the trailing delta-T, and observe that (m/s) * (kg/s) * (1/kg) * s = m/s, which is compatible with train_speed).

I hope by this point you can agree that something is definitely wrong. Specifically, the real equation for the drag force is proportional to the velocity squared (along with air density, cross-sectional area, and drag coefficient which can be baked into the air_resistance_of_front_rolling_stock constant with new dimensions mass / distance).
Suggested (backwards-compatible) fix
The last line of the code should instead look like this:

Code: Select all

train_speed = train_speed - 500 × train_speed × train_speed × air_resistance_of_front_rolling_stock ÷ train_weight
As far as I know I can't modify these equations in Lua to test the changes, but the existing coefficient should probably be increased to counteract the additional multiplication of the speed (I assumed an average speed of 100km/h, and that the speed variable is measured in meters / tick). It can of course be tweaked further to get as close as possible to the existing train behavior.
Conclusion
I believe that with this change, trains in "normal" conditions will change speed/acceleration only slightly, but trains in extreme conditions (trains with dozens of locomotives, modded trains) will behave much more logically.

Rseding91
Factorio Staff
Factorio Staff
Posts: 13348
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: [1.1.91] Train air resistance is calculated incorrectly

Post by Rseding91 »

Thanks for the report however at this point I don't see us ever changing this. Realism vs not; how they work is how they work. We aren't aiming for complete total realism in the game (it is a game after all).

Maybe one of the other developers disagrees and wants to poke at it but trains are already a slow piece of game logic and making the formula more complex (and likely slower) for the sake of realism in a video game isn't going to make people happy.
If you want to get ahold of me I'm almost always on Discord.

magneticflux-
Manual Inserter
Manual Inserter
Posts: 2
Joined: Mon Oct 09, 2023 6:21 pm
Contact:

Re: [1.1.91] Train air resistance is calculated incorrectly

Post by magneticflux- »

Rseding91 wrote:
Mon Oct 09, 2023 10:35 pm
Realism vs not; how they work is how they work.
I agree, this is the way they've always behaved and changing that carries some risk. It's ultimately up to taste, but IMHO the acceleration around top speed just "feels off" and this is what I found when I looked into it.

In lieu, is there any chance of getting a Lua event to modify train speed when accelerating/decelerating? I think looping over locomotives in an on_tick event handler and assigning LuaTrain.speed might work, but I'd need to reimplement each LuaTrain.state value.
Rseding91 wrote:
Mon Oct 09, 2023 10:35 pm
trains are already a slow piece of game logic and making the formula more complex (and likely slower)
I know a huge amount of time and experience has gone into optimizing Factorio, but I doubt it's to the point where having 5 instead of 4 FMULs per train-tick when there are 3 FDIVs on the critical path is going to be measurable :P

Xorimuth
Filter Inserter
Filter Inserter
Posts: 639
Joined: Sat Mar 02, 2019 9:39 pm
Contact:

Re: [1.1.91] Train air resistance is calculated incorrectly

Post by Xorimuth »

magneticflux- wrote:
Mon Oct 09, 2023 11:27 pm

In lieu, is there any chance of getting a Lua event to modify train speed when accelerating/decelerating? I think looping over locomotives in an on_tick event handler and assigning LuaTrain.speed might work, but I'd need to reimplement each LuaTrain.state value.
This mod already loops over all locomotives in on_tick, obviously the performance isn't going to be anything like vanilla, but there's no one complaining about UPS on the mod discussion page so presumably it isn't too bad... https://mods.factorio.com/mod/TrainSpeeds
My mods
Content: Lunar Landings | Freight Forwarding | Spidertron Patrols | Spidertron Enhancements | Power Overload
QoL: Factory Search | Remote Configuration | Module Inserter Simplified | Wire Shortcuts X | Ghost Warnings

Post Reply

Return to “Won't fix.”