Grid And Data Access¶
This guide explains what becomes available after you instantiate
Grid, how
Data(...) lines up time series with workbook rows, and how to retrieve entries
when building your own optimization model.
Quick start¶
from gridforge.opt import Grid
grid = Grid("14bus_config.xlsx", verbose=0)
After this, the main access layers are:
grid.sheets[...]: raw Excel tables as pandas DataFramesgrid.core.*: schema-defined core sheets with derived network objectsgrid.custom[...]: custom sheets attached to buses, such asload,solar, andwind- top-level aliases such as
grid.gen,grid.branch,grid.load, andgrid.solarwhen names are safe Python attributes
Top-level entries¶
Grid(...) defines these top-level entries:
grid.baseMVA: system base power used for per-unit scaling in optimization codegrid.nbus: number of buses in the case; same asgrid.bus.ngrid.ngen: number of generator rows in thegensheet; same asgrid.gen.ngrid.nbranch: number of branch rows in thebranchsheet; same asgrid.branch.ngrid.sheets: raw Excel workbook tables as DataFramesgrid.core: schema-defined core sheet wrappersgrid.custom: generic wrappers for custom sheets attached to busesgrid.aliases: convenience aliases registered at the top levelgrid.metadata: workbook metadata such asbaseMVAand base case name
It also provides small discovery helpers:
grid.sheet_names(): list all sheet names loaded from the Excel workbookgrid.core_names(): list the available core sheet wrappersgrid.custom_names(): list the available custom sheets attached to busesgrid.alias_names(): list top-level aliases such asgen,branch,loadgrid.sheet(name): fetch one raw sheet as a DataFramegrid.core_sheet(name): fetch one core wrapper by namegrid.custom_sheet(name): fetch one custom wrapper by namegrid.has_core(name): check whether a core wrapper existsgrid.has_custom(name): check whether a custom wrapper exists
1. Raw tables: grid.sheets[...]¶
Use grid.sheets[...] when you want the original Excel sheet as a DataFrame.
Examples:
grid.sheets["bus"]
grid.sheets["gen"]
grid.sheets["solar"]
or equivalently:
grid.sheet("bus")
grid.sheet("solar")
2. Core sheets: grid.core.*¶
grid.core contains the schema-defined sheets:
grid.core.busgrid.core.gengrid.core.branch
These are wrapper objects, not raw DataFrames. Each wrapper stores:
nametablen- field arrays in lowercase names
You can access fields in two equivalent ways:
grid.core.gen.pmax
grid.core.gen.field("PMAX")
grid.core.bus¶
Main entries:
grid.core.bus.table: rawbusDataFramegrid.core.bus.n: number of bus rowsgrid.core.bus.bus_idx: 0-based bus indicesgrid.core.bus.slack_bus_idx: the unique slack bus indexgrid.core.bus.non_slack_bus_idx: all non-slack bus indicesgrid.core.bus.ref_theta: reference angle of the slack bus in radians
Plus lowercased raw columns such as:
grid.core.bus.bus_type: bus type array from the workbookgrid.core.bus.pd: active power demand at each busgrid.core.bus.qd: reactive power demand at each busgrid.core.bus.gs: shunt conductance valuesgrid.core.bus.bs: shunt susceptance values
Typical uses:
grid.core.bus.slack_bus_idx
grid.core.bus.pd
grid.core.gen¶
grid.core.gen is the generator sheet wrapped as an object attached to buses.
Main entries:
grid.core.gen.table: rawgenDataFramegrid.core.gen.n: number of generator rowsgrid.core.gen.bus_idx: 0-based bus assignment for each generator rowgrid.core.gen.Cbus: bus-to-generator incidence matrix
Plus lowercased raw columns such as:
grid.core.gen.status: generator on/off availability statusgrid.core.gen.pmax: maximum active power outputgrid.core.gen.pmin: minimum active power outputgrid.core.gen.pg: active power setpoint from the workbookgrid.core.gen.qmax: maximum reactive power outputgrid.core.gen.cost_startup: startup cost coefficientgrid.core.gen.cost_shutdown: shutdown cost coefficientgrid.core.gen.cost_second: quadratic cost coefficientgrid.core.gen.cost_first: linear cost coefficientgrid.core.gen.cost_zero: fixed no-load cost coefficient
Typical uses:
grid.core.gen.pmax
grid.core.gen.cost_first
grid.core.gen.bus_idx
grid.core.gen.Cbus
grid.core.gen.active_mask()
grid.core.branch¶
grid.core.branch contains both raw branch columns and derived DC-power-flow
objects.
Main entries:
grid.core.branch.table: rawbranchDataFramegrid.core.branch.n: number of branch rowsgrid.core.branch.pmax: branch thermal limit taken fromRATE_Agrid.core.branch.Cf: from-bus incidence matrixgrid.core.branch.Ct: to-bus incidence matrixgrid.core.branch.A: signed branch-bus incidence matrixgrid.core.branch.Bf: branch susceptance matrixgrid.core.branch.Bbus: bus susceptance matrixgrid.core.branch.Pfshift: branch flow shift from transformer phase shiftgrid.core.branch.Pbusshift: bus-level equivalent ofPfshiftgrid.core.branch.ptdf: PTDF matrix for DC power flow mapping
Plus lowercased raw columns such as:
grid.core.branch.f_bus_idx: from-bus indices from the workbookgrid.core.branch.t_bus_idx: to-bus indices from the workbookgrid.core.branch.br_x: series reactance valuesgrid.core.branch.rate_a: original branch rating column
Typical uses:
grid.core.branch.ptdf
grid.core.branch.pmax
3. Custom Sheets Attached To Buses: grid.custom[...]¶
Every non-core sheet is required to define BUS_IDX and becomes a generic
custom wrapper under grid.custom[...].
Examples:
grid.custom["load"]
grid.custom["solar"]
grid.custom["wind"]
or equivalently:
grid.custom_sheet("load")
grid.custom_sheet("solar")
Each custom sheet wrapper defines:
name: sheet name as stored in the workbooktable: raw DataFrame for that custom sheetn: number of rows in the custom sheetbus_idx: 0-based bus assignment for each custom-sheet rowCbus: bus-to-custom-sheet incidence matrix
Plus lowercased raw columns from that sheet, except BUS_IDX.
For example, if the solar sheet contains PMAX, STATUS, and
CURTAIL_COST, then you can access:
grid.custom["solar"].pmax
grid.custom["solar"].status
grid.custom["solar"].curtail_cost
Typical uses:
grid.custom["load"].Cbus
grid.custom["load"].shed_cost
grid.custom["solar"].pmax
grid.custom["wind"].active_rows()
4. Convenience aliases: grid.<sheet>¶
Grid(...) also registers safe top-level aliases:
grid.bus # same object as grid.core.bus
grid.gen # same object as grid.core.gen
grid.branch # same object as grid.core.branch
grid.load # same object as grid.custom["load"]
grid.solar # same object as grid.custom["solar"]
Aliases are added only when the sheet name is a valid Python identifier and does
not collide with an existing Grid attribute or method. Explicit access through
grid.core.* and grid.custom[...] remains the clearest form for shared code.
5. Time-series data: Data(...)¶
Data(...) loads case-specific bus_<BUS_IDX>.csv files created by
materialize_bus_data_assignment(...). It uses the workbook to decide which
bus files to open and in what order sheet-backed profile columns should appear.
The full bus CSV files remain available for contextual data access.
from gridforge.opt import Data
data = Data(
grid_xlsx_path="14bus_config.xlsx",
data_dir="14bus_data",
sheet_names=["load", "solar", "wind"],
)
For each requested sheet, Data(...):
- reads that sheet's
BUS_IDXvalues from the Excel workbook, - opens the corresponding
bus_<BUS_IDX>.csvfiles, - extracts the column whose name matches the sheet name case-insensitively,
- stacks those columns in the same row order as the Excel sheet.
Sheet-backed profile examples:
data.get_series("load") # shape: (T, n_load_rows)
data.get_series("solar") # shape: (T, n_solar_rows)
data.get_bus_idx("load") # generated BUS_IDX values for load rows
data.get_n("wind") # number of wind rows
For full per-bus files and contextual columns:
data.bus_ids() # loaded bus CSV IDs
data.get_bus_frame(2) # full bus_2.csv DataFrame
data.get_column("Temperature (k)") # shape: (T, n_loaded_buses)
data.get_column("Weekday_sin", bus_idx=[2, 3])
get_column(...) is for numeric columns. Use get_bus_frame(...) when you need
the original DataFrame, including non-numeric context columns.
Helper methods on wrappers¶
Both grid.core.* wrappers and grid.custom[...] wrappers support field
discovery helpers.
Core wrappers¶
Supported methods:
.field(name): get one field by its original column name.field_names(): list the exposed field names on the wrapper.has_field(name): check whether a field exists
Example:
grid.core.branch.field("RATE_A")
grid.core.gen.field_names()
Custom wrappers¶
Supported methods:
.field(name): get one field by its original column name.field_names(): list the exposed field names on the wrapper.has_field(name): check whether a field exists.active_mask(): boolean mask based onSTATUS > 0when available.active_rows(): row indices corresponding to the active mask
If a sheet has a STATUS column, active_mask() returns True for rows where
STATUS > 0. If the sheet has no STATUS column, all rows are treated as
active.
Example:
solar = grid.custom["solar"]
solar.field("PMAX")
active_mask = solar.active_mask()
active_rows = solar.active_rows()
active_pmax = solar.pmax[active_mask]
Important conventions¶
1. Field names become lowercase attributes¶
Raw Excel columns are exposed as lowercase attribute names on wrappers.
Examples:
PMAX->.pmaxCOST_FIRST->.cost_firstSHED_COST->.shed_costF_BUS_IDX->.f_bus_idx
If you are unsure, use .field("ORIGINAL_NAME").
2. BUS indices are 0-based inside Grid¶
The generated Excel uses 1-based BUS_IDX, but Grid converts internal bus
indices to 0-based arrays where appropriate.
Examples:
grid.core.bus.bus_idxgrid.core.gen.bus_idxgrid.custom["load"].bus_idxgrid.load.bus_idx
3. Cbus maps sheet rows to buses¶
For sheets attached to buses, Cbus has shape (nbus, n_component_rows) and
places a 1 at the bus corresponding to each row.
This is useful when mapping component-level variables to bus injections:
inj = (
grid.gen.Cbus @ pg
+ grid.solar.Cbus @ ps
- grid.load.Cbus @ pl
)
Recommended usage pattern¶
Use:
grid.sheets[...]for raw inspectiongrid.core.*for network-defined sheetsgrid.custom[...]for user-defined assets attached to busesgrid.<sheet>aliases for concise notebook/model code
Example:
gen = grid.gen
branch = grid.branch
load = grid.load
solar = grid.solar
print(gen.pmax)
print(branch.ptdf.shape)
print(load.Cbus.shape)
print(solar.field_names())
Caveats¶
- Every custom sheet is attached to buses through
BUS_IDXand appears ingrid.custom[...]. - Top-level aliases are conveniences, not a replacement for
grid.coreandgrid.custom. - PYPOWER/MATPOWER
gencostrows are merged intogenasCOST_*columns before the workbook is written. - Time-series data is loaded only for sheets requested in
Data(..., sheet_names=[...]); static custom sheets do not need CSV data.
Summary¶
After Grid(...) is instantiated, use:
grid.sheets[...]for raw tables,grid.core.*for built-in network objects,grid.custom[...]for custom assets attached to buses,Data(...)for aligned time-series matrices.