Claim set · data/claims/thermal-expansion.yaml

Thermal Expansion and Thermal Dynamics (1.12.2 source releases)

Game: Thermal Expansion.

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. thermal-expansion/fluid-grid-forming

CodeThermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/multiblock/MultiBlockFormer.java#L14-L26theGrid = theMultiBlock.createGrid(); … while (!blocksToCheck.isEmpty()) { checkMultiBlock(blocksToCheck.remove());Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/multiblock/MultiBlockFormer.java#L44-L61if (aBlock.getGrid() == null && theGrid.canAddBlock(aBlock)) { … } else if (theGrid.canAddBlock(aBlock) && aBlock.getGrid() != null … if (theGrid.size() >= aBlock.getGrid().size()) { theGrid.mergeGrids(aBlock.getGrid()); … aBlock.getGrid().mergeGrids(theGrid); … currentMultiBlock.onConnectionRejected(i);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L204-L225public void mergeGrids(MultiBlockGrid<DuctUnitFluid> theGrid) { … myTank.fill(gridFluid.getFluid(), true); … return aBlock instanceof DuctUnitFluid && … return super.canGridsMerge(grid) &&Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java#L419-L425if (retrace != null && retrace.sideHit != null) { EnumFacing sideHit = retrace.sideHit.getOpposite(); DuctUnitFluid fluids = IDuctHolder.getTokenFromTile(world().getTileEntity(pos().offset(sideHit)), DuctToken.FLUID); if (fluids != null) { fluidStack = fluids.getConnectionFluid(); } }Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java#L427-L442if (fluidStack == null) { fluidStack = connectionFluid; } else if (connectionFluid != null && !fluidStack.isFluidEqual(connectionFluid)) { parent.setConnectionType(facing.ordinal(), ConnectionType.BLOCKED);Checked by claude, 2026-10-26 — read MultiBlockFormer.formGrid and checkMultiBlock (a duct joins when it has no grid and canAddBlock, grids merge when canAddBlock and canGridsMerge, the larger by size() absorbing the smaller; else onConnectionRejected on both), GridFluid.mergeGrids (fills its tank with the other grid's fluid), canAddBlock and canGridsMerge (isFluidEqualOrNull), and DuctUnitFluid.onPlaced: the local fluidStack, only a reference for the comparison, is the aimed-at duct's connection fluid, else the first neighbour's; a neighbour with another fluid has both sides set BLOCKED

Cited by: Fluid network, Power network, Thermal Expansion

In 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. thermal-expansion/fluid-grid-tank

