Coverage for backend/idaes_service/solver/custom/energy/energy_splitter.py: 28%
68 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 Set,
8)
9from pyomo.common.config import ConfigBlock, ConfigValue, In
11# Import IDAES cores
12from idaes.core import (
13 declare_process_block_class,
14 UnitModelBlockData,
15 useDefault,
16)
17from idaes.core.util.config import is_physical_parameter_block
18import idaes.core.util.scaling as iscale
19import idaes.logger as idaeslog
21from idaes.core.util.tables import create_stream_table_dataframe
23from idaes.core.util.exceptions import ConfigurationError
25# Set up logger
26_log = idaeslog.getLogger(__name__)
29# When using this file the name "EnergyMixer" is what is imported
30@declare_process_block_class("EnergySplitter")
31class EnergySplitterData(UnitModelBlockData):
32 """
33 Zero order energy splitter model
34 """
36 # CONFIG are options for the unit model, this simple model only has the mandatory config options
37 CONFIG = ConfigBlock()
39 CONFIG.declare(
40 "dynamic",
41 ConfigValue(
42 domain=In([False]),
43 default=False,
44 description="Dynamic model flag - must be False",
45 doc="""Indicates whether this model will be dynamic or not,
46 **default** = False. The Bus unit does not support dynamic
47 behavior, thus this must be False.""",
48 ),
49 )
50 CONFIG.declare(
51 "has_holdup",
52 ConfigValue(
53 default=False,
54 domain=In([False]),
55 description="Holdup construction flag - must be False",
56 doc="""Indicates whether holdup terms should be constructed or not.
57 **default** - False. The Bus unit does not have defined volume, thus
58 this must be False.""",
59 ),
60 )
61 CONFIG.declare(
62 "property_package",
63 ConfigValue(
64 default=useDefault,
65 domain=is_physical_parameter_block,
66 description="Property package to use for control volume",
67 doc="""Property parameter object used to define property calculations,
68 **default** - useDefault.
69 **Valid values:** {
70 **useDefault** - use default package from parent model or flowsheet,
71 **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
72 ),
73 )
74 CONFIG.declare(
75 "property_package_args",
76 ConfigBlock(
77 implicit=True,
78 description="Arguments to use for constructing property packages",
79 doc="""A ConfigBlock with arguments to be passed to a property block(s)
80 and used when constructing these,
81 **default** - None.
82 **Valid values:** {
83 see property package for documentation.}""",
84 ),
85 )
86 CONFIG.declare(
87 "num_inlets",
88 ConfigValue(
89 default=False,
90 domain=int,
91 description="Number of inlets to add",
92 doc="""Number of inlets to add""",
93 ),
94 )
95 CONFIG.declare(
96 "num_outlets",
97 ConfigValue(
98 default=False,
99 domain=int,
100 description="Number of outlets to add",
101 doc="""Number of outlets to add""",
102 ),
103 )
105 def build(self):
106 # build always starts by calling super().build()
107 # This triggers a lot of boilerplate in the background for you
108 super().build()
110 # This creates blank scaling factors, which are populated later
111 self.scaling_factor = Suffix(direction=Suffix.EXPORT)
114 # Defining parameters of state block class
115 tmp_dict = dict(**self.config.property_package_args)
116 tmp_dict["parameters"] = self.config.property_package
117 tmp_dict["defined_state"] = True # inlet block is an inlet
119 # Add state blocks for inlet, outlet, and waste
120 # These include the state variables and any other properties on demand
121 num_inlets = self.config.num_inlets
123 self.inlet_list = [ "inlet_" + str(i+1) for i in range(num_inlets) ]
126 self.inlet_blocks = []
127 for name in self.inlet_list:
128 # add properties_inlet_1, properties_inlet2 etc
129 state_block = self.config.property_package.state_block_class(
130 self.flowsheet().config.time, doc="inlet power", **tmp_dict
131 )
132 self.inlet_blocks.append(state_block)
133 # Dynamic equivalent to self.properties_inlet_1 = stateblock
134 setattr(self,"properties_" + name, state_block)
135 # also add the port
136 self.add_port(name=name,block=state_block)
139 num_outlets = self.config.num_outlets
140 self.outlet_list = [ "outlet_" + str(i+1) for i in range(num_outlets) ]
141 self.outlet_set = Set(initialize=self.outlet_list)
144 self.outlet_blocks = []
145 for name in self.outlet_list:
146 # add properties_inlet_1, properties_inlet2 etc
147 state_block = self.config.property_package.state_block_class(
148 self.flowsheet().config.time, doc="inlet power", **tmp_dict
149 )
150 self.outlet_blocks.append(state_block)
151 # Dynamic equivalent to self.properties_inlet_1 = stateblock
152 setattr(self,"properties_" + name, state_block)
153 # also add the port
154 self.add_port(name=name,block=state_block)
157 #Add variable for capacity:
158 self.capacity = Var(
159 self.flowsheet().config.time,
160 initialize=1.0,
161 units = pyunits.W,
162 doc="Capacity of the link",
163 )
164 # Add variable for power splitting
165 self.split_fraction = Var(
166 self.flowsheet().time,
167 self.outlet_set,
168 initialize=1.0,
169 #units = pyunits.dimensionless,
170 doc="How the power is split between outlets",
171 )
174 @self.Expression(
175 self.flowsheet().time,
176 )
177 def total_power(b, t):
178 return (
179 sum(
180 state_block[t].power for state_block in self.inlet_blocks
181 )
182 )
184 # Add constraints
185 @self.Constraint(
186 self.flowsheet().time,
187 self.outlet_list,
188 doc="power split",
189 )
190 def eq_power(b,t,o):
191 outlet_block = getattr(self,"properties_" + o)
192 return outlet_block[t].power == (
193 self.total_power[t] * b.split_fraction[t,o])
195 @self.Constraint(
196 self.flowsheet().time,
197 doc="Split fraction sum to 1",
198 )
199 def eq_split_fraction_sum(b, t):
200 return sum(b.split_fraction[t, o] for o in self.outlet_list) == 1.0
203 def calculate_scaling_factors(self):
204 super().calculate_scaling_factors()
206 def initialize(blk, *args, **kwargs):
207 # Just propagate the power from inlet to outlet, good simple method of initialization
208 for t in blk.flowsheet().time:
209 for state_block in blk.inlet_blocks:
210 if(state_block[t].power.value > blk.capacity[t].value):
211 raise ConfigurationError(
212 "Danger: Input power exceeds splitter capacity. Please either increase capacity or lower input power."
213 )
215 def _get_stream_table_contents(self, time_point=0):
216 """
217 Assume unit has standard configuration of 1 inlet and 1 outlet.
219 Developers should overload this as appropriate.
220 """
222 io_dict = {}
223 for inlet_name in self.inlet_list:
224 io_dict[inlet_name] = getattr(self, inlet_name) # get a reference to the port
226 io_dict = {}
227 for outlet_name in self.outlet_list:
228 io_dict[outlet_name] = getattr(self, outlet_name)