e2nIEE / pandapower
1
# -*- coding: utf-8 -*-
2

3
# Copyright (c) 2016-2021 by University of Kassel and Fraunhofer Institute for Energy Economics
4
# and Energy System Technology (IEE), Kassel. All rights reserved.
5 1
import copy
6 1
import functools
7 1
import os
8 1
from time import time
9 1
from types import FunctionType
10

11 1
import numpy as np
12 1
import pandas as pd
13 1
from collections.abc import Iterable
14 1
from pandapower.io_utils import JSONSerializableClass
15 1
from pandapower.io_utils import mkdirs_if_not_existent
16 1
from pandapower.pd2ppc import _pd2ppc
17 1
from pandapower.pypower.idx_bus import VM, VA, NONE, BUS_TYPE
18 1
from pandapower.run import _init_runpp_options
19 1
from pandapower.timeseries.read_batch_results import v_to_i_s, get_batch_line_results, get_batch_trafo3w_results, \
20
    get_batch_trafo_results, get_batch_bus_results
21

22 1
try:
23 1
    import pplog
24 1
except ImportError:
25 1
    import logging as pplog
26 1
logger = pplog.getLogger(__name__)
27

28

29 1
class OutputWriter(JSONSerializableClass):
30
    """
31
    The OutputWriter class is used to store and format specific outputs from a time series calculation.
32

33
    It contains a python-dictionary *output* which is a container for result DataFrames or arbitrary information you
34
    would like to store. By default a pandas DataFrame is initialized for the key *Parameters*, which has the columns
35
    "time_step", "controller_unstable", "powerflow_failed".
36

37
    To simple log outputs during a time series simulation use **ow.log_variable(table, column)** where table is the name
38
    of the (result) table, e.g. "res_bus", and column the name of the column, e.g. "vm_pu".
39

40
    More sophisticated outputs can be added as well since for each value to be stored a function is
41
    added to the *output_list* which is called at the end of each time step.
42
    The function reads the calculation results and returns the desired values to store.
43
    The function is expected to return scalar value by default. If more values are returned, the function should have
44
    "n_columns" as a paramter (see example below for logging of cost function values).
45
    These values are then stored in the *output* DataFrame in a column named after the function you implemented.
46
    Such a function can be used to store only some information of the power flow results, like the highest values
47
    of the line loading in a time step or the mean values. Check the "advanced time series example" jupyter notebook
48
    for an example.
49

50
    INPUT:
51
        **net** - The pandapower format network
52

53
        **time_steps** (list) - time_steps to calculate as a list (or range)
54

55
    OPTIONAL:
56

57
        **output_path** (string, None) - Path to a folder where the output is written to.
58

59
        **output_file_type** (string, ".p") - output filetype to use.
60
        Allowed file extensions: [*.xls, *.xlsx, *.csv, *.p, *.json]
61
        Note: XLS has a maximum number of 256 rows.
62

63
        **csv_separator** (string, ";") - The separator used when writing to a csv file
64

65
        **write_time** (int, None) - Time to save periodically to disk in minutes. Deactivated by default
66

67
        **log_variables** (list, None) - list of tuples with (table, column) values to  be logged by output writer.
68
        Defaults are: res_bus.vm_pu and res_line.loading_percent. Additional variables can be added later on
69
        with ow.log_variable or removed with ow.remove_log_variable
70

71

72

73
    EXAMPLE:
74
        >>> from pandapower.timeseries.output_writer import OutputWriter
75
        >>> from pandapower.networks as nw
76
        >>> net = nw.simple_four_bus_system()
77
        >>> ow = OutputWriter(net) # create an OutputWriter
78
        >>> ow.log_variable('res_bus', 'vm_pu') # add logging for bus voltage magnitudes
79
        >>> ow.log_variable('res_line', 'loading_percent') # add logging for line loadings in percent
80
        >>>  # Getting the cost function slope for each time step:
81
        >>> def cost_logging(result, n_columns=2):
82
        >>>      return array([result[i][0][2] for i in range(len(result))])
83
        >>> ow.log_variable("pwl_cost", "points", eval_function=cost_logging)
84

85

86

87
    """