Value
{
 "defaultThroughput": 120,
 "maxPerDuct": 3000,
 "minThroughput": 80,
 "maxThroughput": 600
}
CodeThermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L27public final FluidTankGrid myTank = new FluidTankGrid(1000, this);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L80-L85public void balanceGrid() { … myTank.setCapacity(size() * myTank.fluidPerDuct);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L135-L143public float getThroughPutModifier() { return 1.0F; … public int getMaxFluidPerDuct() { return 3000;Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/FluidTankGrid.java#L10-L17public int fluidThroughput = 120; public int fluidPerDuct = 3000; … fluidFlowrate.put("steam", 600);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/FluidTankGrid.java#L53-L68viscosity = Math.max(fluid.getFluid().getViscosity(), 100); fluidThroughput = MathHelper.clamp(120000 / viscosity, 80, 600); … fluidThroughput = fluidFlowrate.get(fluid.getFluid().getName()); … fluidThroughput *= myMaster.getThroughPutModifier(); fluidPerDuct = Math.min(25 * fluidThroughput, myMaster.getMaxFluidPerDuct());Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java#L489-L494public int fill(FluidStack resource, boolean doFill) { if (resource != null && isOpen(from) && matchesFilter(from, resource)) { return grid.myTank.fill(resource, doFill);CoFH Core 1.12.2 (CoFH/CoFHCore-1.12-Legacy), tag r4.3.9 · src/main/java/cofh/core/fluid/FluidTankCore.java#L186-L201if (!fluid.isFluidEqual(resource)) { return 0; … int filled = capacity - fluid.amount; if (resource.amount < filled) { fluid.amount += resource.amount; … fluid.amount = capacity;Checked by claude, 2026-10-26 — read GridFluid.myTank, balanceGrid (capacity = size() * fluidPerDuct), getThroughPutModifier and getMaxFluidPerDuct (1.0 and 3000 in GridFluid), FluidTankGrid's defaults and setFluidData, the duct's fluid handler fill (straight to grid.myTank.fill), and FluidTankCore.fill, which takes min(resource, capacity - amount)

Super-laminar ducts (GridFluidSuper) override the throughput modifier and per-duct maximum and are excluded. "Open" means the side is an input or output and its connection allows transfer, with any filter on it passing the fluid (DuctUnitFluid.isOpen, matchesFilter).

Cited by: Fluid network, Thermal Expansion

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. thermal-expansion/fluid-grid-output

CodeThermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/util/WorldGridList.java#L59-L66for (MultiBlockGrid grid : tickingGrids) { grid.tickGrid();Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L162-L176if (myTank.getFluid() != null && nodeSet.size() > 0) { toDistribute = Math.min(myTank.getFluidAmount() / size(), getFluidThroughput()); if (toDistribute <= 0) { toDistribute = Math.min(myTank.getFluidAmount() % size(), getFluidThroughput()); … for (DuctUnitFluid m : nodeSet) { if (!m.tickPass(0) || m.getGrid() == null) {Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L227-L243// if over 3/4 full, full throughput if (myTank.getFluid().amount >= capacity * 3 / 4) { return myTank.fluidThroughput; … // if under 1/4 full, half throughput if (myTank.getFluid().amount <= capacity / 4) { return myTank.fluidThroughput >> 1; … // otherwise scale between half and full return (myTank.fluidThroughput >> 1) +Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java#L89-L110if (pass == 0) { int available = grid.toDistribute; … for (int i = this.internalSideCounter; i < 6 && sent < available; i++) { sent += transfer(i, available - sent, false, grid.myTank.getFluid(), true); … for (int i = 0; i < this.internalSideCounter && sent < available; i++) {Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java#L164-L177tempFluid.amount = available; int amountSent = capability.fill(tempFluid, false); … tempFluid = grid.myTank.drain(amountSent, true); … return capability.fill(tempFluid, true);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/tiles/DuctUnit.java#L157-L162C c = cacheTile(tile, side); … if (isNode(c)) { nodeMask |= (1 << side);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/tiles/DuctUnit.java#L269-L272public boolean isNode() { return nodeMask != 0; }Checked by claude, 2026-10-26 — read WorldGridList.tickEnd (tickGrid on every ticking grid), GridFluid.tickGrid (toDistribute = min(amount / size(), getFluidThroughput()), else min(amount % size(), …); pass 0 over nodeSet), getFluidThroughput's three branches, DuctUnitFluid.tickPass pass 0 (sides from internalSideCounter, until available is sent) and transfer (simulates the neighbour's fill, drains that much from grid.myTank, fills)

The ducts that hand out fluid are the grid's node set: ducts with a fluid-handling block, or a servo, on a side (DuctUnit.loadSignificantCache, isNode). Servos and other attachments tick in the same pass and pull fluid in; their code isn't read here.

Cited by: Fluid network, Thermal Expansion

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. thermal-expansion/fluid-grid-split

CodeThermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/multiblock/MultiBlockGrid.java#L143-L171destroyNode(oldBlock); … if (oldBlock.isSideConnected(i)) { … if (s <= 1) { balanceGrid(); … worldGrid.gridsToRecreate.add(this);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/util/WorldGridList.java#L44-L58for (MultiBlockGrid<?> grid : gridsToRecreate) { for (IGridTile multiBlock : grid.idleSet) { … grid.destroyNode(multiBlock); … for (IGridTile multiBlock : grid.nodeSet) {Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L145-L152if (hasValidFluid()) { ((DuctUnitFluid) node).setFluidForGrid(getNodeShare((DuctUnitFluid) node));Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L265-L275if (!duct.canStoreFluid()) { … int size = getStorableNumberDucts(); … return size == 1 ? myTank.getFluidAmount() : isFirstMultiblock(duct)Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L46-L59if (aMultiBlock.getFluidForGrid() != null) { … myTank.setFluid(aMultiBlock.getFluidForGrid()); … myTank.fill(aMultiBlock.getFluidForGrid(), true);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java#L374-L377mySavedFluid = grid.getNodeShare(this); … mySavedFluid.writeToNBT(nbt);Checked by claude, 2026-10-26 — read MultiBlockGrid.removeBlock (destroyNode on the removed block; s <= 1 connected sides rebalances, else gridsToRecreate), WorldGridList.tickEnd (destroyNode on every idle and node duct of a grid to recreate), GridFluid.destroyNode (setFluidForGrid(getNodeShare)), getNodeAmount, addBlock (fills the tank with getFluidForGrid), and writeToNBT (saves getNodeShare)

The removed duct also takes its share (destroyNode runs on it first); where that share goes once the duct is broken isn't read here.

Cited by: Fluid network, Thermal Expansion

Factorio 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. thermal-expansion/segments-compared

Dev blogFriday Facts #416 - Fluids 2.0 · The new algorithmPipes, 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.Friday Facts #416 - Fluids 2.0 · The new algorithmIn other words, if a segment is half full, then the pulling rate is half of the maximum.Friday Facts #416 - Fluids 2.0 · The new algorithmThere is no longer a realistic fluid "flow" through pipes; fluid pushed to a segment will be immediately available at any point along a segment.Friday Facts #430 - Drowning in Fluids · Algorithm tweaksOutput rate used to be unlimited, but it is now inversely proportional to the fullness ratio of the sink.Friday Facts #430 - Drowning in Fluids · Algorithm tweaksSomething 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).Friday Facts #430 - Drowning in Fluids · Algorithm tweaksThis limit is multiplied with the fullness ratios of the source and sink to produce the actual flow valueThermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L80-L85public void balanceGrid() { … myTank.setCapacity(size() * myTank.fluidPerDuct);Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L215-L219return aBlock instanceof DuctUnitFluid &&Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L162-L167toDistribute = Math.min(myTank.getFluidAmount() / size(), getFluidThroughput()); if (toDistribute <= 0) { toDistribute = Math.min(myTank.getFluidAmount() % size(), getFluidThroughput());Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9 · src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L227-L243// if over 3/4 full, full throughput if (myTank.getFluid().amount >= capacity * 3 / 4) { return myTank.fluidThroughput; … // if under 1/4 full, half throughput if (myTank.getFluid().amount <= capacity / 4) { return myTank.fluidThroughput >> 1; … // otherwise scale between half and full return (myTank.fluidThroughput >> 1) +CoFH Core 1.12.2 (CoFH/CoFHCore-1.12-Legacy), tag r4.3.9 · src/main/java/cofh/core/fluid/FluidTankCore.java#L193-L201int filled = capacity - fluid.amount; … fluid.amount = capacity;Checked by claude, 2026-10-26 — found the four quotes in the pinned FFF #416 and #430 captures (the #430 quote as factorio-production/fluid-segments has it), and read the cited GridFluid and FluidTankCore lines with fluid-grid-tank and fluid-grid-output

Factorio's side is the posts' (factorio-production/fluid-segments), not its 2.1 code. At half full, Thermal Dynamics' getFluidThroughput gives half + half × (amount − capacity/4) / (capacity/2) = three-quarters of the full rate.

Cited by: Fluid network, Thermal Expansion

In 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. thermal-expansion/machine-processing

CodeThermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java#L188-L223processTick(); if (canFinish()) { processFinish(); … energyStorage.modifyEnergyStored(-processRem); … } else if (energyStorage.getEnergyStored() <= 0) { processOff(); … if (timeCheckEighth() && canStart()) {Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java#L270-L273protected void processOff() { processRem = 0; isActive = false;Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileFurnace.java#L164-L168processMax = augmentPyrolysis ? … : FurnaceManager.getRecipe(inventory[0]).getEnergy() * energyMod / ENERGY_BASE; processRem = processMax;Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java#L281-L291int energy = calcEnergy(); energyStorage.modifyEnergyStored(-energy); processRem -= energy;Checked by claude, 2026-10-26 — read TileMachineBase.update (active: processTick, then on canFinish processFinish and modifyEnergyStored(-processRem), with processRem <= 0; stops at 0 stored energy; idle: canStart on timeCheckEighth), processOff (processRem = 0), TileFurnace.processStart (processRem = processMax, the recipe's energy) and processTick (energy = calcEnergy(), taken from the store and from processRem)

Each machine sets the recipe's cost (processMax, processRem) in its own processStart, from the recipe's energy (TileFurnace L166, for one). How often timeCheck and timeCheckEighth fire is in CoFH Core's tile base, not read here. Most machines' own update() adds a fluid transfer before the shared loop; TileCharger and TileTransposer handle processRem themselves in some modes.

Cited by: Power network, Thermal Expansion

In 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. thermal-expansion/machine-power

Value
{
 "energyRamp": 900,
 "levelScalingPercent": [
  100,
  150,
  200,
  250,
  300
 ]
}
CodeThermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java#L39-L42public static final int[] POWER_SCALING = { 100, 150, 200, 250, 300 };Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java#L121energyStorage = new EnergyStorage(energyConfig.maxEnergy, energyConfig.maxPower * 4);Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java#L226-L240return ENERGY_CONFIGS[getType()].maxPower * POWER_SCALING[ … if (energyStorage.getEnergyStored() >= energyConfig.maxPowerLevel) { return energyConfig.maxPower; … if (energyStorage.getEnergyStored() < energyConfig.minPowerLevel) { return Math.min(energyConfig.minPower, energyStorage.getEnergyStored()); … return energyStorage.getEnergyStored() / energyConfig.energyRamp;Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/TileTEBase.java#L201-L226maxPower = basePower; minPower = basePower / 10; maxEnergy = basePower * 1000; maxPowerLevel = 9 * maxEnergy / 10; minPowerLevel = maxEnergy / 10; energyRamp = maxPowerLevel / basePower; … if (!smallStorage) { … minPower = basePower; maxEnergy = basePower * 10;Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java#L421-L425if (TEProps.MACHINE_POWER.equals(id)) { energyConfig.setDefaultParams(energyConfig.maxPower + getBasePower(this.level), smallStorage); energyMod += 15; return true; }Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileFurnace.java#L38public static int basePower = 20;Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileCharger.java#L44public static int basePower = 50;Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileCrucible.java#L43public static int basePower = 50;Checked by claude, 2026-10-26 — read the constants (POWER_SCALING), the constructor (EnergyStorage(maxEnergy, maxPower * 4)), getBasePower and calcEnergy's three branches, EnergyConfig.setDefaultParams for both settings (maxEnergy = 1000 × base, minPowerLevel 10%, maxPowerLevel 90%, energyRamp = maxPowerLevel / base = 900), and every machine's basePower: 20 in all but TileCharger and TileCrucible (50)

Base power and the level scaling are config options (BasePower, CustomScaling); these are the defaults. getBasePower clamps the level to Thermal Foundation's TFProps.LEVEL_MIN and LEVEL_MAX (not read here) and indexes the five-entry POWER_SCALING with it.

Cited by: Power network, Thermal Expansion

Thermal 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. thermal-expansion/rf-replaces-mj

PublisherAbout Team CoFH: the team's history and members · HistoryTE 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.Thermal Expansion (Team CoFH site, December 2014) · page textSoon, 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.Thermal Expansion changelog (Team CoFH wiki, January 2014) · 3.0.0.0: 12/25/2013MJ is no longer supported by Thermal Expansion. The new energy system is Redstone Flux. (RF). Ratio is 10:1 RF:MJ.Thermal Expansion changelog (Team CoFH wiki, January 2014) · 3.0.0.0, BLOCKSMachines: -Now only accept RF.Thermal Expansion changelog (Team CoFH wiki, January 2014) · 3.0.0.0, BLOCKSDynamos: -These replace engines. They generate Redstone flux.Thermal Expansion changelog (Team CoFH wiki, January 2014) · 3.0.0.0, ConduitThere are now 3 levels of Redstone Energy Conduit. They accept RF but will convert to MJ on output if necessary.Checked by claude, 2026-10-26 — found each quote in the pinned captures of Team CoFH's About page, Thermal Expansion page (December 2014) and changelog (January 2014)

The design choices aren't named on the page, and no BuildCraft developer's account of the split was found. "Engines" in the changelog are BuildCraft's MJ engines, which earlier Thermal Expansion versions needed for power (cofh-about).

Cited by: Power network, Thermal Expansion

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. thermal-expansion/rf-api

CodeRedstoneFlux API 1.12 (CoFH/RedstoneFlux-1.12-Legacy) · src/main/java/cofh/redstoneflux/impl/EnergyStorage.java#L15-L20public class EnergyStorage implements IEnergyStorage { protected int energy; protected int capacity; protected int maxReceive; protected int maxExtract;RedstoneFlux API 1.12 (CoFH/RedstoneFlux-1.12-Legacy) · src/main/java/cofh/redstoneflux/impl/EnergyStorage.java#L114-L123public void modifyEnergyStored(int energy) { this.energy += energy; if (this.energy > capacity) { this.energy = capacity; } else if (this.energy < 0) { this.energy = 0; } }Thermal Expansion 1.12.2 (CoFH/ThermalExpansion-1.12-Legacy), tag r5.3.9 · src/main/java/cofh/thermalexpansion/block/machine/TileMachineBase.java#L13import cofh.redstoneflux.impl.EnergyStorage;Checked by claude, 2026-10-26 — read the fields and constructors of impl/EnergyStorage (King Lemming's @author tag) and modifyEnergyStored's clamp; TileMachineBase imports it and builds its energyStorage from it (L121)

Cited by: Thermal Expansion