Coverage for backend/idaes_service/solver/custom/energy/wind.py: 43%
51 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 ast import Expression
3from cmath import pi
4from pyomo.environ import (
5 Var,
6 Suffix,
7 units as pyunits,
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 "Wind" is what is imported
30@declare_process_block_class("Wind")
31class WindData(UnitModelBlockData):
32 """
33 Zero order Link 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 )
87 def build(self):
88 # build always starts by calling super().build()
89 # This triggers a lot of boilerplate in the background for you
90 super().build()
92 # This creates blank scaling factors, which are populated later
93 self.scaling_factor = Suffix(direction=Suffix.EXPORT)
96 # Add state blocks for inlet, outlet, and waste
97 # These include the state variables and any other properties on demand
98 # Add inlet block
99 tmp_dict = dict(**self.config.property_package_args)
100 tmp_dict["parameters"] = self.config.property_package
101 #tmp_dict["defined_state"] = True # inlet block is an inlet
103 # Add outlet
104 tmp_dict["defined_state"] = False # outlet is not an inlet
105 self.properties_out = self.config.property_package.state_block_class(
106 self.flowsheet().config.time,
107 doc="Material properties of outlet",
108 **tmp_dict
109 )
111 # Add ports - oftentimes users interact with these rather than the state blocks
113 self.add_port(name="outlet", block=self.properties_out)
115 # Add variable for efficiency
116 self.efficiency = Var(
117 initialize=1.0,
118 doc="Efficiency of the turbine",
119 )
121 # Add variable for Windspeed
122 self.windspeed = Var(self.flowsheet().config.time,
123 initialize=1.0, units = pyunits.m/pyunits.s,
124 doc="Speed of wind hitting the turbines",
125 )
127 # Add variable for Pressure
128 self.pressure = Var(self.flowsheet().config.time,
129 initialize=1.0, units = pyunits.Pa,
130 doc="Air pressure",
131 )
133 # Add variable for Temperature
134 self.air_temp = Var(self.flowsheet().config.time,
135 initialize=1.0, units = pyunits.degK,
136 doc="Air Temperature",
137 )
139 # Add variable for Humidity
140 self.humidity = Var(self.flowsheet().config.time,
141 initialize=1.0,
142 doc="Air humidity",
143 )
145 # Add variable for rotor diameter
146 self.rotor_diameter = Var(
147 initialize=1.0, units = pyunits.m,
148 doc="Rotor Diameter",
149 )
151 # Add variable for no. of turbines
152 self.turbine_count = Var(
153 initialize=1.0,
154 doc="Number of turbines",
155 )
157 # Simple expressions
158 self.radius = self.rotor_diameter / 2
159 self.area = pi * self.radius**2
160 self.Cp= 0.579 # Power coefficient, typically between 0.4 and 0.6 for wind turbines
163 # Expression indexed by time
164 @self.Expression(
165 self.flowsheet().time
166 )
167 def density(b,t):
168 return(
169 (self.pressure[t]/(287.05 * self.air_temp[t])) * (1-0.378*(self.humidity[t]))
170 )
173 # Add constraints
174 @self.Constraint(
175 self.flowsheet().time,
176 doc="Power usage",
177 )
178 def eq_power_balance(b, t):
179 return (
180 0.5 * self.density[t] * self.area * self.windspeed[t]**3 * self.Cp * self.efficiency * self.turbine_count == b.properties_out[t].power
181 )
184 def calculate_scaling_factors(self):
185 super().calculate_scaling_factors()
187 def initialize(blk, *args, **kwargs):
188 # Just propagate the power from inlet to outlet, good simple method of initialization
189 pass
191 def _get_stream_table_contents(self, time_point=0):
192 """
193 Assume unit has standard configuration of 1 inlet and 1 outlet.
195 Developers should overload this as appropriate.
196 """
197 try:
198 return create_stream_table_dataframe(
199 {"Outlet": self.outlet}, time_point=time_point
200 )
201 except AttributeError:
202 raise ConfigurationError(
203 f"Unit model {self.name} does not have the standard Port "
204 f"names (inlet and outlet). Please contact the unit model "
205 f"developer to develop a unit specific stream table."
206 )