fhops.scenario Package

This package hosts the scenario contract, IO utilities, and synthetic dataset generator. Use it when authoring datasets, validating inputs, or programmatically instantiating fhops.scenario.contract.Problem objects before passing them to solvers. The typical workflow is:

  1. Define CSV tables + YAML metadata (see Data Contract Guide).

  2. Load scenarios with fhops.scenario.io.load_scenario().

  3. Create a fhops.scenario.contract.Problem via Problem.from_scenario for use with MIP/heuristics.

  4. Optionally call fhops.scenario.synthetic helpers to generate benchmark datasets.

Scenario package exposing contracts, IO helpers, and synthetic generators.

class fhops.scenario.Block(*, id, landing_id, work_required, earliest_start=1, latest_finish=None, harvest_system_id=None, avg_stem_size_m3=None, volume_per_ha_m3=None, volume_per_ha_m3_sigma=None, stem_density_per_ha=None, stem_density_per_ha_sigma=None, ground_slope_percent=None, salvage_processing_mode=None)[source]

Bases: BaseModel

Harvest block metadata and scheduling window.

Parameters:
  • id (str)

  • landing_id (str)

  • work_required (float)

  • earliest_start (int | None)

  • latest_finish (int | None)

  • harvest_system_id (str | None)

  • avg_stem_size_m3 (float | None)

  • volume_per_ha_m3 (float | None)

  • volume_per_ha_m3_sigma (float | None)

  • stem_density_per_ha (float | None)

  • stem_density_per_ha_sigma (float | None)

  • ground_slope_percent (float | None)

  • salvage_processing_mode (SalvageProcessingMode | None)

id

Unique block identifier (referenced by production rates and assignments).

Type:

str

landing_id

Landing where wood is forwarded; constrains landing daily capacity.

Type:

str

work_required

Total work units (machine-hours equivalent) necessary to complete the block.

Type:

float

earliest_start

Optional earliest day (inclusive, 1-indexed) when the block can begin.

Type:

Day | None

latest_finish

Optional latest day (inclusive) when the block must finish.

Type:

Day | None

harvest_system_id

Optional harvest system definition that restricts machine roles per block.

Type:

str | None

avg_stem_size_m3 / volume_per_ha_m3 / volume_per_ha_m3_sigma

Stand descriptors (cubic metres) surfaced in analytics and productivity lookups.

stem_density_per_ha / stem_density_per_ha_sigma

Stems per hectare statistics used by some productivity models.

ground_slope_percent

Mean slope (%) for the block — used by productivity heuristics and diagnostics.

Type:

float | None

salvage_processing_mode

Enum describing downstream salvage processing (affects evaluation notes).

Type:

SalvageProcessingMode | None

avg_stem_size_m3: float | None
earliest_start: Day | None
ground_slope_percent: float | None
harvest_system_id: str | None
id: str
landing_id: str
latest_finish: Day | None
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

salvage_processing_mode: SalvageProcessingMode | None
stem_density_per_ha: float | None
stem_density_per_ha_sigma: float | None
volume_per_ha_m3: float | None
volume_per_ha_m3_sigma: float | None
work_required: float
class fhops.scenario.CalendarEntry(*, machine_id, day, available=1)[source]

Bases: BaseModel

Day-level availability for a machine.

Parameters:
machine_id

Identifier of the machine whose availability is being set.

Type:

str

day

One-indexed day number relative to the scenario horizon.

Type:

Day

available

Binary flag (1 available, 0 unavailable) controlling day-level assignment eligibility.

Type:

int

available: int
day: Day
machine_id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

fhops.scenario.Day

alias of int

class fhops.scenario.Landing(*, id, daily_capacity=2)[source]

Bases: BaseModel

Landing metadata including per-day assignment capacity.

Parameters:
  • id (str)

  • daily_capacity (int)

id

Landing identifier referenced by blocks and mobilisation logic.

Type:

str

daily_capacity

Maximum number of machines that can work on the landing concurrently per day.

Type:

int

daily_capacity: int
id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class fhops.scenario.Machine(*, id, crew=None, daily_hours=24.0, operating_cost=0.0, role=None, repair_usage_hours=None)[source]

Bases: BaseModel

Machine definition (identifier, crew, availability, and costing metadata).

