Coverage for backend/idaes_factory/adapters/property_info_adapter.py: 81%

98 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-11-06 23:27 +0000

1from abc import ABC 

2from common.models.idaes import PropertySchema, PropertiesSchema, PropertyValueSchema 

3from core.auxiliary.models.PropertyInfo import PropertyInfo, check_is_except_last 

4from core.auxiliary.models.PropertyValue import PropertyValue, sort_indexes 

5from flowsheetInternals.unitops.models import SimulationObject 

6from common.config_types import * 

7from ..queryset_lookup import get_property, get_value_object 

8from .convert_expression import convert_expression 

9from ..idaes_factory_context import IdaesFactoryContext 

10from flowsheetInternals.unitops.config.config_base import configuration 

11from core.auxiliary.models.IndexedItem import IndexedItem 

12from .property_value_adapter import serialise_property_value 

13 

14def serialise_property_info(ctx: IdaesFactoryContext, property_info: PropertyInfo, 

15 is_tear: bool = False, 

16 is_indexed: bool = True) -> PropertySchema: 

17 """ 

18 A PropertyInfo object represents a single IndexedVar or ScalarVar in IDAES.  

19 This method handles unpacking the indexes and putting it in the format that 

20 idaes_service expects. see pump.json or any of the other idaes_service test files 

21 as an example of what a property looks like. 

22 """ 

23 

24 property_values = property_info.values.all() 

25 

26 has_value = False 

27 

28 if ctx.scenario != None: 

29 enable_rating = ctx.scenario.enable_rating 

30 else: 

31 enable_rating = False 

32 

33 data = [] 

34 

35 for prop in property_values: 

36 # get indexes 

37 # ie. ["0", "benzene", "Vap"] 

38 unsorted_indexes : list[IndexedItem] = list(prop.indexedItems.all()) 

39 

40 # Get the object type and property key (e.g.: split_fraction) 

41 property_key = property_info.key 

42 index_set_order : list[str] = property_info.set.simulationObject.schema.properties[property_key].indexSets 

43 

44 # Sort the indexes so that they are in the order defined in the config file 

45 # and continue serialization with the new indexes 

46 indexes = [index.key for index in sort_indexes(index_set_order, unsorted_indexes)] 

47 

48 # get value data 

49 # ie. {"id": 1, "value": 0.5, "controlled": int?, "guess": bool?, "constraint": str?} value_data = { 

50 property_value = PropertyValueSchema( 

51 id=prop.id, 

52 discrete_indexes=indexes, 

53 ) 

54 value = serialise_property_value(ctx, property_info, prop, is_indexed, is_tear=is_tear) 

55 if value is not None: 

56 has_value = True # used to determine if we should include the unit 

57 if is_tear: 57 ↛ 59line 57 didn't jump to line 59 because the condition on line 57 was never true

58 # only pass value for tear variables 

59 property_value.value = value 

60 if ( 

61 prop.is_control_set_point() 

62 and prop.is_externally_controlled() 

63 ): 

64 # this property is externally controlled, and will be included 

65 # on the other side of the tear. So we need to set the id to -1 

66 # to avoid clashing ids. 

67 # We need to discard this one because this side is just a guess, 

68 # while the other may be used for constraints which can be 

69 # deactivated during optimization. 

70 property_value.id = -1 

71 else: 

72 property_value.value = value 

73 if (prop.is_control_set_point()): 

74 if not enable_rating: 

75 # get the id of the thing we're manipulating 

76 property_value.controlled = prop.controlSetPoint.manipulated.id 

77 else: 

78 property_value.value = None 

79 if (prop.is_control_manipulated() 

80 and not enable_rating): 

81 property_value.guess = True 

82 

83 if prop.formula not in [None, ""]: 

84 property_value.constraint = convert_expression(prop.formula) 

85 

86 data.append(property_value) 

87 

88 property_schema = PropertySchema(data=data) 

89 if has_value: 

90 property_schema.unit = property_info.unit 

91 

92 return property_schema 

93 

94 

95 

96class ValueAdapter(ABC): 

97 def serialise(self, ctx, model: SimulationObject): 

98 pass 

99 

100 

101 

102# TODO: Deprecate this clase (just use the method directly.) 

103class PropertyInfoAdapter(ValueAdapter): 

104 """ 

105 Handles the serialisation and reloading of a single property 

106 """ 

107 def __init__( 

108 self, 

109 is_indexed: bool = True, 

110 ): 

111 self._is_indexed = is_indexed 

112 

