2222
2323# Definition of temporary column names
2424C_ENERGY_CUMSUM = 'energy_cumsum'
25+ AGENT_ID = '1q5Nid2Bwz2WzxG'
2526
2627
2728class 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"""
0 commit comments