Musings: Fluid dynamics
Posted: Thu Nov 09, 2017 3:00 pm
This is a bunch of reflexions on what could be done for better fluids/heat in Factorio. Some aspects are really similar to a post of Rseding91 I found recently and can't find again.
The context: Fluids in Factorio (that's liquids, gas and heat in heat pipes) are currently simulated through a local fluid deynamics-ish simulation of flow. Containers (pipes, tanks) try to equalize the pressure with their neighbours by moving an amount a fluid around. The pressure is defined as the amount of fluid contained in the container divided by the capacity of said container. For instance, a pipe with capacity 20 filled with 10 units of fluid is at equilibrium with a 25K-capacity tank which contains 12500 units of fluid. Pumps take fluid from one side and add it to the other, if there's space, and so do everything using or producing fluids. Update is done per-tick in a single pass and the entities are taken in essentially placement order.
The problems: There are gameplay problems with that system. The main one is that it's very hard to see what's going on. Amount of fluid that can go through a pipeline is extremely dependant on the placement order: in front to back order, a pipe sees added fluid after at least n ticks, where n is the distance to the source, while in back-to-front the fluid propagates through the whole pipe in one tick. Even with the most efficient placement order, the amount going through is inverse exponential with the length of the pipe, and the amount displaced reduces as the pipe fills (less pressure difference). So you need pumps on a regular basis to keep the pressure differencial, but there's no real way to know how often you need pumps. If you have multiple fluid consumers connected to a running pipe the last ones may be somewhat starved but, unlike belts, there's no easy way to see when that happens. And, also unlike belts, there's no easy way to upgrade the piping. So there's a general debuggability/optimizeability problem with piping. A secondary problem, which I personally never encountered, is performance. These are a lot of computations per tick, and they seem to be really noticeable in megabases. It's sad to never see Nuclear power used in megabases just because of that. A final very annoying problem is that when you put the wrong fluid in a pipe network, it's almost impossible to empty it, there's always a near-zero amount lurking in a pipe or two you have to find and remove/replace.
Proposed solution: Drop the fluid dynamics. Create fluid domains which group every connected pipe and tank (and the internal capacity of connected entities, if any) together. Pumps have two different domains on their sides. Each domain has a total capacity, which is the sum of the individual ones, and a total contained amount. A domain can only contain one fluid (or be empty). Pumps, users, producers work the same way, but against the domain instead of the local container, which gives instantaneous transmission of fluids with infinite speed. No more pumps needed except for their basic semantics, which is a one-way active, circuit-controllable valve. Placement order will still matter, but not within the domain. The order in which entities add or remove fluid from the domain will matter, but if inflow equals outflow then after one tick the content level will stabilize (to a not really predictable value) and the flow will be maximal.
Placement/Removal: Placement of a container can merge multiple domains. Removing one can split a domain in multiple parts, or make it disappear entirely. When merging, if there are different fluids the one present in the largest quantity stays and the other(s) is spilled. In case of equality a random but deterministic one is kept (lowest domain id for instance). When splitting a domain the fluid goes into the new domains proportionally to their capacity. What doesn't fit spills. Spilling fluid makes it disappear, and possibly creates pollution in an amount proportional to the fluid quantity.
UI: Hovering over a pipe/tank should give the information for the domain. No scaling to the individual entity, it's way less surprising that way. For a better user experience, the domains should also keep track of some more information. This shouldn't be a performance problem, because there shouldn't be many actual fluid domaines in a base (a half dozen in a normal base, 2-3x that in a megabase, another 2-3x in a A&B mod run, less than a hundred in any case). It should keep track in particular of:
Circuit network: Tanks should give out the total contents of the domain. Multiple connections will then multiply the result, but that's life. People will have to fix that to connect to only one tank per domain, which it not something big.
Possible new entities:
Thoughts?
The context: Fluids in Factorio (that's liquids, gas and heat in heat pipes) are currently simulated through a local fluid deynamics-ish simulation of flow. Containers (pipes, tanks) try to equalize the pressure with their neighbours by moving an amount a fluid around. The pressure is defined as the amount of fluid contained in the container divided by the capacity of said container. For instance, a pipe with capacity 20 filled with 10 units of fluid is at equilibrium with a 25K-capacity tank which contains 12500 units of fluid. Pumps take fluid from one side and add it to the other, if there's space, and so do everything using or producing fluids. Update is done per-tick in a single pass and the entities are taken in essentially placement order.
The problems: There are gameplay problems with that system. The main one is that it's very hard to see what's going on. Amount of fluid that can go through a pipeline is extremely dependant on the placement order: in front to back order, a pipe sees added fluid after at least n ticks, where n is the distance to the source, while in back-to-front the fluid propagates through the whole pipe in one tick. Even with the most efficient placement order, the amount going through is inverse exponential with the length of the pipe, and the amount displaced reduces as the pipe fills (less pressure difference). So you need pumps on a regular basis to keep the pressure differencial, but there's no real way to know how often you need pumps. If you have multiple fluid consumers connected to a running pipe the last ones may be somewhat starved but, unlike belts, there's no easy way to see when that happens. And, also unlike belts, there's no easy way to upgrade the piping. So there's a general debuggability/optimizeability problem with piping. A secondary problem, which I personally never encountered, is performance. These are a lot of computations per tick, and they seem to be really noticeable in megabases. It's sad to never see Nuclear power used in megabases just because of that. A final very annoying problem is that when you put the wrong fluid in a pipe network, it's almost impossible to empty it, there's always a near-zero amount lurking in a pipe or two you have to find and remove/replace.
Proposed solution: Drop the fluid dynamics. Create fluid domains which group every connected pipe and tank (and the internal capacity of connected entities, if any) together. Pumps have two different domains on their sides. Each domain has a total capacity, which is the sum of the individual ones, and a total contained amount. A domain can only contain one fluid (or be empty). Pumps, users, producers work the same way, but against the domain instead of the local container, which gives instantaneous transmission of fluids with infinite speed. No more pumps needed except for their basic semantics, which is a one-way active, circuit-controllable valve. Placement order will still matter, but not within the domain. The order in which entities add or remove fluid from the domain will matter, but if inflow equals outflow then after one tick the content level will stabilize (to a not really predictable value) and the flow will be maximal.
Placement/Removal: Placement of a container can merge multiple domains. Removing one can split a domain in multiple parts, or make it disappear entirely. When merging, if there are different fluids the one present in the largest quantity stays and the other(s) is spilled. In case of equality a random but deterministic one is kept (lowest domain id for instance). When splitting a domain the fluid goes into the new domains proportionally to their capacity. What doesn't fit spills. Spilling fluid makes it disappear, and possibly creates pollution in an amount proportional to the fluid quantity.
UI: Hovering over a pipe/tank should give the information for the domain. No scaling to the individual entity, it's way less surprising that way. For a better user experience, the domains should also keep track of some more information. This shouldn't be a performance problem, because there shouldn't be many actual fluid domaines in a base (a half dozen in a normal base, 2-3x that in a megabase, another 2-3x in a A&B mod run, less than a hundred in any case). It should keep track in particular of:
- underflow and overflow conditions, the first one happens when an entity tries to grab fluid but doesn't have enough, the second when an entity tries to add fluid but there's not enough space. That could then be used for a red/yellow/green color coding
- mean inflow and outflow (in units/second) over the last 5 seconds or so. It can be bucketized with buckets of roughly half a second, one just has to be careful that addition over floating point values is not associative.[/b]
Circuit network: Tanks should give out the total contents of the domain. Multiple connections will then multiply the result, but that's life. People will have to fix that to connect to only one tank per domain, which it not something big.
Possible new entities:
- Splitter pump: a pump that takes from one domain, and puts half-and-half on two other domains, putting on the other what doesn't fit in one. Useful to split between plastics and acid
- Overflow pump: a pump that takes from one domain, puts on a second, and if it doesn't fit sends the overflow to a third. Useful to send oil to cracking only when the primary production (lubricant, solid fuel) is backed up
- Manometer: connects to a domain and reads to the circuit network the contents, capacity and instantaneours flow amounts
- Fluid spiller: a tool not unlike the repair tool that spills the content of a fluid domain on the floor (up to a given amount per tick?), possibly producing pollution. We may want to require construction bots to have one to remove filled pipes/tanks too, then again maybe not. Very useful when you misconnect something anyway
Thoughts?