88

89 1
    def __init__(self, net, time_steps=None, output_path=None, output_file_type=".p", write_time=None,
90
                 log_variables=None, csv_separator=";"):
91 1
        super().__init__()
92 1
        self.output_path = output_path
93 1
        self.output_file_type = output_file_type
94 1
        self.write_time = write_time
95 1
        self.log_variables = log_variables
96
        # these are the default log variables which are added if log_variables is None
97 1
        self.default_log_variables = [("res_bus", "vm_pu"), ("res_line", "loading_percent")]
98 1
        self._add_log_defaults()
99

100 1
        self.csv_separator = csv_separator
101 1
        if write_time is not None:
102 1
            self.write_time *= 60.0  # convert to seconds
103

104
        # init the matrix and the list of output functions
105 1
        self.output = dict()
106
        # internal results stored as numpy arrays in dict. Is created from output_list
107 1
        self.np_results = dict()
108
        # output list contains functools.partial with tables, variables, index...
109 1
        self.output_list = []
110
        # real time is tracked to save results to disk regularly
111 1
        self.cur_realtime = time()
112
        # total time steps to calculate
113 1
        self.time_steps = time_steps
114
        # add output_writer to net
115 1
        self.add_to_net(net, element="output_writer", index=0, overwrite=True)
116
        # inits dataframes and numpy arrays which store results
117
        # self.init_all()
118
        # Saves all parameters as object attributes to store in JSON
119

120 1
    def __str__(self):
121
        # return self.__class__.__name__
122 0
        return self.__repr__()
123

124 1
    def __repr__(self):
125 0
        s = "%s: writes output to '%s'" % (self.__class__.__name__, self.output_path)
126 0
        s += " and logs:"
127 0
        for output in self.log_variables:
128 0
            table, variable = output[0], output[1]
129 0
            s += "\n'" + str(table) + "." + str(variable) + "'"
130 0
        return s
131

132 1
    def _monkey_patch(self, method, new):
133 0
        from types import MethodType
134 0
        setattr(self, method, MethodType(new, self))
135

136 1
    def _add_log_defaults(self):
137 1
        if self.log_variables is None:
138 1
            self.log_variables = list()
139 1
            self.log_variables = copy.copy(self.default_log_variables)
140 1
        if not isinstance(self.log_variables, list):
141 0
            raise TypeError("log_variables must be None or a list of tuples like [('res_bus', 'vm_pu')]")
142

143 1
    def init_log_variables(self, net):
144
        """
145
        inits the log variables given to output writer.
146
        log_variables is a list with tuples of DataFrame columns to log.
147
        Example: [("res_bus", "vm_pu"), ("res_bus", "va_degree")]
148

149
        If None are given the defaults are:
150
        res_bus.vm_pu
151
        res_line.loading_percent
152
        """
153 1
        _init_log_variable = functools.partial(self._init_log_variable, net)
154 1
        for log_args in self.log_variables:
155
            # add log variable
156 1
            _init_log_variable(*log_args)
157

158 1
    def init_all(self, net):
159 1
        if isinstance(self.time_steps, Iterable):
160 1
            self.output = dict()
161 1
            self.np_results = dict()
162 1
            self.output_list = list()
163 1
            self.init_log_variables(net)
164 1
            self.init_timesteps(self.time_steps)
165 1
            self._init_np_results()
166 1
            self._init_output()
167

168
        else:
169 0
            logger.debug("Time steps not set at init ")
170

171 1
    def _init_output(self):
172 1
        self.output = dict()
173
        # init parameters
174 1
        self.output["Parameters"] = pd.DataFrame(False, index=self.time_steps,
175
                                                 columns=["time_step", "controller_unstable",
176
                                                          "powerflow_failed"])
177 1
        self.output["Parameters"].loc[:, "time_step"] = self.time_steps
178

179 1
    def _init_np_results(self):
180
        # inits numpy array (contains results)
181 1
        self.np_results = dict()
182 1
        for partial_func in self.output_list:
