Coverage for backend/PinchAnalysis/models/InputModels.py: 63%

117 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-11-06 23:27 +0000

1from django.db import models 

2from core.auxiliary.enums.unitOpGraphics import ConType 

3from core.auxiliary.enums import pinchEnums 

4from core.managers import AccessControlManager 

5 

6from flowsheetInternals.unitops.models.SimulationObject import SimulationObject 

7from core.auxiliary.enums import SimulationObjectClass 

8from core.auxiliary.models.PropertyInfo import PropertyInfo 

9 

10class PinchInputs(models.Model): 

11 flowsheet = models.ForeignKey("core_auxiliary.Flowsheet", on_delete=models.CASCADE, related_name="PinchInputs") 

12 project_owner = models.OneToOneField("PinchAnalysis.StreamDataProject", on_delete=models.CASCADE, related_name="Inputs", null=True) 

13 name = models.CharField(max_length=32, default="Inputs") 

14 

15 created_at = models.DateTimeField(auto_now_add=True) 

16 objects = AccessControlManager() 

17 

18 

19 

20class StreamDataEntry(models.Model): 

21 flowsheet = models.ForeignKey("core_auxiliary.Flowsheet", on_delete=models.CASCADE, related_name="StreamDataEntries") 

22 custom = models.BooleanField(default=False) 

23 group = models.ForeignKey("flowsheetInternals_graphicData.Grouping", on_delete=models.CASCADE, related_name="StreamDataEntries") 

24 streamDataProject = models.ForeignKey("StreamDataProject", on_delete=models.CASCADE, related_name="StreamDataEntries") 

25 unitop = models.ForeignKey("flowsheetInternals_unitops.SimulationObject", on_delete=models.CASCADE, related_name="StreamDataEntries", null=True) 

26 property_package_mapping = models.CharField(max_length=32, null=True) # eg: "Cold Side" 

27 nextStream = models.ManyToManyField("StreamDataEntry", blank=True) 

28 t_h_data = models.JSONField(null=True, blank=True) 

29 states = models.JSONField(null=True, blank=True) 

30 

31 created_at = models.DateTimeField(auto_now_add=True) 

32 objects = AccessControlManager() 

33 

34 @property 

35 def zone(self) -> str: 

36 return self.group.simulationObject.componentName 

37 

38 # TODO: Null checks are really bad on these, if we have a custom StreamDataEntry, these would break. 

39 # We can use the first and last segment to get the inlet and outlet streams, but thats for the future 

40 @property 

41 def inlet_outlet_stream(self) -> tuple[SimulationObject, SimulationObject]: 

42 unitop_schema = self.unitop.schema 

43 property_package_ports = unitop_schema.propertyPackagePorts[self.property_package_mapping] 

44 # We can use get here because Pinch only worries about heaters and heat exchangers, which only 

45 # have one inlet and one outlet for each property package. 

46 inletPort = self.unitop.ports.get(key__in=property_package_ports, direction=ConType.Inlet) 

47 inletStream = inletPort.stream 

48 

49 outletPort = self.unitop.ports.get(key__in=property_package_ports, direction=ConType.Outlet) 

50 outletStream = outletPort.stream 

51 return (inletStream, outletStream) 

52 

53 @property 

54 def temperatures(self) -> tuple[PropertyInfo, PropertyInfo]: 

55 inlet, outlet = self.inlet_outlet_stream 

56 inlet_temp = inlet.properties.get_property("temperature") 

57 outlet_temp = outlet.properties.get_property("temperature") 

58 return (inlet_temp, outlet_temp) 

59 

60 @property 

61 def pressures(self) -> tuple[PropertyInfo, PropertyInfo]: 

62 inlet, outlet = self.inlet_outlet_stream 

63 inlet_pressure = inlet.properties.get_property("pressure") 

64 outlet_pressure = outlet.properties.get_property("pressure") 

65 return (inlet_pressure, outlet_pressure) 

66 

67 @property 

68 def enthalpies(self) -> tuple[PropertyInfo, PropertyInfo]: 

69 inlet, outlet = self.inlet_outlet_stream 

70 inlet_enthalpy = inlet.properties.get_property("enth_mol") 