Parameters:
  • id (str)

  • crew (str | None)

  • daily_hours (float)

  • operating_cost (float)

  • role (str | None)

  • repair_usage_hours (int | None)

id

Unique machine identifier referenced throughout calendars/assignments.

Type:

str

crew

Optional crew label for reporting/telemetry grouping.

Type:

str | None

daily_hours

Maximum hours the machine can operate per day (defaults to 24).

Type:

float

operating_cost

Cost per scheduled machine hour (SMH) expressed in scenario currency units.

Type:

float

role

Optional machine role string (normalised via normalize_machine_role) used by harvest systems and rental-rate lookups.

Type:

str | None

repair_usage_hours

Optional cumulative repair hours that influences the rental-rate defaults.

Type:

int | None

crew: str | None
daily_hours: float
id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

operating_cost: float
repair_usage_hours: int | None
role: str | None
class fhops.scenario.Problem(**data)[source]

Bases: BaseModel

Runtime representation of a scenario used by solvers.

Problem wraps a validated Scenario and expands it into concrete days and shifts so optimisation code can iterate over deterministic index sets without repeatedly querying the Scenario. Problem.from_scenario is the canonical constructor; it injects the default harvest-system registry (when necessary) and synthesises single-shift calendars for legacy day-indexed inputs.

Parameters:

data (Any)

scenario

Back-reference to the source Scenario.

Type:

Scenario

days

List of integer day indices derived from scenario.num_days.

Type:

list[Day]

shifts

List of ShiftInstance entries representing every (day, shift_id) slot the solver should consider.

Type:

list[ShiftInstance]

Notes

Any code that builds Pyomo models or heuristic plans should accept a Problem rather than the raw Scenario to avoid recomputing shift/day metadata.

days: list[Day]
classmethod from_scenario(scenario)[source]
Parameters:

scenario (Scenario)

Return type:

Problem

model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

scenario: Scenario
shifts: list[ShiftInstance]
class fhops.scenario.ProductionRate(*, machine_id, block_id, rate)[source]

Bases: BaseModel

Per-day production rate measured in work units for a machine/block pair.

Parameters:
machine_id

Machine identifier (must exist in Scenario.machines).

Type:

str

block_id

Block identifier (must exist in Scenario.blocks).

Type:

str

rate

Work units produced per full shift/day assignment. Must be non-negative.

Type:

float

block_id: str
machine_id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

rate: float
class fhops.scenario.Scenario(**data)[source]

Bases: BaseModel

Top-level container for the FHOPS data contract.

The model mirrors the CSV/YAML inputs documented in docs/howto/data_contract.rst and is the object returned by fhops.scenario.io.load_scenario(). Only validated, horizon-bounded data reaches this point, which means downstream solvers (MIP + heuristics) can rely on:

  • every block referencing a known landing/harvest system,

  • machine calendars/shift calendars never exceeding num_days,

  • mobilisation tables referencing existing blocks/machines, and

  • optional extras (crew assignments, road construction, GeoJSON metadata) being present only when fully specified.

Parameters:

data (Any)

name

Human-readable scenario label surfaced in CLI/Evaluation outputs.

Type:

str

num_days

Planning horizon length (integer number of days).

Type:

int

schema_version

Version of the input schema; used to guard loader compatibility.

Type:

str

start_date

Optional ISO date string used for timestamped exports.

Type:

date | None

blocks / machines / landings

Validated lists of the corresponding Pydantic models.

calendar / shift_calendar

Availability tables. shift_calendar may be None for day-level scenarios.

production_rates

Machine/block productivity table measured in work units per assignment.

Type:

list[ProductionRate]

timeline

Optional TimelineConfig describing shifts, blackout windows, etc.

Type:

TimelineConfig | None

mobilisation

Optional MobilisationConfig describing distances and per-machine parameters.

Type:

MobilisationConfig | None

harvest_systems

Optional registry mapping harvest-system IDs to HarvestSystem definitions.

Type:

dict[str, HarvestSystem] | None

geo

Optional GeoMetadata with GeoJSON lookups.

Type:

GeoMetadata | None

crew_assignments

Optional list mapping crew IDs to machines for reporting/telemetry.

Type:

list[CrewAssignment] | None

locked_assignments

