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
« 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
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 """
23 property_values = property_info.values.all()
25 has_value = False
27 if ctx.scenario != None:
28 enable_rating = ctx.scenario.enable_rating
29 else:
30 enable_rating = False
32 data = []
34 for prop in property_values:
35 # get indexes
36 # ie. ["0", "benzene", "Vap"]
37 unsorted_indexes : list[IndexedItem] = list(prop.indexedItems.all())
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
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)]
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
82 if prop.formula not in [None, ""]:
83 property_value.constraint = convert_expression(prop.formula)
85 # track dependencies
86 ctx.add_property_value_dependency(prop)
88 data.append(property_value)
90 property_schema = PropertySchema(data=data)
91 if has_value:
92 property_schema.unit = property_info.unit
94 return property_schema
98class ValueAdapter(ABC):
99 def serialise(self, ctx, model: SimulationObject):
100 pass
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
126class SerialisePropertiesAdapter(ValueAdapter):
127 """
128 This class replaced PropertyDictAdapter and PropertyKeyAdapter.
129 """
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
137 result: PropertiesSchema = {}
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