Simulation Engine¶
Module: hours_eoh/core/simulation.py
Period-by-period simulation of the EOH/TEH economy. Tracks the full physical state across multiple periods, applying all core mechanics.
make_economy_state(epsilon, …) → dict¶
Creates an initial economy state for simulation: every quantity that persists between periods. Fields left unsupplied take the function's defaults — the capital stock, knowledge complexity and monitoring capability from the canonical arc at ε, the rest from fixed defaults. Per-period inputs such as levy rates are passed to simulate_period() rather than stored in the state.
The register is state too: registration_epsilon (default None, the register tracks ε). Set it to hold the register while capability moves; simulate_period() carries it forward unchanged and the pipeline, the guarantee and the period's on-ledger reads all use it.
The knowledge base follows a rule set by knowledge_follows_arc. Left as None, a knowledge base the state takes from the canonical arc follows ε from period to period, as complexity per unit does, and a knowledge_complexity you supply is carried and moved only by its growth rate. A supplied stock is never rescaled by ε. Pass True or False to choose the rule yourself.
simulate_period(state, epsilon_delta, …) → tuple[dict, dict]¶
Advances the economy by one period from the ε carried in state. Per-period
inputs (growth and degradation rates, levy rates, epsilon_delta) are keyword
arguments, not state. Applies, in causal order:
- Population grows or shrinks by the growth rate
- Capital ages, and grows through investment
- Ecosystem degrades or restores, and deferred ecological EOH accumulates
- The EOH → TEH pipeline (machine/human split, registration, TEH creation)
- Fiscal mechanics (levies, the land fee, the sufficiency guarantee, the Trust balance)
- TEH destruction (see the table below)
- State update: ε advances and cumulative TEH is carried forward
The period does not mutate state; it returns a fresh one.
The land fee. Pass guf_assessed, a function of ε that returns the fee
assessed in TEH, and the period charges it at its own ε.
land.collective.per_parcel_fee(parcel_count) builds one from a parcel count;
core/ cannot import land/, so the fee is passed in. Holders pay at most what
the period's labour income leaves after the levy and D3 consumption, so the
period collects min(assessed, ceiling). period_result["land_fee"] reports
assessed, collected, unpaid and payable_ceiling, and
period_result["guf_net_inflow"] is the fee collected. guf_net_inflow=
instead books a fixed inflow uncapped; passing both raises. With neither, the
period charges no fee.
The labour income is the period's mint unless labor_income_scale overrides
it. With growth switched off, one period agrees with
scenarios.stationarity.stationarity_at on the mint, the levy, the fee and
the guarantee.
Returns (next_state, period_result).
from hours_eoh.core.simulation import simulate_period
next_state, period_result = simulate_period(state)
print(next_state["period"], period_result["teh_created"])
run_simulation(initial_state, n_periods, …) → dict¶
Runs simulate_period repeatedly from an initial state; any per-period keyword
(such as epsilon_delta) is forwarded to every period. Returns states,
period_results, final_state, summary, solvent_all and first_insolvency.
from hours_eoh.core.simulation import run_simulation
results = run_simulation(make_economy_state(epsilon=0.30), n_periods=20,
epsilon_delta=0.02)
print(results["solvent_all"], results["first_insolvency"])
for row in results["period_results"]:
print(f"ε={row['epsilon']:.2f} TEH={row['teh_created']:.3e}")
The CLI wrapper:
TEH Lifecycle in the Simulation¶
Each destruction mechanism is switched by a keyword argument of simulate_period():
| Period event | D# | How the period applies it | Default |
|---|---|---|---|
| Capital write-down | D1 | A failure rate on the capital stock, reduced as monitoring improves — an aggregate proxy, not execute_writedown() per asset |
always on |
| Income-driven consumption | D2 | A consumption rate on period income (net wages plus the Trust dividend), falling as purchasing power rises | on, unless use_d3=True |
| Biology-anchored consumption | D3 | On-ledger personal EOH × its human-carried share × M_FLOOR (1 TEH per human obligation hour) |
use_d3=False |
| Capital-delivered services | D4 | cpi_goods_destruction() — paused: D3 already covers delivery, and a machine-delivered good has no floor price to destroy. Kept for a market-level split |
use_cpi_destruction=False |
| Death events | D5 | estate_dissolution(); the estate levy returns to the Trust |
use_estate_dissolution=True |
| Accumulation ceiling | D6 | accumulation_ceiling_commitment() — commits the excess to capital formation rather than destroying it |
use_accumulation_ceiling=False |
Levy collection and Trust spending are circulatory (TEH redirected, not destroyed).