-
Notifications
You must be signed in to change notification settings - Fork 10
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
507 add cross border flows to scenarios #522
base: main
Are you sure you want to change the base?
Changes from all commits
9125d19
5ee4dbb
d42a062
17e8833
c712bb2
9218e09
80c7b2b
b95595e
acd1023
c028d91
cff2eb5
ca260d2
7493466
d522062
d0cef67
4c54fe6
4b4cb7c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
# SPDX-FileCopyrightText: ASSUME Developers | ||
# | ||
# SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
from datetime import datetime | ||
|
||
import numpy as np | ||
|
||
from assume.common.base import SupportsMinMax | ||
from assume.common.fast_pandas import FastSeries | ||
from assume.common.forecasts import Forecaster | ||
|
||
|
||
class Exchanges(SupportsMinMax): | ||
""" | ||
An exchanges unit represents a unit that can import or export energy. | ||
|
||
Attributes: | ||
id (str): The unique identifier of the unit. | ||
unit_operator (str): The operator of the unit. | ||
direction (str): The exchange-direction ("import" or "export") of the unit. | ||
bidding_strategies (dict): The bidding strategies of the unit. | ||
max_power (float): The max. power value of the unit in MW. | ||
min_power (float): The min. power value of the unit in MW. | ||
node (str, optional): The node of the unit. Defaults to "node0". | ||
price (float): The price of the unit. | ||
location (tuple[float, float], optional): The location of the unit. Defaults to (0.0, 0.0). | ||
|
||
Methods | ||
------- | ||
""" | ||
|
||
def __init__( | ||
self, | ||
id: str, | ||
unit_operator: str, | ||
technology: str, | ||
direction: str, | ||
bidding_strategies: dict, | ||
max_power: float, | ||
min_power: float, | ||
forecaster: Forecaster, | ||
node: str = "node0", | ||
price: float = 3000.0, | ||
location: tuple[float, float] = (0.0, 0.0), | ||
**kwargs, | ||
): | ||
super().__init__( | ||
id=id, | ||
unit_operator=unit_operator, | ||
technology=technology, | ||
bidding_strategies=bidding_strategies, | ||
forecaster=forecaster, | ||
node=node, | ||
location=location, | ||
**kwargs, | ||
) | ||
"""Create an exchanges unit.""" | ||
self.max_power = max_power | ||
self.min_power = min_power | ||
|
||
self.direction = direction | ||
|
||
if direction == "import": | ||
self.volume = abs(self.forecaster[self.id]) # import is positive | ||
elif direction == "export": | ||
self.volume = -abs(self.forecaster[self.id]) # export is negative | ||
|
||
self.price = FastSeries(index=self.index, value=price) | ||
|
||
def execute_current_dispatch( | ||
self, | ||
start: datetime, | ||
end: datetime, | ||
) -> np.array: | ||
""" | ||
Execute the current dispatch of the unit. | ||
Returns the volume of the unit within the given time range. | ||
|
||
Args: | ||
start (datetime.datetime): The start time of the dispatch. | ||
end (datetime.datetime): The end time of the dispatch. | ||
|
||
Returns: | ||
np.array: The volume of the unit for the given time range. | ||
""" | ||
|
||
return self.volume.loc[start:end] | ||
|
||
def calculate_min_max_power( | ||
self, start: datetime, end: datetime, product_type="energy" | ||
) -> tuple[np.array, np.array]: | ||
""" | ||
Calculates the minimum and maximum power output of the unit and returns the bid volume as both the minimum and maximum power output of the unit. | ||
|
||
Args: | ||
start (pandas.Timestamp): The start time of the dispatch. | ||
end (pandas.Timestamp): The end time of the dispatch. | ||
|
||
Returns: | ||
tuple[pandas.Series, pandas.Series]: The bid colume as both the minimum and maximum power output of the unit. | ||
""" | ||
|
||
# end includes the end of the last product, to get the last products' start time we deduct the frequency once | ||
end_excl = end - self.index.freq | ||
bid_volume = ( | ||
self.volume.loc[start:end_excl] | ||
- self.outputs[product_type].loc[start:end_excl] | ||
) | ||
|
||
return bid_volume, bid_volume | ||
|
||
def calculate_marginal_cost(self, start: datetime, power: float) -> float: | ||
""" | ||
Calculate the marginal cost of the unit returns the marginal cost of the unit based on the provided time and power. | ||
|
||
Args: | ||
start (pandas.Timestamp): The start time of the dispatch. | ||
power (float): The power output of the unit. | ||
|
||
Returns: | ||
float: the marginal cost of the unit for the given power. | ||
""" | ||
return self.price.at[start] | ||
|
||
def as_dict(self) -> dict: | ||
""" | ||
Returns the unit as a dictionary. | ||
|
||
Returns: | ||
dict: The unit as a dictionary. | ||
""" | ||
unit_dict = super().as_dict() | ||
unit_dict.update( | ||
{ | ||
"max_power": self.max_power, | ||
"min_power": self.min_power, | ||
"unit_type": "demand" if self.direction == "export" else "power_plant", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't that more like a new unit_type "prosumer", if we would have it merged as one? Otherwise we can model them separate as demand and powerplant as of now..? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is done this way so we can also see them in the database without changing anything there |
||
} | ||
) | ||
|
||
return unit_dict | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we have to set a direction here?
Wouldn't it be better to have one exchange unit which bids in both directions instead?