Ejemplo n.º 1
0
    def create_message_log(self, start: datetime, end: datetime,
                           resolution: timedelta, ems_agents):

        self.message_logs = dict()
        ems_names = [ems_agents[x].name for x, ems in enumerate(ems_agents)]
        ems_columns = ["DeviceMessage", "UdiEvent"]
        columns = [
            "Prognosis Request",
            "Prognosis",
            "FlexRequest",
            "FlexOffer",
            "FlexOrder",
        ]
        periods = int(((end - start)) / resolution)

        self.message_logs[next] = dict()
        self.message_logs[next]["TA"] = initialize_df(columns=columns,
                                                      start=start,
                                                      end=end,
                                                      resolution=resolution)
        self.message_logs[next]["MA"] = initialize_df(columns=columns,
                                                      start=start,
                                                      end=end,
                                                      resolution=resolution)
        self.message_logs[next]["EMS"] = {
            ems: initialize_df(columns=ems_columns,
                               start=start,
                               end=end,
                               resolution=resolution)
            for ems in ems_names
        }

        return
Ejemplo n.º 2
0
def single_curtailment_or_shift_each_day_between_12_and_14_pm(
        start: datetime, end: datetime, resolution: timedelta) -> DataFrame:
    """Each day it is valuable to curtail production between 2 and 3 am, or to shift consumption to that period."""
    imbalance_start_time = "12:00"
    imbalance_end_time = "14:00"
    imbalance_value = -2  # MW
    imbalance_price_between_2_and_3_am = 10  # EUR/MWh
    imbalance_price_otherwise = 5  # EUR/MWh
    df = initialize_df(
        columns=["Imbalance (in MW)", "Price (in EUR/MWh)"],
        start=start,
        end=end,
        resolution=resolution,
    )
    df["Imbalance (in MW)"] = 0
    df["Imbalance (in MW)"].iloc[df.index.indexer_between_time(
        start_time=imbalance_start_time,
        end_time=imbalance_end_time,
        include_end=False,
    )] = imbalance_value
    df["Price (in EUR/MWh)"] = imbalance_price_otherwise
    df["Price (in EUR/MWh)"].iloc[df.index.indexer_between_time(
        start_time=imbalance_start_time,
        end_time=imbalance_end_time,
        include_end=False,
    )] = imbalance_price_between_2_and_3_am
    return df
Ejemplo n.º 3
0
    def __init__(
        self,
        name,
        environment,
        flex_trade_horizon: timedelta,
        balancing_opportunities: DataFrame,
        # deviation_prices: Union[Tuple[int], int],
        # prognosis_policy: Callable,
        prognosis_parameter: dict,
        # flexrequest_policy: Callable,
        flexrequest_parameter: dict,
        # sticking_factor: float,
        # deviation_multiplicator: float,
        # imbalance_market_costs: Series,
    ):
        """ Creates an instance of the Class MarketAgent. Inherits from the Class Agent """
        super().__init__(name, environment)

        commitment_data_columns = [
            "Imbalances",
            "Imbalance market price",
            "Imbalance market costs",
            "Prognosis values",
            "Prognosis costs",
            "Requested flexibility",
            "Realised flexibility",
            "Commited flexibility",
            "Deviated flexibility",
            "Flexibility costs",
            "Deviation prices",
            "Deviation revenues",
            "Remaining imbalances",
            "Remaining market costs",
            "Opportunity costs",
        ]
        self.commitment_data = initialize_df(
            commitment_data_columns, environment.start, environment.end, environment.resolution
        )

        self.commitment_data.loc[:, "Deviation prices"] = flexrequest_parameter["Deviation prices"]
        self.commitment_data.loc[:, "Imbalances"] = balancing_opportunities.loc[:, "Imbalance (in MW)"]
        self.commitment_data.loc[:, "Imbalance market price"] = balancing_opportunities.loc[:, "Price (in EUR/MWh)"]
        self.commitment_data.loc[:, "Imbalance market costs"] = self.commitment_data.loc[:, "Imbalances"] \
                                                                * self.commitment_data.loc[:, "Imbalance market price"]
        # self.commitment_data.loc[:, "Received flexibility"] = 0

        self.flex_trade_horizon = flex_trade_horizon

        # self.balancing_opportunities = balancing_opportunities
        # self.deviation_prices = deviation_prices
        # self.deviation_prices_realised = [] #
        # self.sticking_factor = sticking_factor
        # self.prognosis_policy = prognosis_policy
        self.prognosis_parameter = prognosis_parameter
        # self.flexrequest_policy = flexrequest_policy
        self.flexrequest_parameter = flexrequest_parameter
