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-03-26 20:57 +0000

1from abc import ABC 

2from ahuora_builder_types import PropertySchema, PropertiesSchema, PropertyValueSchema 

3from core.auxiliary.models.PropertyInfo import PropertyInfo 

4from core.auxiliary.models.PropertyValue import sort_indexes 

5from flowsheetInternals.unitops.models import SimulationObject 

6from common.config_types import * 

7from ..queryset_lookup import get_property 

8from .convert_expression import convert_expression 

9from ..idaes_factory_context import IdaesFactoryContext 

10from core.auxiliary.models.IndexedItem import IndexedItem 

11from .property_value_adapter import serialise_property_value 

12 

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

14 is_tear: bool = False, 

15 is_indexed: bool = True) -> PropertySchema: 

16 """ 

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

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

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

20 as an example of what a property looks like. 

21 """ 

22 

23 property_values = property_info.values.all() 

24 

25 has_value = False 

26 

27 if ctx.scenario != None: 

28 enable_rating = ctx.scenario.enable_rating 

29 else: 

30 enable_rating = False 

31 

32 data = [] 

33 

34 for prop in property_values: 

35 # get indexes 

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

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

38 

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

40 property_key = property_info.key 

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

42 

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

44 # and continue serialization with the new indexes 

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

46 

47 # get value data 

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

49 property_value = PropertyValueSchema( 

50 id=prop.id, 

51 discrete_indexes=indexes, 

52 ) 

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

54 if value is not None: 

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

56 if is_tear: 

57 # only pass value for tear variables 

58 property_value.value = value 

59 if ( 

60 prop.is_control_set_point() 

61 and prop.is_externally_controlled() 

62 ): 

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

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

65 # to avoid clashing ids. 

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

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

68 # deactivated during optimization. 

69 property_value.id = -1 

70 else: 

71 property_value.value = value 

72 if (prop.is_control_set_point()): 

73 if not enable_rating: 

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

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

76 else: 

77 property_value.value = None 

78 if (prop.is_control_manipulated() 

79 and not enable_rating): 

80 property_value.guess = True 

81 

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

83 property_value.constraint = convert_expression(prop.formula) 

84 

85 # track dependencies 

86 ctx.add_property_value_dependency(prop) 

87 

88 data.append(property_value) 

89 

90 property_schema = PropertySchema(data=data) 

91 if has_value: 

92 property_schema.unit = property_info.unit 

93 

94 return property_schema 

95 

96 

97 

98class ValueAdapter(ABC): 

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

100 pass 

101 

102 

103 

104 

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

106 """ 

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

108 we don't need this property. 

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

110 switched on or off. 

111 """ 

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

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

114 group_schema = model.schema.propertySetGroups[group] 

115 if group_schema.toggle: 

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

117 toggle_status = ctx.get_property_value(toggle_property).value 

118 if toggle_status != True: 

119 return False 

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

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

122 return False 

123 # Otherwise, it's enabled 

124 return True 

125 

126class SerialisePropertiesAdapter(ValueAdapter): 

127 """ 

128 This class replaced PropertyDictAdapter and PropertyKeyAdapter. 

129 """ 

130 

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

132 """ 

133 Serialise all properties in the model. 

134 """ 

135 property_set = model.properties 

136 

137 result: PropertiesSchema = {} 

138 

139 prop_key: str 

140 prop_schema: PropertyType 

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

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

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

144 continue 

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

146 if prop_schema.type != "numeric": 

147 continue 

148 # get the property from the unit model 

149 prop = get_property(property_set, prop_key) 

150 # serialise the property 

151 result[prop_key] = serialise_property_info( 

152 ctx, 

153 prop, 

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

155 is_indexed=prop_schema.hasTimeIndex 

156 ) 

157 return result 

158 

159