Optional list of ScheduleLock entries that pin machines to blocks on specific days.

Type:

list[ScheduleLock] | None

objective_weights

Optional ObjectiveWeights overriding default solver weights.

Type:

ObjectiveWeights | None

road_construction

Optional list of RoadConstruction entries used by telemetry/costing exports.

Type:

list[RoadConstruction] | None

Notes

The helper methods (machine_ids(), window_for(), etc.) are convenience routines for the solver/evaluation layers and are intentionally lightweight so they can be used in tight loops.

block_ids()[source]

Return the list of block identifiers defined in the scenario.

Return type:

list[str]

blocks: list[Block]
calendar: list[CalendarEntry]
crew_assignments: list[CrewAssignment] | None
geo: GeoMetadata | None
harvest_systems: dict[str, HarvestSystem] | None
landing_ids()[source]

Return the list of landing identifiers defined in the scenario.

Return type:

list[str]

landings: list[Landing]
locked_assignments: list[ScheduleLock] | None
machine_ids()[source]

Return the list of machine identifiers defined in the scenario.

Return type:

list[str]

machines: list[Machine]
mobilisation: MobilisationConfig | None
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str
num_days: int
objective_weights: ObjectiveWeights | None
production_rates: list[ProductionRate]
road_construction: list[RoadConstruction] | None
schema_version: str
shift_calendar: list[ShiftCalendarEntry] | None
start_date: date | None
timeline: TimelineConfig | None
window_for(block_id)[source]

Return the inclusive (earliest, latest) day window for the target block.

Parameters:

block_id (str)

Return type:

tuple[int, int]

fhops.scenario.load_scenario(yaml_path)[source]

Load a Scenario from the YAML metadata + CSV bundle.

Parameters:

yaml_path (str | Path) – Path to the scenario.yaml file that references the component CSVs.

Returns:

Fully validated Pydantic model ready to be converted into a fhops.scenario.contract.Problem.

Return type:

Scenario

Notes

The loader performs several quality-of-life tasks that callers usually forget:

  • normalises optional string columns (e.g., harvest_system_id blanks → None),

  • back-fills mobilisation distance matrices from *_block_distances.csv whenever present,

  • accepts inline YAML overrides for optional tables (road construction, shift calendar, crew map),

  • re-roots GeoJSON paths relative to the scenario directory, and

  • ensures every optional extra (timeline, mobilisation config, objective weights) is copied into the resulting Scenario instance.

fhops.scenario.read_csv(path)[source]

Load a CSV file using pandas with UTF-8 defaults.

Parameters:

path (Path)

Return type:

DataFrame

Scenario contract models (Pydantic schemas, validators).

class fhops.scenario.contract.Block(*, id, landing_id, work_required, earliest_start=1, latest_finish=None, harvest_system_id=None, avg_stem_size_m3=None, volume_per_ha_m3=None, volume_per_ha_m3_sigma=None, stem_density_per_ha=None, stem_density_per_ha_sigma=None, ground_slope_percent=None, salvage_processing_mode=None)[source]

Bases: BaseModel

Harvest block metadata and scheduling window.

Parameters:
  • id (str)

  • landing_id (str)

  • work_required (float)

  • earliest_start (int | None)

  • latest_finish (int | None)

  • harvest_system_id (str | None)

  • avg_stem_size_m3 (float | None)

  • volume_per_ha_m3 (float | None)

  • volume_per_ha_m3_sigma (float | None)

  • stem_density_per_ha (float | None)

  • stem_density_per_ha_sigma (float | None)

  • ground_slope_percent (float | None)

  • salvage_processing_mode (SalvageProcessingMode | None)

id

Unique block identifier (referenced by production rates and assignments).

Type:

str

landing_id

Landing where wood is forwarded; constrains landing daily capacity.

Type:

str

work_required

Total work units (machine-hours equivalent) necessary to complete the block.

Type:

float

earliest_start

Optional earliest day (inclusive, 1-indexed) when the block can begin.

Type:

Day | None

latest_finish

Optional latest day (inclusive) when the block must finish.

Type:

Day | None

harvest_system_id

Optional harvest system definition that restricts machine roles per block.

Type:

str | None

avg_stem_size_m3 / volume_per_ha_m3 / volume_per_ha_m3_sigma