Ejemplo n.º 4
0
def none_ever(start: datetime, end: datetime,
              resolution: timedelta) -> DataFrame:
    """No balancing opportunities ever."""

    return initialize_df(
        columns=["Imbalance (in MW)", "Price (in EUR/MWh)"],
        start=start,
        end=end,
        resolution=resolution,
    )
Ejemplo n.º 5
0
def generated_imbalance_profile(
    start: datetime,
    end: datetime,
    resolution: timedelta,
    imbalance_range: Tuple,
    imbalance_price_1: float,
    imbalance_price_2: float,
    frequency: float,
    window_size: Tuple,
    imbalance_profile: Series = None,
    imbalance_prices: Series = None,
) -> DataFrame:
    """Generate imbalances for a given timeperiod."""

    df = initialize_df(
        columns=["Imbalance (in MW)", "Price (in EUR/MWh)"],
        start=start,
        end=end,
        resolution=resolution,
    )

    if imbalance_profile is None:
        dummy_index = DatetimeIndex(start=start, end=end, freq=resolution)
        num_samples = int(len(dummy_index) * frequency)
        windows_data = uniform(size=len(dummy_index))
        windows = Series(data=windows_data, index=dummy_index)
        samples_df = Series(index=dummy_index)
        samples = [
            windows.iloc[x:x + randint(window_size[0], window_size[1])] *
            choice([-1, 1]) for x in randint(len(windows), size=num_samples)
        ]

        for sample in samples:
            samples_df.loc[sample.index[0]:sample.index[-1]] = sample

        samples_df[
            samples_df < 0] = samples_df[samples_df < 0] * imbalance_range[0]
        samples_df[
            samples_df > 0] = samples_df[samples_df > 0] * imbalance_range[1]
        imbalance_profile = samples_df

        df["Imbalance (in MW)"] = 0
        df["Imbalance (in MW)"].loc[start:end] = imbalance_profile.loc[
            start:end]
        df["Price (in EUR/MWh)"] = where(df["Imbalance (in MW)"] == NaN,
                                         imbalance_price_1, imbalance_price_2)
    else:
        df["Imbalance (in MW)"].loc[start:end] = imbalance_profile
        df["Price (in EUR/MWh)"] = imbalance_prices
        # print("balance")
        # print(df)

    return df
Ejemplo n.º 6
0
def completely_unconstrained_profile(start: datetime, end: datetime,
                                     resolution: timedelta) -> DataFrame:
    """Can be used as a base model."""
    return initialize_df(
        columns=[
            "equals",
            "max",
            "min",
            "derivative equals",
            "derivative max",
            "derivative min",
        ],
        start=start,
        end=end,
        resolution=resolution,
    )
Ejemplo n.º 7
0
def single_curtailment_each_day_between_2_and_3_am(
        start: datetime, end: datetime, resolution: timedelta) -> DataFrame:
    """Each day it is valuable to curtail production between 2 and 3 am."""
    opportunity_start_time = "2:00"
    opportunity_end_time = "3:00"
    imbalance_value = 100  # MW
    imbalance_price_between_2_and_3_am = 10  # EUR/MWh
    df = initialize_df(
        columns=["Imbalance (in MW)", "Price (in EUR/MWh)"],
        start=start,
        end=end,
        resolution=resolution,
    )
    df["Imbalance (in MW)"].iloc[df.index.indexer_between_time(
        start_time=opportunity_start_time,
        end_time=opportunity_end_time,
        include_end=False,
    )] = imbalance_value
    df["Price (in EUR/MWh)"].iloc[df.index.indexer_between_time(
        start_time=opportunity_start_time,
        end_time=opportunity_end_time,
        include_end=False,
    )] = imbalance_price_between_2_and_3_am
    return df
