diff --git a/src/physcom/cli.py b/src/physcom/cli.py index ce868c2..d7d3a35 100644 --- a/src/physcom/cli.py +++ b/src/physcom/cli.py @@ -115,7 +115,7 @@ def domain_list(ctx): @click.argument("domain_name") @click.option("--passes", "-p", default="1,2,3", help="Comma-separated pass numbers to run") @click.option("--threshold", "-t", default=0.1, type=float, help="Score threshold for pass 3") -@click.option("--dimensions", "-d", default="platform,power_source", +@click.option("--dimensions", "-d", default="platform,actuator,energy_storage", help="Comma-separated dimension names") @click.pass_context def run(ctx, domain_name, passes, threshold, dimensions): diff --git a/src/physcom/engine/constraint_resolver.py b/src/physcom/engine/constraint_resolver.py index fc7a110..d8a950d 100644 --- a/src/physcom/engine/constraint_resolver.py +++ b/src/physcom/engine/constraint_resolver.py @@ -191,7 +191,11 @@ class ConstraintResolver: def _check_unmet_requirements( self, all_deps: list[tuple[str, Dependency]], result: ConstraintResult ) -> None: - """Rule 5: Required condition not provided by any entity → conditional.""" + """Rule 5: Required condition not provided by any entity → conditional. + + Energy-category requirements (e.g. energy_form) are hard blocks — + you cannot power an actuator with an incompatible energy source. + """ provides = {(d.key, d.value) for _, d in all_deps if d.constraint_type == "provides"} # Ambient conditions that don't need to be explicitly provided ambient = { @@ -207,7 +211,11 @@ class ConstraintResolver: continue # Infrastructure is external, not checked here key_val = (dep.key, dep.value) if key_val not in provides and key_val not in ambient: - result.warnings.append( + msg = ( f"{name} requires {dep.key}={dep.value} " f"but no entity in this combination provides it" ) + if dep.category == "energy": + result.violations.append(msg) + else: + result.warnings.append(msg) diff --git a/src/physcom/seed/transport_example.py b/src/physcom/seed/transport_example.py index 70d118f..1a0ace3 100644 --- a/src/physcom/seed/transport_example.py +++ b/src/physcom/seed/transport_example.py @@ -10,40 +10,32 @@ from physcom.models.domain import Domain, MetricBound GROUND_PLATFORMS: list[Entity] = [ Entity( - name="Car", + name="Road Vehicle", dimension="platform", - description="Four-wheeled enclosed road vehicle", + description="Generic wheeled road vehicle — from motorcycles to trucks", dependencies=[ Dependency("environment", "ground_surface", "true", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "3000", "kg", "range_max"), - Dependency("physical", "mass_kg", "800", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "30", "W/kg", "range_min"), + Dependency("physical", "footprint_m2", "50", "m²", "range_max"), + Dependency("physical", "footprint_m2", "0.5", "m²", "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("infrastructure", "road_network", "true", None, "requires"), Dependency("environment", "medium", "ground", None, "requires"), ], ), Entity( - name="Train", + name="Light Personal Vehicle", dimension="platform", - description="Rail-guided vehicle", + description="Small human-scale vehicle — bicycles, skateboards, wheelchairs", dependencies=[ Dependency("environment", "ground_surface", "true", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "10000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "10", "W/kg", "range_min"), - Dependency("infrastructure", "rail_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Bicycle", - dimension="platform", - description="Two-wheeled human-scale vehicle", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "30", "kg", "range_max"), + Dependency("physical", "footprint_m2", "3", "m²", "range_max"), + Dependency("physical", "footprint_m2", "0.3", "m²", "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("infrastructure", "road_network", "true", None, "requires"), Dependency("environment", "medium", "ground", None, "requires"), @@ -56,6 +48,8 @@ GROUND_PLATFORMS: list[Entity] = [ dependencies=[ Dependency("environment", "ground_surface", "true", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), + Dependency("physical", "footprint_m2", "0.8", "m²", "range_max"), + Dependency("physical", "footprint_m2", "0.3", "m²", "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"), @@ -63,204 +57,33 @@ GROUND_PLATFORMS: list[Entity] = [ ], ), Entity( - name="Wheelchair", + name="Rail Vehicle", dimension="platform", - description="Wheeled chair for seated mobility", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "200", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Scooter", - dimension="platform", - description="Small two-wheeled standing vehicle", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "50", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Motorcycle", - dimension="platform", - description="Two-wheeled motorized road vehicle", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "300", "kg", "range_max"), - Dependency("physical", "mass_kg", "150", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "10", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Bus", - dimension="platform", - description="Large enclosed multi-passenger road vehicle", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "18000", "kg", "range_max"), - Dependency("physical", "mass_kg", "8000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "15", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Truck", - dimension="platform", - description="Heavy-duty cargo road vehicle", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "36000", "kg", "range_max"), - Dependency("physical", "mass_kg", "5000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "20", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Horse", - dimension="platform", - description="Equine animal used for riding or pulling loads", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "700", "kg", "range_max"), - Dependency("physical", "mass_kg", "400", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Skateboard", - dimension="platform", - description="Small board on four wheels for rolling locomotion", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "10", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Snowmobile", - dimension="platform", - description="Tracked vehicle for travel over snow and ice", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("environment", "terrain", "snow", None, "requires"), - Dependency("physical", "mass_kg", "400", "kg", "range_max"), - Dependency("physical", "mass_kg", "200", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "40", "W/kg", "range_min"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Tram", - dimension="platform", - description="Rail-guided urban streetcar", + description="Rail-guided vehicle — from trams to high-speed trains", dependencies=[ Dependency("environment", "ground_surface", "true", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), + Dependency("physical", "footprint_m2", "200", "m²", "range_max"), + Dependency("physical", "footprint_m2", "20", "m²", "range_min"), Dependency("physical", "mass_kg", "40000", "kg", "range_max"), - Dependency("physical", "mass_kg", "20000", "kg", "range_min"), + Dependency("physical", "mass_kg", "10000", "kg", "range_min"), Dependency("force", "power_density_required_w_kg", "10", "W/kg", "range_min"), Dependency("infrastructure", "rail_network", "true", None, "requires"), - Dependency("infrastructure", "overhead_power", "true", None, "requires"), Dependency("environment", "medium", "ground", None, "requires"), ], ), Entity( - name="ATV", + name="Animal Transport", dimension="platform", - description="All-terrain vehicle (quad bike) for off-road use", + 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", "mass_kg", "500", "kg", "range_max"), - Dependency("physical", "mass_kg", "200", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "20", "W/kg", "range_min"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Rickshaw", - dimension="platform", - description="Small passenger cart pulled by human or bicycle", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "150", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "2", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Segway", - dimension="platform", - description="Self-balancing two-wheeled personal transporter", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "60", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "5", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Golf Cart", - dimension="platform", - description="Small low-speed utility vehicle", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "500", "kg", "range_max"), - Dependency("physical", "mass_kg", "300", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "3", "W/kg", "range_min"), - Dependency("infrastructure", "road_network", "true", None, "requires"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Dog Sled", - dimension="platform", - description="Sled pulled by a team of dogs over snow", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("environment", "terrain", "snow", None, "requires"), - Dependency("physical", "mass_kg", "300", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "2", "W/kg", "range_min"), - Dependency("environment", "medium", "ground", None, "requires"), - ], - ), - Entity( - name="Maglev Train", - dimension="platform", - description="Magnetically levitated high-speed rail vehicle", - dependencies=[ - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "30000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "50", "W/kg", "range_min"), - Dependency("infrastructure", "maglev_guideway", "true", None, "requires"), + Dependency("physical", "footprint_m2", "5", "m²", "range_max"), + Dependency("physical", "footprint_m2", "1", "m²", "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"), ], ), @@ -271,31 +94,20 @@ GROUND_PLATFORMS: list[Entity] = [ WATER_PLATFORMS: list[Entity] = [ Entity( - name="Sailboat", + name="Watercraft", dimension="platform", - description="Wind-propelled watercraft with sails", + description="Surface vessel — from kayaks to container ships", dependencies=[ Dependency("environment", "water_surface", "true", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "5000", "kg", "range_max"), - Dependency("physical", "mass_kg", "500", "kg", "range_min"), + Dependency("physical", "footprint_m2", "2000", "m²", "range_max"), + Dependency("physical", "footprint_m2", "2", "m²", "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("environment", "medium", "water", None, "requires"), ], ), - Entity( - name="Motorboat", - dimension="platform", - description="Small powered watercraft with outboard or inboard motor", - dependencies=[ - Dependency("environment", "water_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "3000", "kg", "range_max"), - Dependency("physical", "mass_kg", "500", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "30", "W/kg", "range_min"), - Dependency("environment", "medium", "water", None, "requires"), - ], - ), Entity( name="Submarine", dimension="platform", @@ -303,62 +115,14 @@ WATER_PLATFORMS: list[Entity] = [ dependencies=[ Dependency("environment", "water_surface", "true", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), + Dependency("physical", "footprint_m2", "200", "m²", "range_max"), + Dependency("physical", "footprint_m2", "20", "m²", "range_min"), Dependency("physical", "mass_kg", "10000", "kg", "range_min"), Dependency("force", "power_density_required_w_kg", "20", "W/kg", "range_min"), Dependency("environment", "medium", "water", None, "requires"), Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "range_min"), ], ), - Entity( - name="Kayak", - dimension="platform", - description="Small narrow watercraft propelled by a double-bladed paddle", - dependencies=[ - Dependency("environment", "water_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "35", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), - Dependency("environment", "medium", "water", None, "requires"), - ], - ), - Entity( - name="Ferry", - dimension="platform", - description="Large passenger and vehicle watercraft for short sea crossings", - dependencies=[ - Dependency("environment", "water_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "50000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "10", "W/kg", "range_min"), - Dependency("infrastructure", "port_facility", "true", None, "requires"), - Dependency("environment", "medium", "water", None, "requires"), - ], - ), - Entity( - name="Container Ship", - dimension="platform", - description="Massive ocean-going cargo vessel carrying shipping containers", - dependencies=[ - Dependency("environment", "water_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "100000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "5", "W/kg", "range_min"), - Dependency("infrastructure", "port_facility", "true", None, "requires"), - Dependency("environment", "medium", "water", None, "requires"), - ], - ), - Entity( - name="Canoe", - dimension="platform", - description="Lightweight open watercraft propelled by single-bladed paddles", - dependencies=[ - Dependency("environment", "water_surface", "true", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "40", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), - Dependency("environment", "medium", "water", None, "requires"), - ], - ), ] @@ -366,12 +130,15 @@ WATER_PLATFORMS: list[Entity] = [ AIR_PLATFORMS: list[Entity] = [ Entity( - name="Airplane", + name="Fixed-Wing Aircraft", dimension="platform", - description="Fixed-wing aircraft for atmospheric flight", + description="Airplane — fixed-wing powered atmospheric flight", dependencies=[ Dependency("environment", "atmosphere", "standard", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), + Dependency("physical", "footprint_m2", "500", "m²", "range_max"), + Dependency("physical", "footprint_m2", "10", "m²", "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("infrastructure", "runway", "true", None, "requires"), @@ -380,27 +147,31 @@ AIR_PLATFORMS: list[Entity] = [ ], ), Entity( - name="Helicopter", + name="Rotorcraft", dimension="platform", - description="Rotary-wing aircraft capable of vertical takeoff and hover", + description="Vertical-takeoff aircraft — helicopters, drones", dependencies=[ Dependency("environment", "atmosphere", "standard", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), + Dependency("physical", "footprint_m2", "20", "m²", "range_max"), + Dependency("physical", "footprint_m2", "0.5", "m²", "range_min"), Dependency("physical", "mass_kg", "5000", "kg", "range_max"), - Dependency("physical", "mass_kg", "1000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "100", "W/kg", "range_min"), + Dependency("physical", "mass_kg", "1", "kg", "range_min"), + Dependency("force", "power_density_required_w_kg", "80", "W/kg", "range_min"), Dependency("environment", "medium", "air", None, "requires"), - Dependency("physical", "energy_density_wh_kg", "300", "Wh/kg", "range_min"), + Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "range_min"), ], ), Entity( - name="Hot Air Balloon", + name="Lighter-than-Air Craft", dimension="platform", - description="Buoyancy-based aircraft using heated air in a fabric envelope", + description="Buoyancy-based aircraft — balloons, airships", dependencies=[ Dependency("environment", "atmosphere", "standard", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "500", "kg", "range_max"), + Dependency("physical", "footprint_m2", "1000", "m²", "range_max"), + Dependency("physical", "footprint_m2", "50", "m²", "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("environment", "medium", "air", None, "requires"), @@ -409,66 +180,19 @@ AIR_PLATFORMS: list[Entity] = [ Entity( name="Glider", dimension="platform", - description="Unpowered fixed-wing sailplane relying on thermals and gravity", + description="Unpowered fixed-wing aircraft — sailplanes, hang gliders, paragliders", dependencies=[ Dependency("environment", "atmosphere", "standard", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), + Dependency("physical", "footprint_m2", "20", "m²", "range_max"), + Dependency("physical", "footprint_m2", "5", "m²", "range_min"), Dependency("physical", "mass_kg", "600", "kg", "range_max"), - Dependency("physical", "mass_kg", "200", "kg", "range_min"), + Dependency("physical", "mass_kg", "5", "kg", "range_min"), Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), Dependency("infrastructure", "tow_or_winch", "true", None, "requires"), Dependency("environment", "medium", "air", None, "requires"), ], ), - Entity( - name="Drone", - dimension="platform", - description="Small unmanned rotary-wing aircraft (quadcopter)", - dependencies=[ - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "25", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "80", "W/kg", "range_min"), - Dependency("environment", "medium", "air", None, "requires"), - ], - ), - Entity( - name="Airship", - dimension="platform", - description="Lighter-than-air powered craft (blimp or zeppelin)", - dependencies=[ - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "20000", "kg", "range_max"), - Dependency("physical", "mass_kg", "5000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "5", "W/kg", "range_min"), - Dependency("environment", "medium", "air", None, "requires"), - ], - ), - Entity( - name="Hang Glider", - dimension="platform", - description="Lightweight unpowered glider launched from elevation", - dependencies=[ - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "40", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), - Dependency("environment", "medium", "air", None, "requires"), - ], - ), - Entity( - name="Paraglider", - dimension="platform", - description="Lightweight fabric wing for foot-launched free flight", - dependencies=[ - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "30", "kg", "range_max"), - Dependency("force", "power_density_required_w_kg", "1", "W/kg", "range_min"), - Dependency("environment", "medium", "air", None, "requires"), - ], - ), ] @@ -481,6 +205,8 @@ SPACE_PLATFORMS: list[Entity] = [ description="Vehicle designed for space travel", dependencies=[ Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"), + Dependency("physical", "footprint_m2", "500", "m²", "range_max"), + Dependency("physical", "footprint_m2", "10", "m²", "range_min"), Dependency("physical", "mass_kg", "5000", "kg", "range_min"), Dependency("force", "power_density_required_w_kg", "300", "W/kg", "range_min"), Dependency("infrastructure", "launch_facility", "true", None, "requires"), @@ -494,24 +220,15 @@ SPACE_PLATFORMS: list[Entity] = [ # ── Platforms — Multi-medium ──────────────────────────────────── MULTI_PLATFORMS: list[Entity] = [ - Entity( - name="Hovercraft", - dimension="platform", - description="Air-cushion vehicle that travels over land, water, or ice", - dependencies=[ - Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "10000", "kg", "range_max"), - Dependency("physical", "mass_kg", "2000", "kg", "range_min"), - Dependency("force", "power_density_required_w_kg", "30", "W/kg", "range_min"), - ], - ), Entity( name="Amphibious Vehicle", dimension="platform", - description="Vehicle capable of operation on both land and water", + description="Vehicle capable of operation on land, water, or both", dependencies=[ Dependency("environment", "gravity", "true", None, "requires"), - Dependency("physical", "mass_kg", "5000", "kg", "range_max"), + Dependency("physical", "footprint_m2", "100", "m²", "range_max"), + Dependency("physical", "footprint_m2", "5", "m²", "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"), ], @@ -527,6 +244,8 @@ FICTIONAL_PLATFORMS: list[Entity] = [ dimension="platform", description="Hypothetical matter transmission device", dependencies=[ + Dependency("physical", "footprint_m2", "10", "m²", "range_max"), + Dependency("physical", "footprint_m2", "1", "m²", "range_min"), Dependency("physical", "mass_kg", "0", "kg", "range_min"), Dependency("force", "power_density_required_w_kg", "1000", "W/kg", "range_min"), Dependency("infrastructure", "teleport_network", "true", None, "requires"), @@ -539,6 +258,8 @@ FICTIONAL_PLATFORMS: list[Entity] = [ dependencies=[ Dependency("environment", "ground_surface", "true", None, "requires"), Dependency("environment", "gravity", "true", None, "requires"), + Dependency("physical", "footprint_m2", "50", "m²", "range_max"), + Dependency("physical", "footprint_m2", "5", "m²", "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"), @@ -561,210 +282,103 @@ PLATFORMS: list[Entity] = ( ) -# ── Power Sources — Combustion ────────────────────────────────── +# ── Actuators — Combustion ────────────────────────────────────── -COMBUSTION_SOURCES: list[Entity] = [ +COMBUSTION_ACTUATORS: list[Entity] = [ Entity( - name="Internal Combustion Engine", - dimension="power_source", - description="Gas/petrol-powered engine", + name="Piston Engine", + dimension="actuator", + description="Reciprocating combustion engine — petrol or diesel", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "gas_station", None, "requires"), + Dependency("energy", "energy_form", "chemical_combustible", None, "requires"), Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("physical", "mass_kg", "50", "kg", "range_min"), + Dependency("physical", "mass_kg", "45", "kg", "range_min"), Dependency("force", "thrust_profile", "high_continuous", None, "provides"), Dependency("force", "power_density_w_kg", "1000", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "1500", "Wh/kg", "provides"), ], ), Entity( - name="Diesel Engine", - dimension="power_source", - description="Compression-ignition engine running on diesel fuel", + name="Gas Turbine", + dimension="actuator", + description="Turbine engine — jets, turboshafts, turboprops", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "diesel_station", None, "requires"), - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("physical", "mass_kg", "60", "kg", "range_min"), - Dependency("force", "thrust_profile", "high_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "700", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "1300", "Wh/kg", "provides"), - ], - ), - Entity( - name="Hydrogen Combustion Engine", - dimension="power_source", - description="Hydrogen fuel cell or combustion engine", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "hydrogen_station", None, "requires"), - Dependency("physical", "mass_kg", "30", "kg", "range_min"), - Dependency("force", "thrust_profile", "high_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "1500", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "600", "Wh/kg", "provides"), - ], - ), - Entity( - name="Compressed Natural Gas Engine", - dimension="power_source", - description="Engine running on compressed natural gas (CNG)", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "cng_station", None, "requires"), - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("physical", "mass_kg", "55", "kg", "range_min"), - Dependency("force", "thrust_profile", "high_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "500", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "800", "Wh/kg", "provides"), - ], - ), - Entity( - name="Biofuel Engine", - dimension="power_source", - description="Internal combustion engine running on biodiesel or ethanol", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "biofuel_supply", None, "requires"), - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("physical", "mass_kg", "50", "kg", "range_min"), - Dependency("force", "thrust_profile", "high_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "800", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "1000", "Wh/kg", "provides"), - ], - ), - Entity( - name="Coal Steam Engine", - dimension="power_source", - description="Coal-fired boiler with steam locomotion", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "coal_supply", None, "requires"), - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("physical", "mass_kg", "500", "kg", "range_min"), - Dependency("force", "thrust_profile", "high_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "30", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "400", "Wh/kg", "provides"), - ], - ), -] - - -# ── Power Sources — Turbine ───────────────────────────────────── - -TURBINE_SOURCES: list[Entity] = [ - Entity( - name="Jet Turbine", - dimension="power_source", - description="Turbofan jet engine for high-speed atmospheric flight", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "jet_fuel", None, "requires"), - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("physical", "mass_kg", "500", "kg", "range_min"), - Dependency("force", "thrust_profile", "extreme_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "7000", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "1200", "Wh/kg", "provides"), - Dependency("environment", "medium", "air", None, "requires"), - ], - ), - Entity( - name="Turboshaft Engine", - dimension="power_source", - description="Gas turbine driving a shaft, used in helicopters and ships", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "jet_fuel", None, "requires"), + Dependency("energy", "energy_form", "chemical_combustible", None, "requires"), Dependency("environment", "atmosphere", "standard", None, "requires"), Dependency("physical", "mass_kg", "200", "kg", "range_min"), - Dependency("force", "thrust_profile", "high_continuous", None, "provides"), + Dependency("force", "thrust_profile", "extreme_continuous", None, "provides"), Dependency("force", "power_density_w_kg", "5000", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "1200", "Wh/kg", "provides"), ], ), Entity( - name="Turboprop Engine", - dimension="power_source", - description="Gas turbine driving a propeller for moderate-speed flight", + name="Steam Engine", + dimension="actuator", + description="External combustion engine using steam for mechanical power", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "jet_fuel", None, "requires"), + Dependency("energy", "energy_form", "chemical_combustible", None, "requires"), Dependency("environment", "atmosphere", "standard", None, "requires"), Dependency("physical", "mass_kg", "300", "kg", "range_min"), Dependency("force", "thrust_profile", "high_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "4000", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "1200", "Wh/kg", "provides"), - Dependency("environment", "medium", "air", None, "requires"), + Dependency("force", "power_density_w_kg", "30", "W/kg", "provides"), ], ), ] -# ── Power Sources — Electric ──────────────────────────────────── +# ── Actuators — Electric ─────────────────────────────────────── -ELECTRIC_SOURCES: list[Entity] = [ +ELECTRIC_ACTUATORS: list[Entity] = [ Entity( - name="Lithium Ion Battery", - dimension="power_source", - description="Rechargeable electric battery pack", + name="Electric Motor", + dimension="actuator", + description="Rotary motor converting electrical energy to mechanical motion", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "charging_station", None, "requires"), - Dependency("physical", "mass_kg", "10", "kg", "range_min"), + Dependency("energy", "energy_form", "electrical", None, "requires"), + Dependency("physical", "mass_kg", "5", "kg", "range_min"), Dependency("force", "thrust_profile", "moderate_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "500", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"), - ], - ), - Entity( - name="Solid State Battery", - dimension="power_source", - description="Next-generation rechargeable battery with solid electrolyte", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "charging_station", None, "requires"), - Dependency("physical", "mass_kg", "8", "kg", "range_min"), - Dependency("force", "thrust_profile", "moderate_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "800", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "400", "Wh/kg", "provides"), - ], - ), - Entity( - name="Supercapacitor", - dimension="power_source", - description="High-power energy storage for rapid charge/discharge cycles", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "charging_station", None, "requires"), - Dependency("physical", "mass_kg", "15", "kg", "range_min"), - Dependency("force", "thrust_profile", "high_burst", None, "provides"), - Dependency("force", "power_density_w_kg", "10000", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "10", "Wh/kg", "provides"), + Dependency("force", "power_density_w_kg", "2000", "W/kg", "provides"), ], ), ] -# ── Power Sources — Renewable ─────────────────────────────────── +# ── Actuators — Biological ───────────────────────────────────── -RENEWABLE_SOURCES: list[Entity] = [ +BIOLOGICAL_ACTUATORS: list[Entity] = [ Entity( - name="Wind Sail", - dimension="power_source", - description="Fabric or rigid sail harnessing wind for propulsion", + name="Human Muscle", + dimension="actuator", + description="Human-powered via pedalling, pushing, or rowing", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"), - Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("physical", "mass_kg", "20", "kg", "range_min"), + Dependency("energy", "energy_form", "biological", None, "requires"), + Dependency("physical", "mass_kg", "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_w_kg", "1.5", "W/kg", "provides"), ], ), Entity( - name="Solar Photovoltaic Panel", - dimension="power_source", - description="Photovoltaic cells converting sunlight to electricity", + name="Animal Traction", + dimension="actuator", + description="Animal muscle power (horse, ox, or dog team)", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"), - Dependency("environment", "star_proximity", "true", None, "requires"), - Dependency("physical", "mass_kg", "10", "kg", "range_min"), - Dependency("force", "thrust_profile", "continuous_low", None, "provides"), - Dependency("force", "power_density_w_kg", "20", "W/kg", "provides"), + Dependency("energy", "energy_form", "biological", None, "requires"), + Dependency("environment", "ground_surface", "true", None, "requires"), + Dependency("physical", "mass_kg", "0", "kg", "range_min"), + Dependency("force", "thrust_profile", "low_continuous", None, "provides"), + Dependency("force", "power_density_w_kg", "2", "W/kg", "provides"), ], ), +] + + +# ── Actuators — Renewable / Ambient ──────────────────────────── + +RENEWABLE_ACTUATORS: list[Entity] = [ Entity( name="Solar Sail", - dimension="power_source", - description="Propulsion via radiation pressure from a star", + dimension="actuator", + description="Reflective surface propelled by radiation pressure", dependencies=[ + 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"), @@ -773,143 +387,105 @@ RENEWABLE_SOURCES: list[Entity] = [ Dependency("environment", "medium", "space", None, "requires"), ], ), -] - - -# ── Power Sources — Biological / Human ────────────────────────── - -BIOLOGICAL_SOURCES: list[Entity] = [ Entity( - name="Human Pedalling", - dimension="power_source", - description="Human-powered via pedalling mechanism", + name="Wind Sail", + dimension="actuator", + description="Fabric or rigid sail harnessing wind for propulsion", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"), - Dependency("physical", "mass_kg", "0", "kg", "range_min"), + Dependency("energy", "energy_form", "wind", None, "requires"), + Dependency("environment", "atmosphere", "standard", None, "requires"), + Dependency("physical", "mass_kg", "15", "kg", "range_min"), Dependency("force", "thrust_profile", "low_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "1.5", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"), - ], - ), - Entity( - name="Animal Muscle", - dimension="power_source", - description="Animal traction power (horse, ox, or dog team)", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "feed_supply", None, "requires"), - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("physical", "mass_kg", "0", "kg", "range_min"), - Dependency("force", "thrust_profile", "low_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "2", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"), - ], - ), - Entity( - name="Pushed by a Friend", - dimension="power_source", - description="External human pushing the vehicle", - dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"), - Dependency("physical", "mass_kg", "0", "kg", "range_min"), - Dependency("environment", "ground_surface", "true", None, "requires"), - Dependency("force", "thrust_profile", "low_continuous", None, "provides"), - Dependency("force", "power_density_w_kg", "1", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"), + Dependency("force", "power_density_w_kg", "10", "W/kg", "provides"), ], ), ] -# ── Power Sources — Rocket / Space ────────────────────────────── +# ── Actuators — Rocket / Space ───────────────────────────────── -ROCKET_SOURCES: list[Entity] = [ +ROCKET_ACTUATORS: list[Entity] = [ Entity( - name="Solid Rocket Motor", - dimension="power_source", - description="Solid propellant rocket providing massive thrust in bursts", + name="Rocket Nozzle", + dimension="actuator", + description="Thrust from expanding combustion gases through a nozzle", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "solid_propellant", None, "requires"), - Dependency("physical", "mass_kg", "200", "kg", "range_min"), + Dependency("energy", "energy_form", "chemical_propellant", None, "requires"), + Dependency("physical", "mass_kg", "150", "kg", "range_min"), Dependency("force", "thrust_profile", "extreme_burst", None, "provides"), Dependency("force", "power_density_w_kg", "10000", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "500", "Wh/kg", "provides"), ], ), Entity( - name="Ion Thruster", - dimension="power_source", - description="Electric propulsion using ionized xenon gas for deep space", + name="Ion Drive", + dimension="actuator", + description="Electric propulsion using ionized gas for deep-space travel", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "xenon_propellant", None, "requires"), + Dependency("energy", "energy_form", "ion_propellant", None, "requires"), Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"), - Dependency("physical", "mass_kg", "10", "kg", "range_min"), + Dependency("environment", "medium", "space", None, "requires"), + Dependency("physical", "mass_kg", "8", "kg", "range_min"), Dependency("force", "thrust_profile", "continuous_low", None, "provides"), Dependency("force", "power_density_w_kg", "30", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "3000", "Wh/kg", "provides"), - Dependency("environment", "medium", "space", None, "requires"), ], ), Entity( - name="Modular Nuclear Reactor", - dimension="power_source", - description="Small modular nuclear fission reactor", + name="Nuclear Thermal Drive", + dimension="actuator", + description="Nuclear fission reactor heating propellant for thrust", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "nuclear_fuel", None, "requires"), - Dependency("physical", "mass_kg", "2000", "kg", "range_min"), + Dependency("energy", "energy_form", "nuclear_thermal", None, "requires"), + Dependency("physical", "mass_kg", "1500", "kg", "range_min"), Dependency("force", "thrust_profile", "extreme_continuous", None, "provides"), Dependency("force", "power_density_w_kg", "50", "W/kg", "provides"), Dependency("material", "radiation_shielding", "true", None, "requires"), - Dependency("physical", "energy_density_wh_kg", "500000", "Wh/kg", "provides"), ], ), ] -# ── Power Sources — Exotic / Fun ──────────────────────────────── +# ── Actuators — Exotic ───────────────────────────────────────── -EXOTIC_SOURCES: list[Entity] = [ +EXOTIC_ACTUATORS: list[Entity] = [ Entity( - name="Cannonfire Recoil", - dimension="power_source", + name="Cannon Recoil Drive", + dimension="actuator", description="Propulsion via sequential cannon blasts", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "ammunition", None, "requires"), - Dependency("physical", "mass_kg", "100", "kg", "range_min"), + Dependency("energy", "energy_form", "chemical_explosive", None, "requires"), + Dependency("physical", "mass_kg", "80", "kg", "range_min"), Dependency("force", "thrust_profile", "high_burst", None, "provides"), Dependency("force", "power_density_w_kg", "3000", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"), ], ), Entity( - name="Compressed Air Engine", - dimension="power_source", - description="Engine powered by compressed air stored in high-pressure tanks", + name="Compressed Air Motor", + dimension="actuator", + description="Motor powered by expanding compressed air", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "compressed_air_station", None, "requires"), - Dependency("physical", "mass_kg", "20", "kg", "range_min"), + Dependency("energy", "energy_form", "pneumatic", None, "requires"), + Dependency("physical", "mass_kg", "10", "kg", "range_min"), Dependency("force", "thrust_profile", "moderate_continuous", None, "provides"), Dependency("force", "power_density_w_kg", "100", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "30", "Wh/kg", "provides"), ], ), Entity( - name="Flywheel", - dimension="power_source", - description="Kinetic energy stored in a spinning rotor", + name="Flywheel Drive", + dimension="actuator", + description="Mechanical drive transferring kinetic energy from a spinning rotor", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "charging_station", None, "requires"), - Dependency("physical", "mass_kg", "50", "kg", "range_min"), + Dependency("energy", "energy_form", "kinetic_stored", None, "requires"), + Dependency("physical", "mass_kg", "20", "kg", "range_min"), Dependency("force", "thrust_profile", "high_burst", None, "provides"), Dependency("force", "power_density_w_kg", "2000", "W/kg", "provides"), - Dependency("physical", "energy_density_wh_kg", "50", "Wh/kg", "provides"), ], ), Entity( - name="Gravity Downhill", - dimension="power_source", - description="Gravitational potential energy on slopes and descents", + name="Gravity Coast", + dimension="actuator", + description="Free-rolling descent using gravitational potential energy", dependencies=[ - Dependency("infrastructure", "fuel_infrastructure", "none", None, "requires"), + 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"), @@ -920,16 +496,264 @@ EXOTIC_SOURCES: list[Entity] = [ ] -# ── All Power Sources ─────────────────────────────────────────── +# ── All Actuators ────────────────────────────────────────────── -POWER_SOURCES: list[Entity] = ( - COMBUSTION_SOURCES - + TURBINE_SOURCES - + ELECTRIC_SOURCES - + RENEWABLE_SOURCES - + BIOLOGICAL_SOURCES - + ROCKET_SOURCES - + EXOTIC_SOURCES +ACTUATORS: list[Entity] = ( + COMBUSTION_ACTUATORS + + ELECTRIC_ACTUATORS + + BIOLOGICAL_ACTUATORS + + RENEWABLE_ACTUATORS + + ROCKET_ACTUATORS + + EXOTIC_ACTUATORS +) + + +# ── Energy Storage — Combustible Fuels ───────────────────────── + +COMBUSTIBLE_STORAGE: list[Entity] = [ + Entity( + name="Liquid Fuel", + dimension="energy_storage", + description="Liquid hydrocarbon fuel — gasoline, diesel, or biofuel", + 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"), + ], + ), + Entity( + name="Hydrogen", + dimension="energy_storage", + description="Compressed or liquefied hydrogen gas", + 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"), + ], + ), + Entity( + name="Compressed Natural Gas", + dimension="energy_storage", + description="Methane stored under high pressure in tanks", + 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"), + ], + ), + Entity( + name="Coal", + dimension="energy_storage", + description="Solid fossil fuel for steam boilers", + 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"), + ], + ), + Entity( + name="Jet Fuel", + dimension="energy_storage", + description="Kerosene-based fuel for turbine engines", + 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"), + ], + ), +] + + +# ── Energy Storage — Electrical ──────────────────────────────── + +ELECTRICAL_STORAGE: list[Entity] = [ + Entity( + name="Rechargeable Battery", + dimension="energy_storage", + description="Rechargeable battery pack — lithium-ion or solid-state", + 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"), + ], + ), + Entity( + name="Supercapacitor", + dimension="energy_storage", + description="High-power energy storage for rapid charge/discharge", + 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"), + ], + ), + Entity( + name="Solar Photovoltaic Panel", + dimension="energy_storage", + description="Photovoltaic cells converting sunlight to electricity", + dependencies=[ + 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"), + ], + ), +] + + +# ── Energy Storage — Biological ──────────────────────────────── + +BIOLOGICAL_STORAGE: list[Entity] = [ + Entity( + name="Biological Feed", + dimension="energy_storage", + description="Metabolic energy from food or animal fodder", + 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"), + ], + ), +] + + +# ── Energy Storage — Ambient ─────────────────────────────────── + +AMBIENT_STORAGE: list[Entity] = [ + Entity( + name="Solar Radiation", + dimension="energy_storage", + description="Photon flux from a nearby star providing radiation pressure", + dependencies=[ + 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"), + ], + ), + Entity( + name="Wind", + dimension="energy_storage", + description="Atmospheric air currents providing kinetic energy", + dependencies=[ + 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"), + ], + ), +] + + +# ── Energy Storage — Propellant ──────────────────────────────── + +PROPELLANT_STORAGE: list[Entity] = [ + Entity( + name="Solid Propellant", + dimension="energy_storage", + description="Solid rocket fuel providing chemical propulsion energy", + 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"), + ], + ), + Entity( + name="Xenon Propellant", + dimension="energy_storage", + description="Ionizable xenon gas for electric propulsion", + 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"), + ], + ), + Entity( + name="Nuclear Fuel", + dimension="energy_storage", + description="Enriched uranium or thorium fuel rods for fission reactors", + 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("material", "radiation_shielding", "true", None, "requires"), + ], + ), +] + + +# ── Energy Storage — Exotic ──────────────────────────────────── + +EXOTIC_STORAGE: list[Entity] = [ + Entity( + name="Gunpowder/Ammunition", + dimension="energy_storage", + description="Black powder or cartridge ammunition for explosive propulsion", + 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"), + ], + ), + Entity( + name="Compressed Air", + dimension="energy_storage", + description="Air stored in high-pressure tanks", + 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"), + ], + ), + Entity( + name="Flywheel Store", + dimension="energy_storage", + description="Kinetic energy stored in a spinning rotor", + 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"), + ], + ), + Entity( + name="Gravitational Potential", + dimension="energy_storage", + description="Stored energy from elevation difference (hills, slopes)", + dependencies=[ + Dependency("energy", "energy_form", "gravitational", None, "provides"), + 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"), + ], + ), +] + + +# ── All Energy Storages ──────────────────────────────────────── + +ENERGY_STORAGES: list[Entity] = ( + COMBUSTIBLE_STORAGE + + ELECTRICAL_STORAGE + + BIOLOGICAL_STORAGE + + AMBIENT_STORAGE + + PROPELLANT_STORAGE + + EXOTIC_STORAGE ) @@ -1010,7 +834,7 @@ def load_transport_seed(repo) -> dict: from physcom.db.repository import Repository repo: Repository - counts = {"platforms": 0, "power_sources": 0, "domains": 0} + counts = {"platforms": 0, "actuators": 0, "energy_storages": 0, "domains": 0} for entity in PLATFORMS: try: @@ -1021,10 +845,19 @@ def load_transport_seed(repo) -> dict: if existing: repo.replace_entity_dependencies(existing.id, entity.dependencies) - for entity in POWER_SOURCES: + for entity in ACTUATORS: try: repo.add_entity(entity) - counts["power_sources"] += 1 + counts["actuators"] += 1 + except sqlite3.IntegrityError: + existing = repo.get_entity_by_name(entity.dimension, entity.name) + if existing: + repo.replace_entity_dependencies(existing.id, entity.dependencies) + + for entity in ENERGY_STORAGES: + try: + repo.add_entity(entity) + counts["energy_storages"] += 1 except sqlite3.IntegrityError: existing = repo.get_entity_by_name(entity.dimension, entity.name) if existing: diff --git a/src/physcom_web/routes/admin.py b/src/physcom_web/routes/admin.py index 8f622cc..426d3df 100644 --- a/src/physcom_web/routes/admin.py +++ b/src/physcom_web/routes/admin.py @@ -30,7 +30,7 @@ def admin_index(): def reseed(): repo = get_repo() counts = load_transport_seed(repo) - total = counts["platforms"] + counts["power_sources"] + total = counts["platforms"] + counts["actuators"] + counts["energy_storages"] flash( f"Reseed complete — added {total} entities, {counts['domains']} domains.", "success", @@ -43,7 +43,7 @@ def wipe_and_reseed(): repo = get_repo() repo.clear_all() counts = load_transport_seed(repo) - total = counts["platforms"] + counts["power_sources"] + total = counts["platforms"] + counts["actuators"] + counts["energy_storages"] flash( f"Wiped all data and reseeded — {total} entities, {counts['domains']} domains.", "success", diff --git a/src/physcom_web/templates/home.html b/src/physcom_web/templates/home.html index 52d5c09..9db8ccb 100644 --- a/src/physcom_web/templates/home.html +++ b/src/physcom_web/templates/home.html @@ -118,7 +118,7 @@

Entities

The building blocks. Each entity belongs to a dimension - (e.g. platform, power_source) and carries typed dependencies + (e.g. platform, actuator, energy_storage) and carries typed dependencies that define its physical properties and constraints.

diff --git a/tests/conftest.py b/tests/conftest.py index 61dae27..7a45f46 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -78,9 +78,10 @@ def spaceship(): def solar_sail(): return Entity( name="Solar Sail", - dimension="power_source", + dimension="actuator", description="Propulsion via radiation pressure", dependencies=[ + Dependency("energy", "energy_form", "radiation_pressure", None, "requires"), Dependency("environment", "atmosphere", "vacuum_or_thin", None, "requires"), Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"), Dependency("environment", "medium", "space", None, "requires"), @@ -88,13 +89,27 @@ def solar_sail(): ) +@pytest.fixture +def solar_radiation(): + return Entity( + name="Solar Radiation", + dimension="energy_storage", + description="Photon flux from a nearby star", + dependencies=[ + Dependency("energy", "energy_form", "radiation_pressure", None, "provides"), + Dependency("physical", "mass_kg", "0", "kg", "range_min"), + ], + ) + + @pytest.fixture def human_pedalling(): return Entity( name="Human Pedalling", - dimension="power_source", + dimension="actuator", description="Human-powered via pedalling", dependencies=[ + Dependency("energy", "energy_form", "biological", None, "requires"), Dependency("force", "power_density_w_kg", "1.5", "W/kg", "provides"), Dependency("physical", "mass_kg", "0", "kg", "range_min"), ], @@ -102,14 +117,43 @@ def human_pedalling(): @pytest.fixture -def nuclear_reactor(): +def food_calories(): return Entity( - name="Modular Nuclear Reactor", - dimension="power_source", - description="Small modular nuclear fission reactor", + name="Food/Calories", + dimension="energy_storage", + description="Metabolic energy from food", dependencies=[ + Dependency("energy", "energy_form", "biological", None, "provides"), + Dependency("physical", "mass_kg", "0", "kg", "range_min"), + Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"), + ], + ) + + +@pytest.fixture +def nuclear_thermal_drive(): + return Entity( + name="Nuclear Thermal Drive", + dimension="actuator", + description="Nuclear fission reactor for thrust", + dependencies=[ + Dependency("energy", "energy_form", "nuclear_thermal", None, "requires"), Dependency("force", "power_density_w_kg", "50", "W/kg", "provides"), - Dependency("physical", "mass_kg", "2000", "kg", "range_min"), + Dependency("physical", "mass_kg", "1500", "kg", "range_min"), + ], + ) + + +@pytest.fixture +def nuclear_fuel(): + return Entity( + name="Nuclear Fuel", + dimension="energy_storage", + description="Enriched uranium fuel rods", + dependencies=[ + Dependency("energy", "energy_form", "nuclear_thermal", None, "provides"), + Dependency("physical", "mass_kg", "500", "kg", "range_min"), + Dependency("physical", "energy_density_wh_kg", "500000", "Wh/kg", "provides"), ], ) @@ -118,11 +162,12 @@ def nuclear_reactor(): def hydrogen_engine(): return Entity( name="Hydrogen Combustion Engine", - dimension="power_source", - description="Hydrogen fuel cell", + dimension="actuator", + description="Hydrogen combustion engine", dependencies=[ + Dependency("energy", "energy_form", "chemical_combustible", None, "requires"), Dependency("force", "power_density_w_kg", "1500", "W/kg", "provides"), - Dependency("physical", "mass_kg", "30", "kg", "range_min"), + Dependency("physical", "mass_kg", "25", "kg", "range_min"), ], ) @@ -131,12 +176,41 @@ def hydrogen_engine(): def ice_engine(): return Entity( name="Internal Combustion Engine", - dimension="power_source", - description="Gas-powered engine", + dimension="actuator", + description="Spark-ignition engine", dependencies=[ + Dependency("energy", "energy_form", "chemical_combustible", None, "requires"), Dependency("force", "power_density_w_kg", "1000", "W/kg", "provides"), Dependency("environment", "atmosphere", "standard", None, "requires"), - Dependency("physical", "mass_kg", "50", "kg", "range_min"), + Dependency("physical", "mass_kg", "40", "kg", "range_min"), + ], + ) + + +@pytest.fixture +def gasoline(): + return Entity( + name="Gasoline", + dimension="energy_storage", + description="Liquid hydrocarbon fuel", + dependencies=[ + Dependency("energy", "energy_form", "chemical_combustible", None, "provides"), + Dependency("physical", "mass_kg", "10", "kg", "range_min"), + Dependency("physical", "energy_density_wh_kg", "1500", "Wh/kg", "provides"), + ], + ) + + +@pytest.fixture +def hydrogen(): + return Entity( + name="Hydrogen", + dimension="energy_storage", + description="Compressed hydrogen gas", + dependencies=[ + Dependency("energy", "energy_form", "chemical_combustible", None, "provides"), + Dependency("physical", "mass_kg", "5", "kg", "range_min"), + Dependency("physical", "energy_density_wh_kg", "600", "Wh/kg", "provides"), ], ) diff --git a/tests/test_admin.py b/tests/test_admin.py index c309afc..71356fe 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -32,7 +32,7 @@ def test_reseed_after_clear_restores_data(seeded_repo): counts = load_transport_seed(repo) assert len(repo.list_entities()) == original_entities assert len(repo.list_domains()) == original_domains - assert counts["platforms"] + counts["power_sources"] == original_entities + assert counts["platforms"] + counts["actuators"] + counts["energy_storages"] == original_entities def test_additive_reseed_no_duplicates(seeded_repo): @@ -44,5 +44,5 @@ def test_additive_reseed_no_duplicates(seeded_repo): assert len(repo.list_entities()) == before assert counts["platforms"] == 0 - assert counts["power_sources"] == 0 + assert counts["actuators"] + counts["energy_storages"] == 0 assert counts["domains"] == 0 diff --git a/tests/test_combinator.py b/tests/test_combinator.py index 61354dd..421e2fc 100644 --- a/tests/test_combinator.py +++ b/tests/test_combinator.py @@ -7,20 +7,21 @@ from physcom.models.entity import Entity def test_generates_cartesian_product(seeded_repo): - from physcom.seed.transport_example import PLATFORMS, POWER_SOURCES + from physcom.seed.transport_example import PLATFORMS, ACTUATORS, ENERGY_STORAGES - combos = generate_combinations(seeded_repo, ["platform", "power_source"]) - expected = len(PLATFORMS) * len(POWER_SOURCES) + combos = generate_combinations(seeded_repo, ["platform", "actuator", "energy_storage"]) + expected = len(PLATFORMS) * len(ACTUATORS) * len(ENERGY_STORAGES) assert len(combos) == expected def test_each_combo_has_one_per_dimension(seeded_repo): - combos = generate_combinations(seeded_repo, ["platform", "power_source"]) + combos = generate_combinations(seeded_repo, ["platform", "actuator", "energy_storage"]) for combo in combos: dims = [e.dimension for e in combo.entities] assert "platform" in dims - assert "power_source" in dims - assert len(combo.entities) == 2 + assert "actuator" in dims + assert "energy_storage" in dims + assert len(combo.entities) == 3 def test_missing_dimension_raises(seeded_repo): diff --git a/tests/test_constraint_resolver.py b/tests/test_constraint_resolver.py index ff51a98..0d970c1 100644 --- a/tests/test_constraint_resolver.py +++ b/tests/test_constraint_resolver.py @@ -5,37 +5,37 @@ from physcom.models.combination import Combination from physcom.models.entity import Entity, Dependency -def test_compatible_ground_combo(bicycle, human_pedalling): - """Bicycle + Human Pedalling should be valid.""" +def test_compatible_ground_combo(bicycle, human_pedalling, food_calories): + """Bicycle + Human Pedalling + Food/Calories should be valid.""" resolver = ConstraintResolver() - combo = Combination(entities=[bicycle, human_pedalling]) + combo = Combination(entities=[bicycle, human_pedalling, food_calories]) result = resolver.resolve(combo) assert result.status != "p1_fail", f"Unexpected block: {result.violations}" -def test_solar_sail_blocks_with_walking(walking, solar_sail): +def test_solar_sail_blocks_with_walking(walking, solar_sail, solar_radiation): """Walking (ground) + Solar Sail (space) should be blocked by medium mutex.""" resolver = ConstraintResolver() - combo = Combination(entities=[walking, solar_sail]) + combo = Combination(entities=[walking, solar_sail, solar_radiation]) result = resolver.resolve(combo) assert result.status == "p1_fail" assert any("mutually exclusive" in v for v in result.violations) -def test_spaceship_compatible_with_solar_sail(spaceship, solar_sail): +def test_spaceship_compatible_with_solar_sail(spaceship, solar_sail, solar_radiation): """Spaceship + Solar Sail both need space/vacuum — should not conflict.""" resolver = ConstraintResolver() - combo = Combination(entities=[spaceship, solar_sail]) + combo = Combination(entities=[spaceship, solar_sail, solar_radiation]) result = resolver.resolve(combo) # Should not be blocked by atmosphere or medium medium_blocks = [v for v in result.violations if "mutually exclusive" in v] assert len(medium_blocks) == 0 -def test_nuclear_reactor_blocks_with_bicycle(bicycle, nuclear_reactor): - """Nuclear reactor min_mass=2000kg vs bicycle max_mass=30kg → range incompatibility.""" +def test_nuclear_drive_blocks_with_bicycle(bicycle, nuclear_thermal_drive, nuclear_fuel): + """Nuclear drive min_mass=1500kg + fuel min_mass=500kg vs bicycle max_mass=30kg → range incompatibility.""" resolver = ConstraintResolver() - combo = Combination(entities=[bicycle, nuclear_reactor]) + combo = Combination(entities=[bicycle, nuclear_thermal_drive, nuclear_fuel]) result = resolver.resolve(combo) assert result.status == "p1_fail" assert any("mass" in v.lower() for v in result.violations) @@ -50,7 +50,7 @@ def test_power_density_mismatch_blocks(): ], ) power = Entity( - name="TinyPower", dimension="power_source", + name="TinyPower", dimension="actuator", dependencies=[ Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"), ], @@ -71,7 +71,7 @@ def test_power_density_under_powered_warning(): ], ) power = Entity( - name="WeakPower", dimension="power_source", + name="WeakPower", dimension="actuator", dependencies=[ Dependency("force", "power_density_w_kg", "50", "W/kg", "provides"), ], @@ -91,7 +91,7 @@ def test_requires_vs_excludes(): dependencies=[Dependency("environment", "oxygen", "true", None, "requires")], ) b = Entity( - name="B", dimension="power_source", + name="B", dimension="actuator", dependencies=[Dependency("environment", "oxygen", "true", None, "excludes")], ) resolver = ConstraintResolver() @@ -101,21 +101,21 @@ def test_requires_vs_excludes(): assert any("excludes" in v for v in result.violations) -def test_ice_engine_blocks_with_spaceship(spaceship, ice_engine): +def test_ice_engine_blocks_with_spaceship(spaceship, ice_engine, gasoline): """ICE requires standard atmosphere, spaceship requires vacuum_or_thin → mutex.""" resolver = ConstraintResolver() - combo = Combination(entities=[spaceship, ice_engine]) + combo = Combination(entities=[spaceship, ice_engine, gasoline]) result = resolver.resolve(combo) assert result.status == "p1_fail" assert any("atmosphere" in v for v in result.violations) -def test_hydrogen_bicycle_valid(bicycle, hydrogen_engine): +def test_hydrogen_bicycle_valid(bicycle, hydrogen_engine, hydrogen): """Hydrogen bike — the README's example of a plausible novel concept.""" resolver = ConstraintResolver() - combo = Combination(entities=[bicycle, hydrogen_engine]) + combo = Combination(entities=[bicycle, hydrogen_engine, hydrogen]) result = resolver.resolve(combo) - # Should pass constraints (mass range is compatible: h2 min 30kg, bike max 30kg) + # Should pass constraints (mass range is compatible: h2 engine min 25kg, bike max 30kg) # This is actually a borderline case — let's just verify no hard physics blocks range_blocks = [v for v in result.violations if "mutually exclusive" in v or "atmosphere" in v] assert len(range_blocks) == 0 @@ -129,14 +129,14 @@ def test_energy_density_deficit_blocks(): Dependency("physical", "energy_density_wh_kg", "2000", "Wh/kg", "range_min"), ], ) - power = Entity( - name="Battery", dimension="power_source", + storage = Entity( + name="Battery", dimension="energy_storage", dependencies=[ Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"), ], ) resolver = ConstraintResolver() - combo = Combination(entities=[platform, power]) + combo = Combination(entities=[platform, storage]) result = resolver.resolve(combo) assert result.status == "p1_fail" assert any("energy density deficit" in v for v in result.violations) @@ -150,14 +150,14 @@ def test_energy_density_under_density_warning(): Dependency("physical", "energy_density_wh_kg", "400", "Wh/kg", "range_min"), ], ) - power = Entity( - name="Battery", dimension="power_source", + storage = Entity( + name="Battery", dimension="energy_storage", dependencies=[ Dependency("physical", "energy_density_wh_kg", "200", "Wh/kg", "provides"), ], ) resolver = ConstraintResolver() - combo = Combination(entities=[platform, power]) + combo = Combination(entities=[platform, storage]) result = resolver.resolve(combo) assert result.status != "p1_fail" assert any("under-density" in w for w in result.warnings) @@ -172,14 +172,14 @@ def test_energy_density_no_constraint_if_no_provider(): ], ) # Solar Sail-style: no energy_density_wh_kg declared - power = Entity( - name="Solar Sail", dimension="power_source", + actuator = Entity( + name="Solar Sail", dimension="actuator", dependencies=[ Dependency("force", "power_density_w_kg", "0.01", "W/kg", "provides"), ], ) resolver = ConstraintResolver() - combo = Combination(entities=[platform, power]) + combo = Combination(entities=[platform, actuator]) result = resolver.resolve(combo) density_violations = [v for v in result.violations if "energy density" in v] assert len(density_violations) == 0 diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index 088a94a..0890048 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -13,10 +13,10 @@ def test_pass1_filters_impossible_combos(seeded_repo): scorer = Scorer(domain) pipeline = Pipeline(seeded_repo, resolver, scorer) - result = pipeline.run(domain, ["platform", "power_source"], passes=[1]) + result = pipeline.run(domain, ["platform", "actuator", "energy_storage"], passes=[1]) - from physcom.seed.transport_example import PLATFORMS, POWER_SOURCES - expected = len(PLATFORMS) * len(POWER_SOURCES) + from physcom.seed.transport_example import PLATFORMS, ACTUATORS, ENERGY_STORAGES + expected = len(PLATFORMS) * len(ACTUATORS) * len(ENERGY_STORAGES) assert result.total_generated == expected assert result.pass1_failed > 0 assert result.pass1_valid + result.pass1_conditional + result.pass1_failed == expected @@ -30,7 +30,7 @@ def test_pass123_produces_scored_results(seeded_repo): pipeline = Pipeline(seeded_repo, resolver, scorer) result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3, 5], ) @@ -50,7 +50,7 @@ def test_pass4_with_mock_llm(seeded_repo): pipeline = Pipeline(seeded_repo, resolver, scorer, llm=mock_llm) result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3, 4, 5], ) @@ -65,7 +65,7 @@ def test_blocked_combos_not_scored(seeded_repo): pipeline = Pipeline(seeded_repo, resolver, scorer) result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.0, passes=[1, 2, 3, 5], ) diff --git a/tests/test_pipeline_async.py b/tests/test_pipeline_async.py index b0cf05e..b9189cb 100644 --- a/tests/test_pipeline_async.py +++ b/tests/test_pipeline_async.py @@ -9,7 +9,7 @@ def test_pipeline_run_lifecycle(seeded_repo): """Pipeline run should transition: pending -> running -> completed.""" repo = seeded_repo domain = repo.get_domain("urban_commuting") - config = {"passes": [1, 2, 3], "threshold": 0.1, "dimensions": ["platform", "power_source"]} + config = {"passes": [1, 2, 3], "threshold": 0.1, "dimensions": ["platform", "actuator", "energy_storage"]} run_id = repo.create_pipeline_run(domain.id, config) run = repo.get_pipeline_run(run_id) @@ -19,10 +19,10 @@ def test_pipeline_run_lifecycle(seeded_repo): scorer = Scorer(domain) pipeline = Pipeline(repo, resolver, scorer) - pipeline.run(domain, ["platform", "power_source"], passes=[1, 2, 3], run_id=run_id) + pipeline.run(domain, ["platform", "actuator", "energy_storage"], passes=[1, 2, 3], run_id=run_id) - from physcom.seed.transport_example import PLATFORMS, POWER_SOURCES - expected = len(PLATFORMS) * len(POWER_SOURCES) + from physcom.seed.transport_example import PLATFORMS, ACTUATORS, ENERGY_STORAGES + expected = len(PLATFORMS) * len(ACTUATORS) * len(ENERGY_STORAGES) run = repo.get_pipeline_run(run_id) assert run["status"] == "completed" @@ -35,7 +35,7 @@ def test_pipeline_run_failed(seeded_repo): """Pipeline run should be marked failed on error.""" repo = seeded_repo domain = repo.get_domain("urban_commuting") - config = {"passes": [1], "threshold": 0.1, "dimensions": ["platform", "power_source"]} + config = {"passes": [1], "threshold": 0.1, "dimensions": ["platform", "actuator", "energy_storage"]} run_id = repo.create_pipeline_run(domain.id, config) # Manually mark as failed (simulating what the web route does on exception) @@ -58,7 +58,7 @@ def test_resume_skips_completed_combos(seeded_repo): # First run: passes 1-3 run_id_1 = repo.create_pipeline_run(domain.id, {"passes": [1, 2, 3]}) result1 = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], run_id=run_id_1, ) assert result1.pass2_estimated > 0 @@ -67,7 +67,7 @@ def test_resume_skips_completed_combos(seeded_repo): # Second run: same passes — should skip all combos (already pass_reached >= 3) run_id_2 = repo.create_pipeline_run(domain.id, {"passes": [1, 2, 3]}) result2 = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], run_id=run_id_2, ) # pass2_estimated still counted (reloaded from DB) but no new estimation work @@ -93,7 +93,7 @@ def test_cancellation_stops_processing(seeded_repo): repo.update_pipeline_run(run_id, status="cancelled") result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], run_id=run_id, ) @@ -115,7 +115,7 @@ def test_status_guard_no_downgrade_reviewed(seeded_repo): # Run pipeline to get scored combos result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], ) @@ -148,7 +148,7 @@ def test_human_notes_preserved_on_rerun(seeded_repo): # First run pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], ) @@ -177,7 +177,7 @@ def test_human_notes_preserved_on_rerun(seeded_repo): # Re-run pipeline pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], ) @@ -212,7 +212,7 @@ def test_get_combo_pass_reached(seeded_repo): pipeline = Pipeline(repo, resolver, scorer) pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], ) @@ -238,7 +238,7 @@ def test_blocked_combos_have_results(seeded_repo): pipeline = Pipeline(repo, resolver, scorer) result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], ) @@ -269,7 +269,7 @@ def test_all_passes_run_and_tracked(seeded_repo): run_id = repo.create_pipeline_run(domain.id, {"passes": [1, 2, 3]}) result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3], run_id=run_id, ) @@ -289,7 +289,9 @@ def test_save_combination_loads_existing_status(seeded_repo): repo = seeded_repo from physcom.models.combination import Combination - entities = repo.list_entities(dimension="platform")[:1] + repo.list_entities(dimension="power_source")[:1] + entities = (repo.list_entities(dimension="platform")[:1] + + repo.list_entities(dimension="actuator")[:1] + + repo.list_entities(dimension="energy_storage")[:1]) combo = Combination(entities=entities) saved = repo.save_combination(combo) assert saved.status == "pending" @@ -316,7 +318,7 @@ def test_p3_fail_below_threshold(seeded_repo): # Use a very high threshold so most combos fail pass 3 result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.99, passes=[1, 2, 3], ) @@ -350,7 +352,7 @@ def test_p4_fail_implausible(seeded_repo): pipeline = Pipeline(repo, resolver, scorer, llm=mock_llm) result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.0, passes=[1, 2, 3, 4], ) @@ -381,7 +383,7 @@ def test_p4_pass_plausible(seeded_repo): pipeline = Pipeline(repo, resolver, scorer, llm=mock_llm) result = pipeline.run( - domain, ["platform", "power_source"], + domain, ["platform", "actuator", "energy_storage"], score_threshold=0.01, passes=[1, 2, 3, 4], ) diff --git a/tests/test_repository.py b/tests/test_repository.py index 5b6d37f..418f1c0 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -35,7 +35,7 @@ def test_add_and_get_entity(repo): def test_list_entities_by_dimension(repo): repo.add_entity(Entity(name="A", dimension="platform")) repo.add_entity(Entity(name="B", dimension="platform")) - repo.add_entity(Entity(name="C", dimension="power_source")) + repo.add_entity(Entity(name="C", dimension="actuator")) platforms = repo.list_entities(dimension="platform") assert len(platforms) == 2 @@ -65,7 +65,7 @@ def test_add_and_get_domain(repo): def test_combination_save_and_dedup(repo): e1 = repo.add_entity(Entity(name="A", dimension="platform")) - e2 = repo.add_entity(Entity(name="B", dimension="power_source")) + e2 = repo.add_entity(Entity(name="B", dimension="actuator")) from physcom.models.combination import Combination combo = Combination(entities=[e1, e2]) @@ -79,12 +79,14 @@ def test_combination_save_and_dedup(repo): def test_seed_loads(seeded_repo): - from physcom.seed.transport_example import PLATFORMS, POWER_SOURCES, ALL_DOMAINS + from physcom.seed.transport_example import PLATFORMS, ACTUATORS, ENERGY_STORAGES, ALL_DOMAINS platforms = seeded_repo.list_entities(dimension="platform") - power_sources = seeded_repo.list_entities(dimension="power_source") + actuators = seeded_repo.list_entities(dimension="actuator") + energy_storages = seeded_repo.list_entities(dimension="energy_storage") assert len(platforms) == len(PLATFORMS) - assert len(power_sources) == len(POWER_SOURCES) + assert len(actuators) == len(ACTUATORS) + assert len(energy_storages) == len(ENERGY_STORAGES) domains = seeded_repo.list_domains() assert len(domains) == len(ALL_DOMAINS) diff --git a/tests/test_scorer.py b/tests/test_scorer.py index c728a5e..639247e 100644 --- a/tests/test_scorer.py +++ b/tests/test_scorer.py @@ -71,7 +71,7 @@ class TestScorer: scorer = Scorer(urban_domain) combo = Combination(entities=[ Entity(name="Car", dimension="platform"), - Entity(name="ICE", dimension="power_source"), + Entity(name="ICE", dimension="actuator"), ]) combo.id = 1 raw = {"speed": 60.0, "cost_efficiency": 0.5, "safety": 0.7,