Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
4d1617f
heterogeneous GNN and OPF example added
allaffa Jan 28, 2026
5d1971b
black formatting fixed
allaffa Jan 28, 2026
287ceae
fix on rank printouts
allaffa Jan 29, 2026
93c4109
CI test for heterogeneous GNNs restructures
allaffa Jan 29, 2026
b4b5ab9
test thresholds adjusted
allaffa Jan 29, 2026
3b2aae0
improved handlign of data download
allaffa Jan 29, 2026
bce581f
improved tar extraction o fdata
allaffa Jan 29, 2026
a9a89b8
remove barrier
allaffa Jan 29, 2026
ab66827
cope with errors in reading JSON files
allaffa Jan 29, 2026
a98009e
progression bar added to show decompression of tiles
allaffa Jan 29, 2026
b14057b
added max_neighbors to JSON file
allaffa Feb 3, 2026
c01aa28
output added to JSON file
allaffa Feb 3, 2026
1cc7fb8
corrections applied also to homogeneous case
allaffa Feb 3, 2026
be99912
upgraded data management for transfer to device
allaffa Feb 3, 2026
ce3526a
upgraded data management for transfer to device
allaffa Feb 3, 2026
b42f683
HeteroBase managed to improve data movement to devices
allaffa Feb 3, 2026
6a621f0
HeteroBase managed to improve data movement to devices
allaffa Feb 3, 2026
53adbd0
HeteroBase managed to improve data movement to devices
allaffa Feb 3, 2026
6d3f04d
HeteroBase managed to improve data movement to devices
allaffa Feb 3, 2026
1e8b820
HeteroBase managed to improve data movement to devices
allaffa Feb 3, 2026
b326863
opf solver with nodal predictions added
allaffa Feb 3, 2026
1dee0d7
opf solver with nodal predictions added
allaffa Feb 3, 2026
8a213ef
opf solver with nodal predictions added
allaffa Feb 3, 2026
b928220
opf solver with nodal predictions added
allaffa Feb 3, 2026
7524424
opf solver with nodal predictions added
allaffa Feb 3, 2026
2ef31e1
opf solver with nodal predictions added
allaffa Feb 3, 2026
f667df0
included local data size per rank prinouts
allaffa Feb 3, 2026
4384333
opf solver with nodal predictions added
allaffa Feb 3, 2026
3efea63
opf solver with nodal predictions added
allaffa Feb 3, 2026
2c8744e
opf solver with nodal predictions added
allaffa Feb 3, 2026
5a77d52
opf solver with nodal predictions added
allaffa Feb 3, 2026
77793eb
opf solver with nodal predictions added
allaffa Feb 3, 2026
ece3333
opf solver with nodal predictions added
allaffa Feb 3, 2026
ddfdd3d
data moved to device
allaffa Feb 3, 2026
906ae36
config fixed
allaffa Feb 3, 2026
345082a
added option to extract all cases
allaffa Feb 3, 2026
7a44920
added option to download all cases
allaffa Feb 3, 2026
42538d9
heterogeneous_mpnn
allaffa Feb 4, 2026
f2d1b8b
added case_name as attribute
allaffa Feb 4, 2026
4c8f177
updated JSON file
allaffa Feb 4, 2026
386901b
renamed scripts
allaffa Feb 4, 2026
dbd5f5c
JSON files renamed
allaffa Feb 4, 2026
3b27536
train_opf_solution_homogeneous.py updates
allaffa Feb 4, 2026
4973d6a
case_name fixed
allaffa Feb 4, 2026
a840ffa
fixes to dara format
allaffa Feb 4, 2026
8c1bc8d
fixes to dara format
allaffa Feb 4, 2026
ef5b945
fixes missing data error
allaffa Feb 4, 2026
2855b87
fixes missing data error
allaffa Feb 4, 2026
5a256df
fixes missing data error
allaffa Feb 4, 2026
0cce87d
fixes missing data error
allaffa Feb 4, 2026
19c2d2d
fixes missing data error
allaffa Feb 4, 2026
0870999
fixes missing data error
allaffa Feb 4, 2026
5f804fd
JSON-able
allaffa Feb 4, 2026
466d337
bus type error fixed
allaffa Feb 4, 2026
b491483
refactoring of data utils and inclusion of parity plot
allaffa Feb 5, 2026
8da5c6a
updated load of model checkpoint
allaffa Feb 6, 2026
e76ac2e
model creation fixed
allaffa Feb 6, 2026
a5d71e4
find_unused_parameters set to True
allaffa Feb 6, 2026
2e4ca96
number of samples option included
allaffa Feb 6, 2026
ac59c6e
fixed loading of the model at inference time
allaffa Feb 6, 2026
5753b5f
formatting fixed
allaffa Feb 6, 2026
bd3ea0a
added early stopping and checkpointing in the JSON file
allaffa Feb 6, 2026
6ac0896
updated config utils
allaffa Feb 9, 2026
3f5e78d
updated config utils
allaffa Feb 9, 2026
92f0bf5
updated scalable data management routines in examples/opf
Feb 12, 2026
8f35593
added download_and_uncompress_data.py
Feb 12, 2026
408bccc
updated scripts for data pre-processing
Feb 13, 2026
50338c3
option to move data to NVMe added for Frontier
Feb 13, 2026
2f9651e
formatting fixed
allaffa Feb 13, 2026
412082d
OPF: improve adios training flow and log dataset sizes
Mar 11, 2026
032a0cc
OPF: add NVMe utility updates
Mar 12, 2026
38e8406
OPF heterogeneous MPNN: two-route edge_dim, schema-driven edge attrs,…
Mar 17, 2026
ba32e16
Add HDF5 backend for heterogeneous graph serialization
Mar 17, 2026
c92d302
OPF preonly: fix OOM by streaming raw JSON per-rank + HDF5 streaming …
Mar 19, 2026
89deb1b
Add --hdf5 support to OPF inference script and fix modelname in job s…
Mar 20, 2026
13169a8
Fix HDF5 streaming: resume appending when dataset already exists for …
Mar 20, 2026
92a6b44
Add task-parallel case preprocessing: partition cases across MPI rank…
Mar 20, 2026
1c6d766
Add corrupt JSON tolerance, update job scripts (wall time, nvme, node…
Mar 22, 2026
ec7977e
heterogeneous transformers added
allaffa Jan 29, 2026
d3bbc5a
Fix HeteroRGAT/HeteroGAT bugs, add HeteroHEAT config and docs
Mar 25, 2026
c64d46e
Fix black formatting in train_opf_solution_heterogeneous.py
Mar 25, 2026
b5437f3
Fix black formatting in hdf5dataset.py
Mar 25, 2026
df0ee42
Fix HeteroHEAT bugs, add HPO CLI overrides and DeepHyper HPO script
Mar 25, 2026
d4ad448
Fix HeteroGATStack edge_dim dict bug: resolve per edge type
Mar 25, 2026
cc74988
Fix HPO val loss extraction: use nums[1] to skip rank prefix
Mar 25, 2026
cb973df
HPO: use min val loss, handle walltime kills, 128 nodes, 6h, 10 epochs
Mar 25, 2026
83d7baa
Add CLI arguments for configurable HPO search ranges in opf_deephyper…
Mar 27, 2026
12caa82
Remove OPF_Heterogeneous_Graph_Explanation.docx from tracking
Mar 27, 2026
4b5d4fa
Make h5py import optional in datasets __init__ to fix CI without h5py
Mar 27, 2026
80cb52d
Fix black formatting in datasets __init__
Mar 27, 2026
b322cd1
Fix CI: restore edge_dim backward compat, fix HeteroGAT errors, fix t…
allaffa Apr 2, 2026
2ff5eba
OPF heterogeneous: physics-informed domain loss + HPO analysis tools
Apr 21, 2026
9289e94
OPF domain loss: curriculum scheduling + EMA normalization
Apr 23, 2026
d27e3da
OPF domain loss: drop invalid smoothness penalties
Apr 23, 2026
ae5b4e7
OPF domain loss: per-epoch LossBreakdown logging + line_flow_slack + …
Apr 24, 2026
b5e14fa
OPF domain loss: reduce weights 10x + fix LossBreakdown printing - v…
Apr 24, 2026
2374fa8
Add FT1+FT3 fine-tuning pipeline for OPF
allaffa May 6, 2026
88e0082
Update .gitignore, OPF job scripts, and HPO warm-start support
May 6, 2026
b34a727
OPF finetune: data-efficiency sweep, model-agnostic datasets, --max_t…
May 6, 2026
960ed51
OPF finetune: fix black formatting and dist init for login-node datas…
May 6, 2026
9dd0bce
OPF finetune: pack 4 methods per job (multi-srun), FT1+FT3 only, 38 j…
May 6, 2026
c992455
OPF finetune: per-(arch, method, N) Frontier debug-QOS sweep + plotting
allaffa May 8, 2026
59403f3
Add OPF fine-tuning campaign scripts and paper figure generation
allaffa May 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ logs/
serialized_dataset/
exporter.log
installation_DOE_supercomputers/HydraGNN-Installation-Frontier*/
*.doc
*.docx
2 changes: 1 addition & 1 deletion examples/multidataset_hpo_sc26/job-omnistat-deephyper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function setup_bb()
fi
}

