Coverage for backend/idaes_service/solver/custom/energy/link.py: 95%
35 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 pyomo.environ import (
3 Var,
4 Suffix,
5 units as pyunits,
6)
7from pyomo.common.config import ConfigBlock, ConfigValue, In
9# Import IDAES cores
10from idaes.core import (
11 declare_process_block_class,
12 UnitModelBlockData,
13 useDefault,
14)
15from idaes.core.util.config import is_physical_parameter_block
16import idaes.core.util.scaling as iscale
17import idaes.logger as idaeslog
19# Set up logger
20_log = idaeslog.getLogger(__name__)
23# When using this file the name "Link" is what is imported
24@declare_process_block_class("Link")
25class LinkData(UnitModelBlockData):
26 """
27 Zero order Link model
28 """
30 # CONFIG are options for the unit model, this simple model only has the mandatory config options
31 CONFIG = ConfigBlock()
33 CONFIG.declare(
34 "dynamic",
35 ConfigValue(
36 domain=In([False]),
37 default=False,
38 description="Dynamic model flag - must be False",
39 doc="""Indicates whether this model will be dynamic or not,
40 **default** = False. The Bus unit does not support dynamic
41 behavior, thus this must be False.""",
42 ),
43 )
44 CONFIG.declare(
45 "has_holdup",
46 ConfigValue(
47 default=False,
48 domain=In([False]),
49 description="Holdup construction flag - must be False",
50 doc="""Indicates whether holdup terms should be constructed or not.
51 **default** - False. The Bus unit does not have defined volume, thus
52 this must be False.""",
53 ),
54 )
55 CONFIG.declare(
56 "property_package",
57 ConfigValue(
58 default=useDefault,
59 domain=is_physical_parameter_block,
60 description="Property package to use for control volume",
61 doc="""Property parameter object used to define property calculations,
62 **default** - useDefault.
63 **Valid values:** {
64 **useDefault** - use default package from parent model or flowsheet,
65 **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
66 ),
67 )
68 CONFIG.declare(
69 "property_package_args",
70 ConfigBlock(
71 implicit=True,
72 description="Arguments to use for constructing property packages",
73 doc="""A ConfigBlock with arguments to be passed to a property block(s)
74 and used when constructing these,
75 **default** - None.
76 **Valid values:** {
77 see property package for documentation.}""",
78 ),
79 )
81 def build(self):
82 # build always starts by calling super().build()
83 # This triggers a lot of boilerplate in the background for you
84 super().build()
86 # This creates blank scaling factors, which are populated later
87 self.scaling_factor = Suffix(direction=Suffix.EXPORT)
90 # Add state blocks for inlet, outlet, and waste
91 # These include the state variables and any other properties on demand
92 # Add inlet block
93 tmp_dict = dict(**self.config.property_package_args)
94 tmp_dict["parameters"] = self.config.property_package
95 tmp_dict["defined_state"] = True # inlet block is an inlet
96 self.properties_in = self.config.property_package.state_block_class(
97 self.flowsheet().config.time, doc="Material properties of inlet", **tmp_dict
98 )
99 # Add outlet and waste block
100 tmp_dict["defined_state"] = False # outlet and waste block is not an inlet
101 self.properties_out = self.config.property_package.state_block_class(
102 self.flowsheet().config.time,
103 doc="Material properties of outlet",
104 **tmp_dict
105 )
107 # Add ports - oftentimes users interact with these rather than the state blocks
108 self.add_port(name="inlet", block=self.properties_in)
109 self.add_port(name="outlet", block=self.properties_out)
111 # Add variable for efficiency
112 self.efficiency = Var(self.flowsheet().config.time,
113 initialize=1.0,
114 doc="Efficiency of the bus",
115 )
117 # Add constraints
118 # Usually unit models use a control volume to do the mass, energy, and momentum
119 # balances, however, they will be explicitly written out in this example
120 @self.Constraint(
121 self.flowsheet().time,
122 doc="Power usage",
123 )
124 def eq_power_balance(b, t):
125 return (
126 b.properties_in[t].power * self.efficiency[t] == b.properties_out[t].power
127 )
130 def calculate_scaling_factors(self):
131 super().calculate_scaling_factors()
133 def initialize(blk, *args, **kwargs):
134 # Just propagate the power from inlet to outlet, good simple method of initialization
135 for i in blk.properties_in.index_set():
136 if not blk.properties_out[i].power.fixed: 136 ↛ 135line 136 didn't jump to line 135 because the condition on line 136 was always true
137 blk.properties_out[i].power = blk.properties_in[i].power.value