113 def serialise(self, ctx: IdaesFactoryContext, property_obj: PropertyInfo, is_tear: bool = False) -> PropertySchema: 

114 """ 

115 - is_tear: bool, true if inlet side of a tear 

116 """ 

117 return serialise_property_info( 

118 ctx, 

119 property_obj, 

120 is_tear=is_tear, 

121 is_indexed=self._is_indexed 

122 ) 

123 

124 

125 

126def is_group_enabled(model: SimulationObject, ctx: IdaesFactoryContext, prop_key: str) -> bool: 

127 """ 

128 If the propertySetGroup that it's a part of is not enabled, 

129 we don't need this property. 

130 The main case for this is e.g heaters where "enable dynamics" can be 

131 switched on or off. 

132 """ 

133 # If the property is part of a group that is not enabled, don't serialise it 

134 group = model.schema.properties[prop_key].propertySetGroup 

135 group_schema = model.schema.propertySetGroups[group] 

136 if group_schema.toggle: 

137 toggle_property = ctx.get_property(model.properties, group_schema.toggle) 

138 toggle_status = ctx.get_property_value(toggle_property).value 

139 if toggle_status != True: 

140 return False 

141 # if it's the dynamics group, and it's not dynamic, don't serialise it 

142 if group == "dynamics" and not ctx.is_dynamic(): 142 ↛ 143line 142 didn't jump to line 143 because the condition on line 142 was never true

143 return False 

144 # Otherwise, it's enabled 

145 return True 

146 

147 

148 

149class PropertyKeyAdapter(ValueAdapter): 

150 """ 

151 Used to map one PropKey from a unit model to one property in an idaes 

152 json schema. 

153 """ 

154 def __init__(self, 

155 prop_key: str, 

156 is_indexed: bool = True, 

157 has_unit: bool = True, 

158 ): 

159 """ 

160 prop_key: the key of the property in the Django unit model's PropertySet. 

161 """ 

162 self._prop_key = prop_key 

163 self._has_unit = has_unit # TODO: Replace with something that unwraps the unit if has_unit is False 

164 self.property_adapter = PropertyInfoAdapter( is_indexed=is_indexed) 

165 

166 

167 def serialise(self, ctx:IdaesFactoryContext, model: SimulationObject) -> PropertySchema: 

168 

169 if not is_group_enabled(model, ctx, self._prop_key): 

170 # if the propertySetGroup is not enabled, don't serialise this property 

171 return None 

172 # get the property from the unit model 

173 property_set = model.properties 

174 prop = get_property(property_set, self._prop_key) 

175 # serialise the property 

176 return self.property_adapter.serialise(ctx, prop) 

177 

178 

179class PropertyDictAdapter(ValueAdapter): 

180 """ 

181 Handles running the relevant PropertyAdapter for each key in the schema. 

182 """ 

183 

184 def __init__(self, schema: dict[str, ValueAdapter]): 

185 # convert schema to list if necessary and store in this object 

186 self.schema = schema 

187 

188 

189 def serialise(self, ctx:IdaesFactoryContext, model: SimulationObject) -> PropertiesSchema: 

190 result = {} 

191 for key, adapter in self.schema.items(): 

192 # get value from adapter 

193 value: PropertySchema = adapter.serialise(ctx, model) 

194 if value is not None: 

195 result[key] = value 

196 return result 

197 

198 

199class SerialisePropertiesAdapter(ValueAdapter): 

200 """ 

201 This class should replace PropertyDictAdapter and PropertyKeyAdapter. 

202 """ 

203 

204 def serialise(self, ctx: IdaesFactoryContext, model: SimulationObject) -> PropertiesSchema: 

205 """ 

206 Serialise all properties in the model. 

207 """ 

208 property_set = model.properties 

209 

210 result: PropertiesSchema = {} 

211 

212 prop_key: str 

213 prop_schema: PropertyType 

214 for prop_key,prop_schema in model.schema.properties.items(): 

215 # if the propertySetGroup is not enabled, don't serialise this property 

216 if not is_group_enabled(model, ctx, prop_key): 

217 continue 

218 # if schema type is not number, skip it too 

219 if prop_schema.type != "numeric": 

220 continue 

221 # get the property from the unit model 

222 prop = get_property(property_set, prop_key) 

223 # serialise the property 

224 result[prop_key] = serialise_property_info( 

225 ctx, 

226 prop, 

227 is_tear=False, # tears and streams are handled separately 

228 is_indexed=prop_schema.hasTimeIndex 

229 ) 

230 return result 

231 

232