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 rollingenables it in MILP windows).fhops solve-mip— deprecated alias ofsolve-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) orGRB_THREADSfor 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-solutionstops at the first one and--fail-on-empty-windowat 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-limitseconds, default--mip-time-limit; the window objective is unchanged;--no-mip-earlinessdisables it). Exit codes:0completed,1stopped by a--fail-on-*flag, failed, or no window returned a solution (e.g. every window hit a solver error),2usage 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 intofhops eval-playbackorfhops.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 installingfhops[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.jsonto capture the serialized bundle for debugging, or--bundle-json foo.jsonto replay the solver without reloading the scenario. Telemetry/logging hooks mirror the heuristics (--telemetry-logfor JSONL records,--watchfor a live snapshot even though the run is single-shot).--incumbent seed.csvaccepts 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’sappsi_highsinterface and the CLI prints whether HiGHS accepted it (Warm start: method=appsi_highs … (accepted));gurobi/cbc/cplexreceive Pyomo’swarmstart=True; other solvers run without the start and emit aMilpWarmStartWarning(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-csvto capture per-iteration runtimes/objectives and pass the assignments intofhops eval-playbackto compute KPI deltas quickly.fhops solve-heur tests/fixtures/regression/regression.yaml --out tmp/regression_sa.csvfhops evaluate tests/fixtures/regression/regression.yaml --assignments tmp/regression_sa.csv --kpi-mode extendedfhops eval-playback tests/fixtures/regression/regression.yaml --assignments tmp/regression_sa.csv --shift-out tmp/shift_summary.csv --day-out tmp/day_summary.csvfhops 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-durationdays (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/benchmarksfhops 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--previewto 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 toexamples/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 gurobiwhen Gurobi is installed (retried with--solver highsafter a Gurobi solver error, noted as aWarning:), 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-bundleand--watchexist only onsolve-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 inoperators_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--operatorand--operator-weightoverrides.exploreenables the advanced neighbourhood operators with moderate weights for general diversification,mobilisationprioritises mobilisation shake moves for distance-constrained scenarios, andstabilisetones down advanced operators to focus on consolidation.Tiny ladder defaults:
fhops solve-heur examples/tiny7/scenario.yaml --out tmp/tiny7_sa.csvauto-enables small batched sampling (batch_size=4) and a mobilisation-shake boost after prolonged stalls; pass--batch-neighbours/--parallel-workersto 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(orfhops 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 apreset_labelcolumn and exports per-preset assignment CSVs for side-by-side analysis.fhops bench suite --include-tabu— benchmark the Tabu prototype alongside SA (produces additionaltaburows 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-workersfor 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). Useextendedto 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-limitseconds) 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-workersreuse 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-tabufor 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 (seefhops 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 |
When to pick it |
Key CLI options |
Docstring to consult |
|---|---|---|---|
|
Cut-to-length or shortwood workflows needing payload/distance regressions. |
|
|
|
Full-tree or salvage skidding (ADV6N7, Han 2018, ADV1N12 presets). |
|
|
|
Cable-running or standing skyline studies (TR125/127, FNCY12, TN173, Hi-Skid). |
|
|
|
Loader-forwarder or Barko/Hypro presets (ADV5N1, ADV2N26, TN261, Barko 450). |
|
|
|
Longline or direct-to-water transfer flights with payload/delay modelling. |
|
|
|
Sessions & Boston serpentine shovel logging. |
|
|
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
plansolves Phase I: cost, contractor workload, or goal programming.schedulesolves Phase II/III for one day; it loads an existing Phase I run or solves Phase I in memory.solveruns both phases and writes a merged summary.reportrenders 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.