gtopt Input Data Structure Documentation

This document describes the input data structure required to define and run optimization cases with gtopt (Generation and Transmission Optimization Planning Tool).

For a step-by-step tutorial with worked examples and time-series workflow, see Planning Guide. For auto-generated field tables from source code run: python3 scripts/gtopt_field_extractor.py --format html --output field_reference.html

</blockquote>

Overview

A gtopt case is defined by one or more JSON configuration files that contain three top-level sections:

SectionDescription
optionsGlobal solver and I/O settings (includes solver_options and variable_scales)
simulationTime structure: blocks, stages, scenarios, phases, scenes, apertures
systemPhysical system: buses, generators, demands, lines, etc.

The JSON file may reference external data files (CSV or Parquet) for time-series or profile data. These files are stored in a subdirectory specified by options.input_directory.

Directory Layout

case_name/
├── case_name.json            # Main configuration file
└── <input_directory>/        # External data files
    ├── Demand/
    │   └── lmax.parquet      # Demand load profile
    ├── Generator/
    │   └── pmax.parquet      # Generator capacity profile
    └── ...

1. Options

Global settings that control solver behavior and I/O formats. All fields are optional – when absent, the solver applies built-in defaults (shown below).

Input settings

FieldTypeDefaultDescription
input_directorystring"input"Root directory for external data files (resolved relative to JSON file)
input_formatstring"parquet"Preferred input format: "parquet" or "csv"

Model parameters

FieldTypeDefaultUnitsDescription
demand_fail_costnumber*(none)*$/MWhPenalty cost for unserved demand (value of lost load)
reserve_fail_costnumber*(none)*$/MWhPenalty cost for unserved spinning reserve
use_line_lossesbooleantrueEnable resistive line loss modeling
loss_segmentsinteger1Number of piecewise-linear segments for quadratic line losses (1 = linear)
use_kirchhoffbooleantrueEnable DC Kirchhoff voltage-law constraints
use_single_busbooleanfalseCollapse network to a single bus (copper-plate model)
kirchhoff_thresholdnumber0kVMinimum bus voltage below which Kirchhoff is not applied
scale_objectivenumber1000dimensionlessDivisor applied to all objective coefficients for numerical stability
scale_thetanumber1000dimensionlessScaling factor for voltage-angle variables

Note: annual_discount_rate has moved to the simulation section (see Section 2). For backward compatibility, it is still accepted here.

Output settings

FieldTypeDefaultDescription
output_directorystring"output"Root directory for output result files
output_formatstring"parquet"Output format: "parquet" or "csv"
output_compressionstring"zstd"Compression codec: "uncompressed", "gzip", "zstd", "lz4", "bzip2", "xz"
lp_build_options.names_levelstring/int"minimal" (0)LP naming level: "minimal" (0) = state-variable cols only, "only_cols" (1) = all column + row names (required for LP file output), "cols_and_rows" (2) = same as 1 + warn on duplicates
use_uid_fnamebooleantrueUse element UIDs instead of names in output filenames

Solver selection

FieldTypeDefaultDescription
methodstring"monolithic"Planning solver: "monolithic" (default), "sddp", or "cascade". See SDDP Solver, Cascade Solver, and Monolithic Solver

Logging and debugging

FieldTypeDefaultDescription
log_directorystring"logs"Directory for log, trace, and error LP files
lp_debugbooleanfalseSave LP debug files to log_directory before solving. Monolithic: one file per (scene, phase) named gtopt_lp_<scene>_<phase>.lp. SDDP: one file per (iteration, scene, phase) named gtopt_iter_<iter>_<scene>_<phase>.lp
lp_compressionstring""Compression codec for LP debug files: "" (inherit from output), "none" (no compression), or a codec name ("zstd", "gzip", "lz4", "bzip2", "xz")
lp_buildbooleanfalseBuild all LP matrices but skip solving entirely. Combine with lp_debug: true to export every scene/phase LP
lp_coeff_ratio_thresholdnumber1e7When the global max/min coefficient ratio exceeds this value, per-scene/phase breakdown is printed

Deprecated LP solver fields

These fields are still accepted for backward compatibility but are superseded by the solver_options sub-object (see Section 1.1).

FieldTypeDefaultDescription
lp_algorithmintegerLP algorithm: 0=auto, 1=primal, 2=dual, 3=barrier
lp_threadsintegerSolver threads (0=auto)
lp_presolvebooleanEnable LP presolve

Example

{
  "options": {
    "output_format": "csv",
    "use_single_bus": false,
    "demand_fail_cost": 1000,
    "scale_objective": 1000,
    "use_kirchhoff": true,
    "input_directory": "system_c0",
    "input_format": "parquet",
    "method": "monolithic",
    "solver_options": {
      "algorithm": 3,
      "presolve": true
    }
  }
}

1.1 SolverOptions

LP solver configuration exposed as a sub-object under options. Individual top-level fields (lp_algorithm, lp_threads, lp_presolve) are still respected for backward compatibility and take precedence over the corresponding solver_options sub-fields.

FieldTypeDefaultDescription
algorithminteger3 (barrier)LP algorithm: 0 = auto, 1 = primal simplex, 2 = dual simplex, 3 = barrier
threadsinteger0Number of solver threads (0 = automatic)
presolvebooleantrueEnable LP presolve optimizations
optimal_epsnumbersolver defaultOptimality tolerance (omit to keep solver default)
feasible_epsnumbersolver defaultFeasibility tolerance (omit to keep solver default)
barrier_epsnumbersolver defaultBarrier convergence tolerance (omit to keep solver default)
log_levelinteger0Solver output verbosity (0 = silent)

