Chapter 12: Harvest Cost Curves with FHOPS =========================================== Learning Objectives ------------------- After reading this chapter, you should be able to: - Explain what FHOPS is and its role in the ws3 ecosystem - Generate harvest cost yield curves using fhops - Understand the relationship between productivity, costing, and yield - Inject fhops-generated curves into ws3 models - Use the fhops CLI for common tasks What Is FHOPS? -------------- **FHOPS** (Forest Harvest Operations) is a tool for generating harvest cost yield curves. While ws3 models *what* gets harvested and *when*, FHOPS models *how much it costs* to harvest. FHOPS fills a critical gap: traditional wood supply models often use simplified, static harvest costs. FHOPS generates **dynamic harvest cost curves** that vary by: - **Productivity**: Site quality, stand density, tree size - **Distance**: Distance to landing, road access - **Terrain**: Slope, soil conditions - **Species**: Different species require different harvesting techniques .. mermaid:: graph TD INPUTS["Input Data
(inventory, terrain, roads)"] --> FHOPS["FHOPS
Cost modeling"] FHOPS --> CURVES["Harvest Cost
Yield Curves"] CURVES --> WS3["ws3 ForestModel
(integration)"] WS3 --> OPT["Optimization
(with accurate costs)"] The Productivity Concept ------------------------ FHOPS organizes cost modeling around **productivity**. A productivity class represents a combination of factors that affect harvesting efficiency: - **Stand density**: More trees per hectare = more time per hectare - **Tree size**: Larger trees = more time per tree - **Terrain**: Steeper slopes = slower machine movement - **Soil conditions**: Wet soils = reduced machine mobility .. code-block:: python from fhops.productivity import ProductivityRegistry # Register productivity classes registry = ProductivityRegistry() registry.add_productivity_class( name="high_productivity", description="Flat terrain, good access, moderate density", base_cost_per_m3=25.0, density_factor=1.0, slope_factor=1.0, distance_factor=1.0 ) registry.add_productivity_class( name="low_productivity", description="Steep terrain, poor access, high density", base_cost_per_m3=45.0, density_factor=1.5, slope_factor=1.8, distance_factor=2.0 ) Generating Cost Curves ---------------------- FHOPS generates harvest cost curves that relate harvest volume to cost: .. code-block:: python from fhops.costing import CostCurveGenerator # Generate cost curves for different productivity classes generator = CostCurveGenerator(registry) cost_curves = generator.generate( productivity_classes=["high_productivity", "low_productivity"], volume_range=[0, 1000], # m³/ha num_points=50 ) # Each curve maps volume to cost for pc_name, curve in cost_curves.items(): print(f"{pc_name}: cost at 500 m³/ha = ${curve(500):.2f}") Cost Curve Structure -------------------- A harvest cost curve typically has this structure: .. mermaid:: graph TD VOLUME["Harvest Volume
(m³/ha)"] --> FIXED["Fixed Costs
(setup, mobilization)"] VOLUME --> VARIABLE["Variable Costs
(per m³)"] VARIABLE --> DENSITY["Density adjustment"] VARIABLE --> DISTANCE["Distance adjustment"] VARIABLE --> SLOPE["Slope adjustment"] FIXED --> TOTAL["Total Cost
($/ha)"] VARIABLE --> TOTAL The total cost is: .. math:: \\text{Total Cost} = \\text{Fixed Costs} + \\text{Variable Cost per m³} \\times \\text{Volume} \\times \\text{Adjustment Factors} Integrating with ws3 -------------------- The key integration point: fhops cost curves can be injected into ws3 as part of the financial analysis: .. code-block:: python from ws3.forest import ForestModel from ws3.core import Curve from fhops.costing import CostCurveGenerator # Generate fhops cost curves registry = ProductivityRegistry() # ... add productivity classes ... generator = CostCurveGenerator(registry) cost_curves = generator.generate( productivity_classes=["high_productivity", "low_productivity"], volume_range=[0, 1000], num_points=50 ) # Create ws3 model with required parameters model = ForestModel( model_name="fhops_example", model_path="/path/to/data", base_year=2024, horizon=20, period_length=10, max_age=200 ) # Development types are loaded from Woodstock-format data files # via model.import_areas_section(), import_yields_section(), etc. # Add growth curve (volume) using register_curve volume_curve = Curve( label="DF-SI50_volume", is_volume=True, points=[(0, 0), (10, 5), (20, 25), (30, 65), (40, 120), (50, 200), (60, 300), (70, 400), (80, 470), (90, 500), (100, 510)] ) model.register_curve(volume_curve) # Add cost curve from fhops using register_curve # Note: Curve constructor takes points=[(x,y)], not x= and y= separately high_prod_cost = cost_curves["high_productivity"] cost_curve = Curve( label="DF-SI50_HighProd_cost", points=list(zip(high_prod_cost.x, high_prod_cost.y)) ) model.register_curve(cost_curve) # Now the model has both volume and cost curves # Use them in optimization to maximize net revenue volume_at_60 = volume_curve(60) # m³/ha cost_at_60 = cost_curve(60) # $/ha net_revenue_per_ha = volume_at_60 * 50 - cost_at_60 # $/ha The fhops CLI ------------- FHOPS provides a command-line interface for common tasks: .. code-block:: bash # Generate cost curves from configuration fhops generate-cost-curves \ --config config/costing.yaml \ --output output/cost_curves.csv # List available productivity classes fhops list-productivity-classes # Validate a costing configuration fhops validate-config config/costing.yaml # Export curves for ws3 integration fhops export-ws3 config/costing.yaml output/ws3_curves/ CLI Configuration ----------------- The fhops CLI uses YAML configuration files: .. code-block:: yaml # config/costing.yaml productivity_classes: - name: high_productivity base_cost_per_m3: 25.0 density_factor: 1.0 slope_factor: 1.0 distance_factor: 1.0 - name: low_productivity base_cost_per_m3: 45.0 density_factor: 1.5 slope_factor: 1.8 distance_factor: 2.0 volume_range: min: 0 max: 1000 num_points: 50 output: format: csv path: output/cost_curves.csv Best Practices -------------- 1. **Calibrate costs to local conditions**: Use actual harvesting data to calibrate fhops parameters 2. **Validate against benchmarks**: Compare fhops output to known cost estimates 3. **Use productivity classes consistently**: Define clear criteria for each productivity class 4. **Document assumptions**: Record the data sources and assumptions behind cost parameters 5. **Version configurations**: Track changes to costing parameters over time Exercises --------- **Exercise 1 (Easy)**: Generate harvest cost curves for two productivity classes and plot them. **Exercise 2 (Medium)**: Create a ws3 model with both volume and cost curves, and calculate net revenue at different ages. **Exercise 3 (Hard)**: Build an optimization problem that uses fhops cost curves to find the rotation age that maximizes net present value. Further Reading --------------- - :doc:`ch11_femic_models` — Building models with FEMIC - :doc:`ch13_freshforge` — Automating workflows with FreshForge - :doc:`ch07_financial_analysis` — Financial analysis fundamentals - FHOPS documentation: https://fhops.readthedocs.io