Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using a generator as the slack bus in Grid2Op Environment's PandaPower BackEnd #617

Closed
DEUCE1957 opened this issue Jun 24, 2024 · 4 comments
Labels
bug Something isn't working

Comments

@DEUCE1957
Copy link
Contributor

DEUCE1957 commented Jun 24, 2024

Environment

  • Grid2op version: 1.10.2
  • System: windows
  • Python: 3.11.9

Bug description

When creating a custom environment (without chronics) using the PandaPower backend will throw an index error if one of the generators has slack=True. The work-around right now is to use an ext_grid element as the slack.

This happens because in Lines 354-439 of pandaPowerBackend.py we have:

if np.all(~self._grid.gen["slack"]):
  if i_ref is None:
      i_ref = gen_id_pp
      self._iref_slack = i_ref
      self._id_bus_added = id_added  # self._grid.gen.shape[0]
      # TODO here i force the distributed slack bus too, by removing the other from the ext_grid...
      self._grid.ext_grid = self._grid.ext_grid.iloc[:1]
else:
    self.slack_id = (self._grid.gen["slack"].values).nonzero()[0]

Hence if no slack gen is present, we use self._iref_slack to index the slack bus. (note: iref is None is always True if we reach that line, since it is defined as i_ref = None a few lines earlier (line 353).
However if a slack gen is present, then self.slack_id is used instead. This causes problems inside the powerflow (runpf(...)), line 1161:

self._grid._ppc["gen"][self._iref_slack, 1] = 0.0

Since in this scenario (where there is a gen with slack = True) _iref_slack is still None and will throw an index error.

How to reproduce

import pandapower as pp
import grid2op
from grid2op.Backend.pandaPowerBackend import PandaPowerBackend
from grid2op.Action.playableAction import PlayableAction
from grid2op.Observation.completeObservation import CompleteObservation
from grid2op.Reward.flatReward import FlatReward
from grid2op.Rules.DefaultRules import DefaultRules
from grid2op.Chronics.multiFolder import Multifolder
from grid2op.Chronics.gridStateFromFileWithForecasts import GridStateFromFileWithForecasts
from grid2op.Chronics import ChangeNothing
from pathlib import Path
# >> Select Custom Environment Path <<
env_path = Path.cwd() / "Environments" / "newEnv"
env_path.mkdir(exist_ok=True, parents=True)

# >> Create Minimal PandaPower Network <<
network = pp.create_empty_network()
pp.create_buses(network, nr_buses=2, vn_kv=20.0)
pp.create_gen(network, bus=0, p_mw=10.0, min_p_mw=-1e9, max_p_mw=1e9, slack=True, slack_weight=1.0)
pp.create_line(network, from_bus=0, to_bus=1, length_km=10.0, std_type="NAYY 4x50 SE")
pp.create_load(network, bus=1, p_mw=10.0, controllable=False)
pp.to_json(network, env_path / "grid.json")

# >> Create config for custom environment <<
def create_config(env_path:Path, **kwargs):
    thermal_limits=list(network.line.max_i_ka*10000) # Thermal Limit in Amps (A)
    with open(env_path / "config.py", "w") as config:
        # Import Statements
        config.writelines(
            [f"from {value.__module__} import {value.__name__}\n" for value in kwargs.values() if hasattr(value, "__module__")]
        )

        # Config Dictionary
        config.writelines(
            ["config = {\n"] +
            [f"'{k}':{getattr(v,'__name__', 'None')},\n" for k,v in kwargs.items()] +
            [f"'thermal_limits':{thermal_limits}\n"] + 
            ["}\n"]
        )
create_config(env_path,
              backend=PandaPowerBackend,
              action=PlayableAction,
              observation_class=CompleteObservation,
              reward_class=FlatReward,
              gamerules_class=DefaultRules,
              chronics_class=Multifolder,
              grid_value_class=GridStateFromFileWithForecasts,
              voltagecontroler_class=None,
              names_chronics_to_grid=None)

# >> Initialize Environment <<
env = grid2op.make(env_path, chronics_class=ChangeNothing)

Current output

IndexError: index 1 is out of bounds for axis 0 with size 1

Expected output

No output expected (environment successfully initializes).

Suggest Fix

Change Line 1161 in pandaPowerBackend.py to:

self._grid._ppc["gen"][self.slack_id if self._iref_slack is None else self._iref_slack, 1] = 0.0

Note: in the above minimal example this fix does not seem to work (power flow does not converge due to an 'isolated bus'). I am unsure why, since there is no issue running it inside PandaPower directly:

pp.runpp(network) # Works fine

However using an external grid element instead of a generator with slack=True seems to work so will use that as a workaround for now. One of the built-in environments 'rte_case14_realistic' does have a slack=True gen and does work, hence there is likely more to this issue.

@DEUCE1957 DEUCE1957 added the bug Something isn't working label Jun 24, 2024
@BDonnot
Copy link
Collaborator

BDonnot commented Jun 24, 2024

Hello,

Thanks for reporting this bug. I'll try to fix it asap. You seems to be correct, there appears to be an error in the way pandapower backend handles slack.

@BDonnot
Copy link
Collaborator

BDonnot commented Jun 25, 2024

Hello again,

So after lots of investigation:

  1. I indeed implemented a fix for the self._iref_slack thing. This should never have been there. I'm surprised it has worked for some of our environments (but i'll check how later on - as you pointed out One of the built-in environments 'rte_case14_realistic' does have a slack=True gen and does work, hence there is likely more to this issue. )

  2. about the "divergence in pandapowerbackend but not in pandapower" (which I observe too) it took me a while to understand what is going on, but basically, the powerline is overloaded when grid2op loads the data so it is disconnected. So the grid is not connex.

I'll try to see if there is something elegant to avoid this issue that is an issue (thermal limit are set AFTER the env is created in the make function, so grid2op does not know, when the powerflow is being run, that the thermal limit should not be used yet)... Anyway...

Thanks for noticing these bugs :-)

A fix will be available in the next release and hopefully by the tomorrow evening if you install from source from the dev_1.10.3 branch

BDonnot added a commit that referenced this issue Jun 26, 2024
@BDonnot
Copy link
Collaborator

BDonnot commented Jun 26, 2024

Hi again :)

This should be solved if you install the development grid2op version:

pip install git+https://github.com/rte-france/grid2op.git@dev_1.10.3

Hopefully i'll be able to release the 1.10.3 version this week. Most likely the next one (i still have some things I want to include in this release)

@DEUCE1957
Copy link
Contributor Author

Thank you for fixing this!
Will make the process of generating a new environment from PandaPower smoother.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants