remove food from ambient energy forms, size solar sails to actual power needs
Biological Feed (food) was treated as an "ambient" energy source, so range_fuel always reported the domain's ceiling regardless of how much food was carried -- stopping to eat is a resupply, the same category as refuelling a tank, not a genuinely external/inexhaustible source like sun or wind. Removed "biological" from AMBIENT_ENERGY_FORMS; food now uses the normal storage-mass-limited range formula like any fuel. Solar Sail was still special-cased to a fixed footprint-derived mass (100m^2 -> 5kg) regardless of what a domain's power/velocity target actually needed -- the same "fixed reference instead of a requirement floor" bug biological actuators had before last session's fix. Folded it into the same general requirement-floor + joint-optimizer path: declared footprint becomes a FLOOR (SAIL_AREAL_DENSITY_KG_PER_M2), not a fixed value, so sail size scales with what's actually needed -- "enough panels to supply enough power for actuator impulse" is now enforced the same way structural/mass-ceiling requirements already are, instead of relying on a product-spec constant that happened to work or not. No actuator type is special-cased for mass sizing anymore. Confirmed the fix surfaces an honest result rather than hiding one: a solar sail's declared 0.01 W/kg specific power can never reach interplanetary_travel's 10 W/kg power_density floor at any sail size (the ratio is capped by the sail's own power_density regardless of scale), so it correctly still scores 0 there -- a real technology/domain mismatch, not a sizing bug. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -106,6 +106,16 @@ ENERGY_FORM_RELIABILITY: dict[str, float] = {
|
||||
# validated it against the platform's ceiling), so it's used as the point
|
||||
# estimate rather than an invented one.
|
||||
|
||||
# Radiation-pressure actuators (solar sails) don't declare a "mass" at
|
||||
# all -- thrust scales with sail area, not carried mass -- so their
|
||||
# effective mass is derived from declared footprint via a thin deployable
|
||||
# sail film's areal density. Used the same way as BIOLOGICAL_OPERATOR_MASS_KG
|
||||
# below: converts the entity's declared footprint FLOOR into a mass floor,
|
||||
# not a fixed value -- above it, effective mass is a free, budget-competing
|
||||
# variable like any other actuator (bigger sail = more collected power),
|
||||
# sized by the same joint optimizer, not a one-off product spec.
|
||||
SAIL_AREAL_DENSITY_KG_PER_M2: float = 0.05
|
||||
|
||||
# Human/animal actuators declare mass_min=0 (there's no minimum purchase
|
||||
# quantity for a rider the way there is for an engine), but treated as a
|
||||
# literal floor that lets the optimizer size a payload down toward 0kg of
|
||||
@@ -187,13 +197,16 @@ def _solve_two_requirement_masses(
|
||||
return a_min, s_min
|
||||
return max(a, a_min), max(s, s_min)
|
||||
|
||||
# Ambient energy forms (sun, wind, gravity, food) aren't a depletable
|
||||
# onboard store the way a fuel tank is -- "distance before running out"
|
||||
# doesn't apply (a sailboat doesn't run out of wind). Rather than
|
||||
# degenerate to 0 (mass_min=0, energy_density often undeclared entirely),
|
||||
# range_fuel reports the domain's own declared ceiling for these: full
|
||||
# marks is the physically honest answer, not an error.
|
||||
AMBIENT_ENERGY_FORMS: set[str] = {"biological", "wind", "radiation_pressure", "gravitational"}
|
||||
# Ambient energy forms (sun, wind, gravity) aren't a depletable onboard
|
||||
# store the way a fuel tank is -- "distance before running out" doesn't
|
||||
# apply (a sailboat doesn't run out of wind). Rather than degenerate to 0
|
||||
# (mass_min=0, energy_density often undeclared entirely), range_fuel
|
||||
# reports the domain's own declared ceiling for these: full marks is the
|
||||
# physically honest answer, not an error. Food is deliberately NOT here:
|
||||
# stopping to eat is a resupply, the same category as refuelling a tank,
|
||||
# not a genuinely external/inexhaustible power source -- Biological Feed
|
||||
# uses the normal storage-mass-limited range_fuel formula.
|
||||
AMBIENT_ENERGY_FORMS: set[str] = {"wind", "radiation_pressure", "gravitational"}
|
||||
|
||||
# Resistive energy cost of travel, J per kg of vehicle per meter --
|
||||
# rolling resistance for ground vehicles, cruise-flight lift/drag for
|
||||
@@ -931,13 +944,16 @@ class Pipeline:
|
||||
performance target (accel/thrust, or target_velocity/resistance)
|
||||
sets a FLOOR -- a rotorcraft that can't produce enough thrust to
|
||||
hover isn't a rotorcraft, regardless of how a smaller/cheaper
|
||||
engine might score. Biological actuators (a rider's own body) get
|
||||
the same treatment with one addition: BIOLOGICAL_OPERATOR_MASS_KG
|
||||
sets a floor under the floor -- at least one real operator, even if
|
||||
the performance-derived requirement would otherwise ask for less
|
||||
-- but above that, mass is a free variable exactly like a
|
||||
mechanical actuator's; "bigger" here means more or bigger
|
||||
operators, not a fixed physiological constant. That floor also
|
||||
engine might score. Biological actuators (a rider's own body) and
|
||||
radiation-pressure actuators (a solar sail) get the same treatment
|
||||
with one addition: BIOLOGICAL_OPERATOR_MASS_KG / a footprint-derived
|
||||
floor (see SAIL_AREAL_DENSITY_KG_PER_M2) sets a floor under the
|
||||
floor -- at least one real operator, or the sail's own declared
|
||||
minimum footprint, even if the performance-derived requirement
|
||||
would otherwise ask for less -- but above that, mass is a free
|
||||
variable exactly like a mechanical actuator's; "bigger" means more
|
||||
or bigger operators, or a bigger sail, not a fixed constant. That
|
||||
floor also
|
||||
sets the smallest platform mass that could structurally carry it
|
||||
(CARGO_KG_PER_STRUCTURAL_KG again, applied to the platform
|
||||
carrying its own actuator+storage instead of cargo) -- below that,
|
||||
@@ -970,16 +986,6 @@ class Pipeline:
|
||||
return float(dep.value)
|
||||
return None
|
||||
|
||||
if ctx.actuator_energy_form == "radiation_pressure":
|
||||
# thrust scales with sail area, not carried mass -- derive an
|
||||
# effective mass from declared footprint and a thin-film areal
|
||||
# density estimate rather than the (undeclared) mass attribute.
|
||||
# No meaningful "bigger sail" mass slider here (area-driven,
|
||||
# not budget-driven), so this stays its own case.
|
||||
footprint = dep_value(ctx.actuator, "footprint", "range_min") or 0.0
|
||||
actuator_mass = footprint * 0.05 # kg/m^2, thin deployable sail film
|
||||
return actuator_mass, ctx.s_min, ctx.p_rep, True
|
||||
|
||||
# Step 1: the required floor (same solve as before -- now a floor
|
||||
# for the search below, not the final answer).
|
||||
min_accel = dep_value(ctx.platform, "min_effective_accel", "range_min")
|
||||
@@ -1026,6 +1032,13 @@ class Pipeline:
|
||||
# performance solve above would have asked for -- see the
|
||||
# BIOLOGICAL_OPERATOR_MASS_KG module comment.
|
||||
required_actuator = max(required_actuator, BIOLOGICAL_OPERATOR_MASS_KG[ctx.actuator_energy_form])
|
||||
elif ctx.actuator_energy_form == "radiation_pressure":
|
||||
# At least the entity's own declared minimum sail footprint,
|
||||
# regardless of what the bare performance solve above would
|
||||
# have asked for -- see the SAIL_AREAL_DENSITY_KG_PER_M2
|
||||
# module comment.
|
||||
footprint_floor = dep_value(ctx.actuator, "footprint", "range_min") or 0.0
|
||||
required_actuator = max(required_actuator, footprint_floor * SAIL_AREAL_DENSITY_KG_PER_M2)
|
||||
|
||||
if ctx.p_max is None:
|
||||
# No declared mass ceiling (e.g. Spaceship) -- no bounded
|
||||
@@ -1296,20 +1309,19 @@ class Pipeline:
|
||||
minimum (platform is also ceiling-clamped to its declared max) --
|
||||
never silently allowed below what pass 1 would have rejected.
|
||||
|
||||
Returns None for combos with no free actuator mass to explore
|
||||
(radiation-pressure sails -- thrust is area-driven, not a mass
|
||||
choice, see _decide_masses) or with no declared platform mass
|
||||
ceiling to bound a weight-class slider. Biological actuators DO
|
||||
get sliders: rider/operator mass is a real, budget-competing
|
||||
variable like any other actuator (see BIOLOGICAL_OPERATOR_MASS_KG).
|
||||
Returns None only for combos with no declared platform mass
|
||||
ceiling to bound a weight-class slider (e.g. Spaceship). Every
|
||||
actuator type gets sliders, including biological (rider/operator
|
||||
mass, see BIOLOGICAL_OPERATOR_MASS_KG) and radiation-pressure
|
||||
(effective sail mass derived from footprint, see
|
||||
SAIL_AREAL_DENSITY_KG_PER_M2) -- both are real, budget-competing
|
||||
variables like any mechanical actuator's mass.
|
||||
"""
|
||||
bounds_by_name = {mb.metric_name: mb for mb in domain.metric_bounds}
|
||||
units_by_name = {mb.metric_name: mb.unit for mb in domain.metric_bounds}
|
||||
ctx = self._physics_context(combo, bounds_by_name)
|
||||
if ctx is None or ctx.p_max is None:
|
||||
return None
|
||||
if ctx.actuator_energy_form == "radiation_pressure":
|
||||
return None
|
||||
|
||||
cargo_capacity_kg = (ctx.p_min + ctx.a_min + ctx.s_min) * CARGO_KG_PER_STRUCTURAL_KG
|
||||
default_actuator, default_storage, default_platform, _feasible = self._decide_masses(
|
||||
|
||||
Reference in New Issue
Block a user