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: .. include:: ../includes/softwarex/fhops_tactical_operational_formulation.rst 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: .. code-block:: python 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: .. code-block:: bash fhops validate tactical-operational tests/fixtures/tactical_operational/topm-mini/specification.yaml The legacy operational form remains supported: .. code-block:: bash fhops validate examples/tiny7/scenario.yaml Period templates ---------------- :mod:`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 :class:`~fhops.planning.tactical_operational.models.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: .. math:: V[o,p] = Y[b(o),p] H[o] Semi-continuous mode enforces option-specific minimum and maximum active areas without arbitrary big-M constants: .. math:: L_o Z_o \le H_o \le U_o Z_o 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``: .. math:: \sum_{a \in out(b,p,t)} F_a \le \sum_{o:b(o)=b,t(o)=t} V[o,p] .. math:: I[m,p,t] = I[m,p,t-1] + \sum_{a \in in(m,p,t)} F_a + P_u - C[m,p,t] 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: .. math:: \min \sum_o d_{t(o)} (F_o Z_o + c_o Y^{total}_{b(o)} H_o) + \sum_a d_{t(a)} c_a F_a + \sum_u d_{t(u)} c_u P_u 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``); and - fleet 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: .. code-block:: bash 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.area`` and ``model.harvest_active``. - Product conversion: ``model.product_conversion``. - Area/productivity/fleet limits: ``model.block_area``, ``model.productivity_cap``, and ``model.fleet_capacity``. - Flow supply and arc capacity: ``model.flow_supply`` and ``model.arc_capacity``. - Purchases, consumption, and inventory: ``model.purchase_lower``/``model.purchase_upper``, ``model.consumption_*``, and ``model.inventory_balance``. - Roads: ``model.road_build``, ``model.road_available``, ``model.road_access``, and ``model.road_capacity``. - Silviculture: ``model.silviculture_area`` and ``model.silviculture_fulfillment``. - Fleet investment: ``model.fleet_units`` and the capacity augmentation inside ``model.fleet_capacity``. - Objective assembly: ``model.objective``. - Bundle replay: :func:`fhops.model.milp.tactical_operational.tactical_bundle_to_dict` and :func:`~fhops.model.milp.tactical_operational.tactical_bundle_from_dict`. Solve the fixture from the CLI: .. code-block:: bash 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``. .. code-block:: yaml 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: .. code-block:: bash 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: .. code-block:: yaml cases: - case_id: base scenario: base.yaml - case_id: lower-demand scenario: base.yaml overlay: lower-demand.yaml enable_roads: true .. code-block:: bash 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 :mod:`fhops.planning.tactical_operational.integration` to convert a tactical solve into an operational business window. The handoff contract is explicit: - selected harvest decisions become :class:`TacticalCommitment` records; - a rolling state tracks remaining block area/product volume, facility inventory, active roads, fleet units, and cumulative objective components; - ``compile_business_window_scenario`` filters 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_bundle`` emits a loadable YAML/CSV operational bundle; and - ``apply_operational_realization`` rolls realized production back into aggregate state. CLI example using `topm-mini` commitments against the operational tiny7 bundle: .. code-block:: bash 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: .. code-block:: bash 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: .. code-block:: bash 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: .. code-block:: bash 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: .. code-block:: bash 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.