Coverage for backend/idaes_service/solver/custom/energy/energy_mixer.py: 84%
57 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
1# Import Pyomo libraries
2from stringprep import in_table_a1
3from pyomo.environ import (
4 Var,
5 Suffix,
6 units as pyunits,
7)
8from pyomo.common.config import ConfigBlock, ConfigValue, In
10# Import IDAES cores
11from idaes.core import (
12 declare_process_block_class,
13 UnitModelBlockData,
14 useDefault,
15)
16from idaes.core.util.config import is_physical_parameter_block
17import idaes.core.util.scaling as iscale
18import idaes.logger as idaeslog
20from idaes.core.util.tables import create_stream_table_dataframe
22from idaes.core.util.exceptions import ConfigurationError, BurntToast
24# Set up logger
25_log = idaeslog.getLogger(__name__)
28# When using this file the name "EnergyMixer" is what is imported
29@declare_process_block_class("EnergyMixer")
30class EnergyMixerData(UnitModelBlockData):
31 """
32 Zero order energy_mixer model
33 """
35 # CONFIG are options for the unit model, this simple model only has the mandatory config options
36 CONFIG = ConfigBlock()
38 CONFIG.declare(
39 "dynamic",
40 ConfigValue(
41 domain=In([False]),
42 default=False,
43 description="Dynamic model flag - must be False",
44 doc="""Indicates whether this model will be dynamic or not,
45 **default** = False. The Bus unit does not support dynamic
46 behavior, thus this must be False.""",
47 ),
48 )
49 CONFIG.declare(
50 "has_holdup",
51 ConfigValue(
52 default=False,
53 domain=In([False]),
54 description="Holdup construction flag - must be False",
55 doc="""Indicates whether holdup terms should be constructed or not.
56 **default** - False. The Bus unit does not have defined volume, thus
57 this must be False.""",
58 ),
59 )
60 CONFIG.declare(
61 "property_package",
62 ConfigValue(
63 default=useDefault,
64 domain=is_physical_parameter_block,
65 description="Property package to use for control volume",
66 doc="""Property parameter object used to define property calculations,
67 **default** - useDefault.
68 **Valid values:** {
69 **useDefault** - use default package from parent model or flowsheet,
70 **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
71 ),
72 )
73 CONFIG.declare(
74 "property_package_args",
75 ConfigBlock(
76 implicit=True,
77 description="Arguments to use for constructing property packages",
78 doc="""A ConfigBlock with arguments to be passed to a property block(s)
79 and used when constructing these,
80 **default** - None.
81 **Valid values:** {
82 see property package for documentation.}""",
83 ),
84 )
85 CONFIG.declare(
86 "num_inlets",
87 ConfigValue(
88 default=False,
89 domain=int,
90 description="Number of inlets to add",
91 doc="""Number of inlets to add""",
92 ),
93 )
95 def build(self):
96 # build always starts by calling super().build()
97 # This triggers a lot of boilerplate in the background for you
98 super().build()
100 # This creates blank scaling factors, which are populated later
101 self.scaling_factor = Suffix(direction=Suffix.EXPORT)
104 # Defining parameters of state block class
105 tmp_dict = dict(**self.config.property_package_args)
106 tmp_dict["parameters"] = self.config.property_package
107 tmp_dict["defined_state"] = True # inlet block is an inlet
109 # Add state blocks for inlet, outlet, and waste
110 # These include the state variables and any other properties on demand
111 num_inlets = self.config.num_inlets
113 self.inlet_list = [ "inlet_" + str(i+1) for i in range(num_inlets) ]
116 self.inlet_blocks = []
117 for name in self.inlet_list:
118 # add properties_inlet_1, properties_inlet2 etc
119 state_block = self.config.property_package.state_block_class(
120 self.flowsheet().config.time, doc="inlet power", **tmp_dict
121 )
122 self.inlet_blocks.append(state_block)
123 # Dynamic equivalent to self.properties_inlet_1 = stateblock
124 setattr(self,"properties_" + name, state_block)
125 # also add the port
126 self.add_port(name=name,block=state_block)
129 # Add outlet state block
130 tmp_dict["defined_state"] = False # outlet and waste block is not an inlet
131 self.properties_out = self.config.property_package.state_block_class(
132 self.flowsheet().config.time,
133 doc="Material properties of outlet",
134 **tmp_dict
135 )
137 # Add outlet port
138 self.add_port(name="outlet", block=self.properties_out)
140 #Add variables for capacity and efficiency:
141 self.efficiency = Var(self.flowsheet().config.time,
142 initialize=1.0,
143 doc="Efficiency of the link",
144 )
145 self.capacity = Var(
146 initialize=1.0,
147 units = pyunits.W,
148 doc="Capacity of the link",
149 )
152 # Add constraints
153 # Usually unit models use a control volume to do the mass, energy, and momentum
154 # balances, however, they will be explicitly written out in this example
156 @self.Constraint(
157 self.flowsheet().time,
158 doc="Power usage",
159 )
160 def eq_power_balance(b, t):
161 return (
162 sum(
163 state_block[t].power for state_block in self.inlet_blocks
164 ) * self.efficiency[t]
165 == b.properties_out[t].power
166 )
168 def calculate_scaling_factors(self):
169 super().calculate_scaling_factors()
171 def initialize(blk, *args, **kwargs):
172 # Just propagate the power from inlet to outlet, good simple method of initialization
173 for t in blk.flowsheet().time:
174 power_in = 0
175 for state_block in blk.inlet_blocks:
176 power_in += state_block[t].power.value
177 if not blk.properties_out[t].power.fixed: 177 ↛ 179line 177 didn't jump to line 179 because the condition on line 177 was always true
178 blk.properties_out[t].power = power_in
179 if(power_in > blk.capacity.value): 179 ↛ 180line 179 didn't jump to line 180 because the condition on line 179 was never true
180 raise BurntToast(
181 "Danger: Input power exceeds energy mixer capacity. Please either increase capacity or lower input power.".format(blk.name)
182 )
183 def _get_stream_table_contents(self, time_point=0):
184 """
185 Assume unit has standard configuration of 1 inlet and 1 outlet.
187 Developers should overload this as appropriate.
188 """
190 io_dict = {}
191 for inlet_name in self.inlet_list:
192 io_dict[inlet_name] = getattr(self, inlet_name) # get a reference to the port
194 io_dict["Outlet"] = self.outlet
195 return create_stream_table_dataframe(io_dict, time_point=time_point)