Ejemplo n.º 8
0
    def post_flex_request(self, prognosis: Prognosis) -> FlexRequest:
        """Callback function to let the Market Agent create a FlexRequest based on a Prognosis and post it to the
        Trading Agent."""

        # Todo: rationalise the following
        # We assume that the balancing opportunities, which are put into the simulation as an exogenous variable,
        # already take into account the prognosis.

        # Get the Market Agent's unfulfilled balancing opportunities with positive value, which will become the
        # requested_power,
        flex_trade_window = (
            self.environment.now,
            self.environment.now + self.flex_trade_horizon,
        )

        requested_values = initialize_df(
            columns=["requested_power", "requested_flex", "requested_flex_imbalance_market_costs"],
            start=flex_trade_window[0],
            end=flex_trade_window[1],
            resolution=self.environment.resolution,
        )

        if uniform(0, 0.99) >= self.flexrequest_parameter["Sticking factor"]:

            print("\n----------------MA: POST FLEX REQUEST---------------------")

            original_commitment_opportunities = self.commitment_data.loc[
                flex_trade_window[0] : flex_trade_window[1]
                - self.environment.resolution,
                "Imbalances",
            ].values

            already_bought_commitment = around(self.commitment_data.loc[
                flex_trade_window[0] : flex_trade_window[1]
                - self.environment.resolution,
                "Commited flexibility",
            ].values.astype("float64"),3)

            remaining_commitment_opportunities = [
                opportunity - commitment if not isna(commitment) else opportunity
                for opportunity, commitment in zip(
                    original_commitment_opportunities, already_bought_commitment
                )
            ]
            remaining_commitment_opportunities = [
                nan if opportunity == 0 else opportunity
                for opportunity in remaining_commitment_opportunities
            ]

            requested_values["Requested flexibility"] = remaining_commitment_opportunities

            requested_values["Requested power"] = prognosis.commitment.constants.loc[
                                                        flex_trade_window[0] : flex_trade_window[1]- self.environment.resolution] \
                                                            + remaining_commitment_opportunities

            # Get market costs for flexibility for the acutal horizon
            requested_values["Requested costs"] = self.commitment_data.loc[
                                                            flex_trade_window[0] : flex_trade_window[1] - self.environment.resolution,
                                                            "Imbalance market costs"]

            # Only add market costs to reservation price for timesteps where requested flexibility is not nan
            self.flexrequest_parameter["Reservation price"] = 0
            for enum, val in enumerate(requested_values["Requested flexibility"]):
                if val != nan:
                    self.flexrequest_parameter["Reservation price"] += requested_values["Requested costs"].iloc[enum]

            print("\n MA: Reservation price: {}".format(self.flexrequest_parameter["Reservation price"]))
            print("\nMA: Already bought commitment: {}".format(already_bought_commitment))
            print("MA: Remaining commitment opportunities: {}".format(remaining_commitment_opportunities))
            print("\nMA: Requested Power values: {}".format(requested_values["Requested power"]))
            print("\nMA: Requested Flex values: {}\n".format(requested_values["Requested flexibility"]))
            print("\nMA: Requested Cost values: {}\n".format(requested_values["Requested costs"]))

        else: # TODO: Fix sticking
            requested_values["Requested power"] = prognosis.commitment.constants

            requested_values["Requested flexibility"] = nan

            print("----------------MA: STICKING ---------------------\n")
            print("MA: Request sticking to prognosis values: {}\n".format(prognosis.commitment.constants))

        # Store requested flexibility in MA commitment data
        for val, index in zip(requested_values["Requested flexibility"], requested_values["Requested power"].index):
            if not isnull(val):
                self.commitment_data.loc[index, "Requested flexibility"] = val

        return FlexRequest(
            id=self.environment.plan_board.get_message_id(),
            requested_values=round(requested_values["Requested power"],2),
            requested_flexibility=round(requested_values["Requested flexibility"],2),
            costs=round(self.flexrequest_parameter["Reservation price"] - self.flexrequest_parameter["Markup"] ,2),
            deviation_cost_curve=DeviationCostCurve(
                gradient=(
                    self.commitment_data.loc[self.environment.now, "Deviation prices"] * -1,
                    self.commitment_data.loc[self.environment.now, "Deviation prices"],
                ),
                flow_unit_multiplier=self.environment.flow_unit_multiplier,
            ),
            prognosis=prognosis,
        )
