Source code for equilibria.templates.pep_pyomo.pep_pyomo_scenarios
"""Counterfactual scenarios for the PEP model — the `** 6.4 Simulations` block of
PEP-1-1_v2_1.gms, applied to the CALIBRATED STATE before build_pep_model.
GAMS applies a scenario by fixing a benchmark-derived rate (e.g. `ttix.fx(i)=ttixO(i)*0.75`)
right before the solve. Since the Pyomo port derives every model rate from the calibrated
state's `*O` benchmarks (PEPParams reads `state.trade['ttixO']` etc.), the faithful analog
is to scale that benchmark IN THE STATE, then build — the model then carries the shocked
rate everywhere GAMS's fixed variable would. This keeps the builder scenario-agnostic (the
scenario is external, exactly as in the .gms).
SIM1 (the one uncommented simulation in the reference .gms): a 25% cut to the export-tax
rate — `ttix.fx(i) = ttixO(i)*0.75`. (The comment says "all indirect tax rates" but only
the ttix line is active; GAMS is the source of truth, so only ttix is scaled.)
"""
from __future__ import annotations
from typing import Any
[docs]
def apply_sim1_export_tax_cut(state: Any, factor: float = 0.75) -> Any:
"""Scale the export-tax benchmark `ttixO` in-place by `factor` (default 0.75 = −25%,
the reference SIM1). Mutates and returns `state` so PEPParams derives the shocked ttix.
Idempotent only per fresh calibration — call on a newly calibrated state, not twice."""
trade = getattr(state, "trade", None)
if not isinstance(trade, dict) or "ttixO" not in trade:
raise ValueError("state.trade['ttixO'] not found — cannot apply the SIM1 export-tax shock")
for i in list(trade["ttixO"]):
trade["ttixO"][i] = trade["ttixO"][i] * factor
return state