Conditions | 33 |
Total Lines | 165 |
Code Lines | 104 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like solph.flows._flow.Flow.__init__() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.
There are several approaches to avoid long parameter lists:
1 | # -*- coding: utf-8 -*- |
||
123 | def __init__( |
||
124 | self, |
||
125 | nominal_capacity=None, |
||
126 | # --- BEGIN: To be removed for versions >= v0.7 --- |
||
127 | nominal_value=None, |
||
128 | # --- END --- |
||
129 | variable_costs=0, |
||
130 | min=None, |
||
131 | max=None, |
||
132 | fix=None, |
||
133 | positive_gradient_limit=None, |
||
134 | negative_gradient_limit=None, |
||
135 | full_load_time_max=None, |
||
136 | full_load_time_min=None, |
||
137 | integer=False, |
||
138 | bidirectional=False, |
||
139 | nonconvex=None, |
||
140 | lifetime=None, |
||
141 | age=None, |
||
142 | fixed_costs=None, |
||
143 | custom_attributes=None, |
||
144 | ): |
||
145 | # TODO: Check if we can inherit from pyomo.core.base.var _VarData |
||
146 | # then we need to create the var object with |
||
147 | # pyomo.core.base.IndexedVarWithDomain before any SimpleFlowBlock |
||
148 | # is created. E.g. create the variable in the energy system and |
||
149 | # populate with information afterwards when creating objects. |
||
150 | |||
151 | # --- BEGIN: The following code can be removed for versions >= v0.7 --- |
||
152 | if nominal_value is not None: |
||
153 | msg = ( |
||
154 | "For backward compatibility," |
||
155 | + " the option nominal_value overwrites the option" |
||
156 | + " nominal_capacity." |
||
157 | + " Both options cannot be set at the same time." |
||
158 | ) |
||
159 | if nominal_capacity is not None: |
||
160 | raise AttributeError(msg) |
||
161 | else: |
||
162 | warn(msg, FutureWarning) |
||
163 | nominal_capacity = nominal_value |
||
164 | # --- END --- |
||
165 | |||
166 | super().__init__() |
||
167 | |||
168 | if custom_attributes is not None: |
||
169 | for attribute, value in custom_attributes.items(): |
||
170 | setattr(self, attribute, value) |
||
171 | |||
172 | self.nominal_capacity = None |
||
173 | self.investment = None |
||
174 | |||
175 | infinite_error_msg = ( |
||
176 | "{} must be a finite value. Passing an infinite " |
||
177 | "value is not allowed." |
||
178 | ) |
||
179 | if isinstance(nominal_capacity, numbers.Real): |
||
180 | if not math.isfinite(nominal_capacity): |
||
181 | raise ValueError(infinite_error_msg.format("nominal_capacity")) |
||
182 | self.nominal_capacity = nominal_capacity |
||
183 | elif isinstance(nominal_capacity, Investment): |
||
184 | self.investment = nominal_capacity |
||
185 | |||
186 | if fixed_costs is not None: |
||
187 | msg = ( |
||
188 | "Be aware that the fixed costs attribute is only\n" |
||
189 | "meant to be used for multi-period models to depict " |
||
190 | "fixed costs that occur on a yearly basis.\n" |
||
191 | "If you wish to set up a multi-period model, explicitly " |
||
192 | "set the `periods` attribute of your energy system.\n" |
||
193 | "It has been decided to remove the `fixed_costs` " |
||
194 | "attribute with v0.2 for regular uses.\n" |
||
195 | "If you specify `fixed_costs` for a regular model, " |
||
196 | "this will simply be silently ignored." |
||
197 | ) |
||
198 | warn(msg, debugging.SuspiciousUsageWarning) |
||
199 | |||
200 | self.fixed_costs = sequence(fixed_costs) |
||
201 | self.positive_gradient_limit = sequence(positive_gradient_limit) |
||
202 | self.negative_gradient_limit = sequence(negative_gradient_limit) |
||
203 | |||
204 | self.full_load_time_max = full_load_time_max |
||
205 | self.full_load_time_min = full_load_time_min |
||
206 | self.integer = integer |
||
207 | self.nonconvex = nonconvex |
||
208 | self.bidirectional = bidirectional |
||
209 | self.lifetime = lifetime |
||
210 | self.age = age |
||
211 | |||
212 | # It is not allowed to define min or max if fix is defined. |
||
213 | if fix is not None and (min is not None or max is not None): |
||
214 | raise AttributeError( |
||
215 | "It is not allowed to define `min`/`max` if `fix` is defined." |
||
216 | ) |
||
217 | |||
218 | need_nominal_value = [ |
||
219 | "fix", |
||
220 | "full_load_time_max", |
||
221 | "full_load_time_min", |
||
222 | "min", |
||
223 | "max", |
||
224 | ] |
||
225 | sequences = ["fix", "variable_costs", "min", "max"] |
||
226 | if self.investment is None and self.nominal_capacity is None: |
||
227 | for attr in need_nominal_value: |
||
228 | if isinstance(eval(attr), Iterable): |
||
229 | the_attr = eval(attr)[0] |
||
230 | else: |
||
231 | the_attr = eval(attr) |
||
232 | if the_attr is not None: |
||
233 | raise AttributeError( |
||
234 | f"If {attr} is set in a flow (except InvestmentFlow), " |
||
235 | "nominal_value must be set as well.\n" |
||
236 | "Otherwise, it won't have any effect." |
||
237 | ) |
||
238 | # minimum will be set even without nominal limit |
||
239 | |||
240 | # maximum and minimum (absolute values) should be always set, |
||
241 | # as nominal_value or invest might be defined later |
||
242 | if max is None: |
||
243 | max = 1 |
||
244 | if min is None: |
||
245 | if bidirectional: |
||
246 | min = -1 |
||
247 | else: |
||
248 | min = 0 |
||
249 | |||
250 | for attr in sequences: |
||
251 | setattr(self, attr, sequence(eval(attr))) |
||
252 | |||
253 | if self.nominal_capacity is not None and not math.isfinite( |
||
254 | self.max[0] |
||
255 | ): |
||
256 | raise ValueError(infinite_error_msg.format("max")) |
||
257 | |||
258 | # Checking for impossible gradient combinations |
||
259 | if self.nonconvex: |
||
260 | if self.nonconvex.positive_gradient_limit[0] is not None and ( |
||
261 | self.positive_gradient_limit[0] is not None |
||
262 | or self.negative_gradient_limit[0] is not None |
||
263 | ): |
||
264 | raise ValueError( |
||
265 | "You specified a positive gradient in your nonconvex " |
||
266 | "option. This cannot be combined with a positive or a " |
||
267 | "negative gradient for a standard flow!" |
||
268 | ) |
||
269 | |||
270 | if self.nonconvex.negative_gradient_limit[0] is not None and ( |
||
271 | self.positive_gradient_limit[0] is not None |
||
272 | or self.negative_gradient_limit[0] is not None |
||
273 | ): |
||
274 | raise ValueError( |
||
275 | "You specified a negative gradient in your nonconvex " |
||
276 | "option. This cannot be combined with a positive or a " |
||
277 | "negative gradient for a standard flow!" |
||
278 | ) |
||
279 | |||
280 | if ( |
||
281 | self.investment |
||
282 | and self.nonconvex |
||
283 | and not np.isfinite(self.investment.maximum.max()) |
||
284 | ): |
||
285 | raise AttributeError( |
||
286 | "Investment into a non-convex flows needs a maximum " |
||
287 | + "investment to be set." |
||
288 | ) |
||
289 |