Harvest System Mix Explorer

Analyse machine-role allocation and system mixes for the synthetic medium scenario.

[1]:
import sys
from pathlib import Path

PROJECT_ROOT = Path.cwd().resolve()
while PROJECT_ROOT != PROJECT_ROOT.parent and not (PROJECT_ROOT / "pyproject.toml").exists():
    PROJECT_ROOT = PROJECT_ROOT.parent
if not (PROJECT_ROOT / "pyproject.toml").exists():
    raise RuntimeError(
        "Notebook must be executed within a FHOPS checkout (pyproject.toml not found)."
    )
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))


import pandas as pd
import yaml

from fhops.scenario.io import load_scenario
from fhops.scenario.synthetic import SyntheticDatasetConfig, generate_random_dataset

SCENARIO = PROJECT_ROOT / "examples/synthetic/medium/scenario.yaml"
ASSIGNMENTS = PROJECT_ROOT / "docs/examples/analytics/data/synthetic_medium_sa_assignments.csv"

System Mix Metadata

[2]:
metadata = yaml.safe_load((PROJECT_ROOT / "examples/synthetic/metadata.yaml").read_text())
medium_meta = metadata["medium"]
medium_meta.get("system_mix", {})
[2]:
{'ground_fb_skid': 0.15873015873015875,
 'ctl': 0.15873015873015875,
 'steep_tethered': 0.11904761904761904,
 'cable_running': 0.11904761904761904,
 'cable_running_fncy12': 0.03968253968253969,
 'cable_highlead_tn147': 0.023809523809523808,
 'cable_running_tr122_extended': 0.015873015873015872,
 'cable_running_tr122_shelterwood': 0.015873015873015872,
 'cable_running_tr122_clearcut': 0.015873015873015872,
 'cable_micro_ecologger': 0.03968253968253969,
 'cable_micro_gabriel': 0.03968253968253969,
 'cable_micro_christie': 0.03968253968253969,
 'cable_micro_teletransporteur': 0.03968253968253969,
 'cable_micro_timbermaster': 0.03968253968253969,
 'cable_micro_hi_skid': 0.03968253968253969,
 'cable_running_sr54': 0.015873015873015872,
 'cable_running_tr75_bunched': 0.015873015873015872,
 'cable_running_tr75_handfelled': 0.015873015873015872,
 'cable_standing_tr125_strip': 0.015873015873015872,
 'cable_partial_tr127_block1': 0.015873015873015872,
 'cable_partial_tr127_block5': 0.015873015873015872}

Machine Roles in Scenario

[3]:
scenario = load_scenario(SCENARIO)
roles = pd.Series([machine.role or "unassigned" for machine in scenario.machines], name="role")
roles.value_counts()
[3]:
role
harvester    2
forwarder    2
Name: count, dtype: int64

Generate Custom Mix

[4]:
custom_config = SyntheticDatasetConfig(
    name="synthetic-mix-demo",
    tier="large",
    num_blocks=12,
    num_days=16,
    num_machines=6,
    system_mix={"ground_fb_skid": 0.5, "ctl": 0.3, "steep_tethered": 0.2},
)
bundle = generate_random_dataset(custom_config, seed=404)
custom_roles = bundle.machines["role"].fillna("unassigned")
custom_roles.value_counts()
[4]:
role
harvester    3
forwarder    3
Name: count, dtype: int64

Role Comparison

[5]:
comparison_df = pd.DataFrame(
    {"scenario_roles": roles.value_counts(), "custom_mix_roles": custom_roles.value_counts()}
).fillna(0)
comparison_df
[5]:
scenario_roles custom_mix_roles
role
harvester 2 3
forwarder 2 3