Skip to content

Commit 0246148

Browse files
authored
Merge pull request #80 from tum-ewk/develop
Bugfixes and minor improvements
2 parents 57411e7 + 0ce9e6e commit 0246148

8 files changed

Lines changed: 91 additions & 81 deletions

File tree

4.4 KB
Binary file not shown.

02_config/example_single_market/config_agents.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ sfh:
482482
# unit: º [0, 90]
483483
# note: if file is not set to "specs", this parameter is ignored
484484

485-
controllable: [true , true] # can the pv inverter be controlled? true or false
485+
controllable: [false, false] # can the pv inverter be controlled? true or false
486486

487487
fcast:
488488
method: naive # forecasting method
@@ -587,7 +587,7 @@ sfh:
587587
# - "file name": give specific file name (e.g. "wind_1_pu.csv")
588588
# note: csv files are for timeseries and json files are for specifications
589589

590-
controllable: [true, true] # can the wind inverter be controlled? true or false
590+
controllable: [false, false] # can the wind inverter be controlled? true or false
591591

592592
fcast:
593593
method: weather # forecasting method

02_config/templates/config_agents.yaml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ sfh:
482482
# unit: º [0, 90]
483483
# note: if file is not set to "specs", this parameter is ignored
484484

485-
controllable: [true , true] # can the pv inverter be controlled? true or false
485+
controllable: [false, false] # can the pv inverter be controlled? true or false
486486

487487
fcast:
488488
method: naive # forecasting method
@@ -587,7 +587,7 @@ sfh:
587587
# - "file name": give specific file name (e.g. "wind_1_pu.csv")
588588
# note: csv files are for timeseries and json files are for specifications
589589

590-
controllable: [true, true] # can the wind inverter be controlled? true or false
590+
controllable: [false, false] # can the wind inverter be controlled? true or false
591591

592592
fcast:
593593
method: weather # forecasting method
@@ -1667,7 +1667,7 @@ mfh:
16671667
# unit: º [0, 90]
16681668
# note: if file is not set to "specs", this parameter is ignored
16691669

1670-
controllable: [true , true] # can the pv inverter be controlled? true or false
1670+
controllable: [false, false] # can the pv inverter be controlled? true or false
16711671

16721672
fcast:
16731673
method: average # forecasting method
@@ -1782,7 +1782,7 @@ mfh:
17821782
# - "file name": give specific file name (e.g. "wind_1_pu.csv")
17831783
# note: csv files are for timeseries and json files are for specifications
17841784

1785-
controllable: [true, true] # can the wind inverter be controlled? true or false
1785+
controllable: [false, false] # can the wind inverter be controlled? true or false
17861786

17871787
fcast:
17881788
method: average # forecasting method
@@ -2538,7 +2538,7 @@ ctsp:
25382538
# unit: º [0, 90]
25392539
# note: if file is not set to "specs", this parameter is ignored
25402540

2541-
controllable: [true , true] # can the pv inverter be controlled? true or false
2541+
controllable: [false, false] # can the pv inverter be controlled? true or false
25422542

25432543
fcast:
25442544
method: average # forecasting method
@@ -2635,7 +2635,7 @@ ctsp:
26352635
# - "file name": give specific file name (e.g. "wind_1_pu.csv")
26362636
# note: csv files are for timeseries and json files are for specifications
26372637

2638-
controllable: [true, true] # can the wind inverter be controlled? true or false
2638+
controllable: [false, false] # can the wind inverter be controlled? true or false
26392639

26402640
fcast:
26412641
method: average # forecasting method
@@ -2729,7 +2729,7 @@ ctsp:
27292729
# output file (.csv)
27302730
# - "file name": give specific file name (e.g. "fixed_gen_1_pu.csv")
27312731

2732-
controllable: [true, true] # can the generator be controlled? (can power be decreased?)
2732+
controllable: [false, false] # can the generator be controlled? (can power be decreased?)
27332733

