Coverage for backend/django/idaes_factory/adapters/property_info_adapter.py: 99%

72 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-05-13 02:47 +0000

1from abc import ABC 

2from typing import TYPE_CHECKING 

3 

4from ahuora_builder_types import PropertySchema, PropertiesSchema, PropertyValueSchema 

5from common.config_types import * 

6from ..queryset_lookup import get_property 

7from .convert_expression import convert_expression 

8from .property_value_adapter import serialise_property_value 

9 

10if TYPE_CHECKING: 

11 from core.auxiliary.models.PropertyInfo import PropertyInfo 

12 from ..idaes_factory_context import IdaesFactoryContext 

13 

14def serialise_property_info(ctx, 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 from core.auxiliary.models.PropertyValue import sort_indexes 

39 

40 unsorted_indexes = list(prop.indexedItems.all()) 

41 

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

43 property_key = property_info.key 

44 schema = property_info.set.simulationObject.schema 

45 if property_key not in schema.properties: 

46 # e.g machine learning block: doesn't use the normal property schema methods. 

47 index_set_order = [] 

48 else: 

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

50 

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

52 # and continue serialization with the new indexes 

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

54 

55 # get value data 

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

57 property_value = PropertyValueSchema( 

58 id=prop.id, 

59 discrete_indexes=indexes, 

60 ) 

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

62 if value is not None: 

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

64 if is_tear: 

65 # only pass value for tear variables 

66 property_value.value = value 

67 if ( 

68 prop.is_control_set_point() 

69 and prop.is_externally_controlled() 

70 ): 

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

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

73 # to avoid clashing ids. 

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

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

76 # deactivated during optimization. 

77 property_value.id = -1 

78 else: 

79 property_value.value = value 

80 if (prop.is_control_set_point()): 

81 if not enable_rating: 

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

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

84 else: 

85 property_value.value = None 

86 if (prop.is_control_manipulated() 

87 and not enable_rating): 

88 property_value.guess = True 

89 

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

91 property_value.constraint = convert_expression(prop.formula) 

92 

93 # track dependencies 

94 ctx.add_property_value_dependency(prop) 

95 

96 data.append(property_value) 

97 

98 property_schema = PropertySchema(data=data) 

99 if has_value: 

100 property_schema.unit = property_info.unit 

101 

102 return property_schema 

103 

104 

105 

106class ValueAdapter(ABC): 

107 def serialise(self, ctx, model): 

108 pass 

109 

110 

111 

112 

113def is_group_enabled(model, ctx, prop_key: str) -> bool: 

114 """ 

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

116 we don't need this property. 

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

118 switched on or off. 

119 """ 

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

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

122 group_schema = model.schema.propertySetGroups[group] 

123 if group_schema.toggle: 

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

125 toggle_status = ctx.get_property_value(toggle_property).value 

126 if toggle_status != True: 

127 return False 

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

129 if group == "dynamics" and not ctx.is_dynamic(): 

130 return False 

131 # Otherwise, it's enabled 

132 return True 

133 

134class SerialisePropertiesAdapter(ValueAdapter): 

135 """ 

136 This class replaced PropertyDictAdapter and PropertyKeyAdapter. 

137 """ 

138 

139 def serialise(self, ctx, model, is_tear: bool = False) -> PropertiesSchema: 

140 """ 

141 Serialise all properties in the model. 

142 """ 

143 property_set = model.properties 

144 

145 result: PropertiesSchema = {} 

146 

147 prop_key: str 

148 prop_schema: PropertyType 

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

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

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

152 continue 

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

154 if prop_schema.type != "numeric": 

155 continue 

156 # get the property from the unit model 

157 prop = get_property(property_set, prop_key) 

158 # serialise the property 

159 result[prop_key] = serialise_property_info( 

160 ctx, 

161 prop, 

162 is_tear=is_tear, # tears and streams are handled separately 

163 is_indexed=prop_schema.hasTimeIndex 

164 ) 

165 return result 

166 

167