Coverage for backend/idaes_service/solver/custom/energy/hydro.py: 49%
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
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
19from idaes.core.util.tables import create_stream_table_dataframe
21from idaes.core.util.exceptions import ConfigurationError
23# Set up logger
24_log = idaeslog.getLogger(__name__)
27# When using this file the name "Solar" is what is imported
28@declare_process_block_class("Hydro")
29class HydroData(UnitModelBlockData):
30 """
31 Zero order Link model
32 """
34 # CONFIG are options for the unit model, this simple model only has the mandatory config options
35 CONFIG = ConfigBlock()
37 CONFIG.declare(
38 "dynamic",
39 ConfigValue(
40 domain=In([False]),
41 default=False,
42 description="Dynamic model flag - must be False",
43 doc="""Indicates whether this model will be dynamic or not,
44 **default** = False. The Bus unit does not support dynamic
45 behavior, thus this must be False.""",
46 ),
47 )
48 CONFIG.declare(
49 "has_holdup",
50 ConfigValue(
51 default=False,
52 domain=In([False]),
53 description="Holdup construction flag - must be False",
54 doc="""Indicates whether holdup terms should be constructed or not.
55 **default** - False. The Bus unit does not have defined volume, thus
56 this must be False.""",
57 ),
58 )
59 CONFIG.declare(
60 "property_package",
61 ConfigValue(
62 default=useDefault,
63 domain=is_physical_parameter_block,
64 description="Property package to use for control volume",
65 doc="""Property parameter object used to define property calculations,
66 **default** - useDefault.
67 **Valid values:** {
68 **useDefault** - use default package from parent model or flowsheet,
69 **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
70 ),
71 )
72 CONFIG.declare(
73 "property_package_args",
74 ConfigBlock(
75 implicit=True,
76 description="Arguments to use for constructing property packages",
77 doc="""A ConfigBlock with arguments to be passed to a property block(s)
78 and used when constructing these,
79 **default** - None.
80 **Valid values:** {
81 see property package for documentation.}""",
82 ),
83 )
85 def build(self):
86 # build always starts by calling super().build()
87 # This triggers a lot of boilerplate in the background for you
88 super().build()
90 # This creates blank scaling factors, which are populated later
91 self.scaling_factor = Suffix(direction=Suffix.EXPORT)
94 # Add state blocks for inlet, outlet, and waste
95 # These include the state variables and any other properties on demand
96 # Add inlet block
97 tmp_dict = dict(**self.config.property_package_args)
98 tmp_dict["parameters"] = self.config.property_package
99 #tmp_dict["defined_state"] = True # inlet block is an inlet
101 # Add outlet
102 tmp_dict["defined_state"] = False # outlet is not an inlet
103 self.properties_out = self.config.property_package.state_block_class(
104 self.flowsheet().config.time,
105 doc="Material properties of outlet",
106 **tmp_dict
107 )
109 # Add ports - oftentimes users interact with these rather than the state blocks
111 self.add_port(name="outlet", block=self.properties_out)
113 # Add variable for efficiency
114 self.efficiency = Var(
115 initialize=1.0,
116 doc="Efficiency",
117 )
119 # Add variable for flow rate of water m3/s
120 self.flow_rate = Var(self.flowsheet().config.time,
121 initialize=1.0, units = pyunits.m**3/pyunits.s,
122 doc="Flow rate of water",
123 )
125 # Add variable for Static Head in meters
126 self.static_head = Var(
127 initialize=1.0, units = pyunits.m,
128 doc="Effective Head or height of the water column",
129 )
130 # Add variable for Cp which is density of water in kg/m3
131 self.Cp= 1000
132 # Add variable for Cg which is gravitational acceleration in m/s2
133 self.Cg= 9.81
135 # Add constraints
136 @self.Constraint(
137 self.flowsheet().time,
138 doc="Power usage",
139 )
141 def eq_power_balance(b, t):
142 return (
143 (self.flow_rate[t] * self.Cg * self.Cp * self.efficiency * self.static_head == b.properties_out[t].power)
144 )
147 def calculate_scaling_factors(self):
148 super().calculate_scaling_factors()
150 def initialize(blk, *args, **kwargs):
151 # Just propagate the power from inlet to outlet, good simple method of initialization
152 pass
154 def _get_stream_table_contents(self, time_point=0):
155 """
156 Assume unit has standard configuration of 1 inlet and 1 outlet.
158 Developers should overload this as appropriate.
159 """
160 try:
161 return create_stream_table_dataframe(
162 {"Outlet": self.outlet}, time_point=time_point
163 )
164 except AttributeError:
165 raise ConfigurationError(
166 f"Unit model {self.name} does not have the standard Port "
167 f"names (inlet and outlet). Please contact the unit model "
168 f"developer to develop a unit specific stream table."
169 )