Stand descriptors (cubic metres) surfaced in analytics and productivity lookups.

stem_density_per_ha / stem_density_per_ha_sigma

Stems per hectare statistics used by some productivity models.

ground_slope_percent

Mean slope (%) for the block — used by productivity heuristics and diagnostics.

Type:

float | None

salvage_processing_mode

Enum describing downstream salvage processing (affects evaluation notes).

Type:

SalvageProcessingMode | None

avg_stem_size_m3: float | None
earliest_start: Day | None
ground_slope_percent: float | None
harvest_system_id: str | None
id: str
landing_id: str
latest_finish: Day | None
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

salvage_processing_mode: SalvageProcessingMode | None
stem_density_per_ha: float | None
stem_density_per_ha_sigma: float | None
volume_per_ha_m3: float | None
volume_per_ha_m3_sigma: float | None
work_required: float
class fhops.scenario.contract.CalendarEntry(*, machine_id, day, available=1)[source]

Bases: BaseModel

Day-level availability for a machine.

Parameters:
machine_id

Identifier of the machine whose availability is being set.

Type:

str

day

One-indexed day number relative to the scenario horizon.

Type:

Day

available

Binary flag (1 available, 0 unavailable) controlling day-level assignment eligibility.

Type:

int

available: int
day: Day
machine_id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class fhops.scenario.contract.CrewAssignment(*, crew_id, machine_id, primary_role=None, notes=None)[source]

Bases: BaseModel

Optional mapping of crews to machines/roles.

Parameters:
  • crew_id (str)

  • machine_id (str)

  • primary_role (str | None)

  • notes (str | None)

crew_id

Unique crew identifier.

Type:

str

machine_id

Machine assigned to the crew.

Type:

str

primary_role

Optional role label associated with the crew (e.g., fallers, processors).

Type:

str | None

notes

Additional metadata surfaced in telemetry exports.

Type:

str | None

crew_id: str
machine_id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

notes: str | None
primary_role: str | None
fhops.scenario.contract.Day

alias of int

class fhops.scenario.contract.Landing(*, id, daily_capacity=2)[source]

Bases: BaseModel

Landing metadata including per-day assignment capacity.

Parameters:
  • id (str)

  • daily_capacity (int)

id

Landing identifier referenced by blocks and mobilisation logic.

Type:

str

daily_capacity

Maximum number of machines that can work on the landing concurrently per day.

Type:

int

daily_capacity: int
id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class fhops.scenario.contract.Machine(*, id, crew=None, daily_hours=24.0, operating_cost=0.0, role=None, repair_usage_hours=None)[source]

Bases: BaseModel

Machine definition (identifier, crew, availability, and costing metadata).

Parameters:
  • id (str)

  • crew (str | None)

  • daily_hours (float)

  • operating_cost (float)

  • role (str | None)

  • repair_usage_hours (int | None)

id

Unique machine identifier referenced throughout calendars/assignments.

Type:

str

crew

Optional crew label for reporting/telemetry grouping.

Type:

str | None

daily_hours

Maximum hours the machine can operate per day (defaults to 24).

Type:

float

operating_cost

Cost per scheduled machine hour (SMH) expressed in scenario currency units.

Type:

float

role

Optional machine role string (normalised via normalize_machine_role) used by harvest systems and rental-rate lookups.

Type:

str | None

repair_usage_hours

Optional cumulative repair hours that influences the rental-rate defaults.

Type:

int | None

crew: str | None
daily_hours: float
id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

operating_cost: float
repair_usage_hours: int | None
role: str | None
class fhops.scenario.contract.Problem(*, scenario, days, shifts)[source]

Bases: BaseModel

Runtime representation of a scenario used by solvers.

Problem wraps a validated Scenario and expands it into concrete days and shifts so optimisation code can iterate over deterministic index sets without repeatedly querying the Scenario. Problem.from_scenario is the canonical constructor; it injects the default harvest-system registry (when necessary) and synthesises single-shift calendars for legacy day-indexed inputs.

Parameters:
  • scenario (Scenario)

  • days (list[int])

  • shifts (list[ShiftInstance])

scenario

Back-reference to the source Scenario.

Type:

Scenario

days

List of integer day indices derived from scenario.num_days.

Type:

list[Day]

shifts

