| Conditions | 35 |
| Total Lines | 207 |
| Code Lines | 126 |
| 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 -*- |
||
| 125 | def __init__( |
||
| 126 | self, |
||
| 127 | nominal_capacity=None, |
||
| 128 | # --- BEGIN: To be removed for versions >= v0.7 --- |
||
| 129 | nominal_value=None, |
||
| 130 | # --- END --- |
||
| 131 | variable_costs=0, |
||
| 132 | min=0, |
||
| 133 | max=1, |
||
| 134 | fix=None, |
||
| 135 | positive_gradient_limit=None, |
||
| 136 | negative_gradient_limit=None, |
||
| 137 | full_load_time_max=None, |
||
| 138 | full_load_time_min=None, |
||
| 139 | integer=False, |
||
| 140 | # --- BEGIN: To be removed for versions >= v0.7 --- |
||
| 141 | bidirectional=False, |
||
| 142 | # --- END |
||
| 143 | nonconvex=None, |
||
| 144 | lifetime=None, |
||
| 145 | age=None, |
||
| 146 | fixed_costs=None, |
||
| 147 | custom_attributes=None, # To be removed for versions >= v0.7 |
||
| 148 | custom_properties=None, |
||
| 149 | ): |
||
| 150 | # TODO: Check if we can inherit from pyomo.core.base.var _VarData |
||
| 151 | # then we need to create the var object with |
||
| 152 | # pyomo.core.base.IndexedVarWithDomain before any SimpleFlowBlock |
||
| 153 | # is created. E.g. create the variable in the energy system and |
||
| 154 | # populate with information afterwards when creating objects. |
||
| 155 | |||
| 156 | # --- BEGIN: The following code can be removed for versions >= v0.7 --- |
||
| 157 | if nominal_value is not None: |
||
| 158 | msg = ( |
||
| 159 | "For backward compatibility," |
||
| 160 | + " the option nominal_value overwrites the option" |
||
| 161 | + " nominal_capacity." |
||
| 162 | + " Both options cannot be set at the same time." |
||
| 163 | ) |
||
| 164 | if nominal_capacity is not None: |
||
| 165 | raise AttributeError(msg) |
||
| 166 | else: |
||
| 167 | warn(msg, FutureWarning) |
||
| 168 | nominal_capacity = nominal_value |
||
| 169 | |||
| 170 | if custom_attributes is not None: |
||
| 171 | msg = ( |
||
| 172 | "For backward compatibility," |
||
| 173 | + " the option custom_attributes overwrites the option" |
||
| 174 | + " custom_properties." |
||
| 175 | + " Both options cannot be set at the same time." |
||
| 176 | ) |
||
| 177 | if custom_properties is not None: |
||
| 178 | raise AttributeError(msg) |
||
| 179 | else: |
||
| 180 | warn(msg, FutureWarning) |
||
| 181 | custom_properties = custom_attributes |
||
| 182 | # --- END --- |
||
| 183 | |||
| 184 | super().__init__(custom_properties=custom_properties) |
||
| 185 | |||
| 186 | # --- BEGIN: The following code can be removed for versions >= v0.7 --- |
||
| 187 | if custom_attributes is not None: |
||
| 188 | for attribute, value in custom_attributes.items(): |
||
| 189 | setattr(self, attribute, value) |
||
| 190 | # --- END --- |
||
| 191 | |||
| 192 | self.nominal_capacity = None |
||
| 193 | self.investment = None |
||
| 194 | |||
| 195 | infinite_error_msg = ( |
||
| 196 | "{} must be a finite value. Passing an infinite " |
||
| 197 | "value is not allowed." |
||
| 198 | ) |
||
| 199 | if isinstance(nominal_capacity, numbers.Real): |
||
| 200 | if not math.isfinite(nominal_capacity): |
||
| 201 | raise ValueError(infinite_error_msg.format("nominal_capacity")) |
||
| 202 | self.nominal_capacity = nominal_capacity |
||
| 203 | elif isinstance(nominal_capacity, Investment): |
||
| 204 | self.investment = nominal_capacity |
||
| 205 | |||
| 206 | if fixed_costs is not None: |
||
| 207 | msg = ( |
||
| 208 | "Be aware that the fixed costs attribute is only\n" |
||
| 209 | "meant to be used for multi-period models to depict " |
||
| 210 | "fixed costs that occur on a yearly basis.\n" |
||
| 211 | "If you wish to set up a multi-period model, explicitly " |
||
| 212 | "set the `periods` attribute of your energy system.\n" |
||
| 213 | "It has been decided to remove the `fixed_costs` " |
||
| 214 | "attribute with v0.2 for regular uses.\n" |
||
| 215 | "If you specify `fixed_costs` for a regular model, " |
||
| 216 | "this will simply be silently ignored." |
||
| 217 | ) |
||
| 218 | warn(msg, debugging.SuspiciousUsageWarning) |
||
| 219 | |||
| 220 | self.fixed_costs = sequence(fixed_costs) |
||
| 221 | self.variable_costs = sequence(variable_costs) |
||
| 222 | self.positive_gradient_limit = sequence(positive_gradient_limit) |
||
| 223 | self.negative_gradient_limit = sequence(negative_gradient_limit) |
||
| 224 | |||
| 225 | self.full_load_time_max = full_load_time_max |
||
| 226 | self.full_load_time_min = full_load_time_min |
||
| 227 | self.integer = integer |
||
| 228 | self.nonconvex = nonconvex |
||
| 229 | # --- BEGIN: To be removed for versions >= v0.7 --- |
||
| 230 | self.bidirectional = bidirectional |
||
| 231 | # --- END |
||
| 232 | self.lifetime = lifetime |
||
| 233 | self.age = age |
||
| 234 | |||
| 235 | # It is not allowed to define `min` or `max` if `fix` is defined. |
||
| 236 | # HINT: This also allows `flow`s with `fix` to be bidirectional, if |
||
| 237 | # negative values are used in `fix`, despite `min` and `max` having |
||
| 238 | # the default values (0 and 1). |
||
| 239 | # TODO: Is it intended to have bidirectional fixed flows? |
||
| 240 | if fix is not None and (min != 0 or max != 1): |
||
| 241 | msg = ( |
||
| 242 | "It is not allowed to define `min`/`max` if `fix` is defined." |
||
| 243 | ) |
||
| 244 | raise AttributeError(msg) |
||
| 245 | |||
| 246 | # --- BEGIN: The following code can be removed for versions >= v0.7 --- |
||
| 247 | if self.bidirectional: |
||
| 248 | msg = "The `bidirectional` keyword is deprecated and will be " |
||
| 249 | "removed in a future version, as it sets the value of `min` to -1 " |
||
| 250 | "without the users explicit intent. It is recommended to set a " |
||
| 251 | "negative value for `min` explicitly instead." |
||
| 252 | warn(msg, FutureWarning) |
||
| 253 | if min == 0: |
||
| 254 | min = -1 |
||
| 255 | # --- END |
||
| 256 | |||
| 257 | if sequence(min).min() < 0: |
||
| 258 | msg = ( |
||
| 259 | "Setting `min` to negative values allows for the flow to " |
||
| 260 | "become bidirectional, which is an experimental feature." |
||
| 261 | ) |
||
| 262 | warn(msg, debugging.ExperimentalFeatureWarning) |
||
| 263 | |||
| 264 | self.fix = sequence(fix) |
||
| 265 | self.max = sequence(max) |
||
| 266 | self.min = sequence(min) |
||
| 267 | |||
| 268 | need_nominal_capacity = [ |
||
| 269 | "fix", |
||
| 270 | "full_load_time_max", |
||
| 271 | "full_load_time_min", |
||
| 272 | "min", |
||
| 273 | "max", |
||
| 274 | ] |
||
| 275 | need_nominal_capacity_defaults = { |
||
| 276 | "fix": None, |
||
| 277 | "full_load_time_max": None, |
||
| 278 | "full_load_time_min": None, |
||
| 279 | # --- BEGIN: The following code can be removed for versions >= v0.7 |
||
| 280 | "min": -1 if self.bidirectional else 0, |
||
| 281 | # --- END |
||
| 282 | # "min": 0, |
||
| 283 | "max": 1, |
||
| 284 | } |
||
| 285 | if self.investment is None and self.nominal_capacity is None: |
||
| 286 | for attr in need_nominal_capacity: |
||
| 287 | if isinstance(getattr(self, attr), Iterable): |
||
| 288 | the_attr = getattr(self, attr)[0] |
||
| 289 | else: |
||
| 290 | the_attr = getattr(self, attr) |
||
| 291 | if the_attr != need_nominal_capacity_defaults[attr]: |
||
| 292 | raise AttributeError( |
||
| 293 | f"If {attr} is set in a flow, " |
||
| 294 | "nominal_capacity must be set as well." |
||
| 295 | ) |
||
| 296 | |||
| 297 | if self.nominal_capacity is not None and not math.isfinite( |
||
| 298 | self.max[0] |
||
| 299 | ): |
||
| 300 | raise ValueError(infinite_error_msg.format("max")) |
||
| 301 | |||
| 302 | # Checking for impossible gradient combinations |
||
| 303 | if self.nonconvex: |
||
| 304 | if self.nonconvex.positive_gradient_limit[0] is not None and ( |
||
| 305 | self.positive_gradient_limit[0] is not None |
||
| 306 | or self.negative_gradient_limit[0] is not None |
||
| 307 | ): |
||
| 308 | raise ValueError( |
||
| 309 | "You specified a positive gradient in your nonconvex " |
||
| 310 | "option. This cannot be combined with a positive or a " |
||
| 311 | "negative gradient for a standard flow!" |
||
| 312 | ) |
||
| 313 | |||
| 314 | if self.nonconvex.negative_gradient_limit[0] is not None and ( |
||
| 315 | self.positive_gradient_limit[0] is not None |
||
| 316 | or self.negative_gradient_limit[0] is not None |
||
| 317 | ): |
||
| 318 | raise ValueError( |
||
| 319 | "You specified a negative gradient in your nonconvex " |
||
| 320 | "option. This cannot be combined with a positive or a " |
||
| 321 | "negative gradient for a standard flow!" |
||
| 322 | ) |
||
| 323 | |||
| 324 | if ( |
||
| 325 | self.investment |
||
| 326 | and self.nonconvex |
||
| 327 | and not np.isfinite(self.investment.maximum.max()) |
||
| 328 | ): |
||
| 329 | raise AttributeError( |
||
| 330 | "Investment into a non-convex flows needs a maximum " |
||
| 331 | + "investment to be set." |
||
| 332 | ) |
||
| 333 |