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
« 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
6from flowsheetInternals.unitops.models.SimulationObject import SimulationObject
7from core.auxiliary.enums import SimulationObjectClass
8from core.auxiliary.models.PropertyInfo import PropertyInfo
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")
15 created_at = models.DateTimeField(auto_now_add=True)
16 objects = AccessControlManager()
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)
31 created_at = models.DateTimeField(auto_now_add=True)
32 objects = AccessControlManager()
34 @property
35 def zone(self) -> str:
36 return self.group.simulationObject.componentName
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
49 outletPort = self.unitop.ports.get(key__in=property_package_ports, direction=ConType.Outlet)
50 outletStream = outletPort.stream
51 return (inletStream, outletStream)
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)
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)
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)
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
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)
101 created_at = models.DateTimeField(auto_now_add=True)
102 objects = AccessControlManager()
104 @property
105 def zone(self) -> str:
106 return self.zone_object.simulationObject.componentName
108 @property
109 def zone_object(self):
110 return self.stream_data_entry.group
113 class Meta:
114 ordering = ['created_at']
116 @classmethod
117 def create(cls, **kwargs):
118 instance = cls.objects.create(**kwargs)
119 instance.save()
120 return instance
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)
136 created_at = models.DateTimeField(auto_now_add=True)
137 objects = AccessControlManager()
140 class Meta:
141 ordering = ['created_at']
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 }
159 kwargs.update(defaults)
160 instance = cls.objects.create(**kwargs)
161 instance.save()
162 return instance