Unit Commitment MINLP with Knitro#

unit_commitment_minlp_mp2nl.ipynb Open In Colab Open In Deepnote Open In Kaggle Open In Gradient Open In SageMaker Studio Lab Powered by AMPL

Description: Solving a nonlinear Unit Commitment problem with Knitro using MP features for logic and multi-objective optimization. The goal of this notebook is to show a straightforward and clear way of using nonlinear solvers for complex models with logical expressions and also hierarchical multi-objective optimization.

Tags: mp, knitro, mp2nl, nonlinear, quadratic, minlp, unit-commitment, electric-power-industry, energy, multi-objective, gurobi, xpress, mp2nl

Notebook author: Marcos Dominguez Velad <marcos@ampl.com>

# Install dependencies
%pip install -q amplpy pandas numpy
# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook

ampl = ampl_notebook(
    modules=["mp2nl", "knitro", "xpress", "gurobi"],  # modules to install
    license_uuid="default",  # license to use
)  # instantiate AMPL object and register magics

The problem#

We are solving a version of Unit Commitment with generators with minimum and maximum outputs and ramp limits. There are linear and quadratic costs, so the problem becomes a MINLP, and we aim to minimize CO2 emissions while minimizing production costs, so it is a multi-objective problem.

We will focus on the nonlinear and multi-objective part of the problem for this case. The model is written in AMPL for it to keep easy to read.

Warning: this notebook uses commercial solvers, so when running on Cloud you may need a license for these solvers (Knitro, Gurobi, Xpress), or run locally.

MINLP#

One of the 2 objectives is minimizing total cost, which is a nonlinear expression.

  • Total cost: $$ \min ; \sum_{g \in \text{GEN}} \sum_{t \in \text{T}} \left( c_g^{\text{lin}} , p_{g,t}

  • c_g^{\text{quad}} , p_{g,t}^2 \right) ;+; \sum_{g \in \text{GEN}} \sum_{t \in \text{T}} c_g^{\text{start}} , y_{g,t}$$

Where \(p_{g,t}\) is the amount of energy produced by the generator \(g\) in time \(t\), and \(y_{g,t}\) is 1 if the generator started at period \(t\) (startup cost).

There are also some logical constraints to make the model clear. We have some binary variables to model easier if a generator is turned on or has just started:

  • Producing if and only if commited (binaries \(x_{g,t}\)): $\( p_{g,t} > 0 \;\Longleftrightarrow\; x_{g,t} = 1 \qquad \forall g \in \text{GEN},\; t \in \text{T} \)$

  • Commited if and only if producing: $\( y_{g,t} \;\Longleftrightarrow\; \big( x_{g,t} = 1 \;\land\; x_{g,t-1} = 0 \big) \qquad \forall g \in \text{GEN} \)$

  • Amount of energy produced is either 0 or at least over a threshold (minimum input). $\( p_{g,t} = 0 \;\;\text{or}\;\; p_{g,t} \ge \underline{P}_g \qquad \forall g \in \text{GEN},\; t \in \text{T} \)$

  • Ramp limits:

\[ \left| p_{g,t} - p_{g,t-1} \right| \;\le\; R_g^{\uparrow} \qquad \forall g \in \text{GEN} \]

Solvers don’t necessarily handle logical expressions, but the AMPL/MP interface takes care of reformulating efficiently these expressions.

Multi-objective#

We have 2 objectives. We are going to do hierarchical optimization minimizing first the total cost minimization problem, and later the emissions, allowing a degradation of the total cost of 5%.

This will be handled by suffixes for the objectives, .objpriority and .objreltol.

MINLP Unit Commitment model#

%%writefile unit_commitment.mod
set GENERATORS;
set TIME ordered;

param demand {TIME} >= 0;                   # Power demand at each time
param min_output {GENERATORS} >= 0;         # Minimum power output
param max_output {g in GENERATORS} >= min_output[g];  # Maximum power output
param ramp_up_limit {GENERATORS} >= 0;

param linear_cost {GENERATORS};             # Linear cost coefficient
param quadratic_cost {GENERATORS} >= 0;     # Quadratic cost coefficient
param startup_cost {GENERATORS} >= 0;       # Startup cost

param emission_rate {GENERATORS} >= 0;      # Tons CO2 per MW produced