27342734
fcast:
27352735
method: average # forecasting method
@@ -3279,7 +3279,7 @@ industry:
32793279
# unit: º [0, 90]
32803280
# note: if file is not set to "specs", this parameter is ignored
32813281

3282-
controllable: [true , true] # can the pv inverter be controlled? true or false
3282+
controllable: [false, false] # can the pv inverter be controlled? true or false
32833283

32843284
fcast:
32853285
method: average # forecasting method
@@ -3377,7 +3377,7 @@ industry:
33773377
# - "file name": give specific file name (e.g. "wind_1_pu.csv")
33783378
# note: csv files are for timeseries and json files are for specifications
33793379

3380-
controllable: [true, true] # can the wind inverter be controlled? true or false
3380+
controllable: [false, false] # can the wind inverter be controlled? true or false
33813381

33823382
fcast:
33833383
method: average # forecasting method
@@ -3472,7 +3472,7 @@ industry:
34723472
# output file (.csv)
34733473
# - "file name": give specific file name (e.g. "fixed_gen_1_pu.csv")
34743474

3475-
controllable: [true, true] # can the generator be controlled? (can power be decreased?)
3475+
controllable: [false, false] # can the generator be controlled? (can power be decreased?)
34763476

34773477
fcast:
34783478
method: average # forecasting method
@@ -3819,7 +3819,7 @@ producer:
38193819
# note: if file is not set to "specs", this parameter is ignored
38203820

38213821

3822-
controllable: [true , true, true] # can the pv inverter be controlled? true or false
3822+
controllable: [false, false, false] # can the pv inverter be controlled? true or false
38233823

38243824
fcast:
38253825
method: average # forecasting method
@@ -4060,7 +4060,7 @@ producer:
40604060
# - "file name": give specific file name (e.g. "wind_1_pu.csv")
40614061
# note: csv files are for timeseries and json files are for specifications
40624062

4063-
controllable: [true, true] # can the wind inverter be controlled? true or false
4063+
controllable: [false, false] # can the wind inverter be controlled? true or false
40644064

40654065
fcast:
40664066
method: average # forecasting method
@@ -4297,7 +4297,7 @@ producer:
42974297
# output file (.csv)
42984298
# - "file name": give specific file name (e.g. "fixed_gen_1_pu.csv")
42994299

4300-
controllable: [true, true] # can the generator be controlled? (can power be decreased?)
4300+
controllable: [false, false] # can the generator be controlled? (can power be decreased?)
43014301

43024302
fcast:
43034303
method: average # forecasting method

hamlet/executor/markets/lem/lem.py

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
# Definition of temporary column names
2424
C_ENERGY_CUMSUM = 'energy_cumsum'
25+
AGENT_ID = '1q5Nid2Bwz2WzxG'
2526

2627

2728
class Lem(MarketBase):
@@ -53,6 +54,13 @@ def __init__(self, market: MarketDB, tasks: dict, database: Database):
5354
self.offers_uncleared = self.market.offers_uncleared.clear()
5455
self.transactions = self.market.market_transactions.clear()
5556

57+
# Get the previous transactions for the given timestep when the action is 'settle'
58+
if c.MA_SETTLE in self.tasks[c.TC_ACTIONS]:
59+
self.transactions_prev = (self.market.market_transactions
60+
.filter(pl.col(c.TC_TIMESTEP) == self.tasks[c.TC_TIMESTEP]))
61+
else:
62+
self.transactions_prev = None
63+
5664
# Get the retailer offers
5765
self.retailer = self.market.retailer.filter(pl.col(c.TC_TIMESTAMP) == self.tasks[c.TC_TIMESTEP])
5866

@@ -142,7 +150,9 @@ def __action_clear(self, clearing_type, clearing_method, pricing_method, couplin
142150
self.__create_market_tables(bids, offers, trades_cleared, trades_uncleared, retailer))
143151

144152
# Update the tables and market database
145-
self.__update_database(bids_cleared, offers_cleared, bids_uncleared, offers_uncleared, transactions)
153+
self.__update_database(bids_cleared=bids_cleared, offers_cleared=offers_cleared,
154+
bids_uncleared=bids_uncleared, offers_uncleared=offers_uncleared,
155+
transactions=transactions)
146156