183 1
            self._init_np_array(partial_func)
184

185 1
    def _save_separate(self, append):
186

187 1
        for partial in self.output_list:
188 1
            if isinstance(partial, tuple):
189
                # if batch output is used
190 1
                table = partial[0]
191 1
                variable = partial[1]
192
            else:
193
                # if output_list contains functools.partial
194 1
                table = partial.args[0]
195 1
                variable = partial.args[1]
196 1
            if table != "Parameters":
197 1
                file_path = os.path.join(self.output_path, table)
198 1
                mkdirs_if_not_existent(file_path)
199 1
                if append:
200 0
                    file_name = str(variable) + "_" + str(self.cur_realtime) + self.output_file_type
201
                else:
202 1
                    file_name = str(variable) + self.output_file_type
203 1
                file_path = os.path.join(file_path, file_name)
204 1
                data = self.output[self._get_output_name(table, variable)]
205 1
                if self.output_file_type == ".json":
206 1
                    data.to_json(file_path)
207 1
                elif self.output_file_type == ".p":
208 1
                    data.to_pickle(file_path)
209 0
                elif self.output_file_type in [".xls", ".xlsx"]:
210 0
                    try:
211 0
                        data.to_excel(file_path)
212 0
                    except ValueError as e:
213 0
                        if data.shape[1] > 255:
214 0
                            raise ValueError("pandas.to_excel() is not capable to handle large data" +
215
                                             "with more than 255 columns. Please use other " +
216
                                             "file_extensions instead, e.g. 'json'.")
217
                        else:
218 0
                            raise ValueError(e)
219 0
                elif self.output_file_type == ".csv":
220 0
                    data.to_csv(file_path, sep=self.csv_separator)
221

222 1
    def dump_to_file(self, net, append=False, recycle_options=None):
223
        """
224
        Save the output to separate files in output_path with the file_type output_file_type. This is called after
225
        the time series simulation by default.
226

227
           **append** (bool, False) - Option for appending instead of overwriting the file
228
        """
229 1
        save_single = False
230 1
        self._np_to_pd()
231 1
        if recycle_options not in [None, False]:
232 1
            self.get_batch_outputs(net, recycle_options)
233 1
        if self.output_path is not None:
234 1
            try:
235 1
                if save_single and self.output_file_type in [".xls", ".xlsx"]:
236 0
                    self._save_single_xls_sheet(append)
237 1
                elif self.output_file_type in [".csv", ".xls", ".xlsx", ".json", ".p"]:
238 1
                    self._save_separate(append)
239
                else:
240 0
                    raise UserWarning(
241
                        "Specify output file with .csv, .xls, .xlsx, .p or .json ending")
242 1
                if append:
243 0
                    self._init_output()
244

245 0
            except Exception:
246 0
                raise
247

248 1
    def dump(self, net, recycle_options=None):
249 1
        append = False if self.time_step == self.time_steps[-1] else True
250 1
        self.dump_to_file(net, append=append, recycle_options=recycle_options)
251 1
        self.cur_realtime = time()  # reset real time counter for next period
252

253 1
    def save_results(self, net, time_step, pf_converged, ctrl_converged, recycle_options=None):
254
        # Saves the results of the current time step to a matrix,
255
        # using the output functions in the self.output_list
256

257
        # remember the last time step
258 1
        self.time_step = time_step
259

260
        # add an entry to the output matrix if something failed
261 1
        if not pf_converged:
262 0
            self.save_nans_to_parameters()
263 0
            self.output["Parameters"].loc[time_step, "powerflow_failed"] = True
264 1
        elif not ctrl_converged:
265 1
            self.output["Parameters"].loc[time_step, "controller_unstable"] = True
266
        else:
267 1
            self.save_to_parameters()
268

269
        # if write time is exceeded or it is the last time step, data is written
270 1
        if self.write_time is not None:
271 1
            if time() - self.cur_realtime > self.write_time:
272 0
                self.dump(net)
273 1
        if self.time_step == self.time_steps[-1]:
274 1
            self.dump(net, recycle_options)
275

