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

67 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-07-22 05:22 +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 

9from .serialisation_rules import is_group_enabled 

10 

11if TYPE_CHECKING: 

12 from core.auxiliary.models.PropertyInfo import PropertyInfo 

13 from ..idaes_factory_context import IdaesFactoryContext 

14 

15def serialise_property_info(ctx, property_info: "PropertyInfo", 

16 is_tear: bool = False, 

17 is_indexed: bool = True, 

18 force_enabled: bool = False) -> PropertySchema: 

19 """ 

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

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

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

23 as an example of what a property looks like. 

24 """ 

25 

26 property_values = property_info.values.all() 

27 

28 has_value = False 

29 

30 if ctx.scenario != None: 

31 enable_rating = ctx.scenario.enable_rating 

32 else: 

33 enable_rating = False 

34 

35 data = [] 

36 

37 for prop in property_values: 

38 # get indexes 

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

40 from core.auxiliary.models.PropertyValue import sort_indexes 

41 

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

43 

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

45 property_key = property_info.key 

46 schema = property_info.set.simulationObject.schema 

47 if property_key not in schema.properties: 

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

49 index_set_order = [] 

50 else: 

51 property_schema = schema.properties[property_key] 

52 index_set_order : list[str] = property_schema.indexSets 

53 group = property_schema.propertySetGroup 

54 is_state_var = property_info.key in schema.propertySetGroups[group].stateVars 

55 if force_enabled and not is_state_var: 

56 # if the property is not a state variable, we don't want to force it to be enabled 

57 force_enabled = False 

58 

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

60 # and continue serialization with the new indexes 

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

62 

63 # get value data 

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

65 property_value = PropertyValueSchema( 

66 id=prop.id, 

67 discrete_indexes=indexes, 

68 ) 

69 value = serialise_property_value( 

70 ctx, 

71 property_info, 

72 prop, 

73 is_indexed, 

74 is_tear=is_tear, 

75 force_enabled=force_enabled, 

76 ) 

77 if value is not None: 

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

79 if is_tear: 

80 # only pass value for tear variables 

81 property_value.value = value 

82 if ( 

83 prop.is_control_set_point() 

84 and prop.is_externally_controlled() 

85 ): 

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

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

88 # to avoid clashing ids. 

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

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

91 # deactivated during optimization. 

92 property_value.id = -1 

93 else: 

94 property_value.value = value 

95 if (prop.is_control_set_point()): 

96 if not enable_rating: 

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

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

99 else: 

100 property_value.value = None 

101 

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

103 property_value.constraint = convert_expression(prop.formula, ctx, prop) 

104 if (prop.is_control_manipulated() 

105 and not enable_rating): 

106 property_value.guess = True 

107 

108 # track dependencies 

109 ctx.add_property_value_dependency(prop) 

110 

111 data.append(property_value) 

112 

113 property_schema = PropertySchema(data=data) 

114 if has_value: 

115 property_schema.unit = property_info.unit 

116 

117 return property_schema 

118 

119 

120 

121class ValueAdapter(ABC): 

122 def serialise(self, ctx, model): 

123 pass 

124 

125 

126 

127 

128class SerialisePropertiesAdapter(ValueAdapter): 

129 """ 

130 This class replaced PropertyDictAdapter and PropertyKeyAdapter. 

131 """ 

132 

133 def serialise( 

134 self, 

135 ctx, 

136 model, 

137 is_tear: bool = False, 

138 force_enabled: bool = False, 

139 ) -> 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 force_enabled=force_enabled, 

165 ) 

166 return result 

167 

168