147157
# Print statements to check results
148158
# with pl.Config(set_tbl_width_chars=400, set_tbl_cols=25, set_tbl_rows=20):
@@ -435,18 +445,17 @@ def __action_settle(self, clearing_type, clearing_method, pricing_method, coupli
435445
# Create the bids and offers table from the bids and offers of the agents and the retailers
436446
bids_offers, _ = self.__create_bids_offers(include_retailer=False)
437447
# Split the bids and offers into separate bids and offers tables
438-
self.bids_uncleared, self.offers_uncleared = self.__split_bids_offers(bids_offers, add_cumsum=False)
439-
448+
bids_uncleared, offers_uncleared = self.__split_bids_offers(bids_offers, add_cumsum=False)
440449

441450
# Determine balancing energy
442-
self.transactions, self.bids_uncleared, self.offers_uncleared = self.__determine_balancing_energy()
451+
balancing, _, _ = self.__determine_balancing_energy(bids_uncleared, offers_uncleared)
443452

444453
# Apply levies and taxes
445-
self.transactions = self.__apply_levies_taxes()
454+
grid_levies = self.__apply_grid_levies(balancing)
446455

447-
# Update the market database
448-
self.__update_database(transactions=self.transactions, bids_uncleared=self.bids_uncleared,
449-
offers_uncleared=self.offers_uncleared)
456+
# Update the tables and market database
457+
transactions = pl.concat([grid_levies, balancing], how='diagonal')
458+
self.__update_database(transactions=transactions)
450459

451460
return self.transactions
452461

@@ -457,13 +466,10 @@ def __couple_markets(self, clearing_type, clearing_method, pricing_method, coupl
457466
# Executed with the unsettled bids and offers, if any exist and coupling method to be done
458467
...
459468

460-
def __determine_balancing_energy(self):
469+
def __determine_balancing_energy(self, bids_uncleared, offers_uncleared):
461470
"""Determines the balancing energy"""
462471
# TODO: For now this ignores that there is a maximum amount of energy that can be bought/sold by the retailer
463472
# which needs to be implemented
464-
# Get the uncleared bids and offers
465-
bids_uncleared = self.bids_uncleared
466-
offers_uncleared = self.offers_uncleared
467473

468474
# Get the retailer offers
469475
# Note: This currently only works for one retailer
@@ -494,49 +500,22 @@ def __determine_balancing_energy(self):
494500
pl.lit(0).alias(c.TC_QUALITY).cast(pl.UInt8), # TODO: Take out once quality is included in the table
495501
])
496502
# Calculate the total price
497-
# TODO: Check mpc and rtc to see why this needs to be in there.
498-
flag = False
499-
try:
500-
transactions = transactions.with_columns([
501-
(pl.col(c.TC_PRICE_PU_IN) * pl.col(c.TC_ENERGY_IN)).round().alias(c.TC_PRICE_IN).cast(pl.Int64),
502-
(pl.col(c.TC_PRICE_PU_OUT) * pl.col(c.TC_ENERGY_OUT)).round().alias(c.TC_PRICE_OUT).cast(pl.Int64),
503-
])
504-
except Exception:
505-
# Set maximum amount of energy to 1e6 for both in and out
506-
transactions = transactions.with_columns([
507-
(pl.when(pl.col(c.TC_ENERGY_IN) > 1e6).then(1e6).otherwise(pl.col(c.TC_ENERGY_IN)).alias(c.TC_ENERGY_IN).cast(pl.UInt64)),
508-
(pl.when(pl.col(c.TC_ENERGY_OUT) > 1e6).then(1e6).otherwise(pl.col(c.TC_ENERGY_OUT)).alias(c.TC_ENERGY_OUT).cast(pl.UInt64)),
509-
])
510-
transactions = transactions.with_columns([
511-
(pl.col(c.TC_PRICE_PU_IN) * pl.col(c.TC_ENERGY_IN)).round().alias(c.TC_PRICE_IN).cast(pl.Int64),
512-
(pl.col(c.TC_PRICE_PU_OUT) * pl.col(c.TC_ENERGY_OUT)).round().alias(c.TC_PRICE_OUT).cast(pl.Int64),
513-
])
514-
print('Energy had to be limited to 1e6. Something needs to be checked. Start with the clearing results.')
515-
flag = True
503+
transactions = transactions.with_columns([
504+
(pl.col(c.TC_PRICE_PU_IN) * pl.col(c.TC_ENERGY_IN)).round().alias(c.TC_PRICE_IN).cast(pl.Int64),
505+
(pl.col(c.TC_PRICE_PU_OUT) * pl.col(c.TC_ENERGY_OUT)).round().alias(c.TC_PRICE_OUT).cast(pl.Int64),
506+
])
507+
516508
# Drop unnecessary columns
517509
transactions = transactions.drop(c.TC_ID_AGENT_IN, c.TC_ID_AGENT_OUT,
518510
"balancing_price_sell", "balancing_price_buy")
519511

