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