Tactical–Operational Scenario Contract
Phase 6 adds a TOPM-inspired aggregate planning layer beside the existing day/shift operational
scheduler. The tactical–operational contract is intentionally separate from schema 1.0.0 so
current operational scenario bundles remain unchanged.
Canonical formulation
The full equation set and equation-to-code mapping are maintained from a shared source module used by Sphinx and manuscript/thesis assets:
FHOPS’ TOPM-inspired tactical–operational solver is formulated on an aggregate period grid and chooses harvest areas by planning unit, harvest system, and
period while carrying products through transport, facility consumption, inventory, outside purchases, and optional infrastructure/fleet modules. The equations
below mirror the implemented Pyomo model in fhops.model.milp.tactical_operational.build_tactical_operational_model and the validated contract in
fhops.planning.tactical_operational.models.TacticalOperationalScenario.
Problem statement. Given planning units, period calendars, product yields, harvest-system options, facility demand envelopes, transport arcs, external supply, and optional road/silviculture/fleet modules, choose harvest quantities and downstream flows that satisfy demand and feasibility constraints at minimum discounted delivered cost, or maximize discounted profit/NPV when product values are supplied.
Sets and indices.
\(b \in \mathcal{B}\): planning units/blocks.
\(o \in \mathcal{O}\): eligible block \(\times\) system \(\times\) period harvest options.
\(t \in \mathcal{T}\): ordered tactical–operational periods.
\(p \in \mathcal{P}\): products/species-grade classes.
\(a \in \mathcal{A}\): product transport arcs.
\(u \in \mathcal{U}\): external supply options, indexed by source/destination/product/period.
\((f,p,t) \in \mathcal{F}\): facility/product/period balance keys.
\(r \in \mathcal{R}\): optional road projects.
\(q \in \mathcal{Q}\): optional silviculture transitions.
\(e \in \mathcal{E}\): optional fleet acquisition options.
Parameters.
\(A_b\): operable area of planning unit \(b\) (ha).
\(L_o,U_o\): minimum/maximum active area for option \(o\) (ha).
\(Y_{b,p}\): product yield for block \(b\) and product \(p\) (m\(^3\)/ha).
\(K_o\): optional total production capacity for option \(o\) (m\(^3\)/period).
\(K^{fleet}_{s,t}\): base fleet capacity for system \(s\) in period \(t\) (m\(^3\)).
\(F_o,c_o\): fixed cost and variable cost per m\(^3\) for option \(o\).
\(c_a,c_u\): transport cost per m\(^3\) on arc \(a\) and delivered purchase cost per m\(^3\) for supply \(u\).
\(D^{min}_{f,p,t},D^{target}_{f,p,t},D^{max}_{f,p,t}\): facility demand envelope (m\(^3\)).
\(v_{f,p,t}\): optional delivered product value per m\(^3\) for profit objectives.
\(d_t\): discount factor for period \(t\).
\(I^0_{f,p}\): opening facility inventory (m\(^3\)).
\(C_a,C_r\): transport arc and active-road capacity (m\(^3\)/period).
\(B_r,M_r\): road build cost and maintenance cost per active period.
\(c_q\): silviculture cost per ha for transition \(q\).
\(N^{max}_e,K_e,C^{fleet}_e\): maximum units, added capacity per period, and purchase cost for fleet option \(e\).
Decision variables.
\(H_o \ge 0\): area harvested under option \(o\) (ha).
\(Z_o \in \{0,1\}\): activation indicator for option \(o\).
\(V_{o,p} \ge 0\): product volume produced by option \(o\) (m\(^3\)).
\(F_a \ge 0\): product flow on transport arc \(a\) (m\(^3\)).
\(P_u \ge 0\): external purchase quantity for supply option \(u\) (m\(^3\)).
\(C_{f,p,t} \ge 0\): facility consumption/demand fulfillment (m\(^3\)).
\(I_{f,p,t} \ge 0\): closing facility inventory (m\(^3\)).
\(R^B_{r,t},R^A_{r,t} \in \{0,1\}\): road build and available indicators when roads are enabled.
\(Q_{q,t} \ge 0\): silviculture activity area scheduled for transition \(q\) in period \(t\).
\(N_e \in \mathbb{Z}_{\ge 0}\): units purchased for fleet option \(e\).
Harvest quantity modes.
All modes enforce the physical upper bound:
Semi-continuous mode (the default) additionally enforces option-specific minimum active area:
Continuous mode omits that lower bound. Whole-block mode forces full operable area when active:
Core constraints.
Product conversion and option productivity:
Block area balance:
Fleet capacity with optional acquisition:
where \(\tau(e)\) is the purchase period sequence and \(L_e\) the economic life in periods. Purchases are bounded by \(0\le N_e\le N^{max}_e\).
Flow supply and arc capacity:
External purchase bounds:
Facility consumption bounds and target mode:
Facility inventory balance:
with \(I_{f,p,0}=I^0_{f,p}\).
Optional road module.
Road build timing, one-time build, availability, dependencies, block access, and active capacity are represented as:
Road costs enter the objective as \(\sum_{r,t}d_t(B_rR^B_{r,t}+M_rR^A_{r,t})\).
Optional silviculture module.
For each required transition \(q\) triggered by block/system harvest:
Transition area carries discounted cost \(\sum_{q,t}d_t c_q Q_{q,t}\).
Objective profiles.
Default minimum discounted delivered cost:
When demand rows include value per m\(^3\), max_discounted_profit maximizes
max_npv additionally adds declared final-period terminal inventory value:
Implementation mapping (equation blocks to code).
Equation/constraint block |
Pyomo component / helper |
Data provenance |
|---|---|---|
Harvest upper bound |
|
|
Semi-continuous minimum cut |
|
|
Whole-block mode |
|
|
Productivity cap |
|
|
Product conversion |
|
|
Block area balance |
|
|
Fleet capacity and acquisition |
|
|
Flow supply |
|
|
Arc capacity |
|
|
Purchase bounds |
|
|
Consumption bounds/targets |
|
|
Inventory balance |
|
|
Road build/availability/dependencies/access/capacity |
|
|
Silviculture fulfillment |
|
|
Objective profiles |
|
|
Bundle replay and telemetry |
|
|
This formulation is the canonical mathematical reference for the FHOPS tactical–operational MILP. Generated TeX/RST outputs are derived artifacts and should not be edited directly.
Use the tactical contract for 1–5 year plans that need:
planning periods (four-week periods, months, seasons, or custom tables);
block × harvest-system × period alternatives;
partial, semi-continuous, or whole-block harvest quantities;
product-specific yields;
facilities, demand envelopes, transport arcs, and opening inventories;
outside purchases and discounted economics; and
later road, silviculture, and fleet-investment modules.
Loading and validation
The Python loader accepts either inline YAML sections or a data: section that points each
long-form table at a CSV file:
from fhops.planning import load_tactical_operational_scenario
scenario = load_tactical_operational_scenario(
"tests/fixtures/tactical_operational/topm-mini/specification.yaml"
)
print(scenario.dimension_summary())
The CLI validates the same contract and reports model dimensions before any solver model is built:
fhops validate tactical-operational tests/fixtures/tactical_operational/topm-mini/specification.yaml
The legacy operational form remains supported:
fhops validate examples/tiny7/scenario.yaml
Period templates
fhops.planning.tactical_operational.time provides two starter templates:
four_week_periods(year)— up to thirteen 28-day periods with optional effective annual discounting;seasonal_periods(year)— winter/spring/summer/fall periods with season tags.
Both produce validated PlanningPeriod objects.
Custom period tables can define explicit parent_period_id values for roll-up reporting.
Core MILP formulation
The Phase 6 core model chooses a harvest area H[o] >= 0 and activation Z[o] ∈ {0,1} for
each eligible block × system × period option o. Product volumes are derived from area using
unit-specific yields:
Semi-continuous mode enforces option-specific minimum and maximum active areas without arbitrary big-M constants:
Continuous mode drops the lower bound; whole-block mode uses
H_o = operable_area[b(o)] Z_o. Block area, option productivity, fleet capacity, and
facility-demand targets complete the harvest core.
Product flows, purchases, consumption, and facility inventories are coupled to that harvest core.
For transport arc a, external supply u, facility/product/period (m,p,t), and previous
period t-1:
Consumption is bounded by the facility demand envelope (or fixed at the target when
--demand-basis target). Transport arc capacities and external purchase bounds apply directly.
The default objective is discounted harvest + transport + purchase cost:
Two value-oriented profiles are also available when demand rows carry value_per_m3:
max_discounted_profit maximizes delivered product value minus cost, and max_npv adds the
final-period value of declared terminal inventory. Override the scenario value with
--objective-profile.
Optional infrastructure modules
The contract now includes optional long-form tables for:
road projects, dependencies, and block access (
roads,road_dependencies,block_road_access);silviculture follow-up transitions by block/system (
silviculture_transitions); andfleet acquisition options with economic-life capacity (
fleet_options).
These modules are disabled by default so the harvest/product-flow core remains reproducible. Enable them independently from the CLI:
fhops plan tactical-operational \
tests/fixtures/tactical_operational/topm-mini/specification.yaml \
--enable-roads \
--out-roads-csv tmp/topm-mini-roads.csv
Road activation uses build/available binaries, cumulative timing, prerequisite links, access gates, and active-road capacity. Silviculture transitions schedule required follow-up area in eligible periods and add discounted per-hectare costs. Fleet acquisition variables add capacity during their economic life and charge the purchase-period discounted cost.
The code mapping is intentionally direct:
area/harvest_active:fhops.model.milp.tactical_operational.model.areaandmodel.harvest_active.Product conversion:
model.product_conversion.Area/productivity/fleet limits:
model.block_area,model.productivity_cap, andmodel.fleet_capacity.Flow supply and arc capacity:
model.flow_supplyandmodel.arc_capacity.Purchases, consumption, and inventory:
model.purchase_lower/model.purchase_upper,model.consumption_*, andmodel.inventory_balance.Roads:
model.road_build,model.road_available,model.road_access, andmodel.road_capacity.Silviculture:
model.silviculture_areaandmodel.silviculture_fulfillment.Fleet investment:
model.fleet_unitsand the capacity augmentation insidemodel.fleet_capacity.Objective assembly:
model.objective.Bundle replay:
fhops.model.milp.tactical_operational.tactical_bundle_to_dict()andtactical_bundle_from_dict().
Solve the fixture from the CLI:
fhops plan tactical-operational \
tests/fixtures/tactical_operational/topm-mini/specification.yaml \
--harvest-mode semi_continuous \
--demand-basis target \
--solver highs \
--out-json tmp/topm-mini.json \
--out-harvest-csv tmp/topm-mini-harvest.csv \
--out-production-csv tmp/topm-mini-production.csv
Scenario overlays, diffs, and batch reports
Phase 6 scenarios support sparse overlays so sensitivity cases can inherit a base contract without
copying every table. Overlay rows are matched by stable identifiers (for example block_id,
option_id, arc_id, or composite facility/product/period keys). An overlay can update nested
fields, append rows, and remove rows through _remove.
overlay_id: lower-sawlog-demand
description: Reduce Y1-P1 sawlog target from 780 to 700 m3.
facility_demand:
- facility_id: mill_saw
product_id: sawlog
period_id: Y1-P1
target_m3: 700.0
Apply and inspect the overlay:
fhops scenario overlay base.yaml lower-demand.yaml --out scenario-lower.yaml
fhops scenario diff base.yaml scenario-lower.yaml --out tmp/scenario-diff.csv
A batch manifest can solve base and overlaid cases in one command:
cases:
- case_id: base
scenario: base.yaml
- case_id: lower-demand
scenario: base.yaml
overlay: lower-demand.yaml
enable_roads: true
fhops scenario batch batch.yaml --out-dir tmp/tactical-batch
fhops report tactical tmp/tactical-batch/base/result.json --out-dir tmp/tactical-report
Each batch case writes normalized decision tables and a Markdown objective summary; the batch root
contains comparison.csv and comparison.md.
Tactical-to-operational handoff
Use fhops.planning.tactical_operational.integration to convert a tactical solve into an
operational business window. The handoff contract is explicit:
selected harvest decisions become
TacticalCommitmentrecords;a rolling state tracks remaining block area/product volume, facility inventory, active roads, fleet units, and cumulative objective components;
compile_business_window_scenariofilters an operational scenario to committed blocks, maps tactical block IDs to operational IDs, assigns the selected harvest system, and clamps the window;write_operational_scenario_bundleemits a loadable YAML/CSV operational bundle; andapply_operational_realizationrolls realized production back into aggregate state.
CLI example using topm-mini commitments against the operational tiny7 bundle:
fhops plan tactical-operational \
tests/fixtures/tactical_operational/topm-mini/specification.yaml \
--out-json tmp/topm-mini-result.json
fhops plan compile-tactical \
tmp/topm-mini-result.json \
examples/tiny7/scenario.yaml \
--block-map B1=B01 \
--block-map B2=B02 \
--horizon-days 7 \
--out-dir tmp/topm-mini-operational
The output directory contains a normal operational scenario.yaml + CSV bundle that can be
validated and solved with the existing FHOPS commands.
Scale benchmarks and uncertainty envelopes
Use fhops synth tactical to generate deterministic TOPM-shaped scale scenarios without copying
restricted TOPM/OperMAX data. The generator creates a rectangular block × system × period
eligibility grid with product yields, fleet capacity, facility demand, transport arcs, and optional
external supply:
fhops synth tactical \
--out tmp/topm-scale.yaml \
--blocks 100 \
--years 1 \
--periods-per-year 4 \
--products 2 \
--facilities 2 \
--systems 2 \
--seed 44
Benchmark one or more generated sizes with:
fhops scenario benchmark \
--blocks 25 \
--blocks 100 \
--periods-per-year 4 \
--time-limit 60 \
--out-dir tmp/topm-scale-benchmark
The benchmark exports CSV, JSON, and Markdown summaries containing scenario dimensions, Pyomo
variable/constraint counts, build time, solve time, objective, solver status, and termination
condition. Decomposition methods (Benders, Dantzig–Wolfe, fix-and-optimize) should only be adopted
after these measurements identify a real bottleneck. The committed smoke benchmark in
docs/assets/tactical/tactical_scale_benchmark.md currently shows HiGHS solving generated
25-block and 100-block cases in under one second each (1,018/1,261 and 4,018/4,936
variables/constraints, respectively). The full-scale artifact at
docs/assets/tactical/full_scale/tactical_scale_benchmark.md covers 500 blocks × 5 years ×
4 periods/year: 20,000 harvest options, 100,082 variables, 120,664 constraints, and an optimal
HiGHS result in 40.8 s with about 860 MiB peak memory.
For uncertainty screening, use scenario overlays plus fhops scenario batch to compare demand,
productivity, road-cost, or purchase-price cases before attempting robust or stochastic MILP
variants.
Practitioner-scale validation case
Phase 7 adds a redistributable synthetic practitioner case shaped like a small mixed-terrain tenure: 24 blocks, 8 periods, 3 products, 2 mills, 3 harvest systems, roads/dependencies, silviculture follow-up, and an optional fleet expansion. Generate or inspect it with:
fhops synth tactical-practitioner --out tmp/practitioner.yaml
fhops validate tactical-operational tmp/practitioner.yaml
fhops plan tactical-operational tmp/practitioner.yaml \
--enable-roads --enable-silviculture --enable-fleet-investment \
--out-json tmp/practitioner-result.json
The checked-in fixture lives at
tests/fixtures/tactical_operational/practitioner-case/scenario.yaml and regression tests verify
schema dimensions, minimum-cut feasibility, road/silviculture/fleet module behavior, and every
facility/product/period inventory balance.
Guided tactical planner notebook
The onboarding series now includes examples/05_fhops_tactical_operational.ipynb, an executable
walkthrough covering:
practitioner-case generation and contract validation;
aggregate MILP solve with roads/silviculture/fleet modules;
objective decomposition and normalized decision tables;
facility inventory balance audits;
sparse overlays and scenario diffs;
report exports; and
tactical→operational compilation into an operational scenario bundle.
Run it with:
python scripts/run_example_notebooks.py --notebook 05
topm-mini acceptance fixture
tests/fixtures/tactical_operational/topm-mini/specification.yaml is the copyright-safe
executable specification for the expansion. It contains hand-calculated checks for:
continuous vs. semi-continuous vs. whole-block harvest activation;
a two-option economic dispatch case;
product-specific yield conversion; and
facility inventory balances.
See notes/topm_mini_specification.md for the current expected values and implementation notes.