var is_committed {GENERATORS, TIME} binary;       # 1 if generator is ON
var power_generated {gen in GENERATORS, TIME} >= 0 <= max_output[gen];      # MW produced
var is_startup {GENERATORS, TIME} binary;         # 1 if generator starts up

# Meet demand in each period
subject to Demand_Satisfaction {t in TIME}:
    sum {gen in GENERATORS} power_generated[gen,t] >= demand[t];

# Startup in first period
subject to Startup_First {gen in GENERATORS}:
    is_startup[gen, first(TIME)] == is_committed[gen, first(TIME)];

# Startup logic in subsequent periods
subject to Startup_Transition {gen in GENERATORS, t in TIME: ord(t) > 1}:
    is_startup[gen,t] <==> (is_committed[gen,t] and !is_committed[gen,prev(t)]);

subject to Min_Gen_If_On {gen in GENERATORS, t in TIME}:
    (power_generated[gen,t] <= 0 && is_committed[gen,t] < 0.5)
      or (power_generated[gen,t] >= min_output[gen] && is_committed[gen,t] >= 0.5);

# Ramp-up/down limits
subject to Ramp {gen in GENERATORS, t in TIME: ord(t) > 1}:
    abs(power_generated[gen,t] - power_generated[gen,prev(t)]) <= ramp_up_limit[gen];

# Objective 1: Minimize total operating + startup cost
minimize Total_Cost:
    sum {gen in GENERATORS, t in TIME}
        (linear_cost[gen] * power_generated[gen,t] +
         quadratic_cost[gen] * power_generated[gen,t]^2)
  + sum {gen in GENERATORS, t in TIME} startup_cost[gen] * is_startup[gen,t];

# Objective 2: Minimize total emissions
minimize Total_Emissions:
    sum {gen in GENERATORS, t in TIME} emission_rate[gen] * power_generated[gen,t];

# Multi-objective support
suffix objpriority;
suffix objabstol;
suffix objreltol;
Writing unit_commitment.mod

Solving the model#

Generate the problem data#

The AMPL model is isolated from the input data for more readability and maintainance

import pandas as pd
import numpy as np

# Unit Commitment data
generators = ["G1", "G2", "G3", "G4", "G5", "G6", "G7"]

# Generators data
generators_data = pd.DataFrame(
    {
        "min_output": [20, 30, 25, 15, 10, 40, 0],
        "max_output": [100, 120, 90, 60, 50, 150, 30],
        "ramp_up_limit": [40, 50, 30, 25, 20, 60, 10],
        "linear_cost": [20, 16, 18, 22, 24, 14, 12],
        "quadratic_cost": [0.04, 0.05, 0.06, 0.03, 0.04, 0.036, 0.1],
        "startup_cost": [400, 300, 360, 200, 160, 600, 160],
        "emission_rate": [0.7, 0.5, 0.6, 0.4, 0.3, 0.8, 0.0],
    },
    index=generators,
)

# Generate random demand
num_time_periods = 24 * 3
time_periods = list(range(1, num_time_periods + 1))

np.random.seed(42)
base_demand = 150 + 40 * np.sin(np.linspace(0, 3 * np.pi, num_time_periods))
noise = np.random.normal(0, 10, num_time_periods)
demand = (base_demand + noise).clip(min=100).round().astype(int)

Assign the UC data#

# Optimization model and data
ampl = AMPL()
ampl.read("unit_commitment.mod")

ampl.set["TIME"] = time_periods
ampl.set["GENERATORS"] = generators
ampl.param["demand"] = demand
ampl.set_data(generators_data, "GENERATORS")

Running the solver#

For nonlinear solvers like Knitro, we need the mp2nl interface to make it handle logical constraints and multi-objective. See the following example for the knitro solver.

# Hierarchical Multi-objective configuration
# Higher priority first
ampl.eval("let Total_Cost.objpriority := 2;")
# Set 10% degradation tolerance for total cost
ampl.eval("let Total_Cost.objreltol := 0.10;")
# Second objective (less priority)
ampl.eval("let Total_Emissions.objpriority := 1;")

SOLVER = "knitro"
# SOLVER='gurobi'
# SOLVER='xpress'

# Time limit
max_seconds = 30