List of ShiftInstance entries representing every (day, shift_id) slot the solver should consider.

Type:

list[ShiftInstance]

Notes

Any code that builds Pyomo models or heuristic plans should accept a Problem rather than the raw Scenario to avoid recomputing shift/day metadata.

days: list[Day]
classmethod from_scenario(scenario)[source]
Parameters:

scenario (Scenario)

Return type:

Problem

model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

scenario: Scenario
shifts: list[ShiftInstance]
class fhops.scenario.contract.ProductionRate(*, machine_id, block_id, rate)[source]

Bases: BaseModel

Per-day production rate measured in work units for a machine/block pair.

Parameters:
machine_id

Machine identifier (must exist in Scenario.machines).

Type:

str

block_id

Block identifier (must exist in Scenario.blocks).

Type:

str

rate

Work units produced per full shift/day assignment. Must be non-negative.

Type:

float

block_id: str
machine_id: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

rate: float
class fhops.scenario.contract.RoadConstruction(*, id, machine_slug, road_length_m, include_mobilisation=True, soil_profile_ids=None, notes=None)[source]

Bases: BaseModel

Road/subgrade construction job describing TR-28 soil profiles and costing metadata.

Parameters:
  • id (str)

  • machine_slug (str)

  • road_length_m (float)

  • include_mobilisation (bool)

  • soil_profile_ids (list[str] | None)

  • notes (str | None)

id

Unique job identifier referenced in telemetry and costing exports.

Type:

str

machine_slug

Machine rate slug (tr28 index) used to determine construction costs.

Type:

str

road_length_m

Length of the road section (metres) to construct.

Type:

float

include_mobilisation

When True, mobilisation costs are included in the estimate.

Type:

bool

soil_profile_ids

Optional list of TR-28 soil profile identifiers associated with the job.

Type:

list[str] | None

notes

Free-form comments surfaced in CLI summaries.

Type:

str | None

id: str
include_mobilisation: bool
machine_slug: str
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

notes: str | None
road_length_m: float
soil_profile_ids: list[str] | None
class fhops.scenario.contract.SalvageProcessingMode(value)[source]

Bases: StrEnum

IN_WOODS_CHIPPING = 'in_woods_chipping'
PORTABLE_MILL = 'portable_mill'
STANDARD_MILL = 'standard_mill'
class fhops.scenario.contract.Scenario(*, name, num_days, schema_version='1.0.0', start_date=None, blocks, machines, landings, calendar, shift_calendar=None, production_rates, timeline=None, mobilisation=None, harvest_systems=None, geo=None, crew_assignments=None, locked_assignments=None, objective_weights=None, road_construction=None)[source]

Bases: BaseModel

Top-level container for the FHOPS data contract.

The model mirrors the CSV/YAML inputs documented in docs/howto/data_contract.rst and is the object returned by fhops.scenario.io.load_scenario(). Only validated, horizon-bounded data reaches this point, which means downstream solvers (MIP + heuristics) can rely on:

  • every block referencing a known landing/harvest system,

  • machine calendars/shift calendars never exceeding num_days,

  • mobilisation tables referencing existing blocks/machines, and

  • optional extras (crew assignments, road construction, GeoJSON metadata) being present only when fully specified.

Parameters:
  • name (str)

  • num_days (int)

  • schema_version (str)

  • start_date (date | None)

  • blocks (list[Block])

  • machines (list[Machine])

  • landings (list[Landing])

  • calendar (list[CalendarEntry])

  • shift_calendar (list[ShiftCalendarEntry] | None)

  • production_rates (list[ProductionRate])

  • timeline (TimelineConfig | None)

  • mobilisation (MobilisationConfig | None)

  • harvest_systems (dict[str, HarvestSystem] | None)

  • geo (GeoMetadata | None)

  • crew_assignments (list[CrewAssignment] | None)

  • locked_assignments (list[ScheduleLock] | None)

  • objective_weights (ObjectiveWeights | None)

  • road_construction (list[RoadConstruction] | None)

name

Human-readable scenario label surfaced in CLI/Evaluation outputs.

Type:

str

num_days

Planning horizon length (integer number of days).

Type:

int

schema_version

Version of the input schema; used to guard loader compatibility.

Type:

str

start_date

Optional ISO date string used for timestamped exports.

