Thermal Expansion
Team CoFH's mod of machines, power, and ducts for Minecraft.
Thermal Expansion is a Minecraft mod by Team CoFH: machines that turn ores and other items into products on stored energy, and the blocks that make and move that energy. By the team’s own history, King Lemming and Cynycal began modding as Team CoFH in 2012, mostly so King Lemming could learn Java, and a furnace he built to run on Minecraft Joules grew into the first Thermal Expansion, for Minecraft 1.21. King Lemming’s Minecraft Forum thread for the mod opens on 19 June 20122, the earliest known release, which offered only its “Factory” module and needed BuildCraft or Forestry for power2. The mod depended on BuildCraft, or an addon such as Forestry, until its third version made it independent3. Zeldo worked with King Lemming on Thermal Expansion 2, which brought the original ducts for energy and fluids4; the Liquiducts for fluids came in its 2.1.0 update5.
Thermal Expansion 3, released on 25 December 20136, was a rewrite that dropped BuildCraft’s Minecraft Joules for the team’s own Redstone Flux (RF)7. Team CoFH called RF the most widely adopted energy system in modded Minecraft at that release, and their later documentation lists BuildCraft among the mods that support it8. Version 4.0.0 ported the mod to Minecraft 1.7.10 and moved the energy, fluid, and item ducts into a separate mod, Thermal Dynamics, which RWTema did most of the work of porting9. Thermal Expansion 5 carried the mod to Minecraft 1.12.2, with files for it into May 201910.
No press review of the mod has been found. Its CurseForge page counted 31,443,234 CurseForge downloads, May 201911 downloads in May 2019. Its legacy shows in other projects’ code and words. Minecraft Forge’s energy interface says it is derived from Redstone Flux and was made with Team CoFH’s consent12. Factorio’s developers wrote in 2024 that the fluid algorithm proposed for Factorio 2.0 was “very close to” one from the mod13.
#How it plays
#Machines and Redstone Flux
A machine works through a recipe by spending RF. Each recipe has a cost, every tick the machine takes some RF from its store and pays it toward that cost, and the product comes out when the cost is paid14. A machine with a full store works fastest: near full it draws its whole rated power, and as the store empties it draws less and slows down, to a crawl below a tenth15. In the loop most machines share, a machine that runs dry partway through an item drops its progress, and the furnace then starts that item over at its full cost, so the RF already spent on it is gone14. Higher-level machines are rated for more power15.
Redstone Flux itself is simple: a block holds an amount of energy up to a capacity, with limits on how much it takes in and gives out at once16.
#Fluid ducts
Fluiducts that touch join into one grid, and a grid carries one fluid. Laying a duct between two grids merges them and pours their fluids together, and ducts holding different fluids refuse to connect17. The grid keeps its fluid in one shared tank, which grows with every duct, and fluid poured into any duct lands in that tank at once18. Thinner fluids get a higher rate, and the thickest get both a lower rate and less room in each duct, because both follow the fluid’s viscosity18.
Fluid never creeps along the pipe. Every duct beside a machine or tank hands out from the shared tank each tick, at up to the fluid’s full rate when the grid is three-quarters full or more, and half of it when the grid is a quarter full or less19. Breaking a duct that joins two others rebuilds the grid, and each piece left keeps the shares of the ducts it holds20.
#In Factorio’s lineage
Factorio’s Friday Facts #416 says that while Wube Software was reworking fluids for Factorio 2.0, Rseding proposed an algorithm “very close to” one from Thermal Expansion, in which pipes and tanks merge into fluid segments13. Thermal Dynamics’ duct grid and the segments the post describes share the core of it: connected pipes merge into one unit that holds one fluid, its volume is the sum of its parts, and fluid put in is available anywhere along it at once. They part on the way out: in the design as #416 first described it, a machine pulls in proportion to how full the segment is, and a Thermal Dynamics duct hands out between half and all of its rate as the grid fills. Friday Facts #430 revised Factorio’s rules before 2.0 shipped, scaling every transfer by the fullness of both ends21.
:::gap{internal}[Not read yet] The dynamos, energy cells, and the energy and item ducts in the released code; the servos that pull fluid into a duct grid. :::
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Fluid network | Merged segment | Thermal Dynamics merges connected fluiducts into one grid with one tank, and every outlet duct draws on it |
| Power network | Pooled grid | in Thermal Dynamics, connected fluxducts of one tier keep their RF in one store, and each duct beside a machine hands out an equal share every tick |
Every claim about this game, by concept (21)
References
- ^Team CoFH's own history says that in 2012 King Lemming and Cynycal began modding Minecraft as Team CoFH, mostly so King Lemming could learn Java. While working on a farming and brewing mod, Brewcraft, King Lemming made a furnace that ran on Minecraft Joules instead of fuel, then more blocks like it; this led to the first Thermal Expansion, for Minecraft 1.2, with about seven machines, and Brewcraft was abandoned.PublisherTeam CoFH, About (2017)
How we know
In the year 2012, the name CoFH was used once again when King Lemming teamed up with Cynycal as Team CoFH to start modding for Minecraft, mostly for King Lemming to learn how to program in Java. … As King Lemming was still figuring out how to create Tile Entities, he created a furnace that worked on Minecraft Joules instead of regular item fuel. He then figured he might as well make more blocks like that. … Eventually, this development led to the release of the first version of Thermal Expansion for Minecraft 1.2, leaving Brewcraft abandoned. Back then, the mod contained about 7 machines and a few other things like Rockwool and Hardened Glass.
History · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abKing Lemming's Minecraft Forum thread for Thermal Expansion opens with a post dated 19 June 2012, the earliest known public release. As captured in July 2012, the thread offers version 1.0.2 for Minecraft 1.2.5 with a changelog that begins "1.0: Initial Release", calls the mod a "tech" mod of "what ifs", and says the release is only the "Factory" module and needs power generation from BuildCraft or Forestry.Dev blogKing Lemming, Thermal Expansion 1.0.2 post (2012)
How we know
[1.2.5] [SMP] Thermal Expansion - Now with SMP compatibility!
thread title · archived copy ↗ Posted 19 June 2012 - 01:20 AM "Thermal Expansion" 1.0.2 Thermal Expansion, at its core, is a "tech" mod - it's a mod of what ifs. … This release is only the "Factory" module - there is lots more to come.
#1, KingLemmingCoFH · archived copy ↗ 1.0: Initial Release Important: Please note that this mod in its current state requires power generation from either BuildCraft or Forestry.
#1, KingLemmingCoFH, changelog · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^By Team CoFH's account, Thermal Expansion needed BuildCraft, or an addon such as Forestry, to power its machines until its third version, when it became fully independent.PublisherTeam CoFH, About (2017)
How we know
TE was a BuildCraft addon: it required BuildCraft itself or addons like Forestry in order to power the machines with Minecraft Joules, BuildCraft's old power system. It wasn't until the third iteration that the mod became fully independent.
History · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Zeldo joined Team CoFH in late 2012 and worked with King Lemming on Thermal Expansion 2, which, by the team's account, brought the Redstone Energy Cells, Tesseracts, and the original ducts: Redstone Energy Conduits and Liquiducts.PublisherTeam CoFH, About (2017)
How we know
Zeldo originally joined the team in late 2012 and worked together with King Lemming on Thermal Expansion 2. This version contained the iconic Redstone Energy Cells, Tesseracts and the original Ducts: Redstone Energy Conduits and Liquiducts.
Team members, Zeldo · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Thermal Expansion 2.1.0 added Tanks and Liquiducts, ducts for fluids, by King Lemming's update note in the Minecraft Forum thread as captured on 2 January 2013.Dev blogKing Lemming, Thermal Expansion 2.1.6 post (2013)revised
How we know
So, 2.1.0 is out! It adds Tanks, Liquiducts, and a couple other things.
#1, Update News (2.1.0) · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^Thermal Expansion 3.0.0.0 came out on 25 December 2013, a rewrite in which, by its changelog, "everything has been completely redone"; it introduced Redstone Flux, and its fluid ducts were the Fluiducts, in opaque and transparent kinds.PublisherTeam CoFH, Thermal Expansion changelog (2014)
How we know
3.0.0.0: 12/25/2013 - Merry Christmas! Okay, so everything has been completely redone here. … The new energy system is Redstone Flux. (RF).
3.0.0.0: 12/25/2013 · archived copy ↗ Two types of Fluiducts - Opaque and Transparent.
3.0.0.0, Fluid · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abThermal Expansion began as an addon that ran its machines on BuildCraft's Minecraft Joules (MJ), and Team CoFH say it split into its own mod "due mostly to the design choices by the BuildCraft developers". Its 3.0.0.0 changelog (25 December 2013) drops MJ for its own Redstone Flux (RF), at 10 RF to 1 MJ: machines take only RF, dynamos that make RF replace engines, and the Redstone Energy Conduits take RF but convert it to MJ on output where needed.PublisherTeam CoFH, About (2017) · Team CoFH, Thermal Expansion page (2014) · Team CoFH, Thermal Expansion changelog (2014)
How we know
TE was a BuildCraft addon: it required BuildCraft itself or addons like Forestry in order to power the machines with Minecraft Joules, BuildCraft's old power system.
History · archived copy ↗ Soon, due mostly to the design choices by the BuildCraft developers, Thermal Expansion split into its own mod, with its own power system and no dependency on any mod.
page text · archived copy ↗ MJ is no longer supported by Thermal Expansion. The new energy system is Redstone Flux. (RF). Ratio is 10:1 RF:MJ.
3.0.0.0: 12/25/2013 · archived copy ↗ Machines: -Now only accept RF. … Dynamos: -These replace engines. They generate Redstone flux.
3.0.0.0, BLOCKS · archived copy ↗ There are now 3 levels of Redstone Energy Conduit. They accept RF but will convert to MJ on output if necessary.
3.0.0.0, Conduit · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Team CoFH claimed at Thermal Expansion 3's release that Redstone Flux was already "the most widely adopted energy system in modded Minecraft", and their 2016 documentation lists other mods that support it, BuildCraft among them, and devices in other mods that make RF, BuildCraft's engines among them.PublisherTeam CoFH, Thermal Expansion changelog (2014) · Team CoFH docs, Supporting Mods (2016) · Team CoFH docs, Redstone Flux (2016)
How we know
Lots of mods already support Redstone Flux - it is now the most widely adopted energy system in modded Minecraft!
3.0.0.0: 12/25/2013 · archived copy ↗ BuildCraft Pipes, fillers, builders, and various other tools for automation
list of mods · archived copy ↗ Engines from BuildCraft, Forestry and Railcraft
Production · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Thermal Expansion 4.0.0 ported the mod to Minecraft 1.7.10 and moved its Energy Conduits, Fluiducts, and Itemducts into a separate mod, Thermal Dynamics. Team CoFH's downloads page, captured on 27 December 2014, says it was released for 1.7.10 without the ducts, which would follow as a separate mod. Team CoFH credit RWTema, the author of Extra Utilities, with most of the work of porting the ducts to 1.7.10, from around late 2014.PublisherCoFH changelogs (Version repo) · Team CoFH, Downloads page (2014) · Team CoFH, About (2017)
How we know
8934.0.0: ⋮ 895Welcome to Thermal Expansion 4! 896-Ported to Minecraft 1.7.10.
thermalexpansion_changelog.txt lines 893–896 at 1bdb613 · upstream ↗ 993 -Energy Conduits, Fluiducts, and Itemducts. 994 Now in Thermal Dynamics.
thermalexpansion_changelog.txt lines 993–994 at 1bdb613 · upstream ↗ Thermal Expansion is now RELEASED for 1.7.10. Ducts are not in the release, they will be a separate mod when they are finished.
The Mods · archived copy ↗ Tema stepped in around late 2014 when there wasn't anyone around that had time to work on Thermal Dynamics. He did most of the work on porting the missing piece of the Thermal mods in 1.7.10: ducts!
Team members, RWTema · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Thermal Expansion 5's files on CurseForge run from Minecraft 1.10.2 to 1.12.2; the last 1.12.2 file listed in May 2019 is 5.5.4.43, dated 25 May 2019.PublisherTeam CoFH, Thermal Expansion on CurseForge (2019)
How we know
ThermalExpansion-1.12.2-5.5.4.43-universal.jar May 25, 2019 … ThermalExpansion-1.10.2-5.1.10.28-universal.jar Oct 11, 2017
Recent Files · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Thermal Expansion's CurseForge page counted 31,443,234 downloads in May 2019, and its description, written by the developers, calls it "one of the foundation mods in modded Minecraft gameplay".PublisherTeam CoFH, Thermal Expansion on CurseForge (2019)
How we know
Total Downloads 31,443,234
About This Project · archived copy ↗ It features a tech tree involving multiple machines, balanced and flexible recipes, and is now one of the foundation mods in modded Minecraft gameplay.
description · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Minecraft Forge's energy interface, IEnergyStorage, states in its documentation that it is derived from the Redstone Flux system that King Lemming designed for Thermal Expansion, and that it was made with the consent of King Lemming and Team CoFH. Thermal Expansion's own CurseForge description says its fluid and energy systems are now both part of Forge.PublisherMinecraftForge, IEnergyStorage · Team CoFH, Thermal Expansion on CurseForge (2019)
How we know
15 * Derived from the Redstone Flux power system designed by King Lemming and originally utilized in Thermal Expansion and related mods. 16 * Created with consent and permission of King Lemming and Team CoFH. Released with permission under LGPL 2.1 when bundled with Forge.
src/main/java/net/minecraftforge/energy/IEnergyStorage.java lines 15–16 at 243490d · upstream ↗ Many of the gameplay ideas and mechanics set forth by Thermal Expansion have become staples in the modded Minecraft community, including both the Fluid and Energy (RF) systems, now both included as part of Minecraft Forge.
description · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abFriday Facts #416 (2024) says that in the discussions on Factorio 2.0's fluids, Rseding proposed an algorithm "very close to" one from the Minecraft mod Thermal Expansion by Team CoFH, in which pipes, underground pipes, and storage tanks merge into fluid "segments".Dev blogFriday Facts #416
How we know
Rseding proposed an algorithm very close to one he had played with several times, an algorithm from the Minecraft mod Thermal Expansion by team CoFH: Pipes, underground pipes, and storage tanks are merged into fluid "segments".
The new algorithm · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abIn Thermal Expansion r5.3.9 (Minecraft 1.12.2), in the loop its machines share, a machine's recipe costs an amount of Redstone Flux (RF), and the machine works by spending it: each tick a running machine takes some RF from its store and subtracts it from what the recipe still needs, and finishes when that reaches zero, getting back any RF it spent past the cost. So a machine with more power finishes sooner. A machine whose store runs dry mid-recipe stops and clears what the recipe still needed, so the RF spent on it is lost: the furnace, starting again, sets the recipe's full cost. An idle machine checks on a timer whether it can start. The charger and the transposer replace parts of this loop in some modes, and are excluded.CodeThermal Expansion r5.3.9
How we know
194 processTick(); ⋮ 196 if (canFinish()) { 197 processFinish(); ⋮ 200 energyStorage.modifyEnergyStored(-processRem); ⋮ 203 processOff(); ⋮ 207 } else if (energyStorage.getEnergyStored() <= 0) { 208 processOff(); ⋮ 215 if (timeCheckEighth() && canStart()) { ⋮ 217 processTick();
src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java lines 188–223 at 23f8892 · upstream ↗ 270 protected void processOff() { ⋮ 272 processRem = 0; 273 isActive = false;
src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java lines 270–273 at 23f8892 · upstream ↗ 167 processRem = processMax;src/main/java/cofh/thermalexpansion/block/machine/TileFurnace.java lines 164–168 at 23f8892 · upstream ↗ 286 int energy = calcEnergy(); 287 energyStorage.modifyEnergyStored(-energy); 288 processRem -= energy;
src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java lines 281–291 at 23f8892 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abIn Thermal Expansion r5.3.9 a machine's power follows how full its RF store is. With the default settings and no augments, a machine of base power P stores 1,000 × P RF and takes up to 4 × P a tick. It spends P RF a tick while its store is at least 90% full, the store divided by 900 between 10% and 90% (P/9 at 10%, about 0.56 × P at half), and P/10 below 10%, or what's left once less than that remains. Base power is 20 RF a tick for most machines and 50 for the charger and the crucible, multiplied by 1, 1.5, 2, 2.5, or 3 for the machine's level. With the SmallStorage option a machine stores only 10 × P and always spends P. Augments change the draw and are excluded: the power augment, which any machine can take, adds the level's base power to P and makes recipes cost 15% more; the furnace's pyrolysis and the crucible's lava augment change it too.CodeThermal Expansion r5.3.9revised
How we know
41 public static final int[] POWER_SCALING = { 100, 150, 200, 250, 300 };src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java lines 39–42 at 23f8892 · upstream ↗ 121 energyStorage = new EnergyStorage(energyConfig.maxEnergy, energyConfig.maxPower * 4);src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java line 121 at 23f8892 · upstream ↗ 233 if (energyStorage.getEnergyStored() >= energyConfig.maxPowerLevel) { 234 return energyConfig.maxPower; ⋮ 236 if (energyStorage.getEnergyStored() < energyConfig.minPowerLevel) { 237 return Math.min(energyConfig.minPower, energyStorage.getEnergyStored()); ⋮ 239 return energyStorage.getEnergyStored() / energyConfig.energyRamp;
src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java lines 226–240 at 23f8892 · upstream ↗ 203 maxPower = basePower; 204 minPower = basePower / 10; 205 maxEnergy = basePower * 1000; 206 maxPowerLevel = 9 * maxEnergy / 10; 207 minPowerLevel = maxEnergy / 10; 208 energyRamp = maxPowerLevel / basePower; ⋮ 215 if (!smallStorage) { ⋮ 218 maxPower = basePower; 219 minPower = basePower; 220 maxEnergy = basePower * 10;
src/main/java/cofh/thermalexpansion/block/TileTEBase.java lines 201–226 at 23f8892 · upstream ↗ 421 if (TEProps.MACHINE_POWER.equals(id)) { 422 energyConfig.setDefaultParams(energyConfig.maxPower + getBasePower(this.level), smallStorage); 423 energyMod += 15; 424 return true;
src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java lines 421–425 at 23f8892 · upstream ↗ 38 public static int basePower = 20;src/main/java/cofh/thermalexpansion/block/machine/TileFurnace.java line 38 at 23f8892 · upstream ↗ 44 public static int basePower = 50;src/main/java/cofh/thermalexpansion/block/machine/TileCharger.java line 44 at 23f8892 · upstream ↗ 43 public static int basePower = 50;src/main/java/cofh/thermalexpansion/block/machine/TileCrucible.java line 43 at 23f8892 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^Redstone Flux's reference store, in Team CoFH's RedstoneFlux API for Minecraft 1.12, is an amount of energy with a capacity, a most it takes in at once, and a most it gives out at once; changing the amount directly clamps it between 0 and the capacity. Thermal Expansion r5.3.9's machines use this class for their RF store.CodeRedstoneFlux API 1.12 · Thermal Expansion r5.3.9
How we know
15public class EnergyStorage implements IEnergyStorage { ⋮ 17 protected int energy; 18 protected int capacity; 19 protected int maxReceive; 20 protected int maxExtract;
src/main/java/cofh/redstoneflux/impl/EnergyStorage.java lines 15–20 at 24719b6 · upstream ↗ 114 public void modifyEnergyStored(int energy) { ⋮ 116 this.energy += energy; ⋮ 118 if (this.energy > capacity) { 119 this.energy = capacity; 120 } else if (this.energy < 0) { 121 this.energy = 0;
src/main/java/cofh/redstoneflux/impl/EnergyStorage.java lines 114–123 at 24719b6 · upstream ↗ 13import cofh.redstoneflux.impl.EnergyStorage;src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java line 13 at 23f8892 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^In Thermal Dynamics r2.3.9 (Minecraft 1.12.2), connected fluid ducts form one grid. A grid forms outward from one duct, breadth-first over its connected ducts: a duct with no grid joins, and a duct in another grid merges the two grids, the smaller into the larger, the larger keeping its fluid and taking the other's. A grid holds one fluid: a duct joins, and two grids merge, only when their fluids match or one is empty; otherwise the connection is rejected. When a duct is placed, it compares its neighbours' fluids with one of them, the fluid of the duct the player aimed at or else the first neighbour that has one, and blocks its sides to every neighbouring duct whose fluid differs from that.CodeThermal Dynamics r2.3.9revised
How we know
16 theGrid = theMultiBlock.createGrid(); ⋮ 22 while (!blocksToCheck.isEmpty()) { 23 checkMultiBlock(blocksToCheck.remove());
src/main/java/cofh/thermaldynamics/multiblock/MultiBlockFormer.java lines 14–26 at 5ae9f68 · upstream ↗ 45 if (aBlock.getGrid() == null && theGrid.canAddBlock(aBlock)) { ⋮ 51 if (theGrid.size() >= aBlock.getGrid().size()) { 52 theGrid.mergeGrids(aBlock.getGrid()); ⋮ 54 aBlock.getGrid().mergeGrids(theGrid); ⋮ 59 currentMultiBlock.onConnectionRejected(i);
src/main/java/cofh/thermaldynamics/multiblock/MultiBlockFormer.java lines 44–61 at 5ae9f68 · upstream ↗ 205 public void mergeGrids(MultiBlockGrid<DuctUnitFluid> theGrid) { ⋮ 211 myTank.fill(gridFluid.getFluid(), true);
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 204–225 at 5ae9f68 · upstream ↗ 419 if (retrace != null && retrace.sideHit != null) { 420 EnumFacing sideHit = retrace.sideHit.getOpposite(); 421 DuctUnitFluid fluids = IDuctHolder.getTokenFromTile(world().getTileEntity(pos().offset(sideHit)), DuctToken.FLUID); 422 if (fluids != null) { 423 fluidStack = fluids.getConnectionFluid();
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 419–425 at 5ae9f68 · upstream ↗ 434 if (fluidStack == null) { 435 fluidStack = connectionFluid; 436 } else if (connectionFluid != null && !fluidStack.isFluidEqual(connectionFluid)) { 437 parent.setConnectionType(facing.ordinal(), ConnectionType.BLOCKED);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 427–442 at 5ae9f68 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abIn Thermal Dynamics r2.3.9 a fluid-duct grid stores its fluid in one tank, not in its ducts. The tank holds the grid's duct count times a per-duct volume, which for ordinary fluiducts is 25 times the fluid's throughput, at most 3,000 mB. The throughput is 120,000 divided by the fluid's viscosity (taken as at least 100), clamped to 80-600 mB a tick; steam is set to 600. A fluid of viscosity 1,000 gets 120 mB a tick and 3,000 mB a duct, the tank's defaults. Fluid of the grid's kind put into any open side of any duct goes straight into that tank, up to its free space, with no other limit on the rate.CodeThermal Dynamics r2.3.9 · CoFH Core r4.3.9
How we know
27 public final FluidTankGrid myTank = new FluidTankGrid(1000, this);src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java line 27 at 5ae9f68 · upstream ↗ 81 public void balanceGrid() { ⋮ 84 myTank.setCapacity(size() * myTank.fluidPerDuct);
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 80–85 at 5ae9f68 · upstream ↗ 135 public float getThroughPutModifier() { ⋮ 137 return 1.0F; ⋮ 140 public int getMaxFluidPerDuct() { ⋮ 142 return 3000;
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 135–143 at 5ae9f68 · upstream ↗ 10 public int fluidThroughput = 120; 11 public int fluidPerDuct = 3000; ⋮ 16 fluidFlowrate.put("steam", 600);
src/main/java/cofh/thermaldynamics/duct/fluid/FluidTankGrid.java lines 10–17 at 5ae9f68 · upstream ↗ 58 viscosity = Math.max(fluid.getFluid().getViscosity(), 100); 59 fluidThroughput = MathHelper.clamp(120000 / viscosity, 80, 600); ⋮ 62 fluidThroughput = fluidFlowrate.get(fluid.getFluid().getName()); ⋮ 64 fluidThroughput *= myMaster.getThroughPutModifier(); 65 fluidPerDuct = Math.min(25 * fluidThroughput, myMaster.getMaxFluidPerDuct());
src/main/java/cofh/thermaldynamics/duct/fluid/FluidTankGrid.java lines 53–68 at 5ae9f68 · upstream ↗ 489 public int fill(FluidStack resource, boolean doFill) { ⋮ 491 if (resource != null && isOpen(from) && matchesFilter(from, resource)) { 492 return grid.myTank.fill(resource, doFill);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 489–494 at 5ae9f68 · upstream ↗ 190 if (!fluid.isFluidEqual(resource)) { 191 return 0; ⋮ 193 int filled = capacity - fluid.amount; ⋮ 195 if (resource.amount < filled) { 196 fluid.amount += resource.amount; ⋮ 199 fluid.amount = capacity;
src/main/java/cofh/core/fluid/FluidTankCore.java lines 186–201 at ccf809a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^In Thermal Dynamics r2.3.9 a fluid-duct grid hands out fluid once a tick. If its tank holds fluid, each duct beside a block that takes fluid offers its neighbours, side by side from where it last stopped, up to the tank's amount divided by the grid's duct count (the remainder when that rounds to 0), and no more than the grid's throughput for that tick. That throughput is the fluid's full rate while the tank is at least three-quarters full, half of it at a quarter full or less, and rises in a straight line from half to full in between. Fluid inside a grid doesn't travel from duct to duct: every outlet draws on the one tank.CodeThermal Dynamics r2.3.9
How we know
61 for (MultiBlockGrid grid : tickingGrids) { 62 grid.tickGrid();
src/main/java/cofh/thermaldynamics/util/WorldGridList.java lines 59–66 at 5ae9f68 · upstream ↗ 162 if (myTank.getFluid() != null && nodeSet.size() > 0) { 163 toDistribute = Math.min(myTank.getFluidAmount() / size(), getFluidThroughput()); ⋮ 165 if (toDistribute <= 0) { 166 toDistribute = Math.min(myTank.getFluidAmount() % size(), getFluidThroughput()); ⋮ 170 for (DuctUnitFluid m : nodeSet) { 171 if (!m.tickPass(0) || m.getGrid() == null) {
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 162–176 at 5ae9f68 · upstream ↗ 233 // if over 3/4 full, full throughput 234 if (myTank.getFluid().amount >= capacity * 3 / 4) { 235 return myTank.fluidThroughput; ⋮ 237 // if under 1/4 full, half throughput 238 if (myTank.getFluid().amount <= capacity / 4) { 239 return myTank.fluidThroughput >> 1; ⋮ 241 // otherwise scale between half and full
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 227–243 at 5ae9f68 · upstream ↗ 89 if (pass == 0) { 90 int available = grid.toDistribute; ⋮ 93 for (int i = this.internalSideCounter; i < 6 && sent < available; i++) { 94 sent += transfer(i, available - sent, false, grid.myTank.getFluid(), true); ⋮ 102 for (int i = 0; i < this.internalSideCounter && sent < available; i++) { 103 sent += transfer(i, available - sent, false, grid.myTank.getFluid(), true);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 89–110 at 5ae9f68 · upstream ↗ 165 tempFluid.amount = available; 166 int amountSent = capability.fill(tempFluid, false); ⋮ 173 tempFluid = grid.myTank.drain(amountSent, true); ⋮ 177 return capability.fill(tempFluid, true);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 164–177 at 5ae9f68 · upstream ↗ 157 C c = cacheTile(tile, side); ⋮ 160 if (isNode(c)) { 161 nodeMask |= (1 << side);
src/main/java/cofh/thermaldynamics/duct/tiles/DuctUnit.java lines 157–162 at 5ae9f68 · upstream ↗ 269 public boolean isNode() { ⋮ 271 return nodeMask != 0;
src/main/java/cofh/thermaldynamics/duct/tiles/DuctUnit.java lines 269–272 at 5ae9f68 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^In Thermal Dynamics r2.3.9, removing a duct that had two or more connected ducts queues its grid to be rebuilt; a duct with one connection or none leaves the grid as it is. A rebuilt grid first gives every duct its share of the tank, the amount divided equally among the ducts that store fluid, with the first duct also taking the remainder, and each duct pours its share into whatever grid it joins next. A duct saved with the world keeps its share the same way.CodeThermal Dynamics r2.3.9
How we know
145 destroyNode(oldBlock); ⋮ 160 if (oldBlock.isSideConnected(i)) { ⋮ 164 if (s <= 1) { 165 balanceGrid(); ⋮ 170 worldGrid.gridsToRecreate.add(this);
src/main/java/cofh/thermaldynamics/multiblock/MultiBlockGrid.java lines 143–171 at 5ae9f68 · upstream ↗ 47 for (MultiBlockGrid<?> grid : gridsToRecreate) { 48 for (IGridTile multiBlock : grid.idleSet) { ⋮ 50 grid.destroyNode(multiBlock); ⋮ 52 for (IGridTile multiBlock : grid.nodeSet) { ⋮ 54 grid.destroyNode(multiBlock);
src/main/java/cofh/thermaldynamics/util/WorldGridList.java lines 44–58 at 5ae9f68 · upstream ↗ 148 if (hasValidFluid()) { 149 ((DuctUnitFluid) node).setFluidForGrid(getNodeShare((DuctUnitFluid) node));
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 145–152 at 5ae9f68 · upstream ↗ 267 if (!duct.canStoreFluid()) { ⋮ 270 int size = getStorableNumberDucts();
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 265–275 at 5ae9f68 · upstream ↗ 51 if (aMultiBlock.getFluidForGrid() != null) { ⋮ 53 myTank.setFluid(aMultiBlock.getFluidForGrid()); ⋮ 55 myTank.fill(aMultiBlock.getFluidForGrid(), true);
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 46–59 at 5ae9f68 · upstream ↗ 375 mySavedFluid = grid.getNodeShare(this); ⋮ 377 mySavedFluid.writeToNBT(nbt);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 374–377 at 5ae9f68 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abFactorio 2.0's fluid segments match the description of Thermal Dynamics r2.3.9's design: connected pipes or ducts merge into one unit that holds one fluid, its volume summed from its parts, and fluid put in is available anywhere along it at once (FFF #416, 2024). In the design as FFF #416 first described it, machines push in at an unlimited rate, as Thermal Dynamics takes fluid up to the tank's free space, and pull at a rate proportional to how full the segment is, half the maximum at half full; each Thermal Dynamics outlet duct instead hands its neighbours between half and all of the fluid's throughput, scaled by how full the tank is (three-quarters of it at half full), and no more than the tank's amount divided by its duct count (or the remainder, when that rounds to 0). FFF #430, still before 2.0's release, gave the rules as changed: pushing in is now slowed as the sink fills, and every transfer is capped at 100 units, a limit that already existed, multiplied by the fullness of both source and sink. The scaling by the source's fullness has a counterpart in Thermal Dynamics; the 100-unit cap and the scaling by the sink's fullness don't. FFF #416 doesn't say which version of the mod Rseding played.Dev blogFriday Facts #416 · Friday Facts #430 · Thermal Dynamics r2.3.9 · CoFH Core r4.3.9revised
How we know
Pipes, underground pipes, and storage tanks are merged into fluid "segments". Each segment inherits its volume from the fluid boxes that comprise it and can hold one fluid. Machines can push fluid into a segment at an unlimited rate, and can pull from a segment at a rate proportional to how full the segment is. … In other words, if a segment is half full, then the pulling rate is half of the maximum. … There is no longer a realistic fluid "flow" through pipes; fluid pushed to a segment will be immediately available at any point along a segment.
The new algorithm · archived copy ↗ Output rate used to be unlimited, but it is now inversely proportional to the fullness ratio of the sink. … Something that I failed to adequately explain before is that while there is no limitation on the total flow through a pipeline in a given tick, there is a hardcoded limit of 100 fluid per flow operation (6000/s). … This limit is multiplied with the fullness ratios of the source and sink to produce the actual flow value
Algorithm tweaks · archived copy ↗ 81 public void balanceGrid() { ⋮ 84 myTank.setCapacity(size() * myTank.fluidPerDuct);
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 80–85 at 5ae9f68 · upstream ↗ return aBlock instanceof DuctUnitFluid &&
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L215-L219 · upstream ↗ 163 toDistribute = Math.min(myTank.getFluidAmount() / size(), getFluidThroughput()); ⋮ 165 if (toDistribute <= 0) { 166 toDistribute = Math.min(myTank.getFluidAmount() % size(), getFluidThroughput());
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 162–167 at 5ae9f68 · upstream ↗ 233 // if over 3/4 full, full throughput 234 if (myTank.getFluid().amount >= capacity * 3 / 4) { 235 return myTank.fluidThroughput; ⋮ 237 // if under 1/4 full, half throughput 238 if (myTank.getFluid().amount <= capacity / 4) { 239 return myTank.fluidThroughput >> 1; ⋮ 241 // otherwise scale between half and full
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 227–243 at 5ae9f68 · upstream ↗ 193 int filled = capacity - fluid.amount; ⋮ 199 fluid.amount = capacity;
src/main/java/cofh/core/fluid/FluidTankCore.java lines 193–201 at ccf809a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
Sources
Code
- Forge Development LLC and contributors. 2026. “Minecraft Forge: IEnergyStorage (Forge Energy).” Commit 243490d. MinecraftForge/MinecraftForge @ 243490d · licence, all uses
- Team CoFH. 2018. “Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9.” Commit 23f8892. CoFH/ThermalExpansion-1.12-Legacy @ 23f8892 · licence, all uses
- Team CoFH. 2020. “RedstoneFlux API 1.12 (CoFH/RedstoneFlux-1.12-Legacy).” Commit 24719b6. CoFH/RedstoneFlux-1.12-Legacy @ 24719b6 · licence, all uses
- Team CoFH. 2018. “Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9.” Commit 5ae9f68. CoFH/ThermalDynamics-1.12-Legacy @ 5ae9f68 · licence, all uses
- Team CoFH. 2018. “CoFH Core 1.12.2 (CoFH/CoFHCore-1.12-Legacy), tag r4.3.9.” Commit ccf809a. CoFH/CoFHCore-1.12-Legacy @ ccf809a · licence, all uses
Primary
- Team CoFH. 2017. “About Team CoFH: the team's history and members.” Team CoFH. original · archived · licence, all uses
- Lemming, King. 2012. “Thermal Expansion 1.0.2: King Lemming's Minecraft Forum release post.” Minecraft Forum. original · archived · licence, all uses
- Lemming, King. 2013. “Thermal Expansion 2.1.6: King Lemming's Minecraft Forum post.” Minecraft Forum. original · archived · licence, all uses
- Team CoFH. 2014. “Thermal Expansion changelog (Team CoFH wiki, January 2014).” Team CoFH. original · archived · licence, all uses
- Team CoFH. 2014. “Thermal Expansion (Team CoFH site, December 2014).” Team CoFH. original · archived · licence, all uses
- Team CoFH. 2016. “Supporting Mods (Team CoFH documentation).” Team CoFH. original · archived · licence, all uses
- Team CoFH. 2016. “Redstone Flux (Team CoFH documentation).” Team CoFH. original · archived · licence, all uses
- Team CoFH. 2024. “CoFH/Version: Thermal Expansion and Thermal Dynamics changelogs.” Commit 1bdb613. CoFH/Version @ 1bdb613 · licence, all uses
- Team CoFH. 2014. “Downloads (Team CoFH site, December 2014).” Team CoFH. original · archived · licence, all uses
- Team CoFH. 2019. “Thermal Expansion on CurseForge (project description and statistics, 2019).” CurseForge (Twitch). original · archived · licence, all uses
- raiguard. 2024. “Friday Facts #416 - Fluids 2.0.” Factorio blog. Wube Software. original · archived · licence, all uses
- raiguard. 2024. “Friday Facts #430 - Drowning in Fluids.” Factorio blog. Wube Software. original · archived · licence, all uses