Ejemplo n.º 9
0
    def __init__(
        self,
        name,
        market_agent: MarketAgent,
        ems_agents: List[EMS],
        environment,
        flex_trade_horizon: timedelta,
        reprognosis_period: timedelta,
        # prognosis_policy: Callable,
        prognosis_parameter: dict,
        # prognosis_rounds: int,
        # prognosis_learning_parameter: dict,
        # flexrequest_policy: Callable,
        flexrequest_parameter: dict,
        # flexrequest_rounds: int,
        # flexrequest_learning_parameter: dict,
        central_optimization: bool = False,
    ):
        """ Creates an instance of the Class TradingAgent. Inherits from the Class Agent."""
        super().__init__(name, environment)

        self.market_agent = market_agent
        self.ems_agents = ems_agents

        columns_aggregated_ems_data = [
                  "Activated EMS", "Requested power", "Requested flexibility", \
                  "Prog power", "Plan power", "Realised power", "Deviated power",\
                  "Prog flexibility", "Plan flexibility", "Realised flexibility", "Deviated flexibility", \
                  ]

        self.ems_data = initialize_df(columns_aggregated_ems_data,
                                      environment.start, environment.end,
                                      environment.resolution)

        columns_commitment_data = [
            "Requested power",
            "Requested flexibility",
            "Commited power",
            "Realised power",
            "Deviated power",
            "Commited flexibility",
            "Realised flexibility",
            "Deviated flexibility",
            "Realised commitment costs",
            "Realised profits",
            "Average purchase price",
            "Average feedin price",
            "Deviation price up",
            "Deviation price down",
            "Opportunity costs",
            "Clearing price prognosis negotiations 1",
            "Clearing price flex negotiations 1",
            "Clearing price flex negotiations 2",
        ]

        self.commitment_data = initialize_df(columns_commitment_data,
                                             environment.start,
                                             environment.end,
                                             environment.resolution)

        self.commitment_data["Opportunity costs"] = 0

        # self.cleared_prognosis_negotiations = DataFrame(
        #     index=initialize_index(
        #         environment.start, environment.end, environment.resolution
        #     ),
        #     columns=["Cleared", "Clearing Price"],
        # )
        #
        # self.cleared_flex_negotiations = DataFrame(
        #     index=initialize_index(
        #         environment.start, environment.end, environment.resolution
        #     ),
        #     columns=["Cleared", "Clearing Price"],
        # )
        #
        # self.realised_power = initialize_series(
        #     None, environment.start, environment.end, environment.resolution
        # )
        # self.sold_flex = initialize_series(
        #     None, environment.start, environment.end, environment.resolution
        # )
        # self.flex_revenues = initialize_series(
        #     None, environment.start, environment.end, environment.resolution
        # )
        # self.opportunity_costs = initialize_series(
        #     0, environment.start, environment.end, environment.resolution
        # )
        # self.prognosis = initialize_series(
        #     None, environment.start, environment.end, environment.resolution
        # )

        self.flex_trade_horizon = flex_trade_horizon
        self.reprognosis_period = reprognosis_period
        self.central_optimization = central_optimization
        # self.flexrequest_parameter["Negotiation rounds"] = flexrequest_rounds

        # Prognosis negotiation inputs
        # self.prognosis_policy = prognosis_policy
        self.prognosis_parameter = prognosis_parameter
        # self.prognosis_q_parameter = prognosis_learning_parameter
        # self.prognosis_q_table_df_1 = DataFrame(
        #     data=0,
        #     index=range(1, prognosis_rounds + 1),
        #     columns=self.prognosis_q_parameter["Action function"](
        #         action=None, markup=None, show_actions=True
        #     ).keys(),
        # )
        # self.prognosis_q_table_df_2 = DataFrame(
        #     data=0,
        #     index=range(1, prognosis_rounds + 1),
        #     columns=self.prognosis_q_parameter["Action function"](
        #         action=None, markup=None, show_actions=True
        #     ).keys(),
        # )
        # self.prognosis_q_table_df_1.index.name = "Rounds"
        # self.prognosis_q_table_df_2.index.name = "Rounds"
        # self.prognosis_action_table_df_1 = deepcopy(self.prognosis_q_table_df_1)
        # self.prognosis_action_table_df_2 = deepcopy(self.prognosis_q_table_df_1)
        #
        # # Flexrequest negotiation inputs
        # self.flexrequest_policy = flexrequest_policy
        self.flexrequest_parameter = flexrequest_parameter