From fb38093e6cbc3b243edfb3000af0c8662bf94cd7 Mon Sep 17 00:00:00 2001 From: Andrew Simonson Date: Sat, 15 Aug 2026 20:35:48 -0500 Subject: [PATCH] fix cargo_capacity direction: storage competes with cargo, doesn't pad it cargo_capacity was based on floor_total (platform+actuator+storage), so more battery/fuel made cargo capacity go UP -- backwards. Real deadweight tonnage is a fixed allowance sized off the vessel's own empty (lightship) mass -- hull + machinery, not fuel -- and fuel and cargo then SHARE that one allowance: more fuel bunkered means less room left for cargo. Cargo capacity is now (platform+actuator)*ratio - storage_mass, floored at 0, so reducing battery/fuel now correctly frees up cargo room instead of shrinking it. Confirmed on combo #876: at the battery's declared 9kg floor, cargo is 6.3kg; increasing to 15kg drops it to 0.3kg, to 30kg+ drops it to 0 -- storage size and cargo capacity now trade off in the right direction. Re-ran all five domains: urban_commuting's insufficient_structure count went from 1 to 6 passing combos, since cargo_capacity's changed incentives shifted where the optimizer lands relative to the structural cap -- checked each one, all are 0.007-0.033% over the cap, the same grid-search rounding-noise magnitude already established as acceptable, just now triggered on a few more combos. Co-Authored-By: Claude Sonnet 5 --- src/physcom/engine/pipeline.py | 41 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/physcom/engine/pipeline.py b/src/physcom/engine/pipeline.py index bd3ff9f..bf64168 100644 --- a/src/physcom/engine/pipeline.py +++ b/src/physcom/engine/pipeline.py @@ -932,12 +932,12 @@ class Pipeline: platform's representative mass (ctx.p_rep) -- pass an explicit value to explore a specific weight class instead (see evaluate_allocation). Cargo capacity is derived from THIS build's - actual assembled mass (floor_total below), not a separate - declared-floor constant -- a deadweight/lightship-style ratio of - "how much this vehicle could additionally carry, proportional to - its own (empty of cargo) mass" only makes sense against the mass - it's actually built to, so it responds to the same optimizer/ - explore-slider choices every other metric here does.""" + actual platform+actuator mass (not a separate declared-floor + constant), with storage_mass subtracted out of that allowance -- + see the deadweight/lightship comment at its computation below for + why fuel/battery competes with cargo instead of padding it. It + responds to the same optimizer/explore-slider choices every other + metric here does.""" p_mass = ctx.p_rep if platform_mass is None else platform_mass out: dict[str, float] = {} floor_total = p_mass + actuator_mass + storage_mass @@ -946,14 +946,27 @@ class Pipeline: if "power_density" in bounds_by_name: out["power_density"] = power_density_value - # Deadweight/lightship-style cargo capacity, proportional to this - # build's own actual mass -- two conventions coexist because heavy - # freight/maritime vehicles genuinely carry a much larger multiple - # of their own mass in cargo than light personal/delivery vehicles - # do (see CARGO_KG_PER_STRUCTURAL_KG's module comment); which one - # a domain scores is just which metric_name it declares. - cargo_capacity_2_5x = floor_total * CARGO_KG_PER_STRUCTURAL_KG - cargo_capacity_0_3x = floor_total * 0.3 + # Deadweight/lightship cargo capacity. Real deadweight tonnage is a + # FIXED allowance sized off the vessel's own empty (lightship) mass + # -- hull + machinery, NOT fuel or cargo -- and fuel and cargo then + # SHARE that one allowance: a ship that bunkers more fuel has that + # much less room left for cargo, and vice versa. platform+actuator + # is the lightship analog here (the vehicle's own hardware); + # storage_mass is the fuel/battery competing with cargo for the + # same pool, not part of the base the pool is sized from -- get + # that backwards (basing the pool on platform+actuator+storage, + # as an earlier version of this did) and more battery looks like it + # BUYS more cargo room instead of using it up. Two ratio + # conventions coexist because heavy freight/maritime vehicles + # genuinely carry a much larger multiple of their own mass in + # cargo than light personal/delivery vehicles do (see + # CARGO_KG_PER_STRUCTURAL_KG's module comment); which one a domain + # scores is just which metric_name it declares. Floored at 0: a + # storage mass bigger than the whole allowance leaves no cargo + # room, not negative room. + lightship_mass = p_mass + actuator_mass + cargo_capacity_2_5x = max(0.0, lightship_mass * CARGO_KG_PER_STRUCTURAL_KG - storage_mass) + cargo_capacity_0_3x = max(0.0, lightship_mass * 0.3 - storage_mass) if "cargo_capacity" in bounds_by_name: out["cargo_capacity"] = cargo_capacity_2_5x if "cargo_capacity_kg" in bounds_by_name: