Page 1 of 1

Mods that enable Productivity in all recipes increase rocket capacity

Posted: Sun Nov 10, 2024 3:36 pm
by Urganot
Hi

When using a mod that enables productivity modules for all recipes like https://mods.factorio.com/mod/productivity_fix The rocket capacity for some items is being increased.

Here you can see a long handed inserter
11-10-2024, 16-32-52.png
11-10-2024, 16-32-52.png (178.42 KiB) Viewed 982 times
In case the picture doesnt work https://imgur.com/a/Fb0pzWz

I believe this is a factorio issue because it happens in multiple mods that are doing the productivity change. Also if you look into the code of "productivity fix" there is nothing that indicates a change to the capacity

Re: Mods that enable Productivity in all recipes increase rocket capacity

Posted: Sun Nov 10, 2024 4:45 pm
by Rseding91
Thanks for the report. Mods can adjust the rocket capacity by adjusting item weight as wanted.

Re: Mods that enable Productivity in all recipes increase rocket capacity

Posted: Sun Nov 10, 2024 4:47 pm
by Stargateur
why allow productivity module would increase rocket stack ???!!!

Re: Mods that enable Productivity in all recipes increase rocket capacity

Posted: Sun Nov 10, 2024 5:08 pm
by Urganot
Rseding91 wrote: Sun Nov 10, 2024 4:45 pm Thanks for the report. Mods can adjust the rocket capacity by adjusting item weight as wanted.
Not sure I understand you. Is this a bug?

I am not a pro with factorio mods but when I look into "production fix"s code, there is no mention of a weight.
The interaction between weight and productivity cant be intended, can it?

Re: Mods that enable Productivity in all recipes increase rocket capacity

Posted: Mon Nov 11, 2024 10:01 am
by Urganot
Please take another look at this

Re: Mods that enable Productivity in all recipes increase rocket capacity

Posted: Sat Nov 16, 2024 12:14 am
by mcmodderHD
Rseding91 wrote: Sun Nov 10, 2024 4:45 pm Thanks for the report. Mods can adjust the rocket capacity by adjusting item weight as wanted.
I encounter the same bug with my Mod/Fork of Unlimited Productivity. Whenever I enable productivity for recipes, the rocket capacity for many items changes drastically. I did NOT change any weights of any items. Just enabled productivity for the recipes.

I also already asked twice in the Factorio Discord for the formula of automatic weight calculation and literally noone could tell me exactly how it works.
Would be nice to know.

Another consequence of this is, that the rocket can't be filled, as the rocket capacity exeeds the maximum slots of a rocket, making automated logistics impossible.

Re: Mods that enable Productivity in all recipes increase rocket capacity

Posted: Thu Nov 21, 2024 1:56 am
by warbob5000
mcmodderHD wrote: Sat Nov 16, 2024 12:14 am I also already asked twice in the Factorio Discord for the formula of automatic weight calculation and literally noone could tell me exactly how it works.
Would be nice to know.

Another consequence of this is, that the rocket can't be filled, as the rocket capacity exeeds the maximum slots of a rocket, making automated logistics impossible.
The formula seems to be:

(lift_weight = data.raw["utility-constants"]["default"].rocket_lift_weight = 1000*kg)

weight = sum of ingredient weight * coefficient (default 0.5)
capacity = lift_weight / weight
if capacity > stack size: capacity -= (capacity % stack size) -- i.e. round down to nearest stack size if above one stack.
weight = lift_weight / capacity