if SOLVER == "knitro":
    ampl.solve(
        solver="mp2nl",
        knitro_options="numthreads=8 ms_enable=1 maxtime=" + str(max_seconds),
        verbose=True,
        mp2nl_options="solver=knitro obj:multi=2 outlev=1",
    )
else:
    ampl.solve(
        solver=SOLVER,
        mp_options="obj:multi=2 outlev=1 timelim=" + str(max_seconds),
        verbose=True,
    )
MP2NL 0.1:   nl:solver = knitro
  obj:multi = 2
  tech:outlev = 1

AMPL MP initial flat model has 1512 variables (0 integer, 1008 binary);
Objectives: 2 linear; 
Constraints:  1570 linear;
Algebraic expressions:  497 abs; 504 powconstexp;
Logical expressions:  936 conditional (in)equalitie(s); 1433 and; 504 not; 504 or;



==============================================================================
MULTI-OBJECTIVE MODE: starting with 2 objectives (2 combined) ...
==============================================================================
==============================================================================

AMPL MP final model has 6886 variables (1001 integer, 3881 binary);
Objectives: 2 linear; 
Constraints:  6871 linear;
Algebraic expressions:  504 powconstexp;




MULTI-OBJECTIVE MODE: objective 1 (out of 2) ...
==============================================================================

Artelys Knitro 15.1.0: numthreads=8
ms_enable=1
maxtime=30

=======================================
          Commercial License
         Artelys Knitro 15.1.0
=======================================

Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.

concurrent_evals         0
datacheck                0
feastol                  1e-06
feastol_abs              1e-06
findiff_numthreads       1
hessian_no_f             1
hessopt                  1
maxtime                  30
ms_enable                1
numthreads               8
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 0.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_heuristic_fixpropagate from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQP
Objective: minimize / quadratic
Number of variables:                   6886 |                          6886
  bounds:         lower     upper     range |     lower     upper     range
                      0         0      4378 |         0         0      4378
                             free     fixed |                free     fixed
                                0      2508 |                   0      2508
                  cont.    binary   integer |     cont.    binary   integer
                   3509      3377         0 |      3509      3377         0
Number of constraints:                 6870 |                          6870
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:           504      6366         0 |       504      6366         0
  quadratic:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:          1008     15598           |      1008     15598          
  quadratic:        504         0       504 |       504         0       504
  total:           1008     15598       504 |      1008     15598       504

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.

Coefficient range:
  linear objective:          [1e+01, 6e+02] |                [2e+00, 1e+02]
  linear constraints:        [1e+00, 2e+02] |                [7e-01, 1e+02]
  quadratic objective:       [3e-02, 1e-01] |                [5e-03, 2e-02]
  quadratic constraints:     [0e+00, 0e+00] |                [0e+00, 0e+00]
  variable bounds:           [1e+00, 2e+02] |                [1e+00, 2e+02]
  constraint bounds:         [1e+00, 2e+02] |                [1e+00, 2e+02]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    1        292009.          9.18623           37.6427       0.170
    2        285619.          1.34977           17.3486       0.182
    3        302103.         0.378027           17.3486       0.193
    4        274191.      2.24937e-02           5.13851       0.201
    5        251383.      7.92026e-03           3.28855       0.207
    6        223548.      1.95982e-03           2.73753       0.213
    7        202091.      1.63794e-04           1.27362       0.219
    8        195453.      2.34342e-05          0.345642       0.226
    9        193640.      2.59783e-06          0.111450       0.232
   10        193264.      1.00037e-06       7.51847e-02       0.237
   11        192927.      2.05976e-07       8.36857e-02       0.243
   12        192780.      1.88764e-08       1.59895e-02       0.250
   13        192771.      1.54518e-08       9.04205e-03       0.255
   14        192755.      4.29941e-09       3.28825e-03       0.261
   15        192746.      1.81713e-10       2.07496e-04       0.268
   16        192746.      6.17359e-11       1.71659e-04       0.274
   17        192746.      8.64879e-10       1.78123e-06       0.279
   18        192746.      8.64879e-10       1.78123e-06       0.438
   19        192746.      8.64879e-10       1.78123e-06       8.981

Root node cutting planes
------------------------

 Iter     Cuts      Best solution   Best bound      Gap       Time 
                        value         value                  (secs)
 ----     ----      -------------   ----------      ---      ------
    0        0       248954.           192746.     22.58%     9.083

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       0      248954.           192746.     22.58%    30.017

