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

Bump to version 1.2.1 #78

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ jobs:
name: Run integration tests
# This assumes pytest is installed via the install-package step above
command: pytest tests/integration_tests
- run:
name: Run some grid2op tests (test that it does not break grid2op `FromChronix2grid`)
# This assumes pytest is installed via the install-package step above
command: pytest tests/grid2op_tests


# Invoke jobs via workflows
Expand Down
2 changes: 1 addition & 1 deletion chronix2grid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
# SPDX-License-Identifier: MPL-2.0
# This file is part of Chronix2Grid, A python package to generate "en-masse" chronics for loads and productions (thermal, renewable)

___version__ = "1.2.0.post1"
___version__ = "1.2.1"
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,132 @@ class PypsaDispatcher(Dispatcher, pypsa.Network):

# PATCH
# to avoid problems for respecting pmax and ramps when rounding production values in chronics at the end, we apply a correcting factor
PmaxCorrectingFactor = 1
PmaxCorrectingFactor = 1.
RampCorrectingFactor = 0.1

def __init__(self, *args, **kwargs):
DebugPmax = 1e9 # in debug mode, pmax for the "infinite" generator
DebugCost = 1e7 # in debug mode, cost for the "infinite" generator

def __init__(self,
*args,
pypsa_debug_add_inf_gen=False,
pypsa_debug_add_thermal=True,
pypsa_debug_add_hydro=True,
pypsa_debug_add_nuc=True,
pypsa_debug_add_solar=True,
pypsa_debug_add_wind=True,
pypsa_debug_add_slack=True,
**kwargs):
super().__init__(*args, **kwargs)
self.add('Bus', 'node')
self.add('Load', name='agg_load', bus='node')
self.add('Bus', 'only_bus')
self.add('Load', name='agg_load', bus='only_bus')
self._env = None # The grid2op environment when instanciated with from_gri2dop_env
self._df = None

self._all_gen_names = set()
self._chronix_scenario = None
self._simplified_chronix_scenario = None
self._has_results = False
self._has_simplified_results = False

self._pmax_solar = None
self._pmax_wind = None
self._pypsa_debug_add_inf_gen = bool(pypsa_debug_add_inf_gen)
if self._pypsa_debug_add_inf_gen:
self.add(class_name='Generator',
name="debug_infinite_gen",
bus='only_bus',
marginal_cost=type(self).DebugCost,
p_nom=type(self).DebugPmax,
ramp_limit_up=1.,
ramp_limit_down=1.,
)
self._pypsa_debug_add_thermal = bool(pypsa_debug_add_thermal)
self._pypsa_debug_add_hydro = bool(pypsa_debug_add_hydro)
self._pypsa_debug_add_nuc = bool(pypsa_debug_add_nuc)
self._pypsa_debug_add_solar = bool(pypsa_debug_add_solar)
self._pypsa_debug_add_wind = bool(pypsa_debug_add_wind)
self._pypsa_debug_add_slack = bool(pypsa_debug_add_slack)
self._pypsa_debug_flags = {"wind": self._pypsa_debug_add_wind,
"solar": self._pypsa_debug_add_solar,
"hydro": self._pypsa_debug_add_hydro,
"thermal": self._pypsa_debug_add_thermal,
"nuclear": self._pypsa_debug_add_nuc,
"slack": self._pypsa_debug_add_slack,
"infinite_gen": self._pypsa_debug_add_inf_gen,
"slack_maybe_ignored": ((not self._pypsa_debug_add_nuc) or
(not self._pypsa_debug_add_thermal) or
(not self._pypsa_debug_add_hydro) or
(not self._pypsa_debug_add_slack))}

@classmethod
def from_gri2op_env(cls, grid2op_env):
def _aux_add_solar(cls, net):
if net._pypsa_debug_add_solar and net._pmax_solar > 0.:
net.add('Generator',
name='agg_solar',
bus='only_bus',
carrier="solar",
marginal_cost=0.,
p_nom=net._pmax_solar
)
elif not net._pypsa_debug_add_solar and net._pmax_solar > 0.:
print(f"Solar generators are ignored in the OPF due to a pysa_debug flag")

@classmethod
def _aux_add_wind(cls, net):
if net._pypsa_debug_add_wind and net._pmax_wind > 0.:
net.add('Generator',
name='agg_wind',
bus='only_bus',
carrier="wind",
marginal_cost=0.1, # we prefer to curtail the wind if we have the choice
# that's because solar should be distributed on the grid
p_nom=net._pmax_wind
)
elif not net._pypsa_debug_add_wind and net._pmax_wind > 0.:
print(f"Solar generators are ignored in the OPF due to a pysa_debug flag")