HYDRAGNN_ROOT=/lustre/orion/world-shared/lrn070/jyc/frontier/HydraGNN
HYDRAGNN_ROOT=/lustre/orion/lrn078/proj-shared/HydraGNN

# Load conda environment
module reset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function cmd() {
time $@
}

HYDRAGNN_ROOT=/lustre/orion/world-shared/lrn070/jyc/frontier/HydraGNN
HYDRAGNN_ROOT=/lustre/orion/lrn078/proj-shared/HydraGNN

# Load conda environment
module reset
Expand Down

This file was deleted.

26 changes: 26 additions & 0 deletions examples/opf/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from dataclasses import dataclass

from download_and_uncompress_data import (
_discover_cases,
_ensure_opf_downloaded,
_parse_num_groups,
_resolve_num_groups,
)


@dataclass(frozen=True)
class _DataOps:
discover_cases: callable
ensure_opf_downloaded: callable
parse_num_groups: callable
resolve_num_groups: callable


data_ops = _DataOps(
discover_cases=_discover_cases,
ensure_opf_downloaded=_ensure_opf_downloaded,
parse_num_groups=_parse_num_groups,
resolve_num_groups=_resolve_num_groups,
)

__all__ = ["data_ops"]
178 changes: 178 additions & 0 deletions examples/opf/check_domain_loss_on_gt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"""Check whether domain loss penalties are zero on ground-truth OPF solutions.

Loads N raw JSON samples, substitutes bus.y (ground truth Va, Vm) as the
"prediction", and evaluates each penalty term. A correctly-posed feasibility
penalty should return exactly zero (or near-zero up to float precision) on
any strictly feasible OPF solution.

Usage:
python3 check_domain_loss_on_gt.py [--n_samples N] [--json_dir PATH]
"""

import argparse
import json
import os
import sys

import torch
import torch.nn.functional as F

# ---------------------------------------------------------------------------
# Parse args
# ---------------------------------------------------------------------------
parser = argparse.ArgumentParser()
parser.add_argument("--n_samples", type=int, default=20)
parser.add_argument(
"--json_dir",
default=(
"dataset/dataset_release_1/pglib_opf_case10000_goc/raw/"
"gridopt-dataset-tmp/dataset_release_1/pglib_opf_case10000_goc/group_1"
),
)
args = parser.parse_args()

# ---------------------------------------------------------------------------
# Minimal HeteroData builder (same logic as _raw_json_to_heterodata)
# ---------------------------------------------------------------------------
try:
import torch_geometric.datasets.opf as tg_opf
from torch_geometric.data import HeteroData
except ImportError:
sys.exit("torch_geometric not available")


def load_sample(filepath):
with open(filepath) as f:
obj = json.load(f)
grid = obj["grid"]
solution = obj["solution"]

data = HeteroData()
data["bus"].x = torch.tensor(grid["nodes"]["bus"], dtype=torch.float)
data["bus"].y = torch.tensor(solution["nodes"]["bus"], dtype=torch.float)

def _ei(obj, rel):
try:
return tg_opf.extract_edge_index(obj, rel)
except Exception:
return None

for rel in ("ac_line", "transformer"):
ei = _ei(obj, rel)
if ei is None:
continue
ea = grid["edges"].get(rel, {}).get("features")
if ea is None:
continue
data["bus", rel, "bus"].edge_index = ei
data["bus", rel, "bus"].edge_attr = torch.tensor(ea, dtype=torch.float)

return data


# ---------------------------------------------------------------------------
# Penalty evaluation on ground-truth bus.y
# ---------------------------------------------------------------------------
VMIN_IDX, VMAX_IDX = 2, 3 # bus.x columns for v_min, v_max
VM_IDX = 1 # bus.y column for Vm
VA_IDX = 0 # bus.y column for Va