Inserters are (1 + 1 + 0.5) * 0.5 = 1.25kg, or 800 rocket capacity
Long Handed Inserters are then (1 + 1 + 1.25) * 0.5 = 1.625kg, or 615.4 rocket capacity, which is then rounded to 600.
If the rocket capacity is < stack size, it doesn't get rounded down (one mod I use has an item with 68 capacity and 100 stack size)
For items with no ingredients, or weightless ones (I assume it's recursive?), there's a default_item_weight of 0.1kg

A specified weight overrides the above. Bulk Inserters weigh 20kg and thus always have a capacity of 50. Regular inserters don't have any weight.

As to why enabling productivity affects things, I suspect the formula is actually

weight = sum of ingredient weight * coefficient (default 0.5)
capacity = lift_weight / weight
if capacity > stack size: capacity -= (capacity % stack size) -- i.e. round down to nearest stack size if above one stack.
if not allow_productivity: if capacity > stack size, capacity = stack size -- i.e. non-productivity items are limited to a single stack
weight = lift_weight / capacity


(Which is why non-productivity inserters weigh 20kg instead of 1.25kg)

The actual maximum productivity does not matter.

The weight calculation makes sense, but the productivity affecting it isn't particularly intuitive. Perhaps it's a quick way to assume something is an intermediate?

Some items have ingredient_to_weight_coefficient, e.g. Superconductors.

Electronic Circuits can fit 2040 into one rocket, putting them roughly 0.49kg. With normal stack size, that's rounded to 2000 in one rocket. Electronic Circuits have an ingredient_to_weight_coefficient of 0.28.
An Iron Plate is 1kg, as 1000 fits in a rocket.
Copper Cables are 0.25kg, hardcoded. They also have an ingredient_to_weight_coefficient of 0.25. (Why?)
Thus, an Electronic circuit should weigh 0.28 * (1 + 3*0.25) = 0.49kg.

Superconductors have an ingredient_to_weight_coefficient of 0.6. Their rocket capacity is 1111 (rounded down to 1000) and thus must weigh approximately 0.9kg
1 Copper Plate (1kg) + 1 Plastic Bar (0.5kg) + 1 Holmium plate (1kg) = 2.5kg total input weight for 2 output. So 0.6 * 2.5/2 = 0.75kg weight, or a rocket capacity of... 1333? If we assume light oil weighs the default_item_weight, or 0.1kg per unit. Then (2.5+5*0.1) = 3kg total, 0.6 * 3/2 = 0.9kg per superconductor.

Most results from fluids have hardcoded weights. Batteries don't. 1 Iron Plate (1kg) + 1 Copper Plate (1kg) + 20 Sulfuric Acid (20 * 0.1kg) = 4kg, default coefficient is 0.5 so battery weight is 2kg. 500 total in a rocket, rounded down to 400.
So I'm pretty confident fluids are 0.1kg per unit.

Thus, as a workaround you could set the weight for every item you modify. For instance,

Code: Select all

function get_prototype_from_name(item_name)
  local item_prototype
  for prototype_type in pairs(defines.prototypes.item) do
    if data.raw[prototype_type] then
      item_prototype = data.raw[prototype_type][item_name]
    end
    if item_prototype then break end
  end
  return item_prototype
end

function find_recipe(item_name)
  local recipe_item_name, found_recipe, already_found
  for recipe_name, recipe in pairs(data.raw.recipe) do
    if recipe.subgroup == "fill-barrel" or recipe.subgroup == "empty-barrel" or recipe.category == "recycling" then
      goto continue -- Way too many, and doesn't make sense to check
    end
    recipe_item_name = recipe.results and #recipe.results==1 and recipe.results[1].name
    if recipe_item_name == item_name then
      if already_found then
        if found_recipe.category == "metallurgy" and recipe.category ~= "metallurgy" then
          -- prefer non-casting if possible
          found_recipe = recipe
        elseif recipe.category == "metallurgy" then --skip
        else
          log("already found "..item_name.." in recipe "..recipe.name.." and "..found_recipe.name)
        end
        -- TODO check every recipe weights, choose one (which?)
      else
        found_recipe = recipe
        already_found = true
      end
    end
      ::continue::
  end
  return found_recipe
end

--[[
Weight formula:
weight = sum of ingredients * ingredient_to_weight_coefficient (default 0.5)

rocket capacity = kg*1000 / weight -- 1000kg = rocket_lift_weight
if rocket capacity > stack size then
  rocket capacity = rocket capacity - (rocket capacity % stack size) -- round down to the nearest stack size (as long as its greater than 0)
if allow_productivity and rocket capacity > stack size then
  rrocket capacity = stack size
]]

function get_weight(recipe, item_prototype)
  if item_prototype.weight then
    return item_prototype.weight
  end
  if recipe == nil then -- no way to link an item to a recipe, only a recipe to an item. Horray
    recipe = find_recipe(item_prototype.name)
    if recipe == nil then -- item has no weight and no recipe, assuming default.
      return data.raw["utility-constants"]["default"].default_item_weight -- 100 = 0.1kg
    end
  end
  local calc_weight = 0
  for _, ingredient in pairs(recipe.ingredients) do
    if ingredient.type == "fluid" then -- fluids don't have weight (I think) and so weight is default*amount
      calc_weight = calc_weight + (ingredient.amount * data.raw["utility-constants"]["default"].default_item_weight) -- 100 = 0.1kg
    elseif ingredient.type == "item" then -- ingredients don't have linked recipes, so we'll have to find one
      calc_weight = calc_weight + (get_weight(nil, get_prototype_from_name(ingredient.name)) * ingredient.amount)
    end
  end
  if calc_weight == 0 then calc_weight = data.raw["utility-constants"]["default"].default_item_weight end -- empty recipe, default weight
  if item_prototype.ingredient_to_weight_coefficient then
    calc_weight = calc_weight * item_prototype.ingredient_to_weight_coefficient
  else
    calc_weight = calc_weight * 0.5 -- default ingredient_to_weight_coefficient
  end
  return calc_weight
end

-- This is adapted from "Total Productivity for belts, landfill & everything", comments are my own.
function enable_total_productivity()

  local item_name, success, result
  for recipe_name, recipe in pairs(data.raw.recipe) do
    item_name = recipe.results and #recipe.results==1 and recipe.results[1].name

    success, call_result = pcall(should_enable_prod, item_name) -- checking settings
    if not success then
      error(serpent.line({
        recipe = recipe,
        error = call_result,
      }))
    end

    if call_result and not recipe.allow_productivity then
      recipe.allow_productivity = true
      local item_prototype = get_prototype_from_name(item_name)
	    if item_prototype and not item_prototype.weight then -- if no weight, allowing productivity might change rocket capacity
        local item_weight = get_weight(recipe, item_prototype)
        local rocket_capacity = data.raw["utility-constants"]["default"].rocket_lift_weight / item_weight
        -- non productivity default
        if rocket_capacity > item_prototype.stack_size then
          item_prototype.weight = data.raw["utility-constants"]["default"].rocket_lift_weight / item_prototype.stack_size
        end
        -- productivity default
        --[[
        if rocket_capacity > item_prototype.stack_size then
          if rocket_capacity % item_prototype.stack_size ~= 0 then
            rocket_capacity = rocket_capacity - (rocket_capacity % item_prototype.stack_size)
            item_prototype.weight = data.raw["utility-constants"]["default"].rocket_lift_weight / rocket_capacity
          end
        end
        ]]
        -- Custom check to ensure it can fit in the rocket
        if rocket_capacity / item_prototype.stack_size > 20 then
          item_prototype.weight = data.raw["utility-constants"]["default"].rocket_lift_weight / (20*item_prototype.stack_size) -- limit capacity to 20 * stack size
        end
        
      end
    end
  end
end
Feel free to copy my code (the first bit of "enable_total_productivity" function is taken from the "Total Productivity for belts, landfill & everything" mod, so I don't own that bit)

Edit: Note that if you're cycling through every recipe, you might want to skip weight calculations for e.g. recycling or casting (or at least do the casting ones after the regular recipes).
For instance, if the regular recipe already accepts productivity but the item does not have a weight predefined, then by processing the recycling recipe you would end up recalculating the weight - and in my example code set the capacity to rocket size.
You could perhaps avoid this by first finding a list of all items which already have a productivity recipe somewhere and then not setting their weights. Although in that case if an earlier mod sets the allow_productivity flag it would skip them, which isn't ideal.

The "rocket capacity can exceed 20 stack size aka rocket inventory limit" I would definitely classify as a bug (this one has been raised in a different bug report, viewtopic.php?f=7&t=120671, so should already be on the devs' radar)

Edit2:
I do not agree with the "not-a-bug" designation. allow_productivity being tied into weight/capacity calculations is unintuitive, and the weight calculation being obscured makes it difficult for modders to compensate. If it is indeed intended behaviour, I think it should be better documented.