I love how stupid this project is

si units and redefining speed metric as thrust/weight ratio
This commit is contained in:
2026-03-04 16:30:09 -06:00
parent 216879bdd5
commit 00cc8dd9ef
19 changed files with 494 additions and 341 deletions

View File

@@ -41,7 +41,6 @@ class ConstraintResolver:
self._check_requires_vs_excludes(all_deps, result)
self._check_mutual_exclusion(all_deps, result)
self._check_range_incompatibility(all_deps, result)
self._check_power_density(combination, result)
self._check_energy_density(combination, result)
self._check_unmet_requirements(all_deps, result)
@@ -125,35 +124,6 @@ class ConstraintResolver:
f"but {max_name} limits {key} <= {max_val}"
)
def _check_power_density(
self, combination: Combination, result: ConstraintResult
) -> None:
"""Rule 4: If power source W/kg << platform required W/kg → warn/block."""
density_provided: list[tuple[str, float]] = []
density_required: list[tuple[str, float]] = []
for entity in combination.entities:
for dep in entity.dependencies:
if dep.key == "power_density_w_kg" and dep.constraint_type == "provides":
density_provided.append((entity.name, float(dep.value)))
elif dep.key == "power_density_required_w_kg" and dep.constraint_type == "range_min":
density_required.append((entity.name, float(dep.value)))
for req_name, req_density in density_required:
for prov_name, prov_density in density_provided:
if prov_density < req_density * 0.01:
result.violations.append(
f"{prov_name} provides {prov_density} W/kg but "
f"{req_name} requires {req_density} W/kg "
f"(power density deficit > 100x)"
)
elif prov_density < req_density:
result.warnings.append(
f"{prov_name} provides {prov_density} W/kg but "
f"{req_name} requires {req_density} W/kg "
f"(under-powered)"
)
def _check_energy_density(
self, combination: Combination, result: ConstraintResult
) -> None:
@@ -166,9 +136,9 @@ class ConstraintResolver:
for entity in combination.entities:
for dep in entity.dependencies:
if dep.key == "energy_density_wh_kg" and dep.constraint_type == "provides":
if dep.key == "energy_density" and dep.constraint_type == "provides":
density_provided.append((entity.name, float(dep.value)))
elif dep.key == "energy_density_wh_kg" and dep.constraint_type == "range_min":
elif dep.key == "energy_density" and dep.constraint_type == "range_min":
density_required.append((entity.name, float(dep.value)))
for req_name, req_density in density_required:
@@ -177,14 +147,14 @@ class ConstraintResolver:
for prov_name, prov_density in density_provided:
if prov_density < req_density * 0.25:
result.violations.append(
f"{prov_name} provides {prov_density:.0f} Wh/kg but "
f"{req_name} requires {req_density:.0f} Wh/kg "
f"{prov_name} provides {prov_density:.0f} J/kg but "
f"{req_name} requires {req_density:.0f} J/kg "
f"(energy density deficit > 4x)"
)
elif prov_density < req_density:
result.warnings.append(
f"{prov_name} provides {prov_density:.0f} Wh/kg but "
f"{req_name} requires {req_density:.0f} Wh/kg "
f"{prov_name} provides {prov_density:.0f} J/kg but "
f"{req_name} requires {req_density:.0f} J/kg "
f"(under-density)"
)

View File

@@ -414,28 +414,27 @@ class Pipeline:
def _stub_estimate(
self, combo: Combination, metric_names: list[str]
) -> dict[str, float]:
"""Simple heuristic estimation from dependency data."""
"""Simple heuristic estimation from dependency data (all values in SI base units)."""
raw: dict[str, float] = {m: 0.0 for m in metric_names}
# Extract intrinsic properties from entities
power_density = 0.0 # W/kg
energy_density = 0.0 # Wh/kg
mass_kg = 100.0 # default
energy_density = 0.0 # J/kg
mass = 100.0 # kg, default
for entity in combo.entities:
for dep in entity.dependencies:
if dep.key == "power_density_w_kg" and dep.constraint_type == "provides":
if dep.key == "power_density" and dep.constraint_type == "provides":
power_density = max(power_density, float(dep.value))
if dep.key == "energy_density_wh_kg" and dep.constraint_type == "provides":
if dep.key == "energy_density" and dep.constraint_type == "provides":
energy_density = max(energy_density, float(dep.value))
if dep.key == "mass_kg" and dep.constraint_type == "range_min":
mass_kg = max(mass_kg, float(dep.value))
if dep.key == "mass" and dep.constraint_type == "range_min":
mass = max(mass, float(dep.value))
# Rough speed estimate: higher power density → faster
if "speed" in raw:
raw["speed"] = min(power_density * 0.5, 300000)
if "power_density" in raw:
raw["power_density"] = power_density
if "cost_efficiency" in raw:
raw["cost_efficiency"] = max(0.01, 2.0 - power_density / 1000)
raw["cost_efficiency"] = max(1e-5, 2e-3 - power_density / 1e6)
if "safety" in raw:
raw["safety"] = 0.5
@@ -444,19 +443,19 @@ class Pipeline:
raw["availability"] = 0.5
if "range_fuel" in raw:
raw["range_fuel"] = min(energy_density * 10, 1e10)
raw["range_fuel"] = min(energy_density * 2.78, 1e13)
if "range_degradation" in raw:
raw["range_degradation"] = 365
raw["range_degradation"] = 365 * 86400
if "cargo_capacity" in raw:
raw["cargo_capacity"] = mass_kg * 0.5
raw["cargo_capacity"] = mass * 500
if "cargo_capacity_kg" in raw:
raw["cargo_capacity_kg"] = mass_kg * 0.3
raw["cargo_capacity_kg"] = mass * 0.3
if "environmental_impact" in raw:
raw["environmental_impact"] = max(0.0, power_density * 0.2)
raw["environmental_impact"] = max(0.0, power_density * 2e-7)
if "reliability" in raw:
raw["reliability"] = 0.5