71 outlet_enthalpy = outlet.properties.get_property("enth_mol") 

72 return (inlet_enthalpy, outlet_enthalpy) 

73 

74 @property 

75 def heat_flow(self) -> PropertyInfo: 

76 match self.unitop.objectType: 

77 case SimulationObjectClass.HeatExchanger: 

78 heat_flow = self.unitop.properties.get_property("heat_duty") 

79 case SimulationObjectClass.Heater: 

80 heat_flow = self.unitop.properties.get_property("heat_duty") 

81 case SimulationObjectClass.Cooler: 

82 heat_flow = self.unitop.properties.get_property("heat_duty") 

83 case _: 

84 raise ValueError("Invalid simulation object type for heat flow extraction.") 

85 return heat_flow 

86 

87class Segment(models.Model): 

88 flowsheet = models.ForeignKey("core_auxiliary.Flowsheet", on_delete=models.CASCADE, related_name="Segments") 

89 stream_data_entry = models.ForeignKey(StreamDataEntry, on_delete=models.CASCADE, related_name="Segments", null=True) 

90 name = models.CharField(max_length=128) 

91 t_supply = models.FloatField(default=0) 

92 t_target = models.FloatField(default=0) 

93 p_supply = models.FloatField(default=0) 

94 p_target = models.FloatField(default=0) 

95 h_supply = models.FloatField(default=0) 

96 h_target = models.FloatField(default=0) 

97 heat_flow = models.FloatField(default=0) 

98 dt_cont = models.FloatField(default=5.0) 

99 htc = models.FloatField(default=1.0) 

100 

101 created_at = models.DateTimeField(auto_now_add=True) 

102 objects = AccessControlManager() 

103 

104 @property 

105 def zone(self) -> str: 

106 return self.zone_object.simulationObject.componentName 

107 

108 @property 

109 def zone_object(self): 

110 return self.stream_data_entry.group 

111 

112 

113 class Meta: 

114 ordering = ['created_at'] 

115 

116 @classmethod 

117 def create(cls, **kwargs): 

118 instance = cls.objects.create(**kwargs) 

119 instance.save() 

120 return instance 

121 

122 

123class PinchUtility(models.Model): 

124 flowsheet = models.ForeignKey("core_auxiliary.Flowsheet", on_delete=models.CASCADE, related_name="PinchUtilities") 

125 input_owner = models.ForeignKey(PinchInputs, on_delete=models.CASCADE, related_name="PinchUtilities", null=False) 

126 # zone = models.CharField(max_length=32, null=True) ############ ADD ################### 

127 name = models.CharField(max_length=32) 

128 type = models.CharField(choices=pinchEnums.StreamType.choices , default=pinchEnums.StreamType.Both) 

129 t_supply = models.FloatField(null=True) 

130 t_target = models.FloatField(null=True) 

131 heat_flow = models.FloatField(default=0, null=True) ############ CHANGE TO MAX HEAT FLOW ################### 

132 dt_cont = models.FloatField(default=5.0, null=True) 

133 price = models.FloatField(default=30.0, null=True) 

134 htc = models.FloatField(default=1.0, null=True) 

135 

136 created_at = models.DateTimeField(auto_now_add=True) 

137 objects = AccessControlManager() 

138 

139 

140 class Meta: 

141 ordering = ['created_at'] 

142 

143 @classmethod 

144 def create(cls, **kwargs): 

145 # Default stream values 

146 defaults = { 

147 'input_owner': kwargs.get('input_owner', None), 

148 # 'zone': kwargs.get('zone', "Site"), 

149 'name': kwargs.get('name', "Utility"), 

150 'type': kwargs.get('type', pinchEnums.StreamType.Both), 

151 't_supply': kwargs.get('t_supply', None), 

152 't_target': kwargs.get('t_target', None), 

153 'heat_flow': kwargs.get('heat_flow', 0.0), 

154 'dt_cont': kwargs.get('dt_cont', 5.0), 

155 'htc': kwargs.get('htc', 1.0), 

156 'price': kwargs.get('price', 1.0), 

157 } 

158 

159 kwargs.update(defaults) 

160 instance = cls.objects.create(**kwargs) 

161 instance.save() 

162 return instance