EXIT: Time limit reached. Integer feasible point found.

Final Statistics for MIP
------------------------
Final objective value               =  2.48954353999999992e+05
Final bound value                   =  1.92745723301910068e+05
Final optimality gap (abs / rel)    =  56208.6 / 0.225779 (22.58%)
# of root cutting plane rounds      =  1
# of restarts                       =  0
# of nodes processed                =  1 (29.775s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  3 (29.775s)
Total program time (secs)           =  30.01675 (182.981 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  15 / 5
Mixed-integer rounding cuts         =  0 / 0
Gomory cuts                         =  1 / 1
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  3141 / 1133

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  0 / 0 / 0.000s
Rounding heuristic                  =  1 / 0 / 0.003s
MPEC heuristic                      =  0 / 0 / 0.000s
Local search heuristic              =  4 / 1 / 0.131s
Fix-and-propagate heuristics        =  0 / 0 / 0.000s

===========================================================================



MULTI-OBJECTIVE MODE: objective 2 (out of 2) ...
==============================================================================

Artelys Knitro 15.1.0: numthreads=8
ms_enable=1
maxtime=30

=======================================
          Commercial License
         Artelys Knitro 15.1.0
=======================================

Knitro changing mip_method from AUTO to 1.
concurrent_evals         0
datacheck                0
feastol                  1e-06
feastol_abs              1e-06
findiff_numthreads       1
hessian_no_f             1
hessopt                  1
maxtime                  30
ms_enable                1
numthreads               8
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 0.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_heuristic_fixpropagate from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: convex MIQCQP
Objective: minimize / linear
Number of variables:                   6886 |                          6886
  bounds:         lower     upper     range |     lower     upper     range
                      0         0      4378 |         0         0      4378
                             free     fixed |                free     fixed
                                0      2508 |                   0      2508
                  cont.    binary   integer |     cont.    binary   integer
                   3509      3377         0 |      3509      3377         0
Number of constraints:                 6871 |                          6871
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:           504      6366         0 |       504      6366         0
  quadratic:          0         1         0 |         0         1         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:           432     16606           |       432     16606          
  quadratic:          0       504       504 |         0       504       504
  total:            432     16606       504 |       432     16606       504

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
Read root node initial point:
    Objective value:    6.732200e+03
    Feasibility error:  0.000000e+00
    Feasible:           yes
Read MIP primal point:
    Objective value:    6.732200e+03
    Feasibility error:  0.000000e+00
    Feasible:           yes

Coefficient range:
  linear objective:          [3e-01, 8e-01] |                [3e-01, 8e-01]
  linear constraints:        [1e+00, 6e+02] |                [7e-01, 1e+02]
  quadratic objective:       [0e+00, 0e+00] |                [0e+00, 0e+00]
  quadratic constraints:     [3e-02, 1e-01] |                [5e-03, 2e-02]
  variable bounds:           [1e+00, 2e+02] |                [1e+00, 2e+02]
  constraint bounds:         [1e+00, 3e+05] |                [1e+00, 5e+04]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    1        6730.63      9.76182e-02          0.800000       0.249
    2        6724.66      8.57816e-02          0.800000       0.277
    3        6718.46      5.78552e-02          0.800000       0.296
    4        6682.75      3.20001e-02          0.800000       0.308
    5        6451.29      1.81746e-02          0.800000       0.321
    6        6104.81      7.36658e-03          0.733289       0.335
    7        5587.70      2.66069e-03          0.664096       0.350
    8        4771.72      3.05858e-04          0.497880       0.366
    9        3983.94      3.00275e-05          0.232193       0.381
   10        3522.78      8.55612e-07       5.30708e-02       0.397
   11        3482.59      2.43304e-08       4.77726e-03       0.413
   12        3478.81      1.75048e-10       1.67151e-04       0.428
   13        3478.70      9.59386e-12       3.20852e-08       0.443
   14        3478.70      8.93632e-10       7.28366e-09       0.467
   15        3478.70      8.93632e-10       7.28366e-09       0.487

Root node cutting planes
------------------------

 Iter     Cuts      Best solution   Best bound      Gap       Time 
                        value         value                  (secs)
 ----     ----      -------------   ----------      ---      ------
    0        0       6575.30           3478.70     47.09%     0.636
    1      852       6575.30           3481.17     47.06%     1.052
    2      502       6575.30           3500.80     46.76%     1.697

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       2      6575.30           3500.80     46.76%     2.174
      1       2      3500.80   FP      3500.80     -0.00%     2.200

EXIT: Optimal solution found.

Final Statistics for MIP
------------------------
Final objective value               =  3.50079999999760639e+03
Final bound value                   =  3.50080000165960473e+03
Final optimality gap (abs / rel)    =  -1.66200e-06 / -4.74748e-10 (-0.00%)
# of root cutting plane rounds      =  3
# of restarts                       =  0
# of nodes processed                =  1 (1.523s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  9 (1.547s)
Total program time (secs)           =  2.20018 (18.933 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  72 / 21
Mixed-integer rounding cuts         =  26 / 20
Gomory cuts                         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  5990 / 1259

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  1 / 1 / 0.025s
Rounding heuristic                  =  4 / 0 / 0.017s
MPEC heuristic                      =  0 / 0 / 0.000s
Local search heuristic              =  4 / 1 / 0.196s
Fix-and-propagate heuristics        =  0 / 0 / 0.000s

===========================================================================



==============================================================================
MULTI-OBJECTIVE MODE: done.

MP2NL 0.1: Knitro 15.1.0: Locally optimal or satisfactory solution.
objective 3500.8; optimality gap -1.66e-06
1 nodes; 9 subproblem solves

suffix numiters OUT;
suffix opterror OUT;
suffix relaxbnd OUT;
suffix feaserror OUT;
suffix incumbent OUT;
suffix numfcevals OUT;
Objective = Total_Cost
print("Solution status:", ampl.solve_result, " solve code:", ampl.solve_result_num)

print("=== Objective Values ===")
total_cost = ampl.obj["Total_Cost"].value()
total_emissions = ampl.obj["Total_Emissions"].value()

print(f"Total cost: {total_cost:.2f}$")
print(f"Total emissions: {total_emissions:.2f} tons CO₂")

is_committed = ampl.get_data("is_committed").to_pandas()
display(is_committed.unstack().T)
produce = ampl.get_data("power_generated").to_pandas()
display(produce.unstack().T)
startup = ampl.get_data("is_startup").to_pandas()
display(startup.unstack().T)
Solution status: solved  solve code: 0
=== Objective Values ===
Total cost: 248271.92$
Total emissions: 3500.80 tons CO₂
index0 G1 G2 G3 G4 G5 G6 G7
index1
is_committed 1 0 1 0 1 1 0 1
2 0 1 0 1 1 0 1
3 0 1 0 1 1 0 1
4 0 1 0 1 1 0 1
5 0 1 0 1 1 0 1
... ... ... ... ... ... ... ...
68 0 1 0 1 1 0 1
69 0 1 0 1 1 0 1
70 0 1 0 1 1 0 1
71 0 1 0 1 1 0 1
72 0 1 0 1 1 0 1

72 rows × 7 columns

index0 G1 G2 G3 G4 G5 G6 G7
index1
power_generated 1 0.0 30.0 0.0 45.0 50.0 0.0 30.0
2 0.0 30.0 0.0 44.0 50.0 0.0 30.0
3 0.0 30.0 0.0 57.0 50.0 0.0 30.0
4 0.0 41.0 0.0 60.0 50.0 0.0 30.0
5 0.0 30.0 0.0 58.0 50.0 0.0 30.0
... ... ... ... ... ... ... ...
68 0.0 40.0 0.0 60.0 50.0 0.0 30.0
69 0.0 30.0 0.0 59.0 50.0 0.0 30.0
70 0.0 30.0 0.0 44.0 50.0 0.0 30.0
71 0.0 30.0 0.0 49.0 50.0 0.0 30.0
72 0.0 30.0 0.0 55.0 50.0 0.0 30.0

72 rows × 7 columns

index0 G1 G2 G3 G4 G5 G6 G7
index1
is_startup 1 0 1 0 1 1 0 1
2 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0
... ... ... ... ... ... ... ...
68 0 0 0 0 0 0 0
69 0 0 0 0 0 0 0
70 0 0 0 0 0 0 0
71 0 0 0 0 0 0 0
72 0 0 0 0 0 0 0

72 rows × 7 columns