View File

@@ -14,7 +14,7 @@ estimate the requested metrics using order-of-magnitude physics reasoning.
- Use real-world physics to estimate each metric.
- If the concept is implausible, still provide your best estimate.
- Return ONLY valid JSON mapping metric names to numeric values.
- Example: {{"speed": 45.0, "cost_efficiency": 0.15, "safety": 0.7}}
- Example: {{"power_density": 500.0, "cost_efficiency": 0.15, "safety": 0.7}}
"""
PLAUSIBILITY_REVIEW_PROMPT = """\

View File

@@ -16,11 +16,10 @@ GROUND_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "50", "", "range_max"),
Dependency("physical", "footprint_m2", "0.5", "", "range_min"),
Dependency("physical", "mass_kg", "36000", "kg", "range_max"),
Dependency("physical", "mass_kg", "50", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "5", "W/kg", "range_min"),
Dependency("physical", "footprint", "50", "", "range_max"),
Dependency("physical", "footprint", "0.5", "", "range_min"),
Dependency("physical", "mass", "36000", "kg", "range_max"),
Dependency("physical", "mass", "50", "kg", "range_min"),
Dependency("infrastructure", "road_network", "true", None, "requires"),
Dependency("environment", "medium", "ground", None, "requires"),
],
@@ -32,30 +31,14 @@ GROUND_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "3", "", "range_max"),
Dependency("physical", "footprint_m2", "0.3", "", "range_min"),
Dependency("physical", "mass_kg", "60", "kg", "range_max"),
Dependency("physical", "mass_kg", "5", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"),
Dependency("physical", "footprint", "3", "", "range_max"),
Dependency("physical", "footprint", "0.3", "", "range_min"),
Dependency("physical", "mass", "60", "kg", "range_max"),
Dependency("physical", "mass", "5", "kg", "range_min"),
Dependency("infrastructure", "road_network", "true", None, "requires"),
Dependency("environment", "medium", "ground", None, "requires"),
],
),
Entity(
name="Walking",
dimension="platform",
description="Bipedal locomotion",
dependencies=[
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "0.8", "", "range_max"),
Dependency("physical", "footprint_m2", "0.3", "", "range_min"),
Dependency("physical", "mass_kg", "150", "kg", "range_max"),
Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"),
Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"),
Dependency("environment", "medium", "ground", None, "requires"),
],
),
Entity(
name="Rail Vehicle",
dimension="platform",
@@ -63,30 +46,14 @@ GROUND_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "200", "", "range_max"),
Dependency("physical", "footprint_m2", "20", "", "range_min"),
Dependency("physical", "mass_kg", "40000", "kg", "range_max"),
Dependency("physical", "mass_kg", "10000", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "10", "W/kg", "range_min"),
Dependency("physical", "footprint", "200", "", "range_max"),
Dependency("physical", "footprint", "20", "", "range_min"),
Dependency("physical", "mass", "40000", "kg", "range_max"),
Dependency("physical", "mass", "10000", "kg", "range_min"),
Dependency("infrastructure", "rail_network", "true", None, "requires"),
Dependency("environment", "medium", "ground", None, "requires"),
],
),
Entity(
name="Animal Transport",
dimension="platform",
description="Animal-powered riding or pulling — horses, dog sleds",
dependencies=[
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "5", "", "range_max"),
Dependency("physical", "footprint_m2", "1", "", "range_min"),
Dependency("physical", "mass_kg", "700", "kg", "range_max"),
Dependency("physical", "mass_kg", "100", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"),
Dependency("environment", "medium", "ground", None, "requires"),
],
),
]
@@ -100,11 +67,10 @@ WATER_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "water_surface", "true", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "2000", "", "range_max"),
Dependency("physical", "footprint_m2", "2", "", "range_min"),
Dependency("physical", "mass_kg", "100000", "kg", "range_max"),
Dependency("physical", "mass_kg", "30", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "2", "W/kg", "range_min"),
Dependency("physical", "footprint", "2000", "", "range_max"),
Dependency("physical", "footprint", "2", "", "range_min"),
Dependency("physical", "mass", "100000", "kg", "range_max"),
Dependency("physical", "mass", "30", "kg", "range_min"),
Dependency("environment", "medium", "water", None, "requires"),
],
),
@@ -115,12 +81,11 @@ WATER_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "water_surface", "true", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "200", "", "range_max"),
Dependency("physical", "footprint_m2", "20", "", "range_min"),
Dependency("physical", "mass_kg", "10000", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "20", "W/kg", "range_min"),
Dependency("physical", "footprint", "200", "", "range_max"),
Dependency("physical", "footprint", "20", "", "range_min"),
Dependency("physical", "mass", "10000", "kg", "range_min"),
Dependency("environment", "medium", "water", None, "requires"),
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "range_min"),
Dependency("physical", "energy_density", "720000", "J/kg", "range_min"),
],
),
]
@@ -136,14 +101,13 @@ AIR_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "500", "", "range_max"),
Dependency("physical", "footprint_m2", "10", "", "range_min"),
Dependency("physical", "mass_kg", "100000", "kg", "range_max"),
Dependency("physical", "mass_kg", "500", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "100", "W/kg", "range_min"),
Dependency("physical", "footprint", "500", "", "range_max"),
Dependency("physical", "footprint", "10", "", "range_min"),
Dependency("physical", "mass", "100000", "kg", "range_max"),
Dependency("physical", "mass", "500", "kg", "range_min"),
Dependency("infrastructure", "runway", "true", None, "requires"),
Dependency("environment", "medium", "air", None, "requires"),
Dependency("physical", "energy_density_wh_kg", "400", "Wh/kg", "range_min"),
Dependency("physical", "energy_density", "1440000", "J/kg", "range_min"),
],
),
Entity(
@@ -153,13 +117,12 @@ AIR_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "20", "", "range_max"),
Dependency("physical", "footprint_m2", "0.5", "", "range_min"),
Dependency("physical", "mass_kg", "5000", "kg", "range_max"),
Dependency("physical", "mass_kg", "1", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "80", "W/kg", "range_min"),
Dependency("physical", "footprint", "20", "", "range_max"),
Dependency("physical", "footprint", "0.5", "", "range_min"),
Dependency("physical", "mass", "5000", "kg", "range_max"),
Dependency("physical", "mass", "1", "kg", "range_min"),
Dependency("environment", "medium", "air", None, "requires"),
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "range_min"),
Dependency("physical", "energy_density", "720000", "J/kg", "range_min"),
],
),
Entity(
@@ -169,11 +132,10 @@ AIR_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "1000", "", "range_max"),
Dependency("physical", "footprint_m2", "50", "", "range_min"),
Dependency("physical", "mass_kg", "20000", "kg", "range_max"),
Dependency("physical", "mass_kg", "200", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "5", "W/kg", "range_min"),
Dependency("physical", "footprint", "1000", "", "range_max"),
Dependency("physical", "footprint", "50", "", "range_min"),
Dependency("physical", "mass", "20000", "kg", "range_max"),
Dependency("physical", "mass", "200", "kg", "range_min"),
Dependency("environment", "medium", "air", None, "requires"),
],
),
@@ -184,11 +146,10 @@ AIR_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "20", "", "range_max"),
Dependency("physical", "footprint_m2", "5", "", "range_min"),
Dependency("physical", "mass_kg", "600", "kg", "range_max"),
Dependency("physical", "mass_kg", "5", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"),
Dependency("physical", "footprint", "20", "", "range_max"),
Dependency("physical", "footprint", "5", "", "range_min"),
Dependency("physical", "mass", "600", "kg", "range_max"),
Dependency("physical", "mass", "5", "kg", "range_min"),
Dependency("infrastructure", "tow_or_winch", "true", None, "requires"),
Dependency("environment", "medium", "air", None, "requires"),
],
@@ -205,13 +166,12 @@ SPACE_PLATFORMS: list[Entity] = [
description="Vehicle designed for space travel",
dependencies=[
Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"),
Dependency("physical", "footprint_m2", "500", "", "range_max"),
Dependency("physical", "footprint_m2", "10", "", "range_min"),
Dependency("physical", "mass_kg", "5000", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "300", "W/kg", "range_min"),
Dependency("physical", "footprint", "500", "", "range_max"),
Dependency("physical", "footprint", "10", "", "range_min"),
Dependency("physical", "mass", "5000", "kg", "range_min"),
Dependency("infrastructure", "launch_facility", "true", None, "requires"),
Dependency("environment", "medium", "space", None, "requires"),
Dependency("physical", "energy_density_wh_kg", "2000", "Wh/kg", "range_min"),
Dependency("physical", "energy_density", "7200000", "J/kg", "range_min"),
],
),
]
@@ -226,11 +186,10 @@ MULTI_PLATFORMS: list[Entity] = [
description="Vehicle capable of operation on land, water, or both",
dependencies=[
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "100", "", "range_max"),
Dependency("physical", "footprint_m2", "5", "", "range_min"),
Dependency("physical", "mass_kg", "10000", "kg", "range_max"),
Dependency("physical", "mass_kg", "1500", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "20", "W/kg", "range_min"),
Dependency("physical", "footprint", "100", "", "range_max"),
Dependency("physical", "footprint", "5", "", "range_min"),
Dependency("physical", "mass", "10000", "kg", "range_max"),
Dependency("physical", "mass", "1500", "kg", "range_min"),
],
),
]
@@ -244,10 +203,9 @@ FICTIONAL_PLATFORMS: list[Entity] = [
dimension="platform",
description="Hypothetical matter transmission device",
dependencies=[
Dependency("physical", "footprint_m2", "10", "", "range_max"),
Dependency("physical", "footprint_m2", "1", "", "range_min"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "1000", "W/kg", "range_min"),
Dependency("physical", "footprint", "10", "", "range_max"),
Dependency("physical", "footprint", "1", "", "range_min"),
Dependency("physical", "mass", "0", "kg", "range_min"),
Dependency("infrastructure", "teleport_network", "true", None, "requires"),
],
),
@@ -258,11 +216,10 @@ FICTIONAL_PLATFORMS: list[Entity] = [
dependencies=[
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("physical", "footprint_m2", "50", "", "range_max"),
Dependency("physical", "footprint_m2", "5", "", "range_min"),
Dependency("physical", "mass_kg", "20000", "kg", "range_max"),
Dependency("physical", "mass_kg", "5000", "kg", "range_min"),
Dependency("force", "power_density_required_w_kg", "80", "W/kg", "range_min"),
Dependency("physical", "footprint", "50", "", "range_max"),
Dependency("physical", "footprint", "5", "", "range_min"),
Dependency("physical", "mass", "20000", "kg", "range_max"),
Dependency("physical", "mass", "5000", "kg", "range_min"),
Dependency("infrastructure", "hyperloop_tube", "true", None, "requires"),
Dependency("environment", "medium", "ground", None, "requires"),
],
@@ -292,9 +249,9 @@ COMBUSTION_ACTUATORS: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "requires"),
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("physical", "mass_kg", "45", "kg", "range_min"),
Dependency("physical", "mass", "45", "kg", "range_min"),
Dependency("force", "thrust_profile", "high_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "1000", "W/kg", "provides"),
Dependency("force", "power_density", "1000", "W/kg", "provides"),
],
),
Entity(
@@ -304,9 +261,9 @@ COMBUSTION_ACTUATORS: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "requires"),
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("physical", "mass_kg", "200", "kg", "range_min"),
Dependency("physical", "mass", "200", "kg", "range_min"),
Dependency("force", "thrust_profile", "extreme_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "5000", "W/kg", "provides"),
Dependency("force", "power_density", "5000", "W/kg", "provides"),
],
),
Entity(
@@ -316,9 +273,9 @@ COMBUSTION_ACTUATORS: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "requires"),
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("physical", "mass_kg", "300", "kg", "range_min"),
Dependency("physical", "mass", "300", "kg", "range_min"),
Dependency("force", "thrust_profile", "high_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "30", "W/kg", "provides"),
Dependency("force", "power_density", "30", "W/kg", "provides"),
],
),
]
@@ -333,9 +290,9 @@ ELECTRIC_ACTUATORS: list[Entity] = [
description="Rotary motor converting electrical energy to mechanical motion",
dependencies=[
Dependency("energy", "energy_form", "electrical", None, "requires"),
Dependency("physical", "mass_kg", "5", "kg", "range_min"),
Dependency("physical", "mass", "5", "kg", "range_min"),
Dependency("force", "thrust_profile", "moderate_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "2000", "W/kg", "provides"),
Dependency("force", "power_density", "2000", "W/kg", "provides"),
],
),
]
@@ -350,9 +307,9 @@ BIOLOGICAL_ACTUATORS: list[Entity] = [
description="Human-powered via pedalling, pushing, or rowing",
dependencies=[
Dependency("energy", "energy_form", "biological", None, "requires"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("physical", "mass", "0", "kg", "range_min"),
Dependency("force", "thrust_profile", "low_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "1.5", "W/kg", "provides"),
Dependency("force", "power_density", "1.5", "W/kg", "provides"),
],
),
Entity(
@@ -362,9 +319,9 @@ BIOLOGICAL_ACTUATORS: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "biological", None, "requires"),
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("physical", "mass", "0", "kg", "range_min"),
Dependency("force", "thrust_profile", "low_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "2", "W/kg", "provides"),
Dependency("force", "power_density", "2", "W/kg", "provides"),
],
),
]
@@ -381,9 +338,9 @@ RENEWABLE_ACTUATORS: list[Entity] = [
Dependency("energy", "energy_form", "radiation_pressure", None, "requires"),
Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"),
Dependency("environment", "star_proximity", "true", None, "requires"),
Dependency("physical", "surface_area_m2", "100", "m^2", "range_min"),
Dependency("physical", "surface_area", "100", "m²", "range_min"),
Dependency("force", "thrust_profile", "continuous_low", None, "provides"),
Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"),
Dependency("force", "power_density", "0.01", "W/kg", "provides"),
Dependency("environment", "medium", "space", None, "requires"),
],
),
@@ -394,9 +351,9 @@ RENEWABLE_ACTUATORS: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "wind", None, "requires"),
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("physical", "mass_kg", "15", "kg", "range_min"),
Dependency("physical", "mass", "15", "kg", "range_min"),
Dependency("force", "thrust_profile", "low_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "10", "W/kg", "provides"),
Dependency("force", "power_density", "10", "W/kg", "provides"),
],
),
]
@@ -411,9 +368,9 @@ ROCKET_ACTUATORS: list[Entity] = [
description="Thrust from expanding combustion gases through a nozzle",
dependencies=[
Dependency("energy", "energy_form", "chemical_propellant", None, "requires"),
Dependency("physical", "mass_kg", "150", "kg", "range_min"),
Dependency("physical", "mass", "150", "kg", "range_min"),
Dependency("force", "thrust_profile", "extreme_burst", None, "provides"),
Dependency("force", "power_density_w_kg", "10000", "W/kg", "provides"),
Dependency("force", "power_density", "10000", "W/kg", "provides"),
],
),
Entity(
@@ -424,9 +381,9 @@ ROCKET_ACTUATORS: list[Entity] = [
Dependency("energy", "energy_form", "ion_propellant", None, "requires"),
Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"),
Dependency("environment", "medium", "space", None, "requires"),
Dependency("physical", "mass_kg", "8", "kg", "range_min"),
Dependency("physical", "mass", "8", "kg", "range_min"),
Dependency("force", "thrust_profile", "continuous_low", None, "provides"),
Dependency("force", "power_density_w_kg", "30", "W/kg", "provides"),
Dependency("force", "power_density", "30", "W/kg", "provides"),
],
),
Entity(
@@ -435,9 +392,9 @@ ROCKET_ACTUATORS: list[Entity] = [
description="Nuclear fission reactor heating propellant for thrust",
dependencies=[
Dependency("energy", "energy_form", "nuclear_thermal", None, "requires"),
Dependency("physical", "mass_kg", "1500", "kg", "range_min"),
Dependency("physical", "mass", "1500", "kg", "range_min"),
Dependency("force", "thrust_profile", "extreme_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "50", "W/kg", "provides"),
Dependency("force", "power_density", "50", "W/kg", "provides"),
Dependency("material", "radiation_shielding", "true", None, "requires"),
],
),
@@ -453,9 +410,9 @@ EXOTIC_ACTUATORS: list[Entity] = [
description="Propulsion via sequential cannon blasts",
dependencies=[
Dependency("energy", "energy_form", "chemical_explosive", None, "requires"),
Dependency("physical", "mass_kg", "80", "kg", "range_min"),
Dependency("physical", "mass", "80", "kg", "range_min"),
Dependency("force", "thrust_profile", "high_burst", None, "provides"),
Dependency("force", "power_density_w_kg", "3000", "W/kg", "provides"),
Dependency("force", "power_density", "3000", "W/kg", "provides"),
],
),
Entity(
@@ -464,9 +421,9 @@ EXOTIC_ACTUATORS: list[Entity] = [
description="Motor powered by expanding compressed air",
dependencies=[
Dependency("energy", "energy_form", "pneumatic", None, "requires"),
Dependency("physical", "mass_kg", "10", "kg", "range_min"),
Dependency("physical", "mass", "10", "kg", "range_min"),
Dependency("force", "thrust_profile", "moderate_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "100", "W/kg", "provides"),
Dependency("force", "power_density", "100", "W/kg", "provides"),
],
),
Entity(
@@ -475,9 +432,9 @@ EXOTIC_ACTUATORS: list[Entity] = [
description="Mechanical drive transferring kinetic energy from a spinning rotor",
dependencies=[
Dependency("energy", "energy_form", "kinetic_stored", None, "requires"),
Dependency("physical", "mass_kg", "20", "kg", "range_min"),
Dependency("physical", "mass", "20", "kg", "range_min"),
Dependency("force", "thrust_profile", "high_burst", None, "provides"),
Dependency("force", "power_density_w_kg", "2000", "W/kg", "provides"),
Dependency("force", "power_density", "2000", "W/kg", "provides"),
],
),
Entity(
@@ -488,9 +445,9 @@ EXOTIC_ACTUATORS: list[Entity] = [
Dependency("energy", "energy_form", "gravitational", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("physical", "mass", "0", "kg", "range_min"),
Dependency("force", "thrust_profile", "low_continuous", None, "provides"),
Dependency("force", "power_density_w_kg", "10", "W/kg", "provides"),
Dependency("force", "power_density", "10", "W/kg", "provides"),
],
),
]
@@ -518,8 +475,8 @@ COMBUSTIBLE_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "fuel_station", None, "requires"),
Dependency("physical", "mass_kg", "10", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "1300", "Wh/kg", "provides"),
Dependency("physical", "mass", "10", "kg", "range_min"),
Dependency("physical", "energy_density", "4680000", "J/kg", "provides"),
],
),
Entity(
@@ -529,8 +486,8 @@ COMBUSTIBLE_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "hydrogen_station", None, "requires"),
Dependency("physical", "mass_kg", "5", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "600", "Wh/kg", "provides"),
Dependency("physical", "mass", "5", "kg", "range_min"),
Dependency("physical", "energy_density", "2160000", "J/kg", "provides"),
],
),
Entity(
@@ -540,8 +497,8 @@ COMBUSTIBLE_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "cng_station", None, "requires"),
Dependency("physical", "mass_kg", "15", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "800", "Wh/kg", "provides"),
Dependency("physical", "mass", "15", "kg", "range_min"),
Dependency("physical", "energy_density", "2880000", "J/kg", "provides"),
],
),
Entity(
@@ -551,8 +508,8 @@ COMBUSTIBLE_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "coal_supply", None, "requires"),
Dependency("physical", "mass_kg", "100", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "400", "Wh/kg", "provides"),
Dependency("physical", "mass", "100", "kg", "range_min"),
Dependency("physical", "energy_density", "1440000", "J/kg", "provides"),
],
),
Entity(
@@ -562,8 +519,8 @@ COMBUSTIBLE_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_combustible", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "jet_fuel", None, "requires"),
Dependency("physical", "mass_kg", "50", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "1200", "Wh/kg", "provides"),
Dependency("physical", "mass", "50", "kg", "range_min"),
Dependency("physical", "energy_density", "4320000", "J/kg", "provides"),
],
),
]
@@ -579,8 +536,8 @@ ELECTRICAL_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "electrical", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "charging_station", None, "requires"),
Dependency("physical", "mass_kg", "9", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "300", "Wh/kg", "provides"),
Dependency("physical", "mass", "9", "kg", "range_min"),
Dependency("physical", "energy_density", "1080000", "J/kg", "provides"),
],
),
Entity(
@@ -590,8 +547,8 @@ ELECTRICAL_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "electrical", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "charging_station", None, "requires"),
Dependency("physical", "mass_kg", "15", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "10", "Wh/kg", "provides"),
Dependency("physical", "mass", "15", "kg", "range_min"),
Dependency("physical", "energy_density", "36000", "J/kg", "provides"),
],
),
Entity(
@@ -602,8 +559,8 @@ ELECTRICAL_STORAGE: list[Entity] = [
Dependency("energy", "energy_form", "electrical", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"),
Dependency("environment", "star_proximity", "true", None, "requires"),
Dependency("physical", "mass_kg", "10", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "50", "Wh/kg", "provides"),
Dependency("physical", "mass", "10", "kg", "range_min"),
Dependency("physical", "energy_density", "180000", "J/kg", "provides"),
],
),
]
@@ -619,8 +576,8 @@ BIOLOGICAL_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "biological", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"),
Dependency("physical", "mass", "0", "kg", "range_min"),
Dependency("physical", "energy_density", "720000", "J/kg", "provides"),
],
),
]
@@ -637,7 +594,7 @@ AMBIENT_STORAGE: list[Entity] = [
Dependency("energy", "energy_form", "radiation_pressure", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"),
Dependency("environment", "star_proximity", "true", None, "requires"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("physical", "mass", "0", "kg", "range_min"),
],
),
Entity(
@@ -648,7 +605,7 @@ AMBIENT_STORAGE: list[Entity] = [
Dependency("energy", "energy_form", "wind", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"),
Dependency("environment", "atmosphere", "standard", None, "requires"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("physical", "mass", "0", "kg", "range_min"),
],
),
]
@@ -664,8 +621,8 @@ PROPELLANT_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_propellant", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "solid_propellant", None, "requires"),
Dependency("physical", "mass_kg", "50", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "500", "Wh/kg", "provides"),
Dependency("physical", "mass", "50", "kg", "range_min"),
Dependency("physical", "energy_density", "1800000", "J/kg", "provides"),
],
),
Entity(
@@ -675,8 +632,8 @@ PROPELLANT_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "ion_propellant", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "xenon_propellant", None, "requires"),
Dependency("physical", "mass_kg", "2", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "3000", "Wh/kg", "provides"),
Dependency("physical", "mass", "2", "kg", "range_min"),
Dependency("physical", "energy_density", "10800000", "J/kg", "provides"),
],
),
Entity(
@@ -686,8 +643,8 @@ PROPELLANT_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "nuclear_thermal", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "nuclear_fuel", None, "requires"),
Dependency("physical", "mass_kg", "500", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "500000", "Wh/kg", "provides"),
Dependency("physical", "mass", "500", "kg", "range_min"),
Dependency("physical", "energy_density", "1800000000", "J/kg", "provides"),
Dependency("material", "radiation_shielding", "true", None, "requires"),
],
),
@@ -704,8 +661,8 @@ EXOTIC_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "chemical_explosive", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "ammunition", None, "requires"),
Dependency("physical", "mass_kg", "20", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"),
Dependency("physical", "mass", "20", "kg", "range_min"),
Dependency("physical", "energy_density", "720000", "J/kg", "provides"),
],
),
Entity(
@@ -715,8 +672,8 @@ EXOTIC_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "pneumatic", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "compressed_air_station", None, "requires"),
Dependency("physical", "mass_kg", "10", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "30", "Wh/kg", "provides"),
Dependency("physical", "mass", "10", "kg", "range_min"),
Dependency("physical", "energy_density", "108000", "J/kg", "provides"),
],
),
Entity(
@@ -726,8 +683,8 @@ EXOTIC_STORAGE: list[Entity] = [
dependencies=[
Dependency("energy", "energy_form", "kinetic_stored", None, "provides"),
Dependency("infrastructure", "fuel_infrastructure", "charging_station", None, "requires"),
Dependency("physical", "mass_kg", "30", "kg", "range_min"),
Dependency("physical", "energy_density_wh_kg", "50", "Wh/kg", "provides"),
Dependency("physical", "mass", "30", "kg", "range_min"),
Dependency("physical", "energy_density", "180000", "J/kg", "provides"),
],
),
Entity(
@@ -739,7 +696,7 @@ EXOTIC_STORAGE: list[Entity] = [
Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"),
Dependency("environment", "gravity", "true", None, "requires"),
Dependency("environment", "ground_surface", "true", None, "requires"),
Dependency("physical", "mass_kg", "0", "kg", "range_min"),
Dependency("physical", "mass", "0", "kg", "range_min"),
],
),
]
@@ -763,11 +720,11 @@ URBAN_COMMUTING = Domain(
name="urban_commuting",
description="Daily travel within a city, 1-50km range",
metric_bounds=[
MetricBound("speed", weight=0.25, norm_min=5, norm_max=120, unit="km/h"),
MetricBound("cost_efficiency", weight=0.25, norm_min=0.01, norm_max=2.0, unit="$/km", lower_is_better=True),
MetricBound("power_density", weight=0.25, norm_min=1, norm_max=2000, unit="W/kg"),
MetricBound("cost_efficiency", weight=0.25, norm_min=1e-5, norm_max=2e-3, unit="$/m", lower_is_better=True),
MetricBound("safety", weight=0.25, norm_min=0.0, norm_max=1.0, unit="0-1"),
MetricBound("availability", weight=0.15, norm_min=0.0, norm_max=1.0, unit="0-1"),
MetricBound("range_fuel", weight=0.10, norm_min=5, norm_max=500, unit="km"),
MetricBound("range_fuel", weight=0.10, norm_min=5000, norm_max=500000, unit="m"),
],
)
@@ -775,11 +732,11 @@ INTERPLANETARY = Domain(
name="interplanetary_travel",
description="Travel between planets within a solar system",
metric_bounds=[
MetricBound("speed", weight=0.30, norm_min=1000, norm_max=300000, unit="km/s"),
MetricBound("range_fuel", weight=0.30, norm_min=1e6, norm_max=1e10, unit="km"),
MetricBound("power_density", weight=0.30, norm_min=10, norm_max=10000, unit="W/kg"),
MetricBound("range_fuel", weight=0.30, norm_min=1e9, norm_max=1e13, unit="m"),
MetricBound("safety", weight=0.20, norm_min=0.0, norm_max=1.0, unit="0-1"),
MetricBound("cost_efficiency", weight=0.10, norm_min=1e3, norm_max=1e9, unit="$/km", lower_is_better=True),
MetricBound("range_degradation", weight=0.10, norm_min=100, norm_max=36500, unit="days"),
MetricBound("cost_efficiency", weight=0.10, norm_min=1.0, norm_max=1e6, unit="$/m", lower_is_better=True),
MetricBound("range_degradation", weight=0.10, norm_min=8640000, norm_max=3.1536e9, unit="s"),
],
)
@@ -787,11 +744,11 @@ MARITIME_SHIPPING = Domain(
name="maritime_shipping",
description="Ocean cargo transport between ports, 100-40000km range",
metric_bounds=[
MetricBound("speed", weight=0.15, norm_min=5, norm_max=60, unit="km/h"),
MetricBound("cargo_capacity", weight=0.25, norm_min=1, norm_max=200000, unit="tons"),
MetricBound("cost_efficiency", weight=0.25, norm_min=0.001, norm_max=1.0, unit="$/ton-km", lower_is_better=True),
MetricBound("power_density", weight=0.15, norm_min=1, norm_max=1000, unit="W/kg"),
MetricBound("cargo_capacity", weight=0.25, norm_min=1000, norm_max=2e8, unit="kg"),
MetricBound("cost_efficiency", weight=0.25, norm_min=1e-9, norm_max=1e-6, unit="$/(kg\u00b7m)", lower_is_better=True),
MetricBound("safety", weight=0.20, norm_min=0.0, norm_max=1.0, unit="0-1"),
MetricBound("range_fuel", weight=0.15, norm_min=100, norm_max=40000, unit="km"),
MetricBound("range_fuel", weight=0.15, norm_min=100000, norm_max=40000000, unit="m"),
],
)
@@ -799,11 +756,11 @@ LAST_MILE_DELIVERY = Domain(
name="last_mile_delivery",
description="Short-range package delivery within neighborhoods, 0.5-15km",
metric_bounds=[
MetricBound("speed", weight=0.25, norm_min=2, norm_max=60, unit="km/h"),
MetricBound("cost_efficiency", weight=0.30, norm_min=0.01, norm_max=5.0, unit="$/km", lower_is_better=True),
MetricBound("power_density", weight=0.25, norm_min=1, norm_max=500, unit="W/kg"),
MetricBound("cost_efficiency", weight=0.30, norm_min=1e-5, norm_max=5e-3, unit="$/m", lower_is_better=True),
MetricBound("cargo_capacity_kg", weight=0.20, norm_min=1, norm_max=500, unit="kg"),
MetricBound("safety", weight=0.15, norm_min=0.0, norm_max=1.0, unit="0-1"),
MetricBound("environmental_impact", weight=0.10, norm_min=0, norm_max=500, unit="g CO2/km", lower_is_better=True),
MetricBound("environmental_impact", weight=0.10, norm_min=0, norm_max=5e-4, unit="kg/m", lower_is_better=True),
],
)
@@ -811,10 +768,10 @@ CROSS_COUNTRY_FREIGHT = Domain(
name="cross_country_freight",
description="Long-distance overland cargo transport, 200-5000km",
metric_bounds=[
MetricBound("speed", weight=0.20, norm_min=20, norm_max=200, unit="km/h"),
MetricBound("cargo_capacity", weight=0.25, norm_min=0.5, norm_max=100, unit="tons"),
MetricBound("cost_efficiency", weight=0.25, norm_min=0.01, norm_max=5.0, unit="$/ton-km", lower_is_better=True),
MetricBound("range_fuel", weight=0.20, norm_min=100, norm_max=5000, unit="km"),
MetricBound("power_density", weight=0.20, norm_min=5, norm_max=5000, unit="W/kg"),
MetricBound("cargo_capacity", weight=0.25, norm_min=500, norm_max=100000, unit="kg"),
MetricBound("cost_efficiency", weight=0.25, norm_min=1e-8, norm_max=5e-6, unit="$/(kg\u00b7m)", lower_is_better=True),
MetricBound("range_fuel", weight=0.20, norm_min=100000, norm_max=5000000, unit="m"),
MetricBound("reliability", weight=0.10, norm_min=0.0, norm_max=1.0, unit="0-1"),
],
)

