CLI Reference

FHOPS exposes a Typer-based CLI called fhops. Detailed command documentation will be autogenerated with sphinx-click in upcoming iterations. For now, the primary commands are:

  • fhops validate — validate scenario inputs.

  • fhops solve-mip-operational — build and solve the shift-indexed operational MILP (the formulation documented in the FHOPS paper; bundle + Pyomo builder + HiGHS wrapper). --earliness (off by default) adds a lexicographic second solve that breaks ties in favour of early production without changing the reported objective (#141; fhops plan rolling enables it in MILP windows).

  • fhops solve-mip — deprecated alias of solve-mip-operational (since 1.0.1; see below).

  • fhops solve-heur — run the metaheuristic solver (simulated annealing v0.1).

  • fhops solve-heur ... --iters 5000 --seed 123 — adjust SA iteration budget/seed (additional tuning flags under review).

  • fhops evaluate — replay a schedule and compute KPIs.

  • fhops eval-playback — generate shift/day summaries from a schedule (deterministic playback).

  • fhops benchmark — compare the operational MILP with simulated annealing on a single scenario (legacy helper).

  • fhops bench suite — run the full benchmarking harness across sample or user-supplied scenarios.

  • fhops geo distances — derive block-to-block distance matrices from GeoJSON geometry.

  • fhops tree inventory validate / fhops tree register — validate and pin a producer-neutral tree inventory.

  • fhops tree map / fhops tree stems / fhops tree simulate / fhops tree replay / fhops tree scene — run the tree-scale mapping, stem, simulation, replay, and glTF/GLB export path; see Tree-Scale Workflow.n- fhops tree system — simulate a feller-buncher/skidder/loader ground-based system on one block; see Ground-Based System Simulation.

  • fhops report salvage-shelf-life — join a storage/age CSV to scenario blocks and report published volume retention, optional retained value, and advisory product windows; see Salvage Shelf-Life and Wood Degradation.

  • fhops plan rolling — execute rolling-horizon solves (stub/SA/MIP hooks) with metadata-rich exports for per-iteration telemetry and locked assignments; see Rolling-Horizon Planning. Supports solver options such as --mip-solver-option Threads=64 (repeatable) or GRB_THREADS for Gurobi, plus --out-json, --out-assignments, and per-iteration JSONL/CSV exports (always written, also when a run fails part-way). Windows whose solver finds no solution are recorded and left idle; --fail-on-no-solution stops at the first one and --fail-on-empty-window at the first window without a solution or solved to an empty plan (exit code 1). MILP windows break ties in favour of early production by default (lexicographic second solve, up to --mip-earliness-time-limit seconds, default --mip-time-limit; the window objective is unchanged; --no-mip-earliness disables it). Exit codes: 0 completed, 1 stopped by a --fail-on-* flag, failed, or no window returned a solution (e.g. every window hit a solver error), 2 usage error (invalid --master-days/--sub-days/--lock-days, --max-iterations < 1, unknown --solver, unavailable --mip-solver); see Windows without a plan. Feed the locked assignments into fhops eval-playback or fhops.planning.compute_rolling_kpis() for KPI deltas against a baseline.

Run fhops --help to inspect the full command tree.

Baseline usage:

  • fhops solve-mip-operational examples/med42/scenario.yaml --solver gurobi --time-limit 600 --out tmp/med42_gurobi.csv — run the MILP with the Gurobi backend (requires installing fhops[gurobi] and configuring a licence).

  • fhops solve-mip-operational examples/tiny7/scenario.yaml --out tmp/tiny7_operational.csv --time-limit 60 — run the day×shift operational MILP benchmark and emit assignments/KPIs for the tiny7 scenario. Use --dump-bundle foo.json to capture the serialized bundle for debugging, or --bundle-json foo.json to replay the solver without reloading the scenario. Telemetry/logging hooks mirror the heuristics (--telemetry-log for JSONL records, --watch for a live snapshot even though the run is single-shot). --incumbent seed.csv accepts a heuristic schedule (machine_id, block_id, day, shift_id[, assigned, production]) as a warm start; the CLI expands those rows into the auxiliary Pyomo variables (transitions, activation binaries, landing inventories) before invoking the solver. With --solver highs (default) the start is passed through Pyomo’s appsi_highs interface and the CLI prints whether HiGHS accepted it (Warm start: method=appsi_highs … (accepted)); gurobi/cbc/cplex receive Pyomo’s warmstart=True; other solvers run without the start and emit a MilpWarmStartWarning (see Operational MILP Warm Starts). Today the feature is operationally correct but only practically useful on the smallest scenarios—Gurobi still discards greedy/SA seeds on med42/large84 because they are far from its internal incumbents—so expect little to no runtime improvement unless you provide a near-feasible schedule.

  • fhops plan rolling examples/med42/scenario.yaml --master-days 42 --sub-days 21 --lock-days 7 --solver mip --mip-solver gurobi --mip-solver-option Threads=64 --mip-time-limit 600 --out-json tmp/med42_rolling.json --out-assignments tmp/med42_rolling_assignments.csv — run the rolling MILP planner with a Gurobi backend and archive both telemetry JSON and playback-ready assignments. Use --out-iterations-jsonl/--out-iterations-csv to capture per-iteration runtimes/objectives and pass the assignments into fhops eval-playback to compute KPI deltas quickly.

  • fhops solve-heur tests/fixtures/regression/regression.yaml --out tmp/regression_sa.csv

  • fhops evaluate tests/fixtures/regression/regression.yaml --assignments tmp/regression_sa.csv --kpi-mode extended

  • fhops eval-playback tests/fixtures/regression/regression.yaml --assignments tmp/regression_sa.csv --shift-out tmp/shift_summary.csv --day-out tmp/day_summary.csv

  • fhops eval-playback tests/fixtures/regression/regression.yaml --assignments tmp/regression_sa.csv --samples 10 --downtime-prob 0.1 --weather-prob 0.2 — run stochastic playback, capturing downtime and weather variability.

  • fhops eval-playback ... --downtime-prob 0.1 --downtime-mean 4 --downtime-std 1.5 — each hit machine-shift loses a sampled number of hours (clipped to the shift length) and the matching fraction of its production; a full-shift loss cancels the assignment.

  • fhops eval-playback ... --landing-prob 0.3 --landing-mult-min 0.3 --landing-mult-max 0.7 --landing-duration 2 — simulate landing congestion shocks: each landing-day starts a shock with the given probability, and the shock scales every assignment on that landing for --landing-duration days (minimum multiplier when shocks overlap).

  • fhops eval-playback ... --shift-parquet tmp/shift.parquet --day-parquet tmp/day.parquet --summary-md tmp/playback.md — export Parquet files and a Markdown summary alongside the CSV outputs. See Evaluation Workflows for a full end-to-end example.

  • fhops eval-playback ... --shift-out tmp/shift_summary.csv --day-out tmp/day_summary.csv — recommended when using shift calendars/blackouts so you can verify shift-level KPIs roll up to the day totals.

  • fhops bench suite --scenario examples/tiny7/scenario.yaml --out-dir tmp/benchmarks

  • fhops synth generate tmp/custom_bundle --tier medium --seed 777 --blocks 10:12 — create a synthetic scenario bundle using the medium preset with a custom block range; add --preview to inspect metadata without writing files.

  • fhops synth batch docs/examples/synthetic_batch_plan.yaml --overwrite — process several bundles in one call using a YAML/TOML/JSON plan (each entry supports the same fields as generate), refreshing metadata automatically when writing to examples/synthetic.

Both solve-mip-operational and solve-heur export schedules with the columns machine_id, block_id, day, and shift_id. The shift identifier matches the scenario’s shift calendar (or defaults to S1 when only day-level data is provided) so downstream tooling can analyse sub-daily assignments.

solve-mip (deprecated alias) outcomes and exit codes

Since FHOPS 1.0.1 (#127) fhops solve-mip is a deprecated alias: it prints one Deprecated: … line and solves the operational MILP (the same model as solve-mip-operational, via fhops.optimization.mip.solve_mip(), which emits a LegacyMipDeprecationWarning). Until 1.0.1 it solved a legacy day-level MIP whose loader-buffer constraint cannot hold in the first shift, so every scenario whose harvest systems have a loader role — every bundled example (tiny7, small21, med42, large84) — was infeasible (FHOPS 1.0.0 raised an exception for these models). Use fhops solve-mip-operational.

Option mapping (solve-mip → solve-mip-operational):

  • --out → --out; --time-limit N (default 60) → --time-limit N; --debug → --debug (solver log + tracebacks); --sequencing-debug → --sequencing-debug.

  • --driver auto (default) → --solver gurobi when Gurobi is installed (retried with --solver highs after a Gurobi solver error, noted as a Warning:), otherwise --solver highs; highs / highs-appsi / highs-exec → --solver highs; gurobi → --solver gurobi; gurobi-appsi → --solver appsi_gurobi; gurobi-direct → --solver gurobi_direct.

  • --gap, --solver-option, --incumbent, --telemetry-log, --bundle-json, --dump-bundle and --watch exist only on solve-mip-operational.

The output keeps the 1.0.1 (#124) format:

MIP outcome=<outcome> solver_status=<status> termination=<condition> objective=<value|n/a>

outcome is optimal, feasible (incumbent at the time limit), infeasible, no_solution (time limit before any incumbent) or error (the solver failed, e.g. a HiGHS option error; printed as Solver error: …). Without a solution the CSV is an empty assignment table with the usual columns (machine_id, block_id, day, shift_id, assigned, production) and the KPI summary is skipped; an incumbent that assigns no machine is written as an empty table too. The objective is the operational MILP objective (not comparable with legacy 1.0.0 values). Exit codes:

  • 0 — the solver ran, including infeasible models and time limits without an incumbent;

  • 1 — solver error, or the requested solver is not available;

  • 2 — invalid arguments (e.g. an unknown --driver).

fhops benchmark solves the operational MILP with the same --driver mapping (no deprecation notice). It prints MIP obj=n/a (outcome=…) and skips the MIP metrics when the MILP has no solution; SA still runs, and a MIP solver error exits 1 after the SA results. When SA applies objective-weight overrides (FHOPS Tiny7/Small21 built-ins or --objective-weight) it prints SA obj (override weights) and SA obj (scenario weights) (the SA plan scored under the MILP’s weights) plus a one-line notice; solve-heur/solve-ils/solve-tabu and bench suite print the same notice (#140, Objective Weights, Automatic Overrides, and Hard Violations).

Heuristic configuration reference

See Heuristic Presets & Registry Guide for an end-to-end walkthrough. Common CLI patterns:

  • fhops solve-heur ... --operator swap --operator move — restrict the operator set (defaults to all registered operators).

  • fhops solve-heur ... --operator-weight swap=2 --operator-weight move=0.5 — adjust operator weights; zero disables an operator.

  • fhops bench suite ... --operator swap --operator-weight swap=2 — pass the same options when running aggregate benchmarks; the summary now records the operator configuration in operators_config.

  • fhops solve-heur ... --operator-preset swap-only — apply predefined operator weight profiles (available presets: balanced, move-only, swap-heavy, swap-only, diversify, explore, mobilisation, stabilise). Presets may be combined with explicit --operator and --operator-weight overrides.

  • explore enables the advanced neighbourhood operators with moderate weights for general diversification, mobilisation prioritises mobilisation shake moves for distance-constrained scenarios, and stabilise tones down advanced operators to focus on consolidation.

  • Tiny ladder defaults: fhops solve-heur examples/tiny7/scenario.yaml --out tmp/tiny7_sa.csv auto-enables small batched sampling (batch_size=4) and a mobilisation-shake boost after prolonged stalls; pass --batch-neighbours/--parallel-workers to override.

  • fhops bench suite --operator-preset swap-heavy --operator-weight move=1 — use presets within benchmarking; final configurations are captured in the summary output.

  • fhops bench suite --list-operator-presets (or fhops solve-heur ... --list-operator-presets, which still requires the scenario argument and --out) — display all presets with their weights and descriptions.

  • fhops bench suite --compare-preset explore --compare-preset mobilisation — sweep multiple presets in one run; the benchmark summary adds a preset_label column and exports per-preset assignment CSVs for side-by-side analysis.

  • fhops bench suite --include-tabu — benchmark the Tabu prototype alongside SA (produces additional tabu rows in the summary output).

  • fhops bench suite --include-ils --include-tabu — emit solver comparison columns (best heuristic, gap/ratio metrics) so you can rank heuristics against each other and against MIP when included.

  • fhops solve-heur ... --batch-neighbours 4 --parallel-workers 4 — sample multiple neighbour candidates per iteration and score them with a small worker pool (opt-in; defaults keep sequential evaluation).

  • fhops solve-heur ... --parallel-multistart 8 — launch several SA runs in parallel, using the best result while logging per-run telemetry (requires --parallel-workers for true parallelism).

  • fhops solve-heur ... --telemetry-log fhops_runs.jsonl — append telemetry entries (objective, KPIs, operator stats, parallel configuration) to a JSONL file for later analysis.

  • fhops solve-heur ... --kpi-mode basic — print the concise KPI bundle (production/mobilisation only). Use extended to show utilisation, downtime, and weather metrics.

  • fhops solve-heur ... --show-operator-stats — print per-operator proposal/acceptance statistics at the end of a run (also available in the benchmark summaries).

  • fhops solve-ils ... --perturbation-strength 3 --stall-limit 10 --hybrid-use-mip — run the Iterated Local Search solver. The optional hybrid flag solves the operational MILP (HiGHS, --hybrid-mip-time-limit seconds) with the best ILS schedule as a warm start (MIP start) each time ILS stalls, and keeps the MILP schedule only when it scores higher; --batch-neighbours/--parallel-workers reuse the SA batching infrastructure.

  • fhops solve-tabu ... — run the Tabu Search prototype (–tabu-tenure, –stall-limit, –batch-neighbours, –parallel-workers) and export telemetry consistent with SA runs.

  • fhops bench suite --include-ils — add ILS rows to the benchmark summary (CSV/JSON). Combine with --include-tabu for full solver comparisons.

  • python scripts/render_benchmark_plots.py tmp/benchmarks/summary.csv — turn benchmark summaries into comparison charts for documentation (see Benchmarking Harness).

  • fhops bench suite --include-ils --include-tabu --out-dir tmp/benchmarks_compare — generate the richer comparison columns (best heuristic solver, objective gaps, runtime ratios).

  • fhops solve-heur ... --profile explore — apply a named solver profile that bundles presets, batching, and optional multi-start settings (see fhops bench suite --list-profiles).

  • fhops bench suite --profile mobilisation — reuse the same profile defaults across SA/ILS/Tabu when benchmarking; explicit CLI overrides still win.

The evaluation output should include mobilisation_cost=6.0 and sequencing_violation_count=0 if the regression baseline is satisfied.

Dataset command cheat sheet

fhops dataset estimate-productivity exposes every productivity preset that now carries detailed docstrings (see fhops.cli.dataset). Use the following guide to jump from the CLI role flag to the corresponding API surface:

CLI --machine-role

When to pick it

Key CLI options

Docstring to consult

forwarder

Cut-to-length or shortwood workflows needing payload/distance regressions.

--forwarder-model + payload/length inputs listed in the CLI help.

fhops.productivity.forwarder_bc.estimate_forwarder_productivity_bc()

grapple_skidder

Full-tree or salvage skidding (ADV6N7, Han 2018, ADV1N12 presets).

--grapple-skidder-model with extraction distance, payload, utilisation overrides as needed.

fhops.productivity.grapple_bc.estimate_grapple_skidder_productivity_adv6n7()

grapple_yarder / skyline_yarder

Cable-running or standing skyline studies (TR125/127, FNCY12, TN173, Hi-Skid).

--model plus the lateral/log-count options described in the skyline helper docstrings.

fhops.productivity.cable_logging

loader / loader_forwarder

Loader-forwarder or Barko/Hypro presets (ADV5N1, ADV2N26, TN261, Barko 450).

--loader-model with payload/utilisation/delay switches.

fhops.productivity.loader

helicopter_longline

Longline or direct-to-water transfer flights with payload/delay modelling.

--helicopter-model / --helicopter-preset plus distance/payload overrides.

fhops.productivity.estimate_helicopter_longline_productivity()

shovel_logger

Sessions & Boston serpentine shovel logging.

--shovel-* swing/payload options (defaults seeded by the preset).

fhops.cli.dataset._evaluate_shovel_logger_result()

Each function linked above now documents parameter units, accepted ranges, default templates, and the structure of the result dataclasses. Run sphinx-build -b html docs _build/html -W after adding new roles so these cross-references stay valid.

Log-hauling commands

The optional hauling layer adds a fhops haul command group:

fhops haul validate examples/hauling/scenario.yaml
fhops haul plan examples/hauling/scenario.yaml --out-dir runs/haul-plan --time-limit 60
fhops haul schedule examples/hauling/scenario.yaml --out-dir runs/haul-day1 \
  --flow-dir runs/haul-plan --day 1 --solver sa --sa-iters 5000
fhops haul solve examples/hauling/scenario.yaml --out-dir runs/haul-solve \
  --day 1 --solver mip --time-limit 60
fhops haul report runs/haul-solve --out runs/haul-solve/report.md
  • plan solves Phase I: cost, contractor workload, or goal programming.

  • schedule solves Phase II/III for one day; it loads an existing Phase I run or solves Phase I in memory.

  • solve runs both phases and writes a merged summary.

  • report renders a Markdown report from the stable CSV/JSON artefacts.

See Log Hauling for the data contract, output schemas, and integration notes.

Web commands

The optional web service adds:

fhops web --runs runs/ --port 8765
fhops web --runs runs/ --allow-agent --agent-timeout 120
fhops web --runs runs/ --lidar-bundles /path/to/femic/bundles
fhops web manifest runs/run-a --status optimal
fhops web discover runs/

See FHOPS Web UI for the three planning lenses, run discovery, the run.json manifest, and the security defaults.