@classmethod
def _aux_add_controlable_gen(cls, net, gen_type, carrier_types_to_exclude, p_max, name, ramp_up, ramp_down, gen_cost_per_MW):
net._all_gen_names.add(name)
if gen_type not in carrier_types_to_exclude:
pnom = p_max - cls.PmaxCorrectingFactor
if pnom <= 0.:
print(f"\t Issue for {name}: a negative pmax would be applied. We put {cls.PmaxCorrectingFactor} instead")
pnom = max(pnom, cls.PmaxCorrectingFactor)

rampUp = (ramp_up - cls.RampCorrectingFactor) / p_max
rampDown = (ramp_down - cls.RampCorrectingFactor) / p_max
if rampUp <= 0.:
print(f"\t Issue for {name}: a negative ramp_up would be applied. We put {cls.RampCorrectingFactor / p_max} instead")
rampUp = cls.RampCorrectingFactor / p_max
if rampDown <= 0.:
print(f"\t Issue for {name}: a negative ramp_down would be applied. We put {cls.RampCorrectingFactor / p_max} instead")
rampDown = cls.RampCorrectingFactor / p_max
if net._pypsa_debug_flags[gen_type]:
net.add(
class_name='Generator',
name=name,
bus='only_bus',
p_nom=pnom,
carrier=gen_type,
marginal_cost=gen_cost_per_MW,
ramp_limit_up=rampUp,
ramp_limit_down=rampDown,
)
else:
print(f"\t generator {name} (type {gen_type}) is ignored because of a pypsa_debug flag is set for its type")

@classmethod
def from_gri2op_env(cls,
grid2op_env,
*,
pypsa_debug_add_inf_gen=False,
pypsa_debug_add_thermal=True,
pypsa_debug_add_hydro=True,
pypsa_debug_add_nuc=True,
pypsa_debug_add_solar=True,
pypsa_debug_add_wind=True,
pypsa_debug_add_slack=True,):
"""
Implements the abstract method of *Dispatcher*

Expand All @@ -68,54 +175,49 @@ def from_gri2op_env(cls, grid2op_env):
-------
net: :class:`pypsa.Network`
"""
net = cls()
net = cls(pypsa_debug_add_inf_gen=pypsa_debug_add_inf_gen,
pypsa_debug_add_thermal=pypsa_debug_add_thermal,
pypsa_debug_add_hydro=pypsa_debug_add_hydro,
pypsa_debug_add_nuc=pypsa_debug_add_nuc,
pypsa_debug_add_solar=pypsa_debug_add_solar,
pypsa_debug_add_wind=pypsa_debug_add_wind,
pypsa_debug_add_slack=pypsa_debug_add_slack,
)
net._env = grid2op_env

carrier_types_to_exclude = ['wind', 'solar']
for i, generator in enumerate(grid2op_env.name_gen):
gen_type = grid2op_env.gen_type[i]
if gen_type not in carrier_types_to_exclude:
p_max = grid2op_env.gen_pmax[i]
pnom = p_max - cls.PmaxCorrectingFactor

rampUp = (grid2op_env.gen_max_ramp_up[i] - cls.RampCorrectingFactor) / p_max
rampDown = (grid2op_env.gen_max_ramp_down[i] - cls.RampCorrectingFactor) / p_max

net.add(
class_name='Generator', name=generator, bus='node',
p_nom=pnom, carrier=grid2op_env.gen_type[i],
marginal_cost=grid2op_env.gen_cost_per_MW[i],
ramp_limit_up=rampUp,
ramp_limit_down=rampDown,
)

# add total wind and solar (for curtailment)
net._pmax_solar = np.sum([grid2op_env.gen_pmax[i]
for i in range(grid2op_env.n_gen)
if grid2op_env.gen_type[i] == "solar"])
net.add('Generator',
name='agg_solar',
bus='node',
carrier="solar",
marginal_cost=0.,
p_nom=net._pmax_solar
)
net._pmax_solar = np.sum([env_cls.gen_pmax[i]
for i in range(env_cls.n_gen)
if env_cls.gen_type[i] == "solar"])
cls._aux_add_solar(net)

net._pmax_wind = np.sum([grid2op_env.gen_pmax[i]
for i in range(grid2op_env.n_gen)
if grid2op_env.gen_type[i] == "wind"])
net.add('Generator',
name='agg_wind',
bus='node',
carrier="wind",
marginal_cost=0.1, # we prefer to curtail the wind if we have the choice
# that's because solar should be distributed on the grid
p_nom=net._pmax_wind
)
for i in range(grid2op_env.n_gen)
if grid2op_env.gen_type[i] == "wind"])
cls._aux_add_wind(net)