276 1
    def save_to_parameters(self):
277
        # Saves the results of the current time step to self.output,
278
        # using the output functions in the self.output_list
279

280 1
        for of in self.output_list:
281 1
            try:
282 1
                of()
283 0
            except:
284 0
                import traceback
285 0
                traceback.print_exc()
286 0
                logger.error("Error in output function! Stored NaN for '%s' in time-step %i"
287
                             % (of.__name__, self.time_step))
288 0
                self.save_nans_to_parameters()
289

290 1
    def save_nans_to_parameters(self):
291
        # Saves NaNs to for the given time step.
292 0
        time_step_idx = self.time_step_lookup[self.time_step]
293 0
        for of in self.output_list:
294 0
            self.output["Parameters"].loc[time_step_idx, of.__name__] = np.NaN
295

296 1
    def remove_log_variable(self, table, variable=None):
297
        """
298
        removes a logged variable from outputs
299

300
        INPUT:
301
        **table** (str) - name of the DataFrame table (example: "res_bus")
302

303
        OPTIONAL:
304
        **variable** (str, None) - column name of the DataFrame table (example: "vm_pu"). If None all are variables of
305
        table are removed
306

307
        """
308
        # remove variables from list
309 1
        if variable is not None:
310 1
            self.output_list = [o for o in self.output_list if not (o.args[0] == table and o.args[1] == variable)]
311 1
            self.log_variables = [o for o in self.log_variables if not (o[0] == table and o[1] == variable)]
312
        else:
313 1
            self.output_list = [o for o in self.output_list if not (o.args[0] == table)]
314 1
            self.log_variables = [o for o in self.log_variables if not (o[0] == table)]
315
        # init output container again
316 1
        self._init_np_results()
317

318 1
    def log_variable(self, table, variable, index=None, eval_function=None, eval_name=None):
319
        """
320
        Adds a variable to log during simulation and appends it to output_list.
321
        INPUT:
322

323
        **table** (str) - The DataFrame table where the variable is located as a string (e.g. "res_bus")
324

325
        **variable** (str) -  variable that should be logged as string (e.g. "p_mw")
326

327
        OPTIONAL:
328

329
        **index** (iterable, None) - Can be either one index or a list of indices, or a numpy array of indices,
330
        or a pandas Index, or a pandas Series (e.g. net.load.bus) for which
331
        the variable will be logged. If no index is given, the variable will be logged for all elements in the table
332

333
        **eval_function** (function, None) - A function to be applied on the table / variable / index combination.
334
        example: pd.min or pd.mean
335

336
        **eval_name** (str, None) - The name for an applied function. It *must* be unique.
337
                                    If the name is None the name consists of the table, variable, index and eval function
338
                                    example: "max_load_p_mw_values"
339

340
        EXAMPLE:
341
            >>> ow.log_variable('res_bus', 'vm_pu') # add logging for bus voltage magnitudes
342
            >>> ow.log_variable('res_line', 'loading_percent', index=[0, 2, 5]) # add logging for line loading of lines with indices 0, 2, 5
343
            >>> ow.log_variable('res_line', 'loading_percent', eval_function=pd.max) # get the highest line loading only
344

345
            # Getting the cost function slope for each time step:
346
            >>> def cost_logging(result, n_columns=2):
347
            >>>      return array([result[i][0][2] for i in range(len(result))])
348
            >>> ow.log_variable("pwl_cost", "points", eval_function=cost_logging)
349

350
        """
351 1
        del_indices = list()
352 1
        append_args = set()
353 1
        append = True
354
        # check if new log_variable is already in log_variables. If so either append or delete
355 1
        for i, log_args in enumerate(self.log_variables):
356 1
            if len(log_args) > 4 and eval_name is not None and log_args[4] == eval_name:
357 1
                logger.warning("eval_name '{}' already exists for table '{}' and variable '{}'. "
358
                                 "Please choose a unique eval_name. "
359
                               "I'll use the default instead.".format(eval_name, log_args[0], log_args[1]))
360 1
                eval_name = None
361 1
            if log_args[0] == table and log_args[1] == variable:
