Coverage for backend/idaes_factory/idaes_factory_context.py: 96%
47 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-11-06 23:27 +0000
1from typing import Any
3from django.db import transaction
4from django.db.models import QuerySet, Prefetch
6from common.models.idaes.flowsheet_schema import PropertyPackageType
7from core.auxiliary.models import PropertyInfo, PropertySet
8from core.auxiliary.models.PropertyValue import PropertyValue
9from core.auxiliary.models.SolveState import SolveState
10from flowsheetInternals.unitops.models.SimulationObject import SimulationObject
11from . import queryset_lookup
12from core.auxiliary.models.Scenario import Scenario
15ParameterName = str
16ParameterValue = float
17LiveSolveParams = dict[ParameterName,ParameterValue]
18"""
19Parameters for solving. Used when solving live, to specify the values that each of the input columns in the multi-steady state simulation should be set to.
20Key is the key of the column, should be one of the input columns used.
21Value is a float value to set it to.
22"""
26class IdaesFactoryContext:
27 """
28 The IdaesFactoryContext class provides:
30 - Efficient retrieval of related objects from the
31 database, for both serialisation and reloading
32 of the flowsheet
33 - A container for context variables such as time
34 steps, property packages, etc.
36 It is used within idaes_factory to provide context
37 through the various adapter classes.
38 """
41 def __init__(
42 self,
43 flowsheet_id: int,
44 time_steps: list[int] = [0],
45 time_step_size: float = 1.0,
46 require_variables_fixed: bool = False,
47 solve_index: int | None = None, # If it's from multi-steady state, index to store values at
48 scenario : Scenario = None,
49 ) -> None:
50 """Initialise the context with eager-loaded flowsheet data.
52 Args:
53 flowsheet_id: Identifier of the flowsheet being prepared.
54 time_steps: Time indices that adapters should serialise.
55 require_variables_fixed: Whether adapters must enforce fixed variables.
56 solve_index: Optional multi-steady-state solve index to bind.
57 scenario: Scenario providing state and dynamics configuration.
58 """
59 self.flowsheet_id = flowsheet_id
60 self.simulation_objects: QuerySet[SimulationObject] = None
62 if solve_index is not None: 62 ↛ 63line 62 didn't jump to line 63 because the condition on line 62 was never true
63 self.solve_index = SolveState.objects.get(index=solve_index, scenario_id=scenario.id)
64 else:
65 self.solve_index = None
67 # context vars
68 self.time_steps = time_steps
69 self.time_step_size = time_step_size
70 self.require_variables_fixed = require_variables_fixed
71 self.property_packages: list[PropertyPackageType] = []
72 self.expression_values = {}
73 self.scenario = scenario
75 self.load()
78 def load(self):
79 """Prefetch all simulation objects and related data for the flowsheet."""
80 # load all associated unit models, streams, property sets,
81 # properties, ports, etc. into the context
83 objects_filter = {
84 "flowsheet_id": self.flowsheet_id,
85 "is_deleted": False
86 }
88 # db calls: simulation_objects, 1 for each prefetch_related
89 self.simulation_objects = SimulationObject.objects.filter(
90 **objects_filter
91 ).select_related(
92 "properties",
93 "recycleConnection",
94 ).prefetch_related(
95 Prefetch(
96 "properties__ContainedProperties",
97 queryset=PropertyInfo.objects
98 .select_related("recycleConnection")
99 .prefetch_related(
100 Prefetch("values", queryset=PropertyValue.objects
101 .select_related("controlManipulated", "controlSetPoint", "controlSetPoint__manipulated__property")
102 .prefetch_related(
103 "indexedItems",
104 "controlSetPoint__manipulated__property__values"
105 )
106 )
107 )
108 ),
109 "ports",
110 "connectedPorts",
111 "propertyPackages",
112 )
114 def is_dynamic(self) -> bool:
115 """Return whether the bound scenario has dynamics enabled."""
116 if self.scenario is not None:
117 return self.scenario.enable_dynamics
118 else:
119 return False
121 # Updates the solve index so that the alread-loaded context can be used for multiple solves
122 # (Useful for multi-steady state)
123 def update_solve_index(self, index: int):
124 """Rebind the context to a different solve index for multi-solves.
126 Args:
127 index: Solve index associated with the current flowsheet.
128 """
129 self.solve_index = SolveState.objects.get(index=index, flowsheet_id=self.flowsheet_id)
131 def get_simulation_object(self, obj_id: int) -> SimulationObject:
132 """Return a simulation object by id using the prefetched queryset.
134 Args:
135 obj_id: Primary key of the simulation object to retrieve.
137 Returns:
138 The matching `SimulationObject` instance from the context cache.
139 """
140 return queryset_lookup.get_simulation_object(self.simulation_objects, obj_id)
143 def filter_object_type(self, include: set[str] = set()) -> list[SimulationObject]:
144 """Filter cached simulation objects to the provided set of type keys.
146 Args:
147 include: Unit operation type identifiers that should be returned.
149 Returns:
150 List of `SimulationObject` instances matching the requested types.
151 """
152 return queryset_lookup.filter_simulation_objects(self.simulation_objects, include)
155 def exclude_object_type(self, exclude: set[str]) -> list[SimulationObject]:
156 """Filter cached simulation objects by excluding specific type keys.
158 Args:
159 exclude: Unit operation type identifiers that should be omitted.
161 Returns:
162 List of `SimulationObject` instances not belonging to the excluded types.
163 """
164 return queryset_lookup.exclude_simulation_objects(self.simulation_objects, exclude)
166 def get_property(self, property_set: PropertySet, key: str) -> PropertyInfo:
167 """Retrieve a property from a property set by its key.
169 Args:
170 property_set: Property set that contains the requested property.
171 key: Identifier for the property within the set.
173 Returns:
174 The `PropertyInfo` instance found in the given property set.
175 """
176 return queryset_lookup.get_property(property_set, key)
178 def get_property_value(self, property: PropertyInfo, indexes: list[Any] | None = None) -> PropertyValue:
179 """Retrieve a property value, optionally constrained by index selections.
181 Args:
182 property: Property whose value object should be fetched.
183 indexes: Optional list of indices to select a specific value entry.
185 Returns:
186 The `PropertyValue` matching the property and index configuration.
187 """
188 return queryset_lookup.get_value_object(property, indexes)