Part 0: On how to measure things
This be boring, probabably
By reading off the last memory cell, you can get the exact number of ticks for which the first condition held but not the second. (Note that this setup requires that the start condition be a strict superset of the stop condition). Resetting this counter is merely a matter of cycling the condition in the final combinator back to <.
Caution: Factorio does have some odd quirks in where things matter. I've tried to notate them when I find them, but entity build order and direction of the grid can have measurable impacts on these numbers.Part 1: Belts
Belts are the simplest mechanic in the game to measure and time (although I've been told by devs that they are the most complex entities in terms of game code). Each belt has two lanes of items, and each lane consists of a number of slots. A straight length of belt has 32 slots, while the interior curve has 13.5 slots and the exterior has 37 slots. Yes, the interior has a fractional slot: every odd interior curve is 14 slots and every even interior curve is 13 slots. Every item on a belt takes up 9 slots. In terms of prototype data, belts are specified merely by their speed parameter--the number of tiles moved per tick.
The math to compute speed is pretty simple, so I'll work through the entire formula. The number of items per tile is 32/9 × 2, and each belt moves speed × 60 tiles per second--the number of items per second is 32/9 × 2 × speed × 60 items/sec, or 640 / 3× speed. Speed is usually best thought of not in its Lua terms but in slots per second (the base game has yellow belt moving 1 slot per second, red 2, and blue 3), so the simplest way to remember is that blue belt moves 40 items per second, red ⅔ of that and yellow ⅓ of 40 items per second.
Metering a compressed belt will return between 6 and 8 items per straight belt, depending on how long the front of the belt is (7 items is possible, if you have corners in your belts); curved belts are between 5 and 7 items. To get a consistent read, you need to cover multiple tiles--9 tiles of straight belt will hold exactly 64 items. In fact, this is how I worked out the number of slots on curved belts--by working out the necessary extra straight lines to get a constant count, you can deduce the exact number of slots in use.
Making compressed belts isn't so easy, unfortunately. Inserters can't output on a belt if there's not a 9-slot gap. This means the actual loading of a belt before inserters stop can be anywhere between 52% and 100% (simulations suggest the average is closer to about 75-8%). To compress a belt requires that you output to two belts and merge the two belts together. Well, armed with the knowledge I've imparted, there is something else you can do: if you hook up inserters onto a belt every 9th tile, and ensure that they all move in sync, you can output a fully compressed belt--I've tested it, it actually works.
TODO: investigate underground belts, splitters, and sideloading.
Part 2: Inserters
TODO: copy my number tables.
Part 3: Pipes (and fluid mechanics)
In the game, all fluids must be contained in a fluid-box of some kind. Most fluid transfer works by updating these fluid boxes once per tick, as given in the code below.
Fluidbox update pseudocode
To summarize the effects of the pseudo-code, each tick, a fluid box tries to move an amount of fluid between adjacent fluid boxes. This amount is the differences in pressure (effectively, how full it is) times the pressure_to_speed_ratio property plus the amount transferred in that direction last tick times the flow_to_energy property, up to the baseArea. One key thing to note is that the flow energy is not updated when pumps pump into their fluid box.Actually reading the amounts of fluid in a pipe is difficult to do--the only real way to do it is by using Lua commands. The order that you build pipes matters, too, as well as the pipe directions. There is some scope for handling things out of order (such as, if the amount to be transferred is negative, the game appears to update the transfer amount anyways as if flowing in the opposite direction), but for simplicity I'm going to assume the best case scenario for updates. It's easy to set this scenario up in cheat mode, where you build all pipes from west to east and transfer all fluids from west to east.
Pipe flows have effectively two different flows. The simplest to understand is steady-state flow: as much fluid flows into the pipe as flows out of it; the other flow is the case where fluids are accumulating or draining out of a pipe and will be treated later. In steady-state flow, it follows that the amount transferred in different ticks is the same. This value is goverened by the equation f = (col[p - 1] - col[p]) × pressureToSpeed + min(baseArea, flowToEnergy × f). When f ≤ baseArea ÷ flowToEnergy, some algebra yields that the pressure difference must be f × (1 - flowToEnergy) ÷ pressureToSpeed. In the ideal case, we pump into this connection and then out of it in the same tick, so the difference in pressures is really less that amount by f ÷ baseArea.
Knowing the difference in amounts in adjacent pipes tells us how long the pipe may be before the size of the pipe can no longer accommodate that flow. If we fix the last pipe to have a pressure of 0 (for example, it's the influent pipe to a pump), then after n tiles, the pressure in that pipe is (n - 1) × (f × (1 - flowToEnergy) ÷ pressureToSpeed - f ÷ baseArea). The maximum pressure in a pipe can be 10, and we need to be able to insert f fluid in the head of the pipe on the first tick, so the longest pipe that can accommodate the flow must satisfy the equation (n - 1) × (f × (1 - flowToEnergy) ÷ pressureToSpeed - f ÷ baseArea) + f ÷ baseArea = 10. Solving for n, we get n = 1 + (10 - f ÷ baseArea) ÷ (f × (1 - flowToEnergy) ÷ pressureToSpeed - f ÷ baseArea). If you do the math for vanilla water and pipes and using vanilla small pumps, you get 761 for a single pump, 361 for 2, and 227⅔ for 3--which is fairly close to the numbers presented in the long distance pipes thread (numbers will differ slightly--in part, my experiment found that the final tile did not have a pressure of 0; this may be related to the amount that existed after flow stabilized).
What happens when f ≥ baseArea ÷ flowToEnergy? In this case, the f × flowToEnergy term is reduced to baseArea. The algebraic rearrangement gives us an adjusted formula of (f - baseArea) ÷ pressureToSpeed is the pressure difference, and (n - 1) × ((f - baseArea) ÷ pressureToSpeed - f ÷ baseArea) for the first pipe in that length of pipe. The longest pipe accomdating that flow satisfies (n - 1) × ((f - baseArea) ÷ pressureToSpeed - f ÷ baseArea) + f ÷ baseArea = 10. Solving for n here gives you n = 1 + (10 - f ÷ baseArea) ÷ ((f - baseArea) ÷ pressureToSpeed - f ÷ baseArea). Again, exemplary numbers here for vanilla suggest that 4 small pumps can pump through at most 17 pipes.
We can also solve these equations to find the maximum flow a length of pipe can support. If f ≤ baseArea ÷ flowToEnergy, we get f = 10 × baseArea × pressureToSpeed / (baseArea × (1 - flowEnergy) × (n - 1) - (n - 2) × pressureToSpeed). When f ≥ baseArea ÷ flowToEnergy, we instead get f = baseArea × (baseArea × (n - 1) + 10 × pressureToSpeed) ÷ (baseArea × (n - 1) - (n - 2) × pressureToSpeed).
TODO: Analyze non-steady state solutions.
TODO: Draw conclusions about pipe areas and flows more quantitatively and concretely.
Part 4: Trains
TODO: Coming eventually.
Part 5: Robots
TODO: Coming eventually.
Part 6: Power
TODO: Coming eventually.