Coverage for backend/ahuora-builder/src/ahuora_builder/custom/energy/battery.py: 0%

40 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-03-26 20:57 +0000

1# Import Pyomo libraries 

2from pyomo.environ import ( 

3 Var, 

4 Suffix, 

5 units as pyunits, 

6) 

7from pyomo.common.config import ConfigBlock, ConfigValue, In 

8 

9# Import IDAES cores 

10from idaes.core import ( 

11 declare_process_block_class, 

12 UnitModelBlockData, 

13 useDefault, 

14) 

15from idaes.core.util.config import is_physical_parameter_block 

16import idaes.core.util.scaling as iscale 

17import idaes.logger as idaeslog 

18 

19from idaes.core.util.tables import create_stream_table_dataframe 

20 

21from idaes.core.util.exceptions import ConfigurationError 

22 

23# Set up logger 

24_log = idaeslog.getLogger(__name__) 

25 

26 

27# When using this file the name "Solar" is what is imported 

28@declare_process_block_class("Solar") 

29class SolarData(UnitModelBlockData): 

30 """ 

31 Zero order Link model 

32 """ 

33 

34 # CONFIG are options for the unit model, this simple model only has the mandatory config options 

35 CONFIG = ConfigBlock() 

36 

37 CONFIG.declare( 

38 "dynamic", 

39 ConfigValue( 

40 domain=In([False]), 

41 default=False, 

42 description="Dynamic model flag - must be False", 

43 doc="""Indicates whether this model will be dynamic or not, 

44 **default** = False. The Bus unit does not support dynamic 

45 behavior, thus this must be False.""", 

46 ), 

47 ) 

48 CONFIG.declare( 

49 "has_holdup", 

50 ConfigValue( 

51 default=False, 

52 domain=In([False]), 

53 description="Holdup construction flag - must be False", 

54 doc="""Indicates whether holdup terms should be constructed or not. 

55 **default** - False. The Bus unit does not have defined volume, thus 

56 this must be False.""", 

57 ), 

58 ) 

59 CONFIG.declare( 

60 "property_package", 

61 ConfigValue( 

62 default=useDefault, 

63 domain=is_physical_parameter_block, 

64 description="Property package to use for control volume", 

65 doc="""Property parameter object used to define property calculations, 

66 **default** - useDefault. 

67 **Valid values:** { 

68 **useDefault** - use default package from parent model or flowsheet, 

69 **PhysicalParameterObject** - a PhysicalParameterBlock object.}""", 

70 ), 

71 ) 

72 CONFIG.declare( 

73 "property_package_args", 

74 ConfigBlock( 

75 implicit=True, 

76 description="Arguments to use for constructing property packages", 

77 doc="""A ConfigBlock with arguments to be passed to a property block(s) 

78 and used when constructing these, 

79 **default** - None. 

80 **Valid values:** { 

81 see property package for documentation.}""", 

82 ), 

83 ) 

84 

85 def build(self): 

86 # build always starts by calling super().build() 

87 # This triggers a lot of boilerplate in the background for you 

88 super().build() 

89 

90 # This creates blank scaling factors, which are populated later 

91 self.scaling_factor = Suffix(direction=Suffix.EXPORT) 

92 

93 

94 # Add state blocks for inlet, outlet, and waste 

95 # These include the state variables and any other properties on demand 

96 # Add inlet block 

97 tmp_dict = dict(**self.config.property_package_args) 

98 tmp_dict["parameters"] = self.config.property_package 

99 #tmp_dict["defined_state"] = True # inlet block is an inlet 

100 

101 # Add outlet 

102 tmp_dict["defined_state"] = False # outlet is not an inlet 

103 self.properties_out = self.config.property_package.state_block_class( 

104 self.flowsheet().config.time, 

105 doc="Material properties of outlet", 

106 **tmp_dict 

107 ) 

108 

109 # Add ports - oftentimes users interact with these rather than the state blocks 

110 

111 self.add_port(name="outlet", block=self.properties_out) 

112 

113 # Add variable for efficiency 

114 self.efficiency = Var( 

115 initialize=1.0, 

116 doc="Efficiency of the panel", 

117 ) 

118 

119 # Add variable for solar irradiation 

120 self.irradiation = Var(self.flowsheet().config.time, 

121 initialize=1.0, units = pyunits.W/pyunits.m**2, 

122 doc="Amount of sunlight hitting the panel", 

123 ) 

124 

125 # Add variable for area 

126 self.area = Var( 

127 initialize=1.0, units = pyunits.m**2, 

128 doc="Size of the panel", 

129 ) 

130 

131 # Add variable for no. of solar panels 

132 self.panel_count = Var( 

133 initialize=1.0, 

134 doc="Number of solar panels", 

135 ) 

136 

137 # Add constraints 

138 @self.Constraint( 

139 self.flowsheet().time, 

140 doc="Power usage", 

141 ) 

142 def eq_power_balance(b, t): 

143 return ( 

144 self.irradiation[t] * self.efficiency * self.area * self.panel_count == b.properties_out[t].power 

145 ) 

146 

147 

148 def calculate_scaling_factors(self): 

149 super().calculate_scaling_factors() 

150 

151 def initialize(blk, *args, **kwargs): 

152 # Just propagate the power from inlet to outlet, good simple method of initialization 

153 pass 

154 

155 def _get_stream_table_contents(self, time_point=0): 

156 """ 

157 Assume unit has standard configuration of 1 inlet and 1 outlet. 

158 

159 Developers should overload this as appropriate. 

160 """ 

161 try: 

162 return create_stream_table_dataframe( 

163 {"Outlet": self.outlet}, time_point=time_point 

164 ) 

165 except AttributeError: 

166 raise ConfigurationError( 

167 f"Unit model {self.name} does not have the standard Port " 

168 f"names (inlet and outlet). Please contact the unit model " 

169 f"developer to develop a unit specific stream table." 

170 )