NAGA (Network for ASEAN Grid Analysis) is a standalone, anyone-can-run capacity-expansion and dispatch model of the ASEAN Power Grid (APG) at sub-national zonal resolution, solved by Benders decomposition with a monolithic reference solver for verification. Julia / JuMP, runs on the open-source HiGHS solver out of the box (Gurobi optional).
⚠️ Preliminary dataset — not for citation. The bundleddata_asean/uses a preliminary fleet derived from Global Energy Monitor (CC BY 4.0) and proxy/literature sources for several layers; an in-house dataset is in preparation and will replace it. Treat current numbers as illustrative. See DATA_SOURCES.md for provenance and attribution. The model code and method are stable and verified.
The model quantifies the value of regional grid coordination — and how that value changes when decarbonization pressure is externally imposed (EU CBAM carbon border pricing, corporate 24/7 carbon-free-energy procurement) rather than set by domestic targets. External pressure is an optional module, off by default — out of the box NAGA runs as a conventional regional coordination and planning model; enable the module to study CBAM and 24/7 CFE.
- Grid planners (AIMS III / utilities): a coordination ladder (Bilateral → MarketIntegration → PartialCoordination → FullCoordination) isolates the value of each mechanism — trade friction, shared adequacy, joint transmission and generation planning — and every run exports corridor shadow values ($/MW-yr of interconnection capacity), a demand-side corridor-prioritization criterion.
- MDBs / financiers: per-country cost, capacity, carbon-intensity and CBAM-exposure breakdowns show how coordination benefits are distributed, and adequacy shadow prices put a number on interconnection's reserve value.
- Corporates / 24/7 CFE buyers: hourly clean-energy matching (three modes, including the Riepin & Brown 2024 CFE score) with the import-eligibility question as a switch — whether cross-border clean energy counts toward CFE.
You need Julia (≥ 1.9) and Python 3. No commercial solver or license is required.
pip install -r requirements.txt # click, pyyaml, matplotlib (for tooling)
julia --project=. bootstrap.jl # resolve + instantiate deps; checks HiGHS
julia --project=. scripts/run_example.jl # ~3 min demo on the synthetic mini-ASEANThe example solves the core planning model, then the external-pressure story
(CBAM €100/t + 24/7 CFE 90% under country-scope vs region-scope import
eligibility) and prints a headline cost / CFE / carbon table. Run the full
verification suite with julia --project test/runtests.jl (~5 min, all on
HiGHS).
scripts/run_example.jl runs on data/mini_asean — a small, fully synthetic
6-country / 12-zone mainland dataset (regenerate any time with
python3 tools/make_mini_asean.py). It is the fast, dependency-free way to see
the whole pipeline end to end on a laptop before touching the full dataset.
julia --project=. scripts/run_coordination_value.jl # mini-ASEAN, minutes on a laptopSolves every country self-sufficient (islanded) and the coordinated
regional case on the same dataset, then reports the coordination dividend —
Σ(national plans) − coordinated — with per-country detail (cost, installed
capacity and carbon intensity, islanded vs coordinated). Point it at the full
dataset with a path argument and --method benders for whole-region runs.
Companion engines drill into who benefits, what drives it, and reliability:
julia --project=. scripts/run_coordination_ladder.jl # per-country benefit at each rung of the ladder
julia --project=. scripts/run_line_attribution.jl # each corridor's system value + per-country impact (counterfactual)
julia --project=. scripts/run_welfare.jl # trade-settled benefit per country at zonal LMPs (docs/welfare_settlement.md)
julia --project=. scripts/run_reliability.jl # existing-fleet adequacy: EUE/LOLE, islanded vs coordinated (docs/reliability.md)Turn any run's results into a shareable one-file HTML report:
python3 tools/build_report.py --results <results dir> --out report.html # headline KPIs, mix, per-country, corridors (docs/reporting.md)The real ASEAN dataset lives in data_asean/{current,2030,2035}/ (all 10 ASEAN
members + Timor-Leste, 51 zones). Drive sweeps from a scenario YAML:
python3 scripts/generate_jobs.py -o jobs # default 2030 sweep
cd jobs/<name> && julia --project=../.. ../../scripts/run_model.jl- Scope. Set
scope: mainlandin a scenario'sglobal_paramsfor continental ASEAN (~26 zones; Borneo + maritime excluded) orscope: fullfor all 11 countries / 51 zones. - Single country. Set
country: Vietnam(a name fromzones.csv) to plan one country islanded. - Solver. Auto-selected — the open-source HiGHS out of the box, or Gurobi
automatically if you have it installed. Force one with
solver: highs|gurobiinglobal_params.
One run = one config.json (see scripts/run_model.jl for keys), validated by
the built-in preflight: julia --project scripts/run_model.jl <config> --preflight-only.
| Topic | File |
|---|---|
| Mathematical formulation (sets, constraints, code map) | docs/MODEL.md |
| Input CSV schemas | docs/data_dictionary.md |
| Output CSV semantics + example analysis | docs/outputs_guide.md |
| Environment / solver setup | docs/environment_setup.md |
| Corporate 24/7 CFE design | docs/cfe_formulation.md |
| Dataset provenance + attribution | DATA_SOURCES.md |
| Per-country dataset notes | data_asean/README.md |
Edit a YAML in scenarios/ (e.g. apg_external_pressure.yml): the sweep
dimensions (years, coordination levels, external-pressure blocks, in-country RE
fractions) and a global_params block of run-wide settings (solver, method,
scope, tolerances). No code edits are needed to define a new study.
| Symptom | Fix |
|---|---|
solver = "gurobi" ... not available |
Only if you force solver: gurobi without it installed — use the default (auto) or solver: highs. |
data/mini_asean missing |
python3 tools/make_mini_asean.py |
Out of memory on scope: full monolithic |
Use method: benders (default) and/or scope: mainland, or a single country:. |
| Slow first run | Julia precompiles on first use; subsequent runs are fast. |
For large sweeps, scripts/generate_jobs.py writes one job folder per scenario
with a config.json and a symlink to scripts/submit_template.sb. Edit the
template for your cluster (resources, --mail-user, module load lines),
then sbatch each folder or pass --submit. The data path baked into each
config.json is absolute, so jobs are location-independent on a shared
filesystem.
MIT — see LICENSE. Bundled data carries its own source licenses and attribution requirements; see DATA_SOURCES.md.