520-
# Add the transactions to the transactions table
521-
self.transactions = pl.concat([self.transactions, transactions], how='align')
522-
523-
524512
# Delete the rows of the bids and offers
525513
self.bids_uncleared = bids_uncleared.clear()
526514
self.offers_uncleared = offers_uncleared.clear()
527515

528-
if flag:
529-
with pl.Config(set_tbl_width_chars=400, set_tbl_cols=21, set_tbl_rows=20):
530-
# print(bids_uncleared)
531-
# print(offers_uncleared)
532-
print(transactions)
533-
# print(self.transactions)
534-
raise Warning('Currently used to check what the problem is when the balancing gets too high. '
535-
'Can be ignored if not working on it.')
536-
537-
return self.transactions, self.bids_uncleared, self.offers_uncleared
516+
return transactions, self.bids_uncleared, self.offers_uncleared
538517

539-
def __apply_levies_taxes(self):
518+
def __apply_grid_levies(self, transactions):
540519
"""Applies levies and taxes to the market"""
541520
# Needs to discriminate between the different types of levies and taxes (wholesale or local)
542521

@@ -548,8 +527,43 @@ def __apply_levies_taxes(self):
548527
& (pl.col(c.TC_NAME) == self.tasks[c.TC_NAME]))
549528
retailer = retailer.to_dict()
550529