carrier_types_to_exclude = ['wind', 'solar']
env_cls = type(grid2op_env)
for i, generator in enumerate(env_cls.name_gen):
gen_type = env_cls.gen_type[i]
p_max = env_cls.gen_pmax[i]
ramp_up = env_cls.gen_max_ramp_up[i]
ramp_down = env_cls.gen_max_ramp_down[i]
gen_cost_per_MW = env_cls.gen_cost_per_MW[i]
cls._aux_add_controlable_gen(net, gen_type, carrier_types_to_exclude, p_max, generator, ramp_up, ramp_down, gen_cost_per_MW)
return net

@classmethod
def from_dataframe(cls, env_df):
def from_dataframe(cls,
env_df,
*,
pypsa_debug_add_inf_gen=False,
pypsa_debug_add_thermal=True,
pypsa_debug_add_hydro=True,
pypsa_debug_add_nuc=True,
pypsa_debug_add_solar=True,
pypsa_debug_add_wind=True,
pypsa_debug_add_slack=True):
"""
Implements the abstract method of *Dispatcher*

Expand All @@ -130,60 +232,39 @@ def from_dataframe(cls, env_df):
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=FutureWarning)
# issue with pypsa on some version of numpy
net = cls()
net = cls(pypsa_debug_add_inf_gen=pypsa_debug_add_inf_gen,
pypsa_debug_add_thermal=pypsa_debug_add_thermal,
pypsa_debug_add_hydro=pypsa_debug_add_hydro,
pypsa_debug_add_nuc=pypsa_debug_add_nuc,
pypsa_debug_add_solar=pypsa_debug_add_solar,
pypsa_debug_add_wind=pypsa_debug_add_wind,
pypsa_debug_add_slack=pypsa_debug_add_slack,
)
net._df = env_df

carrier_types_to_exclude = ['wind', 'solar']

for i, (generator, gen_type, p_max, ramp_up, ramp_down, gen_cost_per_MW) in enumerate(zip(env_df['name'],
env_df['type'],
env_df['pmax'],
env_df['max_ramp_up'],
env_df['max_ramp_down'],
env_df['cost_per_mw'])):
if gen_type not in carrier_types_to_exclude:
pnom = (p_max - cls.PmaxCorrectingFactor)
rampUp = (ramp_up- cls.RampCorrectingFactor) / p_max
rampDown = (ramp_down - cls.RampCorrectingFactor) / p_max

net.add(
class_name='Generator',
name=generator,
bus='node',
p_nom=pnom,
carrier=gen_type,
marginal_cost=gen_cost_per_MW,
ramp_limit_up=rampUp,
ramp_limit_down=rampDown,
)


# add total wind and solar (for curtailment)
net._pmax_solar = np.sum([p_max
for i, (gen_type, p_max) in enumerate(zip(env_df['type'],
env_df['pmax']))
if gen_type == "solar"])
net.add('Generator',
name='agg_solar',
bus='node',
carrier="solar",
marginal_cost=0.,
p_nom=net._pmax_solar
)
cls._aux_add_solar(net)

net._pmax_wind = np.sum([p_max
for i, (gen_type, p_max) in enumerate(zip(env_df['type'],
env_df['pmax']))
if gen_type == "wind"])
net.add('Generator',
name='agg_wind',
bus='node',
carrier="wind",
marginal_cost=0.1, # we prefer to curtail the wind if we have the choice
# that's because solar should be distributed on the grid
p_nom=net._pmax_wind
)
return net
cls._aux_add_wind(net)

carrier_types_to_exclude = ['wind', 'solar']

for i, (generator, gen_type, p_max, ramp_up, ramp_down, gen_cost_per_MW) in enumerate(zip(env_df['name'],
env_df['type'],
env_df['pmax'],
env_df['max_ramp_up'],
env_df['max_ramp_down'],
env_df['cost_per_mw'])):
cls._aux_add_controlable_gen(net, gen_type, carrier_types_to_exclude, p_max, generator, ramp_up, ramp_down, gen_cost_per_MW)
return net

def run(self,
load,
Expand All @@ -207,6 +288,7 @@ def run(self,
total_solar = total_solar / self._pmax_solar
if total_wind is not None:
total_wind = total_wind / self._pmax_wind

prods_dispatch, terminal_conditions, marginal_prices = \
main_run_disptach(
self if not by_carrier else self.simplify_net(),
Expand Down
Loading