Skip to content

cli

cli

Command-line interface for acropole.

A thin wrapper over :class:acropole.FuelEstimator: read a flight from a CSV (or parquet), estimate fuel flow, write the enriched table back out. The estimation logic lives entirely in the library — this module only handles I/O and argument parsing.

estimate(flight, *, out=None, typecode='typecode', groundspeed='groundspeed', altitude='altitude', vertical_rate='vertical_rate', airspeed='airspeed', mass='mass', second=None)

Estimate fuel flow for a flight and write the enriched table.

Adds fuel_flow (kg/s), fuel_flow_kgh (kg/h) and, when --second is given, fuel_cumsum (kg).

Source code in src/acropole/cli.py
@app.command
def estimate(
    flight: Annotated[Path, cyclopts.Parameter(help="Input flight CSV or parquet")],
    *,
    out: Annotated[
        Path | None,
        cyclopts.Parameter(
            help="Output path (.csv/.parquet); default: <flight>_fuel.<ext>"
        ),
    ] = None,
    typecode: Annotated[
        str, cyclopts.Parameter(help="Aircraft type column")
    ] = "typecode",
    groundspeed: Annotated[
        str, cyclopts.Parameter(help="Groundspeed column (kt)")
    ] = "groundspeed",
    altitude: Annotated[
        str, cyclopts.Parameter(help="Altitude column (ft)")
    ] = "altitude",
    vertical_rate: Annotated[
        str, cyclopts.Parameter(help="Vertical rate column (ft/min)")
    ] = "vertical_rate",
    airspeed: Annotated[
        str, cyclopts.Parameter(help="Airspeed column (kt)")
    ] = "airspeed",
    mass: Annotated[str, cyclopts.Parameter(help="Mass column (kg)")] = "mass",
    second: Annotated[
        str | None, cyclopts.Parameter(help="Timestamp column (s); enables derivatives")
    ] = None,
) -> None:
    """Estimate fuel flow for a flight and write the enriched table.

    Adds ``fuel_flow`` (kg/s), ``fuel_flow_kgh`` (kg/h) and, when ``--second`` is
    given, ``fuel_cumsum`` (kg).
    """
    if not flight.exists():
        print(f"error: file not found: {flight}", file=sys.stderr)
        raise SystemExit(1)

    frame = _read(flight)
    mapping = {
        "typecode": typecode,
        "groundspeed": groundspeed,
        "altitude": altitude,
        "vertical_rate": vertical_rate,
        "airspeed": airspeed,
        "mass": mass,
    }
    if second is not None:
        mapping["second"] = second

    try:
        estimated = FuelEstimator().estimate(frame, **mapping)
    except (ValueError, KeyError) as exc:
        print(f"error: {exc}", file=sys.stderr)
        raise SystemExit(1) from exc

    # estimate() echoes its input frame type; _read always yields polars, so the
    # result is polars too. Narrow defensively (and convert pandas if it ever isn't).
    result = (
        estimated if isinstance(estimated, pl.DataFrame) else pl.from_pandas(estimated)
    )

    if out is None:
        out = flight.with_name(f"{flight.stem}_fuel{flight.suffix}")
    _write(result, out)
    print(f"wrote {len(result)} rows with fuel columns to {out}")