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
graph TD
INPUTS["Input Data<br/>(inventory, terrain, roads)"] --> FHOPS["FHOPS<br/>Cost modeling"]
FHOPS --> CURVES["Harvest Cost<br/>Yield Curves"]
CURVES --> WS3["ws3 ForestModel<br/>(integration)"]
WS3 --> OPT["Optimization<br/>(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
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:
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:
graph TD
VOLUME["Harvest Volume<br/>(m³/ha)"] --> FIXED["Fixed Costs<br/>(setup, mobilization)"]
VOLUME --> VARIABLE["Variable Costs<br/>(per m³)"]
VARIABLE --> DENSITY["Density adjustment"]
VARIABLE --> DISTANCE["Distance adjustment"]
VARIABLE --> SLOPE["Slope adjustment"]
FIXED --> TOTAL["Total Cost<br/>($/ha)"]
VARIABLE --> TOTAL
The total cost is:
Integrating with ws3
The key integration point: fhops cost curves can be injected into ws3 as part of the financial analysis:
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:
# 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:
# 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
Calibrate costs to local conditions: Use actual harvesting data to calibrate fhops parameters
Validate against benchmarks: Compare fhops output to known cost estimates
Use productivity classes consistently: Define clear criteria for each productivity class
Document assumptions: Record the data sources and assumptions behind cost parameters
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
Chapter 11: Building Models with FEMIC — Building models with FEMIC
Chapter 13: Workflow Automation with FreshForge — Automating workflows with FreshForge
Chapter 7: Financial Analysis — Financial analysis fundamentals
FHOPS documentation: https://fhops.readthedocs.io