Type:

date | None

blocks / machines / landings

Validated lists of the corresponding Pydantic models.

calendar / shift_calendar

Availability tables. shift_calendar may be None for day-level scenarios.

production_rates

Machine/block productivity table measured in work units per assignment.

Type:

list[ProductionRate]

timeline

Optional TimelineConfig describing shifts, blackout windows, etc.

Type:

TimelineConfig | None

mobilisation

Optional MobilisationConfig describing distances and per-machine parameters.

Type:

MobilisationConfig | None

harvest_systems

Optional registry mapping harvest-system IDs to HarvestSystem definitions.

Type:

dict[str, HarvestSystem] | None

geo

Optional GeoMetadata with GeoJSON lookups.

Type:

GeoMetadata | None

crew_assignments

Optional list mapping crew IDs to machines for reporting/telemetry.

Type:

list[CrewAssignment] | None

locked_assignments

Optional list of ScheduleLock entries that pin machines to blocks on specific days.

Type:

list[ScheduleLock] | None

objective_weights

Optional ObjectiveWeights overriding default solver weights.

Type:

ObjectiveWeights | None

road_construction

Optional list of RoadConstruction entries used by telemetry/costing exports.

Type:

list[RoadConstruction] | None

Notes

The helper methods (machine_ids(), window_for(), etc.) are convenience routines for the solver/evaluation layers and are intentionally lightweight so they can be used in tight loops.

block_ids()[source]

Return the list of block identifiers defined in the scenario.

Return type:

list[str]

blocks: list[Block]
calendar: list[CalendarEntry]
crew_assignments: list[CrewAssignment] | None
geo: GeoMetadata | None
harvest_systems: dict[str, HarvestSystem] | None
landing_ids()[source]

Return the list of landing identifiers defined in the scenario.

Return type:

list[str]

landings: list[Landing]
locked_assignments: list[ScheduleLock] | None
machine_ids()[source]

Return the list of machine identifiers defined in the scenario.

Return type:

list[str]

machines: list[Machine]
mobilisation: MobilisationConfig | None
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: str
num_days: int
objective_weights: ObjectiveWeights | None
production_rates: list[ProductionRate]
road_construction: list[RoadConstruction] | None
schema_version: str
shift_calendar: list[ShiftCalendarEntry] | None
start_date: date | None
timeline: TimelineConfig | None
window_for(block_id)[source]

Return the inclusive (earliest, latest) day window for the target block.

Parameters:

block_id (str)

Return type:

tuple[int, int]

Quickstart

from fhops.scenario.io import load_scenario
from fhops.scenario.contract import Problem

scenario = load_scenario("examples/tiny7/scenario.yaml")
problem = Problem.from_scenario(scenario)
print(problem.days, len(problem.shifts))

Key models:

  • fhops.scenario.contract.Scenario – Pydantic model for inputs (blocks, machines, timelines).

  • fhops.scenario.contract.Problem – Derived object used by solvers (days, shifts, scenario).

  • fhops.scenario.contract.MobilisationConfig / TimelineConfig – optional extras for mobilisation/shift data.

Scenario IO helpers (YAML/CSV loaders, schema checks).

fhops.scenario.io.load_scenario(yaml_path)[source]

Load a Scenario from the YAML metadata + CSV bundle.

Parameters:

yaml_path (str | Path) – Path to the scenario.yaml file that references the component CSVs.

Returns:

Fully validated Pydantic model ready to be converted into a fhops.scenario.contract.Problem.

Return type:

Scenario

Notes

The loader performs several quality-of-life tasks that callers usually forget:

  • normalises optional string columns (e.g., harvest_system_id blanks → None),

  • back-fills mobilisation distance matrices from *_block_distances.csv whenever present,

  • accepts inline YAML overrides for optional tables (road construction, shift calendar, crew map),

  • re-roots GeoJSON paths relative to the scenario directory, and

  • ensures every optional extra (timeline, mobilisation config, objective weights) is copied into the resulting Scenario instance.

fhops.scenario.io.read_csv(path)[source]

Load a CSV file using pandas with UTF-8 defaults.

Parameters:

path (Path)

Return type:

DataFrame

Synthetic scenario generators.

class fhops.scenario.synthetic.BlackoutBias(start_day, end_day, probability, duration=None)[source]

