Coverage for backend/idaes_service/solver/custom/energy/load.py: 44%
41 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
8from idaes.core.util.tables import create_stream_table_dataframe
9from idaes.core.util.exceptions import ConfigurationError
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
20# Set up logger
21_log = idaeslog.getLogger(__name__)
24# When using this file the name "Load" is what is imported
25@declare_process_block_class("Load")
26class loadData(UnitModelBlockData):
27 """
28 Zero order Load model
29 """
31 # CONFIG are options for the unit model, this simple model only has the mandatory config options
32 CONFIG = ConfigBlock()
34 CONFIG.declare(
35 "dynamic",
36 ConfigValue(
37 domain=In([False]),
38 default=False,
39 description="Dynamic model flag - must be False",
40 doc="""Indicates whether this model will be dynamic or not,
41 **default** = False. The Bus unit does not support dynamic
42 behavior, thus this must be False.""",
43 ),
44 )
45 CONFIG.declare(
46 "has_holdup",
47 ConfigValue(
48 default=False,
49 domain=In([False]),
50 description="Holdup construction flag - must be False",
51 doc="""Indicates whether holdup terms should be constructed or not.
52 **default** - False. The Bus unit does not have defined volume, thus
53 this must be False.""",
54 ),
55 )
56 CONFIG.declare(
57 "property_package",
58 ConfigValue(
59 default=useDefault,
60 domain=is_physical_parameter_block,
61 description="Property package to use for control volume",
62 doc="""Property parameter object used to define property calculations,
63 **default** - useDefault.
64 **Valid values:** {
65 **useDefault** - use default package from parent model or flowsheet,
66 **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
67 ),
68 )
69 CONFIG.declare(
70 "property_package_args",
71 ConfigBlock(
72 implicit=True,
73 description="Arguments to use for constructing property packages",
74 doc="""A ConfigBlock with arguments to be passed to a property block(s)
75 and used when constructing these,
76 **default** - None.
77 **Valid values:** {
78 see property package for documentation.}""",
79 ),
80 )
82 def build(self):
83 # build always starts by calling super().build()
84 # This triggers a lot of boilerplate in the background for you
85 super().build()
87 # This creates blank scaling factors, which are populated later
88 self.scaling_factor = Suffix(direction=Suffix.EXPORT)
91 # Add state blocks for inlet, outlet, and waste
92 # These include the state variables and any other properties on demand
93 # Add inlet block
94 tmp_dict = dict(**self.config.property_package_args)
95 tmp_dict["parameters"] = self.config.property_package
96 tmp_dict["defined_state"] = True # inlet block is an inlet
97 self.properties_in = self.config.property_package.state_block_class(
98 self.flowsheet().config.time, doc="Material properties of inlet", **tmp_dict
99 )
100 # Add outlet and waste block
101 tmp_dict["defined_state"] = False # outlet and waste block is not an inlet
102 self.properties_out = self.config.property_package.state_block_class(
103 self.flowsheet().config.time,
104 doc="Material properties of outlet",
105 **tmp_dict
106 )
108 # Add ports - oftentimes users interact with these rather than the state blocks
109 self.add_port(name="outlet", block=self.properties_out)
111 # Add variables
112 self.in_power = Var(self.flowsheet().config.time,
113 initialize=1.0,
114 doc="Load voltage",
115 units = pyunits.W
116 )
118 # Add constraints
119 # Usually unit models use a control volume to do the mass, energy, and momentum
120 # balances, however, they will be explicitly written out in this example
121 @self.Constraint(
122 self.flowsheet().time,
123 doc="Power usage",
124 )
125 def eq_power_in_balance(b, t):
126 return (
127 b.properties_out[t].power == self.in_power[t] * -1
128 )
131 def calculate_scaling_factors(self):
132 super().calculate_scaling_factors()
134 def initialize(blk, *args, **kwargs):
135 # Just propagate the power from inlet to outlet, good simple method of initialization
136 for i in blk.properties_in.index_set():
137 if not blk.properties_out[i].power.fixed:
138 blk.properties_out[i].power = blk.properties_in[i].power.value
140 def _get_stream_table_contents(self, time_point=0):
141 """
142 Assume unit has standard configuration of 1 inlet and 1 outlet.
144 Developers should overload this as appropriate.
145 """
146 try:
147 return create_stream_table_dataframe(
148 {"outlet": self.outlet}, time_point=time_point
149 )
150 except AttributeError:
151 raise ConfigurationError(
152 f"Unit model {self.name} does not have the standard Port "
153 f"names (inlet and outlet). Please contact the unit model "
154 f"developer to develop a unit specific stream table."
155 )