110
src/physcom/units.py Normal file
View File

@@ -0,0 +1,110 @@
"""Unit-aware formatting: SI base units in DB → human-friendly display."""
from __future__ import annotations
# Each rule list is sorted descending by threshold.
# format_quantity walks the list and picks the first entry where abs(value) >= threshold.
# A threshold of 0 is the fallback.
DISPLAY_RULES: dict[str, list[tuple[float, str, float]]] = {
# (threshold_in_si, display_unit, divisor_to_display)
"m": [
(9.461e15, "ly", 9.461e15),
(1.496e11, "AU", 1.496e11),
(1e3, "km", 1e3),
(1, "m", 1),
(1e-2, "cm", 1e-2),
(0, "mm", 1e-3),
],
"m/s": [
(1e3, "km/s", 1e3),
(0.3, "km/h", 1 / 3.6),
(0, "m/s", 1),
],
"m\u00b2": [
(1e6, "km\u00b2", 1e6),
(1, "m\u00b2", 1),
(0, "cm\u00b2", 1e-4),
],
"kg": [
(1e3, "t", 1e3),
(1, "kg", 1),
(0, "g", 1e-3),
],
"W/kg": [
(1e3, "kW/kg", 1e3),
(0, "W/kg", 1),
],
"J/kg": [
(3.6e6, "kWh/kg", 3.6e6),
(3600, "Wh/kg", 3600),
(1e3, "kJ/kg", 1e3),
(0, "J/kg", 1),
],
"s": [
(3.156e7, "yr", 3.156e7),
(86400, "d", 86400),
(3600, "h", 3600),
(60, "min", 60),
(0, "s", 1),
],
# Always display in human-friendly derived unit
"$/m": [
(0, "$/km", 1e-3),
],
"$/(kg\u00b7m)": [
(0, "$/t\u00b7km", 1e-6),
],
"kg/m": [
(0, "g/km", 1e-6),
],
}
def format_quantity(value: object, si_unit: str | None = None) -> str:
"""Format a numeric value with its SI unit into a human-readable string.
Returns e.g. "120 km/h" for (33.33, "m/s") or "1.0 ly" for (9.461e15, "m").
"""
if value is None:
return "\u2014"
# Coerce to float
if isinstance(value, str):
try:
num = float(value)
except (ValueError, TypeError):
return value
elif isinstance(value, (int, float)):
num = float(value)
else:
return str(value)
import math
if math.isnan(num) or math.isinf(num):
return str(value)
if not si_unit or si_unit == "0-1":
return _fmt_number(num)
rules = DISPLAY_RULES.get(si_unit)
if rules is None:
# No display rules — just show number + unit
return f"{_fmt_number(num)} {si_unit}"
abs_val = abs(num)
for threshold, display_unit, divisor in rules:
if abs_val >= threshold:
display_val = num / divisor
return f"{_fmt_number(display_val)} {display_unit}"
# Fallback (shouldn't reach here if rules end with threshold=0)
return f"{_fmt_number(num)} {si_unit}"
def _fmt_number(num: float) -> str:
"""Format a number concisely: drop trailing zeros, cap at 4 significant figures."""
if num == 0:
return "0"
if num == int(num) and abs(num) < 1e6:
return str(int(num))
return f"{num:.4g}"