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

Pandas wrapper #483

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
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
69 changes: 40 additions & 29 deletions assume/common/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@
import numpy as np
import pandas as pd

from assume.common.fast_pandas import FastSeries, TensorFastSeries
from assume.common.forecasts import Forecaster
from assume.common.market_objects import MarketConfig, Orderbook, Product

try:
import torch as th
except ImportError:
th = None


class BaseStrategy:
pass
Expand Down Expand Up @@ -68,7 +64,7 @@ def __init__(
self.location = location
self.bidding_strategies: dict[str, BaseStrategy] = bidding_strategies
self.index = index
self.outputs = defaultdict(lambda: pd.Series(0.0, index=self.index))
self.outputs = defaultdict(lambda: FastSeries(value=0.0, index=self.index))
# series does not like to convert from tensor to float otherwise

# RL data stored as lists to simplify storing to the buffer
Expand All @@ -77,16 +73,19 @@ def __init__(
self.outputs["rl_rewards"] = []

# some data is stored as series to allow to store it in the outputs
self.outputs["actions"] = pd.Series(0.0, index=self.index, dtype=object)
self.outputs["exploration_noise"] = pd.Series(
0.0, index=self.index, dtype=object
self.outputs["actions"] = TensorFastSeries(value=0.0, index=self.index)
self.outputs["exploration_noise"] = TensorFastSeries(
value=0.0,
index=self.index,
)
self.outputs["reward"] = pd.Series(0.0, index=self.index, dtype=object)
self.outputs["reward"] = FastSeries(value=0.0, index=self.index)

if forecaster:
self.forecaster = forecaster
else:
self.forecaster = defaultdict(lambda: pd.Series(0.0, index=self.index))
self.forecaster = defaultdict(
lambda: FastSeries(value=0.0, index=self.index)
)

def calculate_bids(
self,
Expand Down Expand Up @@ -192,12 +191,17 @@ def calculate_generation_cost(
start = self.index[0]

product_type_mc = product_type + "_marginal_costs"
product_data = self.outputs[product_type].loc[start:end]
# Adjusted code for accessing product data and mapping over the index
product_data = self.outputs[product_type][
start:end
] # Slicing directly without `.loc`

marginal_costs = product_data.index.map(
lambda t: self.calculate_marginal_cost(t, product_data.loc[t])
)
new_values = np.abs(marginal_costs * product_data.values)
marginal_costs = [
self.calculate_marginal_cost(t, product_data[idx])
for idx, t in enumerate(self.index[start:end])
]

new_values = np.abs(marginal_costs * product_data)
self.outputs[product_type_mc].loc[start:end] = new_values

def execute_current_dispatch(
Expand Down Expand Up @@ -355,7 +359,6 @@ def calculate_ramp(
Returns:
float: The corrected possible power to offer according to ramping restrictions.
"""

# was off before, but should be on now and min_down_time is not reached
if power > 0 and op_time < 0 and op_time > -self.min_down_time:
power = 0
Expand Down Expand Up @@ -402,27 +405,35 @@ def get_operation_time(self, start: datetime) -> int:
Returns the time the unit is operating (positive) or shut down (negative).

Args:
start (datetime.datetime): The start time.
start (datetime): The start time.

Returns:
int: The operation time.
int: The operation time as a positive integer if operating, or negative if shut down.
"""
before = start - self.index.freq
# Set the time window based on max of min operating/down time
max_time = max(self.min_operating_time, self.min_down_time, 1)
begin = max(start - self.index.freq * max_time, self.index[0])
end = start - self.index.freq

max_time = max(self.min_operating_time, self.min_down_time)
begin = start - self.index.freq * max_time
end = before
arr = self.outputs["energy"][begin:end][::-1] > 0
if len(arr) < 1:
if start <= self.index[0]:
# before start of index
return max_time
is_off = not arr.iloc[0]

# Check energy output in the defined time window, reversed for most recent state
arr = (self.outputs["energy"][begin:end] > 0)[::-1]

# Determine initial state (off if the first period shows zero energy output)
is_off = not arr[0]
run = 0

# Count consecutive periods with the same status, break on change
for val in arr:
if val == is_off:
if val != (not is_off): # Stop if the state changes
break
run += 1
return (-1) ** is_off * run

# Return positive time if operating, negative if shut down
return -run if is_off else run

def get_average_operation_times(self, start: datetime) -> tuple[float, float]:
"""
Expand All @@ -447,7 +458,7 @@ def get_average_operation_times(self, start: datetime) -> tuple[float, float]:
return max(self.min_operating_time, 1), min(-self.min_down_time, -1)

op_series = []
status = arr.iloc[0]
status = arr[0]
run = 0
for val in arr:
if val == status:
Expand Down
Loading
Loading