362
                # table and variable exist in log_variables
363 1
                if eval_function is not None or eval_name is not None:
364 1
                    append = True
365 1
                    continue
366 1
                if len(log_args) == 2 and eval_function is None:
367
                    # everything from table / variable is logged
368 1
                    append = False
369 1
                    continue
370 1
                if log_args[2] is not None and index is not None and eval_function is None:
371
                    # if index is given and an index was given before extend the index and get unique
372 0
                    log_args[2] = set(log_args[2].extend(index))
373
                else:
374 1
                    del_indices.append(i)
375 1
                    append_args.add((table, variable))
376 1
                    append = False
377

378 1
        for i in del_indices:
379 1
            del self.log_variables[i]
380 1
        for log_arg in append_args:
381 1
            self.log_variables.append(log_arg)
382 1
        if append:
383 1
            self.log_variables.append((table, variable, index, eval_function, eval_name))
384

385 1
    def _init_ppc_logging(self, table, variable, net, eval_function, eval_name):
386 1
        var_name = self._get_output_name(table, variable)
387 1
        ppc = net["_ppc"]
388 1
        if ppc is None:
389
            # if no ppc is in net-> create one
390 1
            options = dict(algorithm='nr', calculate_voltage_angles="auto", init="auto",
391
                           max_iteration="auto", tolerance_mva=1e-8, trafo_model="t",
392
                           trafo_loading="current", enforce_q_lims=False, check_connectivity=True,
393
                           voltage_depend_loads=True, consider_line_temperature=False)
394 1
            _init_runpp_options(net, **options)
395 1
            ppc, _ = _pd2ppc(net)
396 1
            net["_ppc"] = ppc
397 1
        index = list(range(sum(ppc['bus'][:, BUS_TYPE] != NONE)))
398 1
        self._append_output_list(table, variable, net, index, eval_function, eval_name, var_name, func=self._log_ppc)
399 1
        return index
400

401 1
    def _init_log_variable(self, net, table, variable, index=None, eval_function=None, eval_name=None):
402 1
        if "ppc" in table:
403 1
            index = self._init_ppc_logging(table, variable, net, eval_function, eval_name)
404

405 1
        if np.any(pd.isnull(index)):
406
            # check how many elements there are in net
407 1
            index = net[table.split("res_")[-1]].index
408 1
        if not hasattr(index, '__iter__'):
409 1
            index = [index]
410 1
        if isinstance(index, (np.ndarray, pd.Index, pd.Series)):
411 1
            index = index.tolist()
412 1
        if eval_function is not None and eval_name is None:
413 1
            eval_name = "%s.%s.%s.%s" % (table, variable, str(index), eval_function.__name__)
414 1
        if eval_function is None and eval_name is not None:
415 1
            logger.info("'eval_name' is to give a name in case of evaluation functions. Since " +
416
                        "no function is given for eval_name '%s', " % eval_name +
417
                        "eval_name is neglected.")
418 1
            eval_name = None
419 1
        if eval_name is not None and eval_function is not None:
420 1
            if isinstance(eval_function, FunctionType):
421 1
                if "n_columns" in eval_function.__code__.co_varnames:
422 1
                    logger.info("'eval_name' is to give a name in case of single value evaluation functions. Since " +
423
                                "n_columns is given as a parameter of the evaluation function, the given for eval_name "
424
                                "'%s', " % eval_name + "eval_name is neglected.")
425 1
                    eval_name = None
426

427
        # var_name = self._get_hash((table, variable, index, eval_function))
428 1
        var_name = self._get_output_name(table, variable)
429 1
        idx = self._get_same_log_variable_partial_func_idx(table, variable, eval_function,
430
                                                           eval_name)
431 1
        if idx is not None:
432 1
            self._append_existing_log_variable_partial_func(idx, index)
433
        else:
434 1
            self._append_output_list(table, variable, net, index, eval_function, eval_name, var_name)
435

436 1
    def _get_same_log_variable_partial_func_idx(self, table, variable, eval_function, eval_name):
