Coverage for agentlib_flexquant/data_structures/flex_offer.py: 100%

32 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-10-20 14:09 +0000

1""" 

2Data models for flexibility offers. 

3 

4This module defines data structures for representing flexibility offers in energy markets, 

5including baseline power profiles, positive and negative flexibility pricing, and offer 

6status tracking. The FlexOffer class encapsulates all information needed to represent 

7a flexibility bid, including power differences from baseline and acceptance status. 

8""" 

9from enum import Enum 

10from typing import Optional 

11 

12import pandas as pd 

13import pydantic 

14from agentlib.core.datamodels import _TYPE_MAP 

15from pydantic import BaseModel 

16 

17 

18class OfferStatus(Enum): 

19 """Status of the FlexOffer""" 

20 

21 NOT_ACCEPTED = "Not Accepted" 

22 ACCEPTED_POSITIVE = "Accepted Positive" 

23 ACCEPTED_NEGATIVE = "Accepted Negative" 

24 

25 

26class FlexOffer(BaseModel): 

27 """Data class for the flexibility offer.""" 

28 

29 base_power_profile: pd.Series = pydantic.Field( 

30 default=None, 

31 unit="W", 

32 scalar=False, 

33 description="Power profile of the baseline MPC", 

34 ) 

35 pos_price: Optional[float] = pydantic.Field( 

36 default=None, 

37 unit="ct", 

38 scalar=True, 

39 description="Price for positive flexibility", 

40 ) 

41 pos_diff_profile: pd.Series = pydantic.Field( 

42 default=None, 

43 unit="W", 

44 scalar=False, 

45 description="Power profile for the positive difference", 

46 ) 

47 neg_price: Optional[float] = pydantic.Field( 

48 default=None, 

49 unit="ct", 

50 scalar=True, 

51 description="Price for negative flexibility", 

52 ) 

53 neg_diff_profile: pd.Series = pydantic.Field( 

54 default=None, 

55 unit="W", 

56 scalar=False, 

57 description="Power profile for the negative difference", 

58 ) 

59 status: OfferStatus = pydantic.Field( 

60 default=OfferStatus.NOT_ACCEPTED.value, 

61 scalar=True, 

62 description="Status of the FlexOffer", 

63 ) 

64 

65 class Config: 

66 """Allow arbitrary (non-Pydantic) types such as pandas.Series or numpy.ndarray 

67 in model fields without requiring custom validators.""" 

68 

69 arbitrary_types_allowed = True 

70 

71 def as_dataframe(self) -> pd.DataFrame: 

72 """Store the flexibility offer in a pd.DataFrame 

73 

74 Returns: 

75 DataFrame containing the flexibility offer. 

76 Scalar values are written on the first timestep. 

77 

78 """ 

79 data = [] 

80 cols = [] 

81 

82 # append scalar values 

83 for name, field in self.model_fields.items(): 

84 if field.json_schema_extra["scalar"]: 

85 ser = pd.Series(getattr(self, name)) 

86 ser.index += self.base_power_profile.index[0] 

87 data.append(ser) 

88 cols.append(name) 

89 

90 df = pd.DataFrame(data).T 

91 df.columns = cols 

92 return df 

93 

94 

95# add the offer type to agent variables 

96_TYPE_MAP["FlexOffer"] = FlexOffer