Coverage for backend/ahuora-builder/src/ahuora_builder/custom/custom_compressor.py: 87%
44 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-05-13 02:47 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-05-13 02:47 +0000
1# Import Pyomo libraries
2from pyomo.environ import (
3 Component,
4 Var,
5 Suffix,
6 units as pyunits,
7)
8import pyomo.environ as pyo
9from pyomo.common.config import ConfigBlock, ConfigValue, In
10from idaes.core.util.tables import create_stream_table_dataframe
11from idaes.core.util.exceptions import ConfigurationError
12# Import IDAES cores
13from idaes.core import (
14 declare_process_block_class,
15 UnitModelBlockData,
16 useDefault,
17)
18from idaes.core.util.config import is_physical_parameter_block
19import idaes.core.util.scaling as iscale
20import idaes.logger as idaeslog
22from ..custom.updated_pressure_changer import (
24 CompressorData,
25)
30# When using this file the name "CustomCompressor" is what is imported
31@declare_process_block_class("CustomCompressor")
32class CustomCompressorData(CompressorData):
33 """
34 Zero order Load model
35 """
37 # CONFIG are options for the unit model, this simple model only has the mandatory config options
38 CONFIG = CompressorData.CONFIG()
40 CONFIG.declare(
41 "power_property_package",
42 ConfigValue(
43 default=useDefault,
44 domain=is_physical_parameter_block,
45 description="Property package to use for power",
46 doc="""Power Property parameter object used to define power calculations,
47 **default** - useDefault.
48 **Valid values:** {
49 **useDefault** - use default package from parent model or flowsheet,
50 **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
51 ),
52 )
53 CONFIG.declare(
54 "power_property_package_args",
55 ConfigBlock(
56 implicit=True,
57 description="Arguments to use for constructing power property packages",
58 doc="""A ConfigBlock with arguments to be passed to a property block(s)
59 and used when constructing these,
60 **default** - None.
61 **Valid values:** {
62 see property package for documentation.}""",
63 ),
64 )
66 def build(self):
67 # build always starts by calling super().build()
68 # This triggers a lot of boilerplate in the background for you
69 super().build()
71 # This creates blank scaling factors, which are populated later
72 self.scaling_factor = Suffix(direction=Suffix.EXPORT)
75 # Add state blocks for inlet, outlet, and waste
76 # These include the state variables and any other properties on demand
78 tmp_dict = dict(**self.config.property_package_args)
79 tmp_dict["parameters"] = self.config.property_package
80 tmp_dict["defined_state"] = True # inlet block is an inlet
81 # Add inlet block
82 # self.properties_in = self.config.property_package.state_block_class(
83 # self.flowsheet().config.time, doc="Material properties of inlet", **tmp_dict
84 # )
87 # Add outlet and waste block
88 tmp_dict["defined_state"] = False # outlet and waste block is not an inlet
89 self.power_properties_out = self.config.power_property_package.state_block_class(
90 self.flowsheet().config.time,
91 doc="Material properties of outlet",
92 **tmp_dict
93 )
95 # Add ports - oftentimes users interact with these rather than the state blocks
96 self.add_port(name="power_outlet", block=self.power_properties_out)
98 # Add constraints
99 # Usually unit models use a control volume to do the mass, energy, and momentum
100 # balances, however, they will be explicitly written out in this example
101 @self.Constraint(
102 self.flowsheet().time,
103 doc="Power out",
104 )
105 def eq_power_out(b, t):
106 return (
107 self.power_properties_out[t].power == self.work_mechanical[t] * -1
108 )
110 def diagnose(self) -> list[tuple[Component, str]]:
111 """
112 Test a few common issues with the heat exchanger model and provide hints to the user.
113 returns a list with the variable the it is most relevant to and a message describing the issue
114 """
115 problems = []
116 inlet_vf = pyo.value(self.control_volume.properties_in[0].vapor_frac) or -1
117 if inlet_vf < 0.9: 117 ↛ 126line 117 didn't jump to line 126 because the condition on line 117 was always true
118 problems.append(
119 (
120 self.control_volume.properties_in[0].vapor_frac,
121 f"""Inlet vapor fraction is {inlet_vf:.2f}. Compressors are typically used for gases,
122 as liquids are incompressible.
123 This low vapor fraction may indicate a problem with your formulation upstream."""
124 )
125 )
126 outlet_pressure = pyo.value(self.control_volume.properties_out[0].pressure) or 0
127 outlet_pressure_bound = self.control_volume.properties_out[0].pressure.ub or 1e8 # 100 MPa
128 if outlet_pressure > outlet_pressure_bound * 0.9: 128 ↛ 129line 128 didn't jump to line 129 because the condition on line 128 was never true
129 problems.append(
130 (
131 self.work_mechanical,
132 f"""Outlet pressure is {outlet_pressure:.2f} Pa, which is close to the upper bound of {outlet_pressure_bound:.2f} Pa.
133 This may indicate that you are putting in
134 an unreasonably large amount of work for this volume of gas"""
135 )
136 )
137 ratioP = pyo.value(self.ratioP[0]) or 0
138 ratioP_bound = self.ratioP[0].ub or 1e3
139 if ratioP > ratioP_bound * 0.9: 139 ↛ 140line 139 didn't jump to line 140 because the condition on line 139 was never true
140 problems.append(
141 (
142 self.work_mechanical,
143 f"""Pressure ratio is {ratioP:.2f}, which is very high.
144 This may indicate that you are putting in
145 an unreasonably large amount of work for this volume of gas."""
146 )
147 )
148 inlet_pressure = pyo.value(self.control_volume.properties_in[0].pressure) or 0
149 if outlet_pressure < inlet_pressure: 149 ↛ 150line 149 didn't jump to line 150 because the condition on line 149 was never true
150 problems.append(
151 (
152 self.control_volume.properties_out[0].pressure,
153 f"""Outlet pressure ({outlet_pressure:.2f} Pa) is less than inlet pressure ({inlet_pressure:.2f} Pa).
154 Compressors should increase pressure, did you mean to use a turbine or valve instead?"""
155 )
156 )
157 return problems