437
        """ Returns the position index in self.output_list of partial_func which has the same table
438
        and variable and no evaluation function. """
439 1
        if eval_function is None and eval_name is None:
440 1
            for i, partial_func in enumerate(self.output_list):
441 1
                partial_args = partial_func.args
442 1
                match = partial_args[0] == table
443 1
                match &= partial_args[1] == variable
444 1
                if match:
445 1
                    return i
446

447 1
    def _append_existing_log_variable_partial_func(self, idx, index):
448
        """ Appends the index of existing, same partial_func in output_list. """
449 1
        for i in index:
450 1
            if i not in self.output_list[idx].args[3]:
451 1
                self.output_list[idx].args[3].append(i)
452

453 1
    def _append_output_list(self, table, variable, net, index, eval_function, eval_name, var_name, func=None):
454
        """ Appends the output_list by an additional partial_func. """
455 1
        func = self._log if func is None else func
456 1
        partial_func = functools.partial(func, table, variable, net, index, eval_function, eval_name)
457 1
        partial_func.__name__ = var_name
458 1
        self.output_list.append(partial_func)
459 1
        if self.time_steps is not None:
460 1
            self._init_np_array(partial_func)
461

462 1
    def _log(self, table, variable, net, index, eval_function=None, eval_name=None):
463 1
        try:
464
            # ToDo: Create a mask for the numpy array in the beginning and use this one for getting the values. Faster
465 1
            if net[table].index.equals(pd.Index(index)):
466
                # if index equals all values -> get numpy array directly
467 1
                result = net[table][variable].values
468
            else:
469
                # get by loc (slow)
470 1
                result = net[table].loc[index, variable].values
471

472 1
            if eval_function is not None:
473 1
                result = eval_function(result)
474

475
            # save results to numpy array
476 1
            time_step_idx = self.time_step_lookup[self.time_step]
477 1
            hash_name = self._get_np_name((table, variable, net, index, eval_function, eval_name))
478 1
            self.np_results[hash_name][time_step_idx, :] = result
479

480 0
        except Exception as e:
481 0
            logger.error("Error at index %s for %s[%s]: %s" % (index, table, variable, e))
482

483 1
    def _log_ppc(self, table, variable, net, index, eval_function=None, eval_name=None):
484
        # custom log function fo ppc results
485 1
        ppci = net["_ppc"]["internal"]
486 1
        if variable == "vm":
487 1
            v = VM
488 1
        elif variable == "va":
489 1
            v = VA
490
        else:
491 0
            raise NotImplementedError("No other variable implemented yet.")
492 1
        result = ppci[table.split("_")[-1]][:, v]
493 1
        if eval_function is not None:
494 0
            result = eval_function(result)
495

496
        # save results to numpy array
497 1
        time_step_idx = self.time_step_lookup[self.time_step]
498 1
        hash_name = self._get_np_name((table, variable, net, index, eval_function, eval_name))
499 1
        self.np_results[hash_name][time_step_idx, :] = result
500

501 1
    def _np_to_pd(self):
502
        # convert numpy arrays (faster so save results) into pd Dataframes (user friendly)
503
        # intended use: At the end of time series simulation write results to pandas
504

505 1
        for partial_func in self.output_list:
506 1
            (table, variable, net, index, eval_func, eval_name) = partial_func.args
507
            # res_name = self._get_hash(table, variable)
508 1
            res_name = self._get_output_name(table, variable)
509 1
            np_name = self._get_np_name(partial_func.args)
510 1
            columns = index
511 1
            if eval_name is not None and eval_func is not None:
512 1
                if isinstance(eval_func, FunctionType):
513 1
                    if "n_columns" not in eval_func.__code__.co_varnames:
514 1
                        columns = [eval_name]
515
                else:
516 1
                    columns = [eval_name]
517

518 1
            res_df = pd.DataFrame(self.np_results[np_name], index=self.time_steps, columns=columns)
519 1
            if res_name in self.output and eval_name is not None:
520 1
                try:
521 1
                    self.output[res_name] = pd.concat([self.output[res_name], res_df], axis=1,
522
                                                      sort=False)
