Conditions | 22 |
Total Lines | 154 |
Code Lines | 86 |
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._energy_system.EnergySystem.__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 -*- |
||
86 | def __init__( |
||
87 | self, |
||
88 | timeindex=None, |
||
89 | timeincrement=None, |
||
90 | infer_last_interval=False, |
||
91 | periods=None, |
||
92 | tsa_parameters=None, |
||
93 | use_remaining_value=False, |
||
94 | groupings=None, |
||
95 | ): |
||
96 | # Doing imports at runtime is generally frowned upon, but should work |
||
97 | # for now. See the TODO in :func:`constraint_grouping |
||
98 | # <oemof.solph.groupings.constraint_grouping>` for more information. |
||
99 | from oemof.solph import GROUPINGS |
||
100 | |||
101 | if groupings is None: |
||
102 | groupings = [] |
||
103 | groupings = GROUPINGS + groupings |
||
104 | |||
105 | if infer_last_interval is True and timeindex is not None: |
||
106 | # Add one time interval to the timeindex by adding one time point. |
||
107 | if timeindex.freq is None: |
||
108 | msg = ( |
||
109 | "You cannot infer the last interval if the 'freq' " |
||
110 | "attribute of your DatetimeIndex is None. Set " |
||
111 | " 'infer_last_interval=False' or specify a DatetimeIndex " |
||
112 | "with a valid frequency." |
||
113 | ) |
||
114 | raise AttributeError(msg) |
||
115 | |||
116 | timeindex = timeindex.union( |
||
117 | pd.date_range( |
||
118 | timeindex[-1] + timeindex.freq, |
||
119 | periods=1, |
||
120 | freq=timeindex.freq, |
||
121 | ) |
||
122 | ) |
||
123 | |||
124 | # catch wrong combinations and infer timeincrement from timeindex. |
||
125 | if timeincrement is not None: |
||
126 | if timeindex is None: |
||
127 | msg = ( |
||
128 | "Initialising an EnergySystem using a timeincrement" |
||
129 | " is deprecated. Please give a timeindex instead." |
||
130 | ) |
||
131 | warnings.warn(msg, FutureWarning) |
||
132 | else: |
||
133 | if periods is None: |
||
134 | msg = ( |
||
135 | "Specifying the timeincrement and the timeindex" |
||
136 | " parameter at the same time is not allowed since" |
||
137 | " these might be conflicting to each other." |
||
138 | ) |
||
139 | raise AttributeError(msg) |
||
140 | else: |
||
141 | msg = ( |
||
142 | "Ensure that your timeindex and timeincrement are " |
||
143 | "consistent." |
||
144 | ) |
||
145 | warnings.warn(msg, debugging.ExperimentalFeatureWarning) |
||
146 | |||
147 | elif timeindex is not None and timeincrement is None: |
||
148 | if tsa_parameters is not None: |
||
149 | pass |
||
150 | else: |
||
151 | try: |
||
152 | df = pd.DataFrame(timeindex) |
||
153 | except ValueError: |
||
154 | raise ValueError("Invalid timeindex.") |
||
155 | timedelta = df.diff() |
||
156 | if isinstance(timeindex, pd.DatetimeIndex): |
||
157 | timeincrement = timedelta / np.timedelta64(1, "h") |
||
158 | else: |
||
159 | timeincrement = timedelta |
||
160 | # we want a series (squeeze) |
||
161 | # without the first item (no delta defined for first entry) |
||
162 | # but starting with index 0 (reset) |
||
163 | timeincrement = timeincrement.squeeze()[1:].reset_index( |
||
164 | drop=True |
||
165 | ) |
||
166 | |||
167 | if timeincrement is not None and (pd.Series(timeincrement) <= 0).any(): |
||
168 | msg = ( |
||
169 | "The time increment is inconsistent. Negative values and zero " |
||
170 | "are not allowed.\nThis is caused by a inconsistent " |
||
171 | "timeincrement parameter or an incorrect timeindex." |
||
172 | ) |
||
173 | raise TypeError(msg) |
||
174 | if tsa_parameters is not None: |
||
175 | msg = ( |
||
176 | "CAUTION! You specified the 'tsa_parameters' attribute for " |
||
177 | "your energy system.\n This will lead to setting up " |
||
178 | "energysystem with aggregated timeseries. " |
||
179 | "Storages and flows will be adapted accordingly.\n" |
||
180 | "Please be aware that the feature is experimental as of " |
||
181 | "now. If you find anything suspicious or any bugs, " |
||
182 | "please report them." |
||
183 | ) |
||
184 | warnings.warn(msg, debugging.SuspiciousUsageWarning) |
||
185 | |||
186 | if isinstance(tsa_parameters, dict): |
||
187 | # Set up tsa_parameters for single period: |
||
188 | tsa_parameters = [tsa_parameters] |
||
189 | |||
190 | # Construct occurrences of typical periods |
||
191 | if periods is not None: |
||
192 | for p in range(len(periods)): |
||
193 | tsa_parameters[p]["occurrences"] = collections.Counter( |
||
194 | tsa_parameters[p]["order"] |
||
195 | ) |
||
196 | else: |
||
197 | tsa_parameters[0]["occurrences"] = collections.Counter( |
||
198 | tsa_parameters[0]["order"] |
||
199 | ) |
||
200 | |||
201 | # If segmentation is used, timesteps is set to number of |
||
202 | # segmentations per period. |
||
203 | # Otherwise, default timesteps_per_period is used. |
||
204 | for params in tsa_parameters: |
||
205 | if "segments" in params: |
||
206 | params["timesteps"] = int( |
||
207 | len(params["segments"]) / len(params["occurrences"]) |
||
208 | ) |
||
209 | else: |
||
210 | params["timesteps"] = params["timesteps_per_period"] |
||
211 | self.tsa_parameters = tsa_parameters |
||
212 | |||
213 | timeincrement = self._init_timeincrement( |
||
214 | timeincrement, timeindex, periods, tsa_parameters |
||
215 | ) |
||
216 | super().__init__( |
||
217 | groupings=groupings, |
||
218 | timeindex=timeindex, |
||
219 | timeincrement=timeincrement, |
||
220 | ) |
||
221 | |||
222 | self.periods = periods |
||
223 | if self.periods is not None: |
||
224 | msg = ( |
||
225 | "CAUTION! You specified the 'periods' attribute for your " |
||
226 | "energy system.\n This will lead to creating " |
||
227 | "a multi-period optimization modeling which can be " |
||
228 | "used e.g. for long-term investment modeling.\n" |
||
229 | "Please be aware that the feature is experimental as of " |
||
230 | "now. If you find anything suspicious or any bugs, " |
||
231 | "please report them." |
||
232 | ) |
||
233 | warnings.warn(msg, debugging.ExperimentalFeatureWarning) |
||
234 | self._extract_periods_years() |
||
235 | self._extract_periods_matrix() |
||
236 | self._extract_end_year_of_optimization() |
||
237 | self.use_remaining_value = use_remaining_value |
||
238 | else: |
||
239 | self.end_year_of_optimization = 1 |
||
240 | |||
347 |