Example:

{
  "options": {
    "solver_options": {
      "algorithm": 3,
      "threads": 4,
      "presolve": true,
      "optimal_eps": 1e-8,
      "feasible_eps": 1e-8,
      "log_level": 1
    }
  }
}

1.2 VariableScale

Per-class or per-element LP variable scaling factors. Convention: physical_value = LP_value * scale. Defined as an array under options.variable_scales.

Resolution priority when the solver looks up a scale:

  1. Per-element entry matching (class_name, variable, uid)
  2. Per-class entry matching (class_name, variable) with uid = -1
  3. Fallback: 1.0 (no scaling)

Note: Per-element fields (Battery::energy_scale, Reservoir::energy_scale) and global options (scale_theta) take precedence over entries in variable_scales.

FieldTypeDefaultDescription
class_namestringElement class (e.g. "Bus", "Reservoir", "Battery")
variablestringVariable name (e.g. "theta", "volume", "energy")
uidinteger-1Element UID (-1 = apply to all elements of this class)
scalenumber1.0Scale factor: physical = LP * scale

Example:

{
  "options": {
    "variable_scales": [
      {"class_name": "Bus", "variable": "theta",
       "uid": -1, "scale": 0.001},
      {"class_name": "Reservoir", "variable": "volume",
       "uid": -1, "scale": 1000.0},
      {"class_name": "Battery", "variable": "energy",
       "uid": 1, "scale": 10.0}
    ]
  }
}

1.3 SDDPOptions

SDDP-specific solver configuration, specified as a sub-object under options.sddp_options. Field names omit the sddp_ prefix since the section name already provides the namespace. All fields are optional.

For full algorithmic details, see SDDP Solver.

Iteration control

FieldTypeDefaultDescription
max_iterationsinteger100Maximum number of forward/backward iterations
min_iterationsinteger2Minimum iterations before declaring convergence
convergence_tolnumber1e-4Relative gap tolerance for convergence (primary criterion)
stationary_tolnumber0.0Secondary convergence: relative gap-change tolerance. When the gap stops improving over the look-back window, convergence is declared even if gap > convergence_tol. Set to e.g. 0.01 to enable. 0.0 = disabled
stationary_windowinteger10Number of iterations to look back when checking gap stationarity (only used when stationary_tol > 0)

Advanced tuning

FieldTypeDefaultDescription
elastic_penaltynumber1e6Penalty for elastic slack variables in feasibility
elastic_modestring"single_cut"Elastic filter mode: "single_cut" (alias "cut"), "multi_cut", or "backpropagate"
multi_cut_thresholdinteger10Forward-pass infeasibility count before auto-switching from single_cut to multi_cut (0 = never)
alpha_minnumber0.0Lower bound for future cost variable alpha
alpha_maxnumber1e12Upper bound for future cost variable alpha
cut_sharing_modestring"none"Cut sharing across scenes: "none", "expected", "accumulate", or "max"
efficiency_update_skipinteger0Iterations to skip between efficiency coefficient updates (0 = every iteration)

Cut file management

FieldTypeDefaultDescription
cut_directorystring"cuts"Directory for Benders cut files
save_per_iterationbooleantrueSave cuts to CSV after each iteration (vs. only at end)
cuts_input_filestring""File path for loading initial cuts (empty = cold start)
named_cuts_filestring""CSV file with named-variable cuts for hot-start across all phases
sentinel_filestring""Path to a sentinel file; if it exists, the solver stops gracefully

Boundary cuts

Note: boundary_cuts_file has moved to the simulation section (see Section 2). For backward compatibility, it is still accepted in sddp_options.

FieldTypeDefaultDescription
boundary_cuts_modestring"separated"Load mode: "noload", "separated" (per-scene), or "combined" (broadcast)
boundary_max_iterationsinteger0Max SDDP iterations to load from boundary file (0 = all)

Apertures

FieldTypeDefaultDescription
aperturesint array*(absent)*Aperture UIDs for backward pass. Absent = use per-phase aperture_set. Empty [] = pure Benders. Non-empty = use exactly these UIDs
aperture_directorystring""Directory for aperture-specific scenario data (empty = use input_directory)
aperture_timeoutnumber15.0Timeout in seconds for individual aperture LP solves (0 = no timeout)
save_aperture_lpbooleanfalseSave LP files for infeasible apertures to log_directory

Timeouts and warm-start