Bases: object

Bias blackout probabilities for specific windows.

Parameters:
duration: tuple[int, int] | int | None = None
end_day: int
probability: float
start_day: int
class fhops.scenario.synthetic.SyntheticDatasetBundle(scenario, blocks, machines, landings, calendar, production_rates, metadata=None)[source]

Bases: object

Container for generated scenario tables and helpers to persist them.

Parameters:
  • scenario (Scenario)

  • blocks (DataFrame)

  • machines (DataFrame)

  • landings (DataFrame)

  • calendar (DataFrame)

  • production_rates (DataFrame)

  • metadata (dict[str, object] | None)

blocks: DataFrame
calendar: DataFrame
landings: DataFrame
machines: DataFrame
metadata: dict[str, object] | None = None
production_rates: DataFrame
scenario: Scenario
write(out_dir, *, include_yaml=True, metadata_path=None)[source]
Parameters:
  • out_dir (Path)

  • include_yaml (bool)

  • metadata_path (Path | None)

Return type:

Path

class fhops.scenario.synthetic.SyntheticDatasetConfig(name, num_blocks, num_days, num_machines, num_landings=1, shift_hours=(8.0, 12.0), shifts_per_day=1, machine_daily_hours=24.0, landing_capacity=(1, 3), work_required=(6.0, 18.0), production_rate=(6.0, 18.0), availability_probability=0.9, blackout_probability=0.1, blackout_duration=(1, 2), role_pool=<factory>, tier=None, terrain_pool=<factory>, terrain_weights=None, prescription_pool=<factory>, prescription_weights=None, crew_pool=<factory>, capability_pool=<factory>, crew_capability_span=(1, 2), system_mix=None, blackout_biases=<factory>, sampling_overrides=None, block_metric_section='daily')[source]

Bases: object

Configuration for generating random synthetic datasets.

Parameters:
availability_probability: float = 0.9
blackout_biases: list[BlackoutBias]
blackout_duration: tuple[int, int] | int = (1, 2)
blackout_probability: float = 0.1
block_metric_section: str = 'daily'
capability_pool: list[str]
crew_capability_span: tuple[int, int] = (1, 2)
crew_pool: list[str]
landing_capacity: tuple[int, int] | int = (1, 3)
machine_daily_hours: float = 24.0
name: str
num_blocks: tuple[int, int] | int
num_days: tuple[int, int] | int
num_landings: tuple[int, int] | int = 1
num_machines: tuple[int, int] | int
prescription_pool: list[str]
prescription_weights: list[float] | None = None
production_rate: tuple[float, float] = (6.0, 18.0)
role_pool: list[str]
sampling_overrides: dict[str, object] | None = None
shift_hours: tuple[float, float] = (8.0, 12.0)
shifts_per_day: int = 1
system_mix: dict[str, float] | None = None
terrain_pool: list[str]
terrain_weights: list[float] | None = None
tier: str | None = None
work_required: tuple[float, float] = (6.0, 18.0)
class fhops.scenario.synthetic.SyntheticScenarioSpec(num_blocks, num_days, num_machines, landing_capacity=1, blackout_days=None)[source]

Bases: object

Configuration for generating synthetic scenarios.

Parameters:
  • num_blocks (int)

  • num_days (int)

  • num_machines (int)

  • landing_capacity (int)

  • blackout_days (list[int] | None)

blackout_days: list[int] | None = None
landing_capacity: int = 1
num_blocks: int
num_days: int
num_machines: int
fhops.scenario.synthetic.generate_basic(spec)[source]

Generate a minimal scenario matching the supplied specification.

Parameters:

spec (SyntheticScenarioSpec)

Return type:

Scenario

fhops.scenario.synthetic.generate_random_dataset(config, *, seed=123, systems=None)[source]

Generate a random synthetic dataset bundle (scenario + CSV tables).

Parameters:
Return type:

SyntheticDatasetBundle

fhops.scenario.synthetic.generate_with_systems(spec, systems=None)[source]

Generate a scenario and assign blocks round-robin to harvest systems.

Parameters:
Return type:

Scenario

fhops.scenario.synthetic.sampling_config_for(config)[source]
Parameters:

config (SyntheticDatasetConfig)

Return type:

SamplingConfig