523 0
                except TypeError:
524
                    # pandas legacy < 0.21
525 0
                    self.output[res_name] = pd.concat([self.output[res_name], res_df], axis=1)
526
            else:
527
                # new dataframe
528 1
                self.output[res_name] = res_df
529

530 1
    def _get_output_name(self, table, variable):
531 1
        return "%s.%s" % (table, variable)
532

533 1
    def _get_np_name(self, partial_args):
534 1
        eval_name = partial_args[5]
535 1
        if eval_name is not None:
536 1
            return eval_name
537
        else:
538 1
            table = partial_args[0]
539 1
            variable = partial_args[1]
540 1
            return "%s.%s" % (table, variable)
541

542 1
    def _save_single_xls_sheet(self, append):
543
        # ToDo: implement save to a single sheet
544 0
        raise NotImplementedError("Sorry not implemented yet")
545

546 1
    def init_timesteps(self, time_steps):
547 1
        self.time_steps = time_steps
548 1
        self.time_step = time_steps[0]
549 1
        self.time_step_lookup = {t: idx for idx, t in enumerate(time_steps)}
550

551 1
    def _init_np_array(self, partial_func):
552 1
        (table, variable, net, index, eval_function, eval_name) = partial_func.args
553 1
        hash_name = self._get_np_name(partial_func.args)
554 1
        n_columns = len(index)
555 1
        if eval_function is not None:
556 1
            n_columns = 1
557 1
            if isinstance(eval_function, FunctionType):
558 1
                if "n_columns" in eval_function.__code__.co_varnames:
559 1
                    n_columns = eval_function.__defaults__[0]
560 1
        self.np_results[hash_name] = np.zeros((len(self.time_steps), n_columns))
561

562 1
    def get_batch_outputs(self, net, recycle_options):
563
        # read the results in batch from vm, va (ppci values)
564

565 1
        if isinstance(recycle_options["batch_read"], list) and len(recycle_options["batch_read"]):
566
            # vm, va is without out of service elements
567 1
            vm, va = self.output["ppc_bus.vm"], self.output["ppc_bus.va"]
568 1
            _, s_abs, i_abs = v_to_i_s(net, vm, va)
569 1
            results = dict()
570 1
            new_output_list = list()
571 1
            for table, variable in recycle_options["batch_read"]:
572 1
                if table == "res_line" and "res_line" not in results:
573 1
                    i_ka, i_from_ka, i_to_ka, loading_percent = get_batch_line_results(net, i_abs)
574 1
                    results["res_line"] = dict(i_ka=i_ka, i_from_ka=i_from_ka, i_to_ka=i_to_ka,
575
                                               loading_percent=loading_percent)
576 1
                elif table == "res_trafo" and "res_trafo" not in results:
577 0
                    i_ka, i_hv_ka, i_lv_ka, s_mva, loading_percent = get_batch_trafo_results(net, i_abs, s_abs)
578 0
                    results["res_trafo"] = dict(i_ka=i_ka, i_hv_ka=i_hv_ka, i_lv_ka=i_lv_ka,
579
                                                loading_percent=loading_percent)
580 1
                elif table == "res_trafo3w":
581 0
                    i_h, i_m, i_l, loading_percent = get_batch_trafo3w_results(net, i_abs, s_abs)
582 0
                    results["res_trafo3w"] = dict(i_h=i_h, i_m=i_m, i_l=i_l, loading_percent=loading_percent)
583 1
                elif table == "res_bus" and "res_bus" not in results:
584 1
                    vm_full, va_full = get_batch_bus_results(net, vm, va)
585 1
                    results["res_bus"] = dict(vm_pu=vm_full, va_degree=va_full)
586
                else:
587 0
                    raise ValueError("Something went wrong")
588 1
                output_name = "%s.%s" % (table, variable)
589
                # convert to dataframe
590 1
                self.output[output_name] = pd.DataFrame(data=results[table][variable], index=self.time_steps)
591 1
                new_output_list.append((table, variable))
592 1
            self.output_list = new_output_list

Read our documentation on viewing source code .

Loading