# ac_line edge_attr indices
AC_X_IDX, AC_RATE_A_IDX = 5, 6
AC_THETA_MIN_IDX, AC_THETA_MAX_IDX = 0, 1

# transformer edge_attr indices
TR_X_IDX, TR_RATE_A_IDX = 3, 4
TR_THETA_MIN_IDX, TR_THETA_MAX_IDX = 0, 1


def evaluate(data):
bus_true = data["bus"].y # shape [N_bus, 2] (Va, Vm)
bus_x = data["bus"].x # shape [N_bus, 4]

results = {}

# 1. Voltage bound: vmin <= Vm <= vmax
Vm = bus_true[:, VM_IDX]
vmin = bus_x[:, VMIN_IDX]
vmax = bus_x[:, VMAX_IDX]
vbound = torch.mean(F.relu(vmin - Vm).pow(2) + F.relu(Vm - vmax).pow(2))
results["voltage_bound"] = vbound.item()

# 2. Angle difference limit: theta_min <= Va_i - Va_j <= theta_max
Va = bus_true[:, VA_IDX]
for rel, tmin_i, tmax_i, tag in [
(("bus", "ac_line", "bus"), AC_THETA_MIN_IDX, AC_THETA_MAX_IDX, "ac_angle_diff"),
(("bus", "transformer", "bus"), TR_THETA_MIN_IDX, TR_THETA_MAX_IDX, "tr_angle_diff"),
]:
if rel not in data.edge_types:
continue
ea = getattr(data[rel], "edge_attr", None)
ei = getattr(data[rel], "edge_index", None)
if ea is None or ei is None or ea.shape[1] <= max(tmin_i, tmax_i):
continue
theta_min = ea[:, tmin_i]
theta_max = ea[:, tmax_i]
src, dst = ei
delta = Va[src] - Va[dst]
p = torch.mean(F.relu(delta - theta_max).pow(2) + F.relu(theta_min - delta).pow(2))
results[tag] = p.item()

# 3. DC thermal limit: |P_ij| = |(Va_i - Va_j) / x_ij| <= rate_a
for rel, x_i, ra_i, tag in [
(("bus", "ac_line", "bus"), AC_X_IDX, AC_RATE_A_IDX, "ac_line_flow"),
(("bus", "transformer", "bus"), TR_X_IDX, TR_RATE_A_IDX, "tr_line_flow"),
]:
if rel not in data.edge_types:
continue
ea = getattr(data[rel], "edge_attr", None)
ei = getattr(data[rel], "edge_index", None)
if ea is None or ei is None or ea.shape[1] <= max(x_i, ra_i):
continue
x_ij = ea[:, x_i].clamp(min=1e-6)
rate_a = ea[:, ra_i].clamp(min=0.0)
src, dst = ei
P_ij = (Va[src] - Va[dst]) / x_ij
p = torch.mean(F.relu(P_ij.abs() - rate_a).pow(2))
results[tag] = p.item()

return results


# ---------------------------------------------------------------------------
# Run over N samples
# ---------------------------------------------------------------------------
json_dir = os.path.join(os.path.dirname(__file__), args.json_dir)
files = sorted(
[os.path.join(json_dir, f) for f in os.listdir(json_dir) if f.endswith(".json")]
)[: args.n_samples]

if not files:
sys.exit(f"No JSON files found in {json_dir}")

print(f"Evaluating {len(files)} samples from {json_dir}\n")

accum = {}
n_ok = 0
for path in files:
try:
data = load_sample(path)
r = evaluate(data)
for k, v in r.items():
accum.setdefault(k, []).append(v)
n_ok += 1
except Exception as e:
print(f" WARNING: {os.path.basename(path)}: {e}")

print(f"{'Term':<25} {'mean':>12} {'max':>12} {'min':>12} {'#non-zero':>10}")
print("-" * 75)
eps = 1e-9
for term in sorted(accum):
vals = accum[term]
mean_v = sum(vals) / len(vals)
max_v = max(vals)
min_v = min(vals)
nonzero = sum(1 for v in vals if abs(v) > eps)
print(f"{term:<25} {mean_v:>12.6e} {max_v:>12.6e} {min_v:>12.6e} {nonzero:>10}/{len(vals)}")

print(f"\nProcessed {n_ok}/{len(files)} samples successfully.")
Loading
Loading