FieldTypeDefaultDescription
solve_timeoutnumber180.0Forward-pass LP solve timeout in seconds (0 = no timeout)
warm_startbooleantrueEnable warm-start (dual simplex from saved basis) for aperture and elastic resolves
state_variable_lookup_modestring"warm_start"How update_lp elements (seepage, production factor, discharge limit) obtain reservoir volume between phases: "warm_start" (warm solution / vini, no cross-phase lookup) or "cross_phase" (previous phase's efin). Does not affect SDDP state-variable chaining or cut generation.

Hot-start modes

FieldTypeDefaultDescription
hot_startbooleanfalseLoad previously saved cuts on startup (deprecated: use cut_recovery_mode)
cut_recovery_modestring"none"Hot-start mode: "none", "keep", "append", or "replace". Takes precedence over boolean hot_start

Cut pruning

FieldTypeDefaultDescription
max_cuts_per_phaseinteger0Maximum retained cuts per (scene, phase) LP. 0 = unlimited
cut_prune_intervalinteger10Iterations between cut pruning passes
prune_dual_thresholdnumber1e-8Dual threshold for inactive cut detection
single_cut_storagebooleanfalseStore cuts in per-scene vectors only
max_stored_cutsinteger0Maximum total stored cuts per scene (0 = unlimited)
use_clone_poolbooleantrueReuse cached LP clones for aperture solves

Simulation mode

FieldTypeDefaultDescription
simulation_modebooleanfalseRun in simulation mode: forward-only evaluation of the policy from loaded cuts. Sets max_iterations=0 and disables cut saving. Feasibility cuts from the simulation pass are discarded by default for hot-start reproducibility

Monitoring API

FieldTypeDefaultDescription
api_enabledbooleantrueEnable the SDDP monitoring API (writes JSON status file each iteration)

1.4 CascadeOptions (multi-level hybrid solver)

These options configure the cascade solver (method = "cascade"), which runs a multi-level hybrid algorithm with progressive LP refinement. They are set in their own cascade_options sub-object (not inside sddp_options). See Cascade Method for full documentation, and SDDP Solver – S10 for a summary.

The cascade_options object contains three top-level fields:

FieldTypeDescription
model_optionsobjectGlobal ModelOptions defaults for all levels
sddp_optionsobjectGlobal SddpOptions defaults for all levels. max_iterations here is the global budget across all levels
level_arrayarrayArray of CascadeLevel objects. When empty or omitted, a built-in 4-level default is used

Each element in the level_array is an object with the following optional fields:

FieldTypeDescription
uidintegerUnique identifier for this level
namestringHuman-readable level name (for logging)
model_optionsobjectLP formulation overrides; when present, a new LP is built. When absent, the previous level's LP is reused
sddp_optionsobjectSolver parameters for this level (per-level overrides)
transitionobjectTransfer rules from the previous level

**model_options fields** (same structure at both cascade and level scope):

FieldTypeDefaultDescription
use_single_busbooleanfalseAggregate all buses into one
use_kirchhoffbooleantrueEnable DC power flow (voltage angles)
use_line_lossesbooleanfalseModel line losses
kirchhoff_thresholdnumber0.0Threshold for Kirchhoff constraint activation
loss_segmentsinteger1Number of piecewise-linear loss segments
scale_objectivenumber1000Divisor for objective coefficients
scale_thetanumber1000Scaling factor for voltage-angle variables
demand_fail_costnumber*(none)*Penalty cost for unserved demand [$/MWh]
reserve_fail_costnumber*(none)*Penalty cost for unserved reserve [$/MWh]

Note: annual_discount_rate has moved to the simulation section. For backward compatibility, it is still accepted in model_options.

**sddp_options fields** (per-level solver parameters):

FieldTypeDefaultDescription
max_iterationsintegerfrom global sddp_optionsMaximum iterations for this level
min_iterationsintegerfrom global sddp_optionsMinimum iterations before convergence
aperturesint arrayfrom global sddp_optionsAperture UIDs (absent = inherit, [] = Benders)
convergence_tolnumberfrom global sddp_optionsConvergence tolerance
stationary_tolnumberfrom global sddp_optionsStationary gap-change tolerance (0 = disabled)
stationary_windowintegerfrom global sddp_optionsLook-back window for stationary gap check

**transition fields:**

See CASCADE_SOLVER.md §4.5 for detailed cut forgetting semantics and the two-phase solve behavior.

FieldTypeDefaultDescription
inherit_optimality_cutsinteger00 = do not inherit; -1 = inherit and keep forever; N > 0 = inherit, then forget after N training iterations
inherit_feasibility_cutsinteger0Same semantics as inherit_optimality_cuts
inherit_targetsinteger00 = no targets; -1 = inherit forever; N > 0 = inherit with forgetting
target_rtolnumber0.05Relative tolerance for target band (fraction of abs(v))
target_min_atolnumber1.0Minimum absolute tolerance for target band
target_penaltynumber500Elastic penalty cost per unit target violation
optimality_dual_thresholdnumber0.0Minimum abs(dual) threshold for transferring cuts. Cuts with abs(dual) below this are skipped. 0 = transfer all

SDDP example:

{
  "options": {
    "method": "sddp",
    "sddp_options": {
      "max_iterations": 200,
      "convergence_tol": 1e-5,
      "stationary_tol": 0.01,
      "stationary_window": 10,
      "cut_sharing_mode": "expected",
      "cut_directory": "cuts",
      "elastic_mode": "single_cut",
      "elastic_penalty": 1e7,
      "cut_recovery_mode": "keep",
      "boundary_cuts_mode": "separated",
      "apertures": []
    }
  }
}

Cascade example:

{
  "options": {
    "method": "cascade",
    "sddp_options": {
      "max_iterations": 30,
      "convergence_tol": 0.01
    },
    "cascade_options": {
      "level_array": [
        {
          "name": "uninodal_benders",
          "model_options": {
            "use_single_bus": true,
            "use_kirchhoff": false,
            "use_line_losses": false
          },
          "sddp_options": {
            "max_iterations": 10,
            "apertures": [],
            "convergence_tol": 0.05
          }
        },
        {
          "name": "transport_sddp",
          "model_options": {
            "use_single_bus": false,
            "use_kirchhoff": false
          },
          "sddp_options": {
            "max_iterations": 15,
            "apertures": [1, 2, 3]
          },
          "transition": {
            "inherit_targets": true,
            "target_rtol": 0.05,
            "target_min_atol": 1.0,
            "target_penalty": 500
          }
        },
        {
          "name": "full_sddp",
          "model_options": {
            "use_kirchhoff": true,
            "use_line_losses": true
          },
          "sddp_options": {
            "max_iterations": 30,
            "apertures": [1, 2, 3]
          },
          "transition": {
            "inherit_optimality_cuts": true,
            "inherit_feasibility_cuts": true,
            "optimality_dual_threshold": 1e-6
          }
        }
      ]
    }
  }
}

Simulation mode example:

{
  "options": {
    "method": "sddp",
    "sddp_options": {
      "simulation_mode": true,
      "cut_recovery_mode": "keep",
      "cut_directory": "cuts"
    }
  }
}

1.5 MonolithicOptions

Monolithic-solver-specific configuration, specified as a sub-object under options.monolithic_options. All fields are optional.

For full details, see Monolithic Solver.

FieldTypeDefaultDescription
solve_modestring"monolithic"Solve mode: "monolithic" (all phases in one LP) or "sequential" (phase-by-phase)
boundary_cuts_modestring"separated"Load mode: "noload", "separated" (per-scene), or "combined" (broadcast)
boundary_max_iterationsinteger0Max iterations to load from boundary file (0 = all)
solve_timeoutnumber18000.0LP solve timeout in seconds (0 = no timeout)

Note: boundary_cuts_file has moved to the simulation section. For backward compatibility, it is still accepted in monolithic_options.

Example:

{
  "options": {
    "method": "monolithic",
    "monolithic_options": {
      "solve_mode": "monolithic",
      "boundary_cuts_mode": "separated"
    }
  },
  "simulation": {
    "boundary_cuts_file": "boundary_cuts.csv"
  }
}

2. Simulation

Defines the temporal structure of the optimization. The simulation object contains arrays of time-structure elements organized in a hierarchy:

Scenario  (probability_factor)
  └─ Phase
       └─ Stage  (discount_factor, first_block, count_block)
            └─ Block  (duration [h])

Scenes group scenarios; Apertures define SDDP backward-pass openings.

Simulation-level fields

In addition to the arrays below, the simulation object accepts these scalar fields:

FieldTypeDefaultDescription
annual_discount_ratenumber0.0Annual discount rate for multi-stage CAPEX discounting [p.u./year]
boundary_cuts_filestring""CSV file with boundary (future-cost) cuts for the last phase
boundary_cuts_valuationstring"end_of_horizon"Boundary-cut valuation mode: "end_of_horizon" (default) or "present_value"

Backward compatibility: annual_discount_rate is also accepted in options or options.model_options. boundary_cuts_file is also accepted in options.sddp_options or options.monolithic_options. The simulation location is preferred.

ArrayElementRequiredDescription
block_arrayBlockYesIndivisible time units
stage_arrayStageYesPlanning/investment periods
scenario_arrayScenarioYesStochastic realizations
phase_arrayPhaseNoGroups of consecutive stages
scene_arraySceneNoGroups of scenarios
aperture_arrayApertureNoSDDP backward-pass openings

When phase_array or scene_array are empty, a single default Phase / Scene covering all stages / scenarios is created automatically.

2.1 Block

A block is the smallest indivisible time unit. energy [MWh] = power [MW] × duration [h].

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringNoOptional name
durationnumberhYesDuration of the block

2.2 Stage

A stage groups consecutive blocks into a planning/investment period.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringNoOptional name
first_blockintegerYes0-based index of the first block in this stage
count_blockintegerYesNumber of consecutive blocks in this stage
discount_factornumberp.u.NoPresent-value cost multiplier for this stage
activebooleanNoWhether the stage is active

2.3 Scenario

A scenario represents a possible future realization (hydrology, demand level, etc.).

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringNoOptional name
probability_factornumberp.u.NoProbability weight (values are normalised to sum to 1)
activebooleanNoWhether the scenario is active

2.4 Phase

A phase groups consecutive planning stages into a higher-level period. This allows modelling distinct investment or operational windows (e.g. a 5-year construction phase followed by a 20-year operational phase). When only one phase is needed (the common case), it is created automatically with defaults covering all stages.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringNoOptional name
activebooleanNoWhether the phase is active
first_stageintegerYes0-based index of the first stage in this phase
count_stageintegerNoNumber of stages (-1 or omit = all remaining)
aperture_setarrayNoArray of aperture UIDs for this phase's SDDP backward pass (empty = use all)

2.5 Scene

A scene groups consecutive scenarios into a logical set. Scenes are used by the solver to partition scenarios when building LP sub-problems (one LP per scene/phase combination). When no scene_array is provided, a single default scene covering all scenarios is created.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringNoOptional name
activebooleanNoWhether the scene is active
first_scenariointegerYes0-based index of the first scenario in this scene
count_scenariointegerNoNumber of scenarios (-1 or omit = all remaining)

2.6 Aperture

An aperture represents one hydrological (or stochastic) realization used in the SDDP backward pass. Each aperture references a source scenario whose affluent data (flow bounds) are applied to the cloned phase LP before solving. Apertures allow the backward pass to sample a different set of scenarios than the forward pass.

When no aperture_array is provided, the SDDP solver falls back to the legacy behaviour controlled by sddp_num_apertures (first N scenarios or all scenarios).

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringNoOptional name
activebooleanNoWhether the aperture is active
source_scenariointegerYesUID of the scenario whose affluent data to use
probability_factornumberp.u.NoProbability weight (normalised to sum 1 across active apertures; default: 1)

Note: When aperture_directory is set in sddp_options, the source scenario is first looked up in that directory; if not found there, it falls back to the regular input_directory.

Example

{
  "simulation": {
    "block_array": [
      {"uid": 1, "duration": 1},
      {"uid": 2, "duration": 2}
    ],
    "stage_array": [
      {"uid": 1, "first_block": 0, "count_block": 1, "active": 1},
      {"uid": 2, "first_block": 1, "count_block": 1, "active": 1}
    ],
    "scenario_array": [
      {"uid": 1, "probability_factor": 1}
    ],
    "phase_array": [
      {"uid": 1, "first_stage": 0, "count_stage": 2}
    ],
    "scene_array": [
      {"uid": 1, "first_scenario": 0, "count_scenario": 1}
    ],
    "aperture_array": [
      {"uid": 1, "source_scenario": 1, "probability_factor": 0.5},
      {"uid": 2, "source_scenario": 2, "probability_factor": 0.5}
    ]
  }
}

3. System

The system section defines all physical components of the power system.

3.1 Bus

An electrical bus (node) in the network.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesBus name
activebooleanNoWhether the bus is active
voltagenumberkVNoNominal voltage level
reference_thetanumberradNoFixed voltage angle (reference bus: set to 0)
use_kirchhoffbooleanNoOverride global Kirchhoff setting for this bus

3.2 Generator

A generation unit connected to a bus.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesGenerator name
businteger|stringYesConnected bus UID or name
activebooleanNoWhether the generator is active
pminnumber|array|stringMWNoMinimum active power output
pmaxnumber|array|stringMWNoMaximum active power output
gcostnumber|array|string$/MWhNoVariable generation cost
lossfactornumber|array|stringp.u.NoNetwork loss factor
capacitynumber|array|stringMWNoInstalled capacity
expcapnumber|array|stringMWNoCapacity added per expansion module
expmodnumber|array|stringNoMaximum number of expansion modules
capmaxnumber|array|stringMWNoAbsolute maximum capacity
annual_capcostnumber|array|string$/MW-yearNoAnnualized investment cost
annual_deratingnumber|array|stringp.u./yearNoAnnual capacity derating factor

Note: Fields that accept number|array|string can be a numeric constant, an inline array (indexed by [stage][block]), or a filename referencing an external Parquet/CSV file in input_directory/Generator/.

3.3 Demand

An electrical demand (load) connected to a bus.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesDemand name
businteger|stringYesConnected bus UID or name
activebooleanNoWhether the demand is active
lmaxnumber|array|stringMWNoMaximum served load
lossfactornumber|array|stringp.u.NoNetwork loss factor
fcostnumber|array|string$/MWhNoDemand curtailment cost
eminnumber|array|stringMWhNoMinimum energy that must be served per stage
ecostnumber|array|string$/MWhNoEnergy-shortage cost
capacitynumber|array|stringMWNoInstalled capacity
expcapnumber|array|stringMWNoCapacity added per expansion module
expmodnumber|array|stringNoMaximum number of expansion modules
capmaxnumber|array|stringMWNoAbsolute maximum capacity
annual_capcostnumber|array|string$/MW-yearNoAnnualized investment cost
annual_deratingnumber|array|stringp.u./yearNoAnnual capacity derating factor

3.4 Line

A transmission line connecting two buses.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesLine name
bus_ainteger|stringYesSending-end (from) bus
bus_binteger|stringYesReceiving-end (to) bus
activebooleanNoWhether the line is active
voltagenumber|array|stringkVNoNominal voltage level. Omit or set to 1.0 for per-unit mode
resistancenumber|array|stringΩ (p.u.)NoSeries resistance. Use Ω when voltage is in kV; p.u. when voltage is omitted
reactancenumber|array|stringΩ (p.u.)NoSeries reactance (DC power flow). Use Ω when voltage is in kV; p.u. when voltage is omitted. Susceptance: $B = V^2 / X$
lossfactornumber|array|stringp.u.NoLumped loss factor
typestringNoElement type tag; use "transformer" for transformers
tap_rationumber|array|stringp.u.NoOff-nominal tap ratio τ (default 1.0). A tap-changing transformer with τ ≠ 1 has effective susceptance $B/\tau$. Supports per-stage schedule.
phase_shift_degnumber|array|stringdegreesNoPhase-shift angle φ in degrees (default 0). Models a Phase-Shifting Transformer (PST); shifts the Kirchhoff constraint RHS by $-\sigma_\theta \cdot \phi_{\text{rad}}$.
tmax_abnumber|array|stringMWNoMax flow in A→B direction
tmax_banumber|array|stringMWNoMax flow in B→A direction
tcostnumber|array|string$/MWhNoVariable transmission cost
capacitynumber|array|stringMWNoInstalled capacity
expcapnumber|array|stringMWNoCapacity added per expansion module
expmodnumber|array|stringNoMaximum number of expansion modules
capmaxnumber|array|stringMWNoAbsolute maximum capacity
annual_capcostnumber|array|string$/MW-yearNoAnnualized investment cost
annual_deratingnumber|array|stringp.u./yearNoAnnual capacity derating factor

3.5 Battery

A battery energy storage system (BESS).

Standalone battery (unified definition)

When the optional bus field is set (without source_generator), the system auto-generates a discharge Generator, a charge Demand, and a linking Converter during preprocessing. Both charge and discharge connect to the same external bus. Only a single Battery element is needed — no separate Converter, Generator, or Demand definitions required.

{
  "uid": 1, "name": "bess1",
  "bus": 3,
  "input_efficiency": 0.95, "output_efficiency": 0.95,
  "emin": 0, "emax": 200,
  "pmax_charge": 60, "pmax_discharge": 60,
  "gcost": 0,
  "capacity": 200
}

Generation-coupled battery (hybrid / behind-the-meter)

When both bus and source_generator are set, the battery operates in generation-coupled mode: the referenced generator directly charges the battery through an auto-created internal bus. This models a solar or wind plant that sits "behind" the battery (e.g. a DC-coupled or AC-coupled hybrid system).

System::expand_batteries() will:

  • Create an internal bus (name = <battery.name>_int_bus)
  • Connect the discharge Generator to the external bus
  • Connect the charge Demand to the internal bus
  • Rewire the source generator to the internal bus

The source_generator value is the UID or name of a Generator element already defined in generator_array. Its own bus field is overwritten with the internal bus.

{
  "uid": 1, "name": "bess1",
  "bus": 3,
  "source_generator": "solar1",
  "input_efficiency": 0.95, "output_efficiency": 0.95,
  "emin": 0, "emax": 200,
  "pmax_charge": 60, "pmax_discharge": 60,
  "gcost": 0,
  "capacity": 200
}

Traditional definition

Without the bus field, a separate Converter, Generator, and Demand must be defined manually (see §3.6 Converter).

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesBattery name
activebooleanNoWhether the battery is active
businteger|stringNoBus connection (enables unified / coupled definition)
source_generatorinteger|stringNoSource generator for generation-coupled mode (battery name or UID)
input_efficiencynumber|array|stringp.u.NoCharging efficiency
output_efficiencynumber|array|stringp.u.NoDischarging efficiency
annual_lossnumber|array|stringp.u./yearNoAnnual self-discharge rate
eminnumber|array|stringMWhNoMinimum state of charge
emaxnumber|array|stringMWhNoMaximum state of charge
ecostnumber|array|string$/MWhNoStorage usage cost (penalty)
eininumberMWhNoInitial state of charge
efinnumberMWhNoTerminal state of charge
pmax_chargenumber|array|stringMWNoMax charging power (unified definition)
pmax_dischargenumber|array|stringMWNoMax discharging power (unified definition)
gcostnumber|array|string$/MWhNoDischarge generation cost (unified definition)
capacitynumber|array|stringMWhNoInstalled energy capacity
expcapnumber|array|stringMWhNoEnergy capacity per expansion module
expmodnumber|array|stringNoMaximum number of expansion modules
capmaxnumber|array|stringMWhNoAbsolute maximum energy capacity
annual_capcostnumber|array|string$/MWh-yearNoAnnualized investment cost
annual_deratingnumber|array|stringp.u./yearNoAnnual capacity derating factor

3.6 Converter

Links a battery to a discharge generator and a charge demand.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesConverter name
activebooleanNoWhether the converter is active
batteryinteger|stringYesAssociated battery UID or name
generatorinteger|stringYesDischarge generator UID or name
demandinteger|stringYesCharge demand UID or name
conversion_ratenumber|array|stringMW/(MWh/h)NoElectrical output per unit stored energy withdrawn
capacitynumber|array|stringMWNoInstalled power capacity
expcapnumber|array|stringMWNoPower capacity per expansion module
expmodnumber|array|stringNoMaximum number of expansion modules
capmaxnumber|array|stringMWNoAbsolute maximum power capacity
annual_capcostnumber|array|string$/MW-yearNoAnnualized investment cost
annual_deratingnumber|array|stringp.u./yearNoAnnual capacity derating factor

3.7 Junction

A hydraulic node in the water network.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesJunction name
activebooleanNoWhether the junction is active
drainbooleanNoIf true, excess water can leave the system freely

3.8 Waterway

A water channel connecting two junctions.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesWaterway name
activebooleanNoWhether the waterway is active
junction_ainteger|stringYesUpstream junction UID or name
junction_binteger|stringYesDownstream junction UID or name
capacitynumber|array|stringm³/sNoMaximum flow capacity
lossfactornumber|array|stringp.u.NoTransit loss coefficient
fminnumber|array|stringm³/sNoMinimum required water flow
fmaxnumber|array|stringm³/sNoMaximum allowed water flow

3.9 Reservoir

A water reservoir connected to a junction. Volume units: hm³ (1 hm³ = 10⁶ m³).

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesReservoir name
activebooleanNoWhether the reservoir is active
junctioninteger|stringYesAssociated junction UID or name
spillway_capacitynumberm³/sNoMaximum uncontrolled spill capacity
spillway_costnumber$/hm³NoPenalty per unit of spilled water
capacitynumber|array|stringhm³NoTotal usable storage capacity
annual_lossnumber|array|stringp.u./yearNoAnnual evaporation/seepage loss rate
eminnumber|array|stringhm³NoMinimum allowed stored volume
emaxnumber|array|stringhm³NoMaximum allowed stored volume
ecostnumber|array|string$/hm³NoWater value (shadow cost of stored water)
eininumberhm³NoInitial stored volume
efinnumberhm³NoTarget final stored volume
fminnumberm³/sNoMinimum net inflow
fmaxnumberm³/sNoMaximum net inflow
energy_scalenumberNoMultiplicative scaling factor for volume
flow_conversion_ratenumberhm³/(m³/s·h)NoConverts m³/s × hours to hm³ (default: 0.0036)

3.10 Turbine

A hydro turbine linking a waterway to a generator.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesTurbine name
activebooleanNoWhether the turbine is active
waterwayinteger|stringYesAssociated waterway UID or name
generatorinteger|stringYesAssociated generator UID or name
drainbooleanNoIf true, turbine can spill water without generating
conversion_ratenumber|array|stringMW·s/m³NoWater-to-power conversion factor
capacitynumber|array|stringMWNoMaximum turbine power output
main_reservoirinteger|stringNoReservoir whose volume drives the efficiency curve

3.11 Flow (Inflow)

A water inflow or outflow at a junction.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesFlow name
activebooleanNoWhether the flow is active
directionintegerNo+1 = inflow, −1 = outflow
junctioninteger|stringYesAssociated junction UID or name
dischargenumber|array|stringm³/sYesWater discharge schedule

3.12 Reservoir Seepage (formerly Filtration)

Piecewise-linear seepage model from a waterway to an adjacent reservoir, representing water losses due to soil permeability (Darcy's law approximation). The seepage flow through the waterway is constrained to:

seepage [m³/s] = slope [m³/s/hm³] × avg_reservoir_volume [hm³] + constant [m³/s]

where avg_reservoir_volume = (eini + efin) / 2. This captures the hydrostatic-head dependence of seepage. Corresponds to PLP plpfilemb.dat.

When piecewise-linear segments are provided, the active segment is selected based on the reservoir's current volume (vini from the previous phase). The LP constraint coefficients (slope and RHS) are updated dynamically — the same mechanism used by Reservoir Production Factor for turbine conversion rates. If segments is empty, the static slope and constant fields are used as a simple linear model.

JSON array name: reservoir_seepage_array

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesSeepage element name
activebooleanNoWhether the seepage element is active
waterwayinteger|stringYesSource waterway UID or name
reservoirinteger|stringYesReceiving reservoir UID or name
slopenumberm³/s / hm³NoDefault seepage slope (used when segments is empty)
constantnumberm³/sNoDefault constant seepage rate (used when segments is empty)
segmentsarrayNoPiecewise-linear concave segments (see below)

Each segment in the segments array describes one piece of the concave filtration envelope:

FieldTypeUnitsDescription
volumenumberhm³Volume breakpoint
slopenumberm³/s / hm³Seepage slope at this breakpoint
constantnumberm³/sSeepage rate at this breakpoint

The seepage rate at volume V is computed as the minimum over all segments: seepage(V) = min_i { constant_i + slope_i × (V − volume_i) }. This is analogous to the piecewise-linear evaluation used in Reservoir Production Factor.

Example with segments:

{
  "uid": 1,
  "name": "filt1",
  "waterway": "w1_2",
  "reservoir": "r1",
  "slope": 0.001,
  "constant": 1.0,
  "segments": [
    { "volume": 0.0, "slope": 0.0003, "constant": 0.5 },
    { "volume": 5000.0, "slope": 0.0001, "constant": 2.0 }
  ]
}

3.13 Reservoir Production Factor

Piecewise-linear turbine efficiency as a function of reservoir volume (hydraulic head). Models the PLP "rendimiento" concept: the turbine conversion rate varies with the current water level in the reservoir. Corresponds to PLP plpcenre.dat (Archivo de Rendimiento de Embalses).

When the SDDP solver runs, it updates the turbine's conversion-rate LP coefficient at each forward-pass iteration using the current reservoir volume. For the first iteration, the reservoir's eini value is used. If the LP solver does not support in-place coefficient modification (supports_set_coeff()), the static conversion_rate from the Turbine element is used unchanged.

JSON array name: reservoir_production_factor_array

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesProduction factor element name
activebooleanNoWhether the element is active
turbineinteger|stringYesAssociated turbine UID or name
reservoirinteger|stringYesAssociated reservoir UID or name
mean_efficiencynumberMW·s/m³NoFallback efficiency (default: 1.0)
segmentsarrayNoPiecewise-linear concave segments
sddp_efficiency_update_skipintegerNoSDDP iterations to skip between updates

Each segment in the segments array has the following fields:

FieldTypeUnitsDescription
volumenumberhm³Volume breakpoint
slopenumberefficiency/hm³Slope at this breakpoint
constantnumberMW·s/m³Efficiency at this breakpoint (point-slope form)

The efficiency is computed as the minimum over all segments:

efficiency(V) = min_i { constant_i + slope_i × (V − volume_i) }

Segments should have slopes in decreasing order so the function forms a concave envelope. The result is clamped to zero (efficiency cannot be negative).

Example:

{
  "reservoir_production_factor_array": [
    {
      "uid": 1,
      "name": "eff_colbun",
      "turbine": "COLBUN",
      "reservoir": "COLBUN",
      "mean_efficiency": 1.53,
      "segments": [
        { "volume": 0.0, "slope": 0.0002294, "constant": 1.2558 },
        { "volume": 500.0, "slope": 0.0001, "constant": 1.53 }
      ]
    }
  ]
}

3.14 Reservoir Discharge Limit

Piecewise-linear volume-dependent discharge limit for reservoirs. This is a safety/environmental constraint that limits the hourly-average discharge as a function of the reservoir volume (e.g. to prevent landslides or excessive drawdown).

The constraint per stage is:

qeh ≤ slope × V_avg + constant

where qeh is the stage-average hourly discharge [m³/s], V_avg is the average reservoir volume (eini + efin) / 2 [hm³], and slope/constant are from the active piecewise-linear segment.

Generalizes the PLP "Ralco" constraint (plpralco.dat).

JSON array name: reservoir_discharge_limit_array

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesDischarge limit element name
activebooleanNoWhether the element is active
reservoirinteger|stringYesAssociated reservoir UID or name
segmentsarrayNoPiecewise-linear segments

Each segment has:

FieldTypeUnitsDescription
volumenumberhm³Volume breakpoint
slopenumberm³/s / hm³Discharge limit slope
constantnumberm³/sDischarge limit intercept

3.15 Generator Profile

A time-varying capacity-factor profile for a generator.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesProfile name
activebooleanNoWhether the profile is active
generatorinteger|stringYesAssociated generator UID or name
profilenumber|array|stringp.u.YesCapacity-factor profile (0–1)
scostnumber|array|string$/MWhNoShort-run generation cost override

3.16 Demand Profile

A time-varying load-shape profile for a demand element.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesProfile name
activebooleanNoWhether the profile is active
demandinteger|stringYesAssociated demand UID or name
profilenumber|array|stringp.u.YesLoad-scaling profile (0–1)
scostnumber|array|string$/MWhNoShort-run load-shedding cost override

3.17 Reserve Zone

A spinning-reserve requirement zone.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesZone name
activebooleanNoWhether the zone is active
urreqnumber|array|stringMWNoUp-reserve requirement
drreqnumber|array|stringMWNoDown-reserve requirement
urcostnumber|array|string$/MWNoUp-reserve shortage penalty
drcostnumber|array|string$/MWNoDown-reserve shortage penalty

3.18 Reserve Provision

A generator's contribution to reserve zones.

FieldTypeUnitsRequiredDescription
uidintegerYesUnique identifier
namestringYesProvision name
activebooleanNoWhether the provision is active
generatorinteger|stringYesAssociated generator UID or name
reserve_zonesstringYesComma-separated reserve zone UIDs or names
urmaxnumber|array|stringMWNoMaximum up-reserve offer
drmaxnumber|array|stringMWNoMaximum down-reserve offer
ur_capacity_factornumber|array|stringp.u.NoUp-reserve capacity factor
dr_capacity_factornumber|array|stringp.u.NoDown-reserve capacity factor
ur_provision_factornumber|array|stringp.u.NoUp-reserve provision factor
dr_provision_factornumber|array|stringp.u.NoDown-reserve provision factor
urcostnumber|array|string$/MWNoUp-reserve bid cost
drcostnumber|array|string$/MWNoDown-reserve bid cost

3.19 User Constraint

User-defined linear constraints applied to the LP formulation. See User Constraints for the full syntax reference and examples.

FieldTypeUnitRequiredDescription
uidintegerYesUnique identifier
namestringYesHuman-readable constraint name
activebooleanNoActivation flag (default: true)
expressionstringYesConstraint expression in AMPL-inspired syntax

System-level fields:

FieldTypeDescription
user_constraint_arrayarrayInline array of UserConstraint objects
user_constraint_filestringPath to external JSON file with constraint array

4. External Data Files

When a field value is a string instead of a number, it refers to an external data file in the input_directory. Files are organized by component type:

<input_directory>/
├── Bus/
├── Generator/
│   └── pmax.parquet
├── Demand/
│   └── lmax.parquet
├── Line/
├── Battery/
└── ...

File Format

External data files (CSV or Parquet) follow a tabular format with columns:

ColumnDescription
scenarioScenario UID
stageStage UID
blockBlock UID
uid:<N>Value for element with UID N

Example CSV

"scenario","stage","block","uid:1"
1,1,1,10
1,2,2,15
1,3,3,8

5. Output Structure

After running gtopt, results are written to the output_directory:

output/
├── solution.csv              # Objective value, status
├── Bus/
│   └── balance_dual.csv      # Marginal costs per bus
├── Generator/
│   ├── generation_sol.csv    # Generation dispatch
│   └── generation_cost.csv   # Generation costs
└── Demand/
    ├── load_sol.csv           # Served load
    ├── fail_sol.csv           # Unserved demand
    ├── capacity_dual.csv      # Capacity dual values
    └── ...

Output files use the same tabular format as input files, with scenario, stage, block, and uid:<N> columns.


6. Planning (Root Container)

The Planning object is the root of the gtopt input data model. A JSON configuration file represents a single Planning instance with three top-level sections:

FieldTypeRequiredDescription
optionsOptionsNoGlobal solver and I/O settings (see Section 1)
simulationSimulationYesTime structure: blocks, stages, scenarios, phases, scenes, apertures (see Section 2)
systemSystemYesPhysical network components (see Section 3)

Multiple JSON files can be merged with Planning::merge(), allowing the configuration to be split across files (e.g. time structure in one file, network elements in another). The merge uses first-value semantics for scalar options and concatenation for arrays.

Example (minimal single-file):

{
  "options": {
    "demand_fail_cost": 1000,
    "use_single_bus": true,
    "input_directory": "input",
    "output_directory": "output"
  },
  "simulation": {
    "block_array": [{"uid": 1, "duration": 1}],
    "stage_array": [
      {"uid": 1, "first_block": 0, "count_block": 1}
    ],
    "scenario_array": [{"uid": 1, "probability_factor": 1}]
  },
  "system": {
    "bus_array": [{"uid": 1, "name": "bus1"}],
    "generator_array": [
      {"uid": 1, "name": "gen1", "bus": 1, "pmax": 100,
       "gcost": 20}
    ],
    "demand_array": [
      {"uid": 1, "name": "dem1", "bus": 1, "lmax": 50}
    ]
  }
}

7. Complete Example

See cases/c0/system_c0.json for a minimal working example with:

  • 1 bus, 1 generator, 1 demand
  • 5 time blocks, 5 stages, 1 scenario
  • Demand expansion planning

See also

  • Mathematical Formulation — Full LP/MIP optimization formulation with academic references
  • Planning Guide — Step-by-step planning guide with worked examples
  • Usage Guide — Command-line options and advanced usage
  • Scripts Guide — Python conversion utilities