530+
# Concat all transactions for the given timestep to calculate the net energy
531+
transactions = pl.concat([self.transactions_prev, transactions], how='diagonal')
532+
# Remove retailer from the transactions (as they are not subject to levies and taxes)
533+
transactions = transactions.filter(~pl.col(c.TC_ID_AGENT).is_in(retailer["retailer"].to_list()))
534+
# Compute the net energy for each agent and assign it to the according energy column (in or out)
535+
net_energy = (transactions.groupby(c.TC_ID_AGENT).agg([
536+
pl.sum(c.TC_ENERGY_IN),
537+
pl.sum(c.TC_ENERGY_OUT),
538+
]))
539+
net_energy = net_energy.fill_null(0)
540+
net_energy = net_energy.with_columns([
541+
(pl.col(c.TC_ENERGY_IN).cast(pl.Int64) - pl.col(c.TC_ENERGY_OUT)
542+
.cast(pl.Int64)).alias(c.TC_ENERGY).cast(pl.Int64),
543+
])
544+
# Assign the value of the net energy to the energy in or out column depending on if it is positive or negative
545+
net_energy = net_energy.with_columns([
546+
(pl.when(pl.col(c.TC_ENERGY) > 0).then(pl.col(c.TC_ENERGY)).otherwise(None))
547+
.alias(c.TC_ENERGY_IN).cast(pl.Int64),
548+
(pl.when(pl.col(c.TC_ENERGY) < 0).then(pl.col(c.TC_ENERGY)).otherwise(None))
549+
.alias(c.TC_ENERGY_OUT).cast(pl.Int64),
550+
])
551+
net_energy = net_energy.drop(c.TC_ENERGY)
552+
# Select only the first row of each agent in transactions
553+
transactions = transactions.unique(c.TC_ID_AGENT)
554+
# Join the dataframes to have the information about the net energy
555+
suffix = '_right'
556+
transactions = transactions.join(net_energy, on=c.TC_ID_AGENT, how='left', suffix=suffix)
557+
# Replace the new energy columns with the old ones
558+
transactions = transactions.with_columns([
559+
pl.col(f'{c.TC_ENERGY_IN}{suffix}').alias(c.TC_ENERGY_IN).cast(pl.UInt64),
560+
pl.col(f'{c.TC_ENERGY_OUT}{suffix}').alias(c.TC_ENERGY_OUT).cast(pl.UInt64),
561+
])
562+
# Drop the unnecessary columns to finally have the transactions that are relevant for the grid fees and levies
563+
transactions = transactions.drop(f'{c.TC_ENERGY_IN}{suffix}', f'{c.TC_ENERGY_OUT}{suffix}')
564+
551565
# Copy the transactions table to apply the grid fees
552-
grid = self.transactions.clone()
566+
grid = transactions.clone()
553567
# Add temporary columns
554568
grid = grid.with_columns([
555569
pl.lit(retailer["grid_local_sell"].alias("grid_market_sell")),
@@ -576,7 +590,7 @@ def __apply_levies_taxes(self):
576590
grid = grid.drop("grid_market_sell", "grid_market_buy")
577591

578592
# Copy the transactions table to apply the levies
579-
levies = self.transactions.clone()
593+
levies = transactions.clone()
580594
# Add temporary columns
581595
levies = levies.with_columns([
582596
pl.lit(retailer["levies_price_sell"].alias("levies_sell")),
@@ -600,16 +614,16 @@ def __apply_levies_taxes(self):
600614
# Drop unnecessary columns
601615
levies = levies.drop("levies_sell", "levies_buy")
602616

603-
# Add the levies and taxes to the transactions table
604-
self.transactions = pl.concat([self.transactions, grid, levies], how='align')
617+
# Concat the grids and levies
618+
transactions = pl.concat([grid, levies], how='align')
605619

606-
# with pl.Config(set_tbl_width_chars=400, set_tbl_cols=21, set_tbl_rows=40):
620+
# with pl.Config(set_tbl_width_chars=400, set_tbl_cols=100, set_tbl_rows=100):
607621
# print(grid)
608622
# print(levies)
609623
# print(self.transactions)
610624
# exit()
611625

612-
return self.transactions
626+
return transactions
613627

614628
def __type__ex_ante(self):
615629
"""Clears the market ex-ante"""

hamlet/executor/markets/market.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ def execute(self) -> MarketDB:
7373

7474
result = self.market.execute()
7575

76+
# Delete the data attribute to avoid memory issues
77+
del self.data
78+
7679
return result
7780

7881

hamlet/executor/utilities/controller/mpc/mpc.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,14 @@
1919
from hamlet import functions as f
2020
import sys
2121

22-
AGENT_ID = '288prNBci5QNMEv'
22+
AGENT_ID = '1q5Nid2Bwz2WzxG' # 'HnYxh1u9BEFpWyK'
2323

2424

2525
class MpcBase:
2626
def run(self):
2727
raise NotImplementedError()
2828

2929

30-
31-
3230
class Mpc(ControllerBase):
3331

3432
def __init__(self, method='linopy', **kwargs):
@@ -140,14 +138,6 @@ def __init__(self, **kwargs):
140138
self.define_constraints()
141139
self.define_objective()
142140

143-
# if self.agent.agent_id == AGENT_ID:
144-
# for name, var in self.model.variables.items():
145-
# print(var)
146-
# for name, con in self.model.constraints.items():
147-
# print(con)
148-
# print(self.model.objective)
149-
# exit()
150-
151141
def create_plants(self):
152142
for plant_name, plant_data in self.plants.items():
153143

0 commit comments

Comments
 (0)