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

6

7
# Additional copyright for modified code by Brendan Curran-Johnson (ADict class):
8
# Copyright (c) 2013 Brendan Curran-Johnson
9
#
10
# Permission is hereby granted, free of charge, to any person obtaining a copy
11
# of this software and associated documentation files (the "Software"), to deal
12
# in the Software without restriction, including without limitation the rights
13
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
# copies of the Software, and to permit persons to whom the Software is
15
# furnished to do so, subject to the following conditions:
16
#
17
# The above copyright notice and this permission notice shall be included in
18
# all copies or substantial portions of the Software.
19
#
20
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
# THE SOFTWARE.
27
# (https://github.com/bcj/AttrDict/blob/master/LICENSE.txt)
28

29 1
import copy
30 1
from collections.abc import MutableMapping
31

32 1
import numpy as np
33 1
import numpy.core.numeric as ncn
34 1
import pandas as pd
35 1
import scipy as sp
36 1
import six
37 1
from packaging import version
38

39 1
from pandapower.pypower.idx_brch import F_BUS, T_BUS, BR_STATUS
40 1
from pandapower.pypower.idx_bus import BUS_I, BUS_TYPE, NONE, PD, QD, VM, VA, REF, VMIN, VMAX, PV
41 1
from pandapower.pypower.idx_gen import PMIN, PMAX, QMIN, QMAX
42

43 1
try:
44 1
    from numba import jit
45 1
    from numba._version import version_version as numba_version
46 0
except ImportError:
47 0
    from .pf.no_numba import jit
48

49 1
try:
50 1
    from lightsim2grid.newtonpf import newtonpf as newtonpf_ls
51 1
except ImportError:
52 1
    newtonpf_ls = None
53 1
try:
54 1
    import pplog as logging
55 1
except ImportError:
56 1
    import logging
57

58 1
lightsim2grid_available = True if newtonpf_ls is not None else False
59 1
logger = logging.getLogger(__name__)
60

61

62 1
class ADict(dict, MutableMapping):
63 1
    def __init__(self, *args, **kwargs):
64 1
        super().__init__(*args, **kwargs)
65

66
        # to prevent overwrite of internal attributes by new keys
67
        # see _valid_name()
68 1
        self._setattr('_allow_invalid_attributes', False)
69

70 1
    def _build(self, obj, **kwargs):
71
        """
72
        We only want dict like elements to be treated as recursive AttrDicts.
73
        """
74 1
        return obj
75

76
    # --- taken from AttrDict
77

78 1
    def __getstate__(self):
79 0
        return self.copy(), self._allow_invalid_attributes
80

81 1
    def __dir__(self):
82 0
        return list(six.iterkeys(self))
83

84 1
    def __setstate__(self, state):
85 0
        mapping, allow_invalid_attributes = state
86 0
        self.update(mapping)
87 0
        self._setattr('_allow_invalid_attributes', allow_invalid_attributes)
88

89 1
    @classmethod
90 1
    def _constructor(cls, mapping):
91 0
        return cls(mapping)
92

93
    # --- taken from MutableAttr
94

95 1
    def _setattr(self, key, value):
96
        """
97
        Add an attribute to the object, without attempting to add it as
98
        a key to the mapping (i.e. internals)
99
        """
100 1
        super(MutableMapping, self).__setattr__(key, value)
101

102 1
    def __setattr__(self, key, value):
103
        """
104
        Add an attribute.
105

106
        key: The name of the attribute
107
        value: The attributes contents
108
        """
109 1
        if self._valid_name(key):
110 1
            self[key] = value
111 0
        elif getattr(self, '_allow_invalid_attributes', True):
112 0
            super(MutableMapping, self).__setattr__(key, value)
113
        else:
114 0
            raise TypeError(
115
                "'{cls}' does not allow attribute creation.".format(
116
                    cls=self.__class__.__name__
117
                )
118
            )
119

120 1
    def _delattr(self, key):
121
        """
122
        Delete an attribute from the object, without attempting to
123
        remove it from the mapping (i.e. internals)
124
        """
125 0
        super(MutableMapping, self).__delattr__(key)
126

127 1
    def __delattr__(self, key, force=False):
128
        """
129
        Delete an attribute.
130

131
        key: The name of the attribute
132
        """
133 1
        if self._valid_name(key):
134 1
            del self[key]
135 0
        elif getattr(self, '_allow_invalid_attributes', True):
136 0
            super(MutableMapping, self).__delattr__(key)
137
        else:
138 0
            raise TypeError(
139
                "'{cls}' does not allow attribute deletion.".format(
140
                    cls=self.__class__.__name__
141
                )
142
            )
143

144 1
    def __call__(self, key):
145
        """
146
        Dynamically access a key-value pair.
147

148
        key: A key associated with a value in the mapping.
149

150
        This differs from __getitem__, because it returns a new instance
151
        of an Attr (if the value is a Mapping object).
152
        """
153 0
        if key not in self:
154 0
            raise AttributeError(
155
                "'{cls} instance has no attribute '{name}'".format(
156
                    cls=self.__class__.__name__, name=key
157
                )
158
            )
159

160 0
        return self._build(self[key])
161

162 1
    def __getattr__(self, key):
163
        """
164
        Access an item as an attribute.
165
        """
166 1
        if key not in self or not self._valid_name(key):
167 1
            raise AttributeError(
168
                "'{cls}' instance has no attribute '{name}'".format(
169
                    cls=self.__class__.__name__, name=key
170
                )
171
            )
172

173 1
        return self._build(self[key])
174

175 1
    def __deepcopy__(self, memo):
176
        """
177
        overloads the deepcopy function of pandapower if at least one DataFrame with column "object" is in net
178

179
        in addition, line geodata can contain mutable objects like lists, and it is also treated specially
180

181
        reason: some of these objects contain a reference to net which breaks the default deepcopy function.
182
        Also, the DataFrame doesn't deepcopy its elements if geodata changes in the lists, it affects both net instances
183
        This fix was introduced in pandapower 2.2.1
184

185
        """
186 1
        deep_columns = {'object', 'coords', 'geometry'}
187 1
        cls = self.__class__
188 1
        result = cls.__new__(cls)
189 1
        memo[id(self)] = result
190 1
        for k, v in self.items():
191 1
            if isinstance(v, pd.DataFrame) and not set(v.columns).isdisjoint(deep_columns):
192 1
                if k not in result:
193 1
                    result[k] = v.__class__(index=v.index, columns=v.columns)
194 1
                for col in v.columns:
195 1
                    if col in deep_columns:
196 1
                        result[k][col] = v[col].apply(lambda x: copy.deepcopy(x, memo))
197
                    else:
198 1
                        result[k][col] = copy.deepcopy(v[col], memo)
199 1
                _preserve_dtypes(result[k], v.dtypes)
200
            else:
201 1
                setattr(result, k, copy.deepcopy(v, memo))
202

203 1
        result._setattr('_allow_invalid_attributes', self._allow_invalid_attributes)
204 1
        return result
205

206 1
    @classmethod
207 1
    def _valid_name(cls, key):
208
        """
209
        Check whether a key is a valid attribute name.
210

211
        A key may be used as an attribute if:
212
         * It is a string
213
         * The key doesn't overlap with any class attributes (for Attr,
214
            those would be 'get', 'items', 'keys', 'values', 'mro', and
215
            'register').
216
        """
217 1
        return (
218
                isinstance(key, six.string_types) and
219
                not hasattr(cls, key)
220
        )
221

222

223 1
class pandapowerNet(ADict):
224 1
    def __init__(self, *args, **kwargs):
225 1
        super().__init__(*args, **kwargs)
226 1
        if isinstance(args[0], self.__class__):
227 1
            net = args[0]
228 1
            self.clear()
229 1
            self.update(**net.deepcopy())
230

231 1
    def deepcopy(self):
232 1
        return copy.deepcopy(self)
233

234
    def __repr__(self):  # pragma: no cover
235
        r = "This pandapower network includes the following parameter tables:"
236
        par = []
237
        res = []
238
        for tb in list(self.keys()):
239
            if not tb.startswith("_") and isinstance(self[tb], pd.DataFrame) and len(self[tb]) > 0:
240
                if 'res_' in tb:
241
                    res.append(tb)
242
                else:
243
                    par.append(tb)
244
        for tb in par:
245
            length = len(self[tb])
246
            r += "\n   - %s (%s %s)" % (tb, length, "elements" if length > 1 else "element")
247
        if res:
248
            r += "\n and the following results tables:"
249
            for tb in res:
250
                length = len(self[tb])
251
                r += "\n   - %s (%s %s)" % (tb, length, "elements" if length > 1 else "element")
252
        return r
253

254

255 1
def _preserve_dtypes(df, dtypes):
256 1
    for item, dtype in list(dtypes.iteritems()):
257 1
        if df.dtypes.at[item] != dtype:
258 1
            try:
259 1
                df[item] = df[item].astype(dtype)
260 1
            except ValueError:
261 1
                df[item] = df[item].astype(float)
262

263

264 1
def get_free_id(df):
265
    """
266
    Returns next free ID in a dataframe
267
    """
268 1
    return np.int64(0) if len(df) == 0 else df.index.values.max() + 1
269

270

271 1
class ppException(Exception):
272
    """
273
    General pandapower custom parent exception.
274
    """
275 1
    pass
276

277

278 1
def _sum_by_group(bus, first_val, second_val):
279 1
    order = np.argsort(bus)
280 1
    bus = bus[order]
281 1
    index = np.ones(len(bus), 'bool')
282 1
    index[:-1] = bus[1:] != bus[:-1]
283 1
    bus = bus[index]
284 1
    first_val = first_val[order]
285 1
    first_val.cumsum(out=first_val)
286 1
    first_val = first_val[index]
287 1
    first_val[1:] = first_val[1:] - first_val[:-1]
288 1
    second_val = second_val[order]
289 1
    second_val.cumsum(out=second_val)
290 1
    second_val = second_val[index]
291 1
    second_val[1:] = second_val[1:] - second_val[:-1]
292 1
    return bus, first_val, second_val
293

294

295 1
def _sum_by_group_nvals(bus, *vals):
296 1
    order = np.argsort(bus)
297 1
    bus = bus[order]
298 1
    index = np.ones(len(bus), 'bool')
299 1
    index[:-1] = bus[1:] != bus[:-1]
300 1
    bus = bus[index]
301 1
    newvals = tuple(np.zeros((len(vals), len(bus))))
302 1
    for val, newval in zip(vals, newvals):
303 1
        val = val[order]
304 1
        val.cumsum(out=val)
305 1
        val = val[index]
306 1
        val[1:] = val[1:] - val[:-1]
307 1
        newval[:] = val
308
        # Returning vals keeps the original array dimensions, which causes an error if more than one element is
309
        # connected to the same bus. Instead, we create a second tuple of arrays on which we map the results.
310
        # Todo: Check if this workaround causes no problems
311 1
    return (bus,) + newvals
312

313

314 1
def get_indices(selection, lookup, fused_indices=True):
315
    """
316
    Helper function during pd2mpc conversion. It resolves the mapping from a
317
    given selection of indices to the actual indices, using a dict lookup being
318
    passed as well.
319

320
    :param selection: Indices we want to select
321
    :param lookup: The mapping itself
322
    :param fused_indices: Flag which way the conversion is working.
323
    :return:
324
    """
325 1
    if fused_indices:
326 1
        return np.array([lookup[k] for k in selection], dtype="int")
327
    else:
328 1
        return np.array([lookup["before_fuse"][k] for k in selection], dtype="int")
329

330

331 1
def _get_values(source, selection, lookup):
332
    """
333
    Returns values for a selection of values after a lookup.
334

335
    :param source: The array of values to select from.
336
    :param selection: An array of keys, for the selection.
337
    :param lookup: The mapping to resolve actual indices of the
338
    value array from the selection.
339
    :return:
340
    """
341 0
    v = np.zeros(len(selection))
342 0
    for i, k in enumerate(selection):
343 0
        v[i] = source[lookup[np.int(k)]]
344 0
    return v
345

346

347 1
def _set_isolated_nodes_out_of_service(ppc, bus_not_reachable):
348 1
    isolated_nodes = np.where(bus_not_reachable)[0]
349 1
    if len(isolated_nodes) > 0:
350 1
        logger.debug("There are isolated buses in the network!")
351
        # set buses in ppc out of service
352 1
        ppc['bus'][isolated_nodes, BUS_TYPE] = NONE
353

354 1
        pus = abs(ppc['bus'][isolated_nodes, PD] * 1e3).sum()
355 1
        qus = abs(ppc['bus'][isolated_nodes, QD] * 1e3).sum()
356 1
        if pus > 0 or qus > 0:
357 1
            logger.debug("%.0f kW active and %.0f kVar reactive power are unsupplied" % (pus, qus))
358
    else:
359 1
        pus = qus = 0
360

361 1
    return isolated_nodes, pus, qus, ppc
362

363

364 1
def _check_connectivity_opf(ppc):
365
    """
366
    Checks if the ppc contains isolated buses and changes slacks to PV nodes if multiple slacks are
367
    in net.
368
    :param ppc: pypower case file
369
    :return:
370
    """
371 1
    br_status = ppc['branch'][:, BR_STATUS] == True
372 1
    nobranch = ppc['branch'][br_status, :].shape[0]
373 1
    nobus = ppc['bus'].shape[0]
374 1
    bus_from = ppc['branch'][br_status, F_BUS].real.astype(int)
375 1
    bus_to = ppc['branch'][br_status, T_BUS].real.astype(int)
376 1
    slacks = ppc['bus'][ppc['bus'][:, BUS_TYPE] == 3, BUS_I].astype(int)
377

378 1
    adj_matrix = sp.sparse.coo_matrix((np.ones(nobranch),
379
                                       (bus_from, bus_to)),
380
                                      shape=(nobus, nobus))
381

382 1
    bus_not_reachable = np.ones(ppc["bus"].shape[0], dtype=bool)
383 1
    slack_set = set(slacks)
384 1
    for slack in slacks:
385 1
        if ppc['bus'][slack, BUS_TYPE] == PV:
386 1
            continue
387 1
        reachable = sp.sparse.csgraph.breadth_first_order(adj_matrix, slack, False, False)
388 1
        bus_not_reachable[reachable] = False
389 1
        reach_set = set(reachable)
390 1
        intersection = slack_set & reach_set
391 1
        if len(intersection) > 1:
392
            # if slack is in reachable other slacks are connected to this one. Set it to Gen bus
393 1
            demoted_slacks = list(intersection - {slack})
394 1
            ppc['bus'][demoted_slacks, BUS_TYPE] = PV
395 1
            logger.warning("Multiple connected slacks in one area found. This would probably lead "
396
                           "to non-convergence of the OPF. I'll change all but one slack (ext_grid)"
397
                           " to gens. To avoid undesired behaviour, rather convert the slacks to "
398
                           "gens yourself and set slack=True for one of them.")
399

400 1
    isolated_nodes, pus, qus, ppc = _set_isolated_nodes_out_of_service(ppc, bus_not_reachable)
401 1
    return isolated_nodes, pus, qus
402

403

404 1
def _check_connectivity(ppc):
405
    """
406
    Checks if the ppc contains isolated buses. If yes this isolated buses are set out of service
407
    :param ppc: pypower case file
408
    :return:
409
    """
410 1
    br_status = ppc['branch'][:, BR_STATUS] == True
411 1
    nobranch = ppc['branch'][br_status, :].shape[0]
412 1
    nobus = ppc['bus'].shape[0]
413 1
    bus_from = ppc['branch'][br_status, F_BUS].real.astype(int)
414 1
    bus_to = ppc['branch'][br_status, T_BUS].real.astype(int)
415 1
    slacks = ppc['bus'][ppc['bus'][:, BUS_TYPE] == 3, BUS_I]
416

417
    # we create a "virtual" bus thats connected to all slack nodes and start the connectivity
418
    # search at this bus
419 1
    bus_from = np.hstack([bus_from, slacks])
420 1
    bus_to = np.hstack([bus_to, np.ones(len(slacks)) * nobus])
421

422 1
    adj_matrix = sp.sparse.coo_matrix((np.ones(nobranch + len(slacks)),
423
                                       (bus_from, bus_to)),
424
                                      shape=(nobus + 1, nobus + 1))
425

426 1
    reachable = sp.sparse.csgraph.breadth_first_order(adj_matrix, nobus, False, False)
427
    # TODO: the former impl. excluded ppc buses that are already oos, but is this necessary ?
428
    # if so: bus_not_reachable = np.hstack([ppc['bus'][:, BUS_TYPE] != 4, np.array([False])])
429 1
    bus_not_reachable = np.ones(ppc["bus"].shape[0] + 1, dtype=bool)
430 1
    bus_not_reachable[reachable] = False
431 1
    isolated_nodes, pus, qus, ppc = _set_isolated_nodes_out_of_service(ppc, bus_not_reachable)
432 1
    return isolated_nodes, pus, qus
433

434

435
def _python_set_elements_oos(ti, tis, bis, lis):  # pragma: no cover
436
    for i in range(len(ti)):
437
        if tis[i] and bis[ti[i]]:
438
            lis[i] = True
439

440

441
def _python_set_isolated_buses_oos(bus_in_service, ppc_bus_isolated, bus_lookup):  # pragma: no cover
442
    for k in range(len(bus_in_service)):
443
        if ppc_bus_isolated[bus_lookup[k]]:
444
            bus_in_service[k] = False
445

446

447 1
try:
448 1
    get_values = jit(nopython=True, cache=True)(_get_values)
449 1
    set_elements_oos = jit(nopython=True, cache=True)(_python_set_elements_oos)
450 1
    set_isolated_buses_oos = jit(nopython=True, cache=True)(_python_set_isolated_buses_oos)
451 0
except RuntimeError:
452 0
    get_values = jit(nopython=True, cache=False)(_get_values)
453 0
    set_elements_oos = jit(nopython=True, cache=False)(_python_set_elements_oos)
454 0
    set_isolated_buses_oos = jit(nopython=True, cache=False)(_python_set_isolated_buses_oos)
455

456

457 1
def _select_is_elements_numba(net, isolated_nodes=None, sequence=None):
458
    # is missing sgen_controllable and load_controllable
459 1
    max_bus_idx = np.max(net["bus"].index.values)
460 1
    bus_in_service = np.zeros(max_bus_idx + 1, dtype=bool)
461 1
    bus_in_service[net["bus"].index.values] = net["bus"]["in_service"].values.astype(bool)
462 1
    if isolated_nodes is not None and len(isolated_nodes) > 0:
463 1
        ppc = net["_ppc"] if sequence is None else net["_ppc%s" % sequence]
464 1
        ppc_bus_isolated = np.zeros(ppc["bus"].shape[0], dtype=bool)
465 1
        ppc_bus_isolated[isolated_nodes] = True
466 1
        set_isolated_buses_oos(bus_in_service, ppc_bus_isolated, net["_pd2ppc_lookups"]["bus"])
467
    #    mode = net["_options"]["mode"]
468 1
    elements = ["load", "motor", "sgen", "asymmetric_load", "asymmetric_sgen", "gen" \
469
        , "ward", "xward", "shunt", "ext_grid", "storage"]  # ,"impedance_load"
470 1
    is_elements = dict()
471 1
    for element in elements:
472 1
        len_ = len(net[element].index)
473 1
        element_in_service = np.zeros(len_, dtype=bool)
474 1
        if len_ > 0:
475 1
            element_df = net[element]
476 1
            set_elements_oos(element_df["bus"].values, element_df["in_service"].values,
477
                             bus_in_service, element_in_service)
478 1
        if net["_options"]["mode"] == "opf" and element in ["load", "sgen", "storage"]:
479 1
            if "controllable" in net[element]:
480 1
                controllable = net[element].controllable.fillna(False).values.astype(bool)
481 1
                controllable_is = controllable & element_in_service
482 1
                if controllable_is.any():
483 1
                    is_elements["%s_controllable" % element] = controllable_is
484 1
                    element_in_service = element_in_service & ~controllable_is
485 1
        is_elements[element] = element_in_service
486

487 1
    is_elements["bus_is_idx"] = net["bus"].index.values[bus_in_service[net["bus"].index.values]]
488 1
    is_elements["line_is_idx"] = net["line"].index[net["line"].in_service.values]
489 1
    return is_elements
490

491

492 1
def _add_ppc_options(net, calculate_voltage_angles, trafo_model, check_connectivity, mode,
493
                     switch_rx_ratio, enforce_q_lims, recycle, delta=1e-10,
494
                     voltage_depend_loads=False, trafo3w_losses="hv", init_vm_pu=1.0,
495
                     init_va_degree=0, p_lim_default=1e9, q_lim_default=1e9,
496
                     neglect_open_switch_branches=False, consider_line_temperature=False):
497
    """
498
    creates dictionary for pf, opf and short circuit calculations from input parameters.
499
    """
500
    # if recycle is None:
501
    #     recycle = dict(trafo=False, bus_pq=False, bfsw=False)
502

503 1
    init_results = (isinstance(init_vm_pu, str) and (init_vm_pu == "results")) or \
504
                   (isinstance(init_va_degree, str) and (init_va_degree == "results"))
505

506 1
    options = {
507
        "calculate_voltage_angles": calculate_voltage_angles,
508
        "trafo_model": trafo_model,
509
        "check_connectivity": check_connectivity,
510
        "mode": mode,
511
        "switch_rx_ratio": switch_rx_ratio,
512
        "enforce_q_lims": enforce_q_lims,
513
        "recycle": recycle,
514
        "voltage_depend_loads": voltage_depend_loads,
515
        "consider_line_temperature": consider_line_temperature,
516
        "delta": delta,
517
        "trafo3w_losses": trafo3w_losses,
518
        "init_vm_pu": init_vm_pu,
519
        "init_va_degree": init_va_degree,
520
        "init_results": init_results,
521
        "p_lim_default": p_lim_default,
522
        "q_lim_default": q_lim_default,
523
        "neglect_open_switch_branches": neglect_open_switch_branches,
524
    }
525 1
    _add_options(net, options)
526

527

528 1
def _check_bus_index_and_print_warning_if_high(net, n_max=1e7):
529 1
    max_bus = max(net.bus.index.values)
530 1
    if max_bus >= n_max > len(net["bus"]):
531 0
        logger.warning("Maximum bus index is high (%i). You should avoid high bus indices because "
532
                       "of perfomance reasons. Try resetting the bus indices with the toolbox "
533
                       "function create_continuous_bus_index()" % max_bus)
534

535

536 1
def _check_gen_index_and_print_warning_if_high(net, n_max=1e7):
537 1
    if net.gen.empty:
538 1
        return
539 1
    max_gen = max(net.gen.index.values)
540 1
    if max_gen >= n_max > len(net["gen"]):
541 0
        logger.warning("Maximum generator index is high (%i). You should avoid high generator "
542
                       "indices because of perfomance reasons. Try resetting the bus indices with "
543
                       "the toolbox function create_continuous_elements_index()" % max_gen)
544

545

546 1
def _add_pf_options(net, tolerance_mva, trafo_loading, numba, ac,
547
                    algorithm, max_iteration, **kwargs):
548
    """
549
    creates dictionary for pf, opf and short circuit calculations from input parameters.
550
    """
551

552 1
    options = {
553
        "tolerance_mva": tolerance_mva,
554
        "trafo_loading": trafo_loading,
555
        "numba": numba,
556
        "ac": ac,
557
        "algorithm": algorithm,
558
        "max_iteration": max_iteration
559
    }
560

561 1
    options.update(kwargs)  # update options with some algorithm-specific parameters
562 1
    _add_options(net, options)
563

564

565 1
def _add_opf_options(net, trafo_loading, ac, v_debug=False, **kwargs):
566
    """
567
    creates dictionary for pf, opf and short circuit calculations from input parameters.
568
    """
569 1
    options = {
570
        "trafo_loading": trafo_loading,
571
        "ac": ac,
572
        "v_debug": v_debug
573
    }
574

575 1
    options.update(kwargs)  # update options with some algorithm-specific parameters
576 1
    _add_options(net, options)
577

578

579 1
def _add_sc_options(net, fault, case, lv_tol_percent, tk_s, topology, r_fault_ohm,
580
                    x_fault_ohm, kappa, ip, ith, branch_results, kappa_method, return_all_currents,
581
                    inverse_y):
582
    """
583
    creates dictionary for pf, opf and short circuit calculations from input parameters.
584
    """
585 1
    options = {
586
        "fault": fault,
587
        "case": case,
588
        "lv_tol_percent": lv_tol_percent,
589
        "tk_s": tk_s,
590
        "topology": topology,
591
        "r_fault_ohm": r_fault_ohm,
592
        "x_fault_ohm": x_fault_ohm,
593
        "kappa": kappa,
594
        "ip": ip,
595
        "ith": ith,
596
        "branch_results": branch_results,
597
        "kappa_method": kappa_method,
598
        "return_all_currents": return_all_currents,
599
        "inverse_y": inverse_y
600
    }
601 1
    _add_options(net, options)
602

603

604 1
def _add_options(net, options):
605
    # double_parameters = set(net.__internal_options.keys()) & set(options.keys())
606 1
    double_parameters = set(net._options.keys()) & set(options.keys())
607 1
    if len(double_parameters) > 0:
608 0
        raise UserWarning(
609
            "Parameters always have to be unique! The following parameters where specified " +
610
            "twice: %s" % double_parameters)
611
    # net.__internal_options.update(options)
612 1
    net._options.update(options)
613

614

615 1
def _clean_up(net, res=True):
616
    # mode = net.__internal_options["mode"]
617

618
    # set internal selected _is_elements to None. This way it is not stored (saves disk space)
619
    # net._is_elements = None
620

621
    #    mode = net._options["mode"]
622
    #    if res:
623
    #        res_bus = net["res_bus_sc"] if mode == "sc" else \
624
    #            net["res_bus_3ph"] if mode == "pf_3ph" else \
625
    #                net["res_bus"]
626
    #    if len(net["trafo3w"]) > 0:
627
    #        buses_3w = net.trafo3w["ad_bus"].values
628
    #        net["bus"].drop(buses_3w, inplace=True)
629
    #        net["trafo3w"].drop(["ad_bus"], axis=1, inplace=True)
630
    #        if res:
631
    #            res_bus.drop(buses_3w, inplace=True)
632
    #
633
    #    if len(net["xward"]) > 0:
634
    #        xward_buses = net["xward"]["ad_bus"].values
635
    #        net["bus"].drop(xward_buses, inplace=True)
636
    #        net["xward"].drop(["ad_bus"], axis=1, inplace=True)
637
    #        if res:
638
    #            res_bus.drop(xward_buses, inplace=True)
639 1
    if len(net["dcline"]) > 0:
640 1
        dc_gens = net.gen.index[(len(net.gen) - len(net.dcline) * 2):]
641 1
        net.gen.drop(dc_gens, inplace=True)
642 1
        if res:
643 1
            net.res_gen.drop(dc_gens, inplace=True)
644

645

646 1
def _set_isolated_buses_out_of_service(net, ppc):
647
    # set disconnected buses out of service
648
    # first check if buses are connected to branches
649 1
    disco = np.setxor1d(ppc["bus"][:, 0].astype(int),
650
                        ppc["branch"][ppc["branch"][:, 10] == 1, :2].real.astype(int).flatten())
651

652
    # but also check if they may be the only connection to an ext_grid
653 1
    net._isolated_buses = np.setdiff1d(disco, ppc['bus'][ppc['bus'][:, 1] == REF, :1].real.astype(int))
654 1
    ppc["bus"][net._isolated_buses, 1] = NONE
655

656

657 1
def _write_lookup_to_net(net, element, element_lookup):
658
    """
659
    Updates selected lookups in net
660
    """
661 1
    net["_pd2ppc_lookups"][element] = element_lookup
662

663

664 1
def _check_if_numba_is_installed(numba):
665 1
    numba_warning_str = 'numba cannot be imported and numba functions are disabled.\n' \
666
                        'Probably the execution is slow.\n' \
667
                        'Please install numba to gain a massive speedup.\n' \
668
                        '(or if you prefer slow execution, set the flag numba=False to avoid ' + \
669
                        'this warning!)\n'
670

671 1
    try:
672
        # get numba Version (in order to use it it must be > 0.25)
673 1
        if version.parse(numba_version) < version.parse("0.2.5"):
674 0
            logger.warning('Warning: numba version too old -> Upgrade to a version > 0.25.\n' +
675
                           numba_warning_str)
676 0
            numba = False
677 0
    except:
678 0
        logger.warning(numba_warning_str)
679 0
        numba = False
680

681 1
    return numba
682

683

684 1
def _deactive(msg):
685 0
    logger.error(msg)
686 0
    return False
687

688

689 1
def _check_lightsim2grid_compatibility(net, lightsim2grid, voltage_dependend_loads, algorithm, enforce_q_lims):
690 1
    if lightsim2grid:
691 0
        if not lightsim2grid_available:
692 0
            return _deactive("option 'lightsim2grid' is True activates but module cannot be imported. "
693
                             "I'll deactive lightsim2grid.")
694 0
        if algorithm != 'nr':
695 0
            raise ValueError("option 'lightsim2grid' is True activates but algorithm is not 'nr'.")
696 0
        if voltage_dependend_loads:
697 0
            return _deactive("option 'lightsim2grid' is True but voltage dependend loads are in your grid."
698
                             "I'll deactive lightsim2grid.")
699 0
        if enforce_q_lims:
700 0
            return _deactive("option 'lightsim2grid' is True and enforce_q_lims is True. This is not supported."
701
                             "I'll deactive lightsim2grid.")
702 0
        if len(net.ext_grid) > 1:
703 0
            return _deactive("option 'lightsim2grid' is True and multiple ext_grids are in the grid."
704
                             "I'll deactive lightsim2grid.")
705 0
        if np.any(net.gen.bus.isin(net.ext_grid.bus)):
706 0
            return _deactive("option 'lightsim2grid' is True and gens are at slack buses."
707
                             "I'll deactive lightsim2grid.")
708

709 1
    return lightsim2grid
710

711

712
# =============================================================================
713
# Functions for 3 Phase Unbalanced Load Flow
714
# =============================================================================
715

716
# =============================================================================
717
# Convert to three decoupled sequence networks
718
# =============================================================================
719

720

721 1
def X012_to_X0(X012):
722 1
    return np.transpose(X012[0, :])
723

724

725 1
def X012_to_X1(X012):
726 1
    return np.transpose(X012[1, :])
727

728

729 1
def X012_to_X2(X012):
730 1
    return np.transpose(X012[2, :])
731

732

733
# =============================================================================
734
# Three decoupled sequence network to 012 matrix conversion
735
# =============================================================================
736

737 1
def combine_X012(X0, X1, X2):
738 1
    comb = np.vstack((X0, X1, X2))
739 1
    return comb
740

741

742
# =============================================================================
743
# Symmetrical transformation matrix
744
# Tabc : 012 > abc
745
# T012 : abc >012
746
# =============================================================================
747

748 1
def phase_shift_unit_operator(angle_deg):
749 1
    return 1 * np.exp(1j * np.deg2rad(angle_deg))
750

751

752 1
a = phase_shift_unit_operator(120)
753 1
asq = phase_shift_unit_operator(-120)
754 1
Tabc = np.array(
755
    [
756
        [1, 1, 1],
757
        [1, asq, a],
758
        [1, a, asq]
759
    ])
760

761 1
T012 = np.divide(np.array(
762
    [
763
        [1, 1, 1],
764
        [1, a, asq],
765
        [1, asq, a]
766
    ]), 3)
767

768

769 1
def sequence_to_phase(X012):
770 1
    return np.asarray(np.matmul(Tabc, X012))
771

772

773 1
def phase_to_sequence(Xabc):
774 1
    return np.asarray(np.matmul(T012, Xabc))
775

776

777
# def Y_phase_to_sequence(Xabc):
778
#   return np.asarray(np.matmul(T012,Xabc,Tabc))
779
# =============================================================================
780
# Calculating Sequence Current from sequence Voltages
781
# =============================================================================
782

783 1
def I0_from_V012(V012, Y):
784 1
    V0 = X012_to_X0(V012)
785 1
    if type(Y) in [sp.sparse.csr.csr_matrix, sp.sparse.csc.csc_matrix]:
786 1
        return np.asarray(np.matmul(Y.todense(), V0))
787
    else:
788 0
        return np.asarray(np.matmul(Y, V0))
789

790

791 1
def I1_from_V012(V012, Y):
792 1
    V1 = X012_to_X1(V012)[:, np.newaxis]
793 1
    if type(Y) in [sp.sparse.csr.csr_matrix, sp.sparse.csc.csc_matrix]:
794 1
        i1 = np.asarray(np.matmul(Y.todense(), V1))
795 1
        return np.transpose(i1)
796
    else:
797 0
        i1 = np.asarray(np.matmul(Y, V1))
798 0
        return np.transpose(i1)
799

800

801 1
def I2_from_V012(V012, Y):
802 1
    V2 = X012_to_X2(V012)
803 1
    if type(Y) in [sp.sparse.csr.csr_matrix, sp.sparse.csc.csc_matrix]:
804 1
        return np.asarray(np.matmul(Y.todense(), V2))
805
    else:
806 0
        return np.asarray(np.matmul(Y, V2))
807

808

809 1
def V1_from_ppc(ppc):
810 1
    return np.transpose(
811
        np.array(
812
            ppc["bus"][:, VM] * np.exp(1j * np.deg2rad(ppc["bus"][:, VA]))
813
        )
814
    )
815

816

817 1
def V_from_I(Y, I):
818 1
    return np.transpose(np.array(sp.sparse.linalg.spsolve(Y, I)))
819

820

821 1
def I_from_V(Y, V):
822 0
    if type(Y) in [sp.sparse.csr.csr_matrix, sp.sparse.csc.csc_matrix]:
823 0
        return np.asarray(np.matmul(Y.todense(), V))
824
    else:
825 0
        return np.asarray(np.matmul(Y, V))
826

827

828
# =============================================================================
829
# Calculating Power
830
# =============================================================================
831

832 1
def S_from_VI_elementwise(V, I):
833 1
    return np.multiply(V, I.conjugate())
834

835

836 1
def I_from_SV_elementwise(S, V):
837 1
    return np.conjugate(np.divide(S, V, out=np.zeros_like(S), where=V != 0))  # Return zero if div by zero
838

839

840 1
def SVabc_from_SV012(S012, V012, n_res=None, idx=None):
841 1
    if n_res is None:
842 0
        n_res = S012.shape[1]
843 1
    if idx is None:
844 0
        idx = np.ones(n_res, dtype="bool")
845 1
    I012 = np.array(np.zeros((3, n_res)), dtype=np.complex128)
846 1
    I012[:, idx] = I_from_SV_elementwise(S012[:, idx], V012[:, idx])
847 1
    Vabc = sequence_to_phase(V012[:, idx])
848 1
    Iabc = sequence_to_phase(I012[:, idx])
849 1
    Sabc = S_from_VI_elementwise(Vabc, Iabc)
850 1
    return Sabc, Vabc
851

852

853 1
def _add_auxiliary_elements(net):
854 1
    if len(net.dcline) > 0:
855 1
        _add_dcline_gens(net)
856

857

858 1
def _add_dcline_gens(net):
859 1
    from pandapower.create import create_gen
860 1
    for dctab in net.dcline.itertuples():
861 1
        pfrom = dctab.p_mw
862 1
        pto = (pfrom * (1 - dctab.loss_percent / 100) - dctab.loss_mw)
863 1
        pmax = dctab.max_p_mw
864 1
        create_gen(net, bus=dctab.to_bus, p_mw=pto, vm_pu=dctab.vm_to_pu,
865
                   min_p_mw=0, max_p_mw=pmax,
866
                   max_q_mvar=dctab.max_q_to_mvar, min_q_mvar=dctab.min_q_to_mvar,
867
                   in_service=dctab.in_service)
868 1
        create_gen(net, bus=dctab.from_bus, p_mw=-pfrom, vm_pu=dctab.vm_from_pu,
869
                   min_p_mw=-pmax, max_p_mw=0,
870
                   max_q_mvar=dctab.max_q_from_mvar, min_q_mvar=dctab.min_q_from_mvar,
871
                   in_service=dctab.in_service)
872

873

874 1
def _replace_nans_with_default_limits(net, ppc):
875 1
    qlim = net._options["q_lim_default"]
876 1
    plim = net._options["p_lim_default"]
877

878 1
    for matrix, column, default in [("gen", QMAX, qlim), ("gen", QMIN, -qlim), ("gen", PMIN, -plim),
879
                                    ("gen", PMAX, plim), ("bus", VMAX, 2.0), ("bus", VMIN, 0.0)]:
880 1
        limits = ppc[matrix][:, [column]]
881 1
        ncn.copyto(limits, default, where=np.isnan(limits))
882 1
        ppc[matrix][:, [column]] = limits
883

884

885 1
def _init_runpp_options(net, algorithm, calculate_voltage_angles, init,
886
                        max_iteration, tolerance_mva, trafo_model,
887
                        trafo_loading, enforce_q_lims, check_connectivity,
888
                        voltage_depend_loads, passed_parameters=None,
889
                        consider_line_temperature=False, **kwargs):
890
    """
891
    Inits _options in net for runpp.
892
    """
893 1
    overrule_options = {}
894 1
    if passed_parameters is not None:
895 1
        overrule_options = {key: val for key, val in net.user_pf_options.items()
896
                            if key not in passed_parameters.keys()}
897

898 1
    kwargs.update(overrule_options)
899

900 1
    trafo3w_losses = kwargs.get("trafo3w_losses", "hv")
901 1
    v_debug = kwargs.get("v_debug", False)
902 1
    delta_q = kwargs.get("delta_q", 0)
903 1
    switch_rx_ratio = kwargs.get("switch_rx_ratio", 2)
904 1
    numba = kwargs.get("numba", True)
905 1
    init_vm_pu = kwargs.get("init_vm_pu", None)
906 1
    init_va_degree = kwargs.get("init_va_degree", None)
907 1
    neglect_open_switch_branches = kwargs.get("neglect_open_switch_branches", False)
908
    # recycle options
909 1
    recycle = kwargs.get("recycle", None)
910 1
    only_v_results = kwargs.get("only_v_results", False)
911
    # scipy spsolve options in NR power flow
912 1
    use_umfpack = kwargs.get("use_umfpack", True)
913 1
    permc_spec = kwargs.get("permc_spec", None)
914 1
    lightsim2grid = kwargs.get("lightsim2grid", False)
915

916 1
    if "init" in overrule_options:
917 1
        init = overrule_options["init"]
918

919
    # check if numba is available and the corresponding flag
920 1
    if numba:
921 1
        numba = _check_if_numba_is_installed(numba)
922

923 1
    if voltage_depend_loads:
924 1
        if not (np.any(net["load"]["const_z_percent"].values)
925
                or np.any(net["load"]["const_i_percent"].values)):
926 1
            voltage_depend_loads = False
927

928 1
    if algorithm not in ['nr', 'bfsw', 'iwamoto_nr'] and voltage_depend_loads == True:
929 0
        logger.warning("voltage-dependent loads not supported for {0} power flow algorithm -> "
930
                       "loads will be considered as constant power".format(algorithm))
931

932 1
    lightsim2grid = _check_lightsim2grid_compatibility(net, lightsim2grid, voltage_depend_loads,
933
                                                       algorithm, enforce_q_lims)
934

935 1
    ac = True
936 1
    mode = "pf"
937 1
    if calculate_voltage_angles == "auto":
938 1
        calculate_voltage_angles = False
939 1
        is_hv_bus = np.where(net.bus.vn_kv.values > 70)[0]
940 1
        if any(is_hv_bus) > 0:
941 1
            line_buses = set(net.line.from_bus.values) & set(net.line.to_bus.values)
942 1
            hv_buses = net.bus.index[is_hv_bus]
943 1
            if any(a in line_buses for a in hv_buses):
944 1
                calculate_voltage_angles = True
945

946 1
    default_max_iteration = {"nr": 10, "iwamoto_nr": 10, "bfsw": 100, "gs": 10000, "fdxb": 30,
947
                             "fdbx": 30}
948 1
    if max_iteration == "auto":
949 1
        max_iteration = default_max_iteration[algorithm]
950

951 1
    if init != "auto" and (init_va_degree is not None or init_vm_pu is not None):
952 0
        raise ValueError("Either define initialization through 'init' or through 'init_vm_pu' and "
953
                         "'init_va_degree'.")
954

955 1
    init_from_results = init == "results" or \
956
                        (isinstance(init_vm_pu, str) and init_vm_pu == "results") or \
957
                        (isinstance(init_va_degree, str) and init_va_degree == "results")
958 1
    if init_from_results and len(net.res_bus) == 0:
959 1
        init = "auto"
960 1
        init_vm_pu = None
961 1
        init_va_degree = None
962

963 1
    if init == "auto":
964 1
        if init_va_degree is None or (isinstance(init_va_degree, str) and init_va_degree == "auto"):
965 1
            init_va_degree = "dc" if calculate_voltage_angles else "flat"
966 1
        if init_vm_pu is None or (isinstance(init_vm_pu, str) and init_vm_pu == "auto"):
967 1
            init_vm_pu = (net.ext_grid.vm_pu.values.sum() + net.gen.vm_pu.values.sum()) / \
968
                         (len(net.ext_grid.vm_pu.values) + len(net.gen.vm_pu.values))
969 1
    elif init == "dc":
970 1
        init_vm_pu = "flat"
971 1
        init_va_degree = "dc"
972
    else:
973 1
        init_vm_pu = init
974 1
        init_va_degree = init
975

976
    # init options
977 1
    net._options = {}
978 1
    _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles,
979
                     trafo_model=trafo_model, check_connectivity=check_connectivity,
980
                     mode=mode, switch_rx_ratio=switch_rx_ratio, init_vm_pu=init_vm_pu,
981
                     init_va_degree=init_va_degree, enforce_q_lims=enforce_q_lims, recycle=recycle,
982
                     voltage_depend_loads=voltage_depend_loads, delta=delta_q,
983
                     trafo3w_losses=trafo3w_losses,
984
                     neglect_open_switch_branches=neglect_open_switch_branches,
985
                     consider_line_temperature=consider_line_temperature)
986 1
    _add_pf_options(net, tolerance_mva=tolerance_mva, trafo_loading=trafo_loading,
987
                    numba=numba, ac=ac, algorithm=algorithm, max_iteration=max_iteration,
988
                    v_debug=v_debug, only_v_results=only_v_results, use_umfpack=use_umfpack,
989
                    permc_spec=permc_spec, lightsim2grid=lightsim2grid)
990 1
    net._options.update(overrule_options)
991

992

993 1
def _init_nx_options(net):
994 1
    net._options = {}
995 1
    _add_ppc_options(net, calculate_voltage_angles=False,
996
                     trafo_model="t", check_connectivity=False,
997
                     mode="nx", switch_rx_ratio=2, init_vm_pu='flat', init_va_degree="flat",
998
                     enforce_q_lims=False, recycle=False,
999
                     voltage_depend_loads=False, delta=0, trafo3w_losses="hv")
1000

1001

1002 1
def _init_rundcpp_options(net, trafo_model, trafo_loading, recycle, check_connectivity,
1003
                          switch_rx_ratio, trafo3w_losses, **kwargs):
1004 1
    ac = False
1005 1
    numba = True
1006 1
    mode = "pf"
1007 1
    init = 'flat'
1008

1009 1
    numba = _check_if_numba_is_installed(numba)
1010

1011
    # the following parameters have no effect if ac = False
1012 1
    calculate_voltage_angles = True
1013 1
    enforce_q_lims = False
1014 1
    algorithm = None
1015 1
    max_iteration = None
1016 1
    tolerance_mva = None
1017 1
    only_v_results = kwargs.get("only_v_results", False)
1018 1
    net._options = {}
1019 1
    _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles,
1020
                     trafo_model=trafo_model, check_connectivity=check_connectivity,
1021
                     mode=mode, switch_rx_ratio=switch_rx_ratio, init_vm_pu=init,
1022
                     init_va_degree=init, enforce_q_lims=enforce_q_lims, recycle=recycle,
1023
                     voltage_depend_loads=False, delta=0, trafo3w_losses=trafo3w_losses)
1024 1
    _add_pf_options(net, tolerance_mva=tolerance_mva, trafo_loading=trafo_loading,
1025
                    numba=numba, ac=ac, algorithm=algorithm, max_iteration=max_iteration,
1026
                    only_v_results=only_v_results)
1027

1028

1029 1
def _init_runopp_options(net, calculate_voltage_angles, check_connectivity, switch_rx_ratio, delta,
1030
                         init, numba, trafo3w_losses, consider_line_temperature=False, **kwargs):
1031 1
    if numba:
1032 1
        numba = _check_if_numba_is_installed(numba)
1033 1
    mode = "opf"
1034 1
    ac = True
1035 1
    trafo_model = "t"
1036 1
    trafo_loading = 'current'
1037 1
    enforce_q_lims = True
1038 1
    recycle = None
1039 1
    only_v_results = False
1040
    # scipy spsolve options in NR power flow
1041 1
    use_umfpack = kwargs.get("use_umfpack", True)
1042 1
    permc_spec = kwargs.get("permc_spec", None)
1043 1
    lightsim2grid = kwargs.get("lightsim2grid", False)
1044

1045 1
    net._options = {}
1046 1
    _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles,
1047
                     trafo_model=trafo_model, check_connectivity=check_connectivity,
1048
                     mode=mode, switch_rx_ratio=switch_rx_ratio, init_vm_pu=init,
1049
                     init_va_degree=init, enforce_q_lims=enforce_q_lims, recycle=recycle,
1050
                     voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses,
1051
                     consider_line_temperature=consider_line_temperature)
1052 1
    _add_opf_options(net, trafo_loading=trafo_loading, ac=ac, init=init, numba=numba, lightsim2grid=lightsim2grid,
1053
                     only_v_results=only_v_results, use_umfpack=use_umfpack, permc_spec=permc_spec)
1054

1055

1056 1
def _init_rundcopp_options(net, check_connectivity, switch_rx_ratio, delta, trafo3w_losses, **kwargs):
1057 1
    mode = "opf"
1058 1
    ac = False
1059 1
    init = "flat"
1060 1
    trafo_model = "t"
1061 1
    trafo_loading = 'current'
1062 1
    calculate_voltage_angles = True
1063 1
    enforce_q_lims = True
1064 1
    recycle = None
1065 1
    only_v_results = False
1066
    # scipy spsolve options in NR power flow
1067 1
    use_umfpack = kwargs.get("use_umfpack", True)
1068 1
    permc_spec = kwargs.get("permc_spec", None)
1069
    # net.__internal_options = {}
1070 1
    net._options = {}
1071 1
    _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles,
1072
                     trafo_model=trafo_model, check_connectivity=check_connectivity,
1073
                     mode=mode, switch_rx_ratio=switch_rx_ratio, init_vm_pu=init,
1074
                     init_va_degree=init, enforce_q_lims=enforce_q_lims, recycle=recycle,
1075
                     voltage_depend_loads=False, delta=delta, trafo3w_losses=trafo3w_losses)
1076 1
    _add_opf_options(net, trafo_loading=trafo_loading, init=init, ac=ac, only_v_results=only_v_results,
1077
                     use_umfpack=use_umfpack, permc_spec=permc_spec)
1078

1079

1080 1
def _init_runse_options(net, v_start, delta_start, calculate_voltage_angles,
1081
                        **kwargs):
1082

1083 1
    check_connectivity = kwargs.get("check_connectivity", True)
1084 1
    trafo_model = kwargs.get("trafo_model", "t")
1085 1
    trafo3w_losses = kwargs.get("trafo3w_losses", "hv")
1086 1
    switch_rx_ratio = kwargs.get("switch_rx_ratio", 2)
1087

1088 1
    net._options = {}
1089 1
    _add_ppc_options(net, calculate_voltage_angles=calculate_voltage_angles,
1090
                     trafo_model=trafo_model, check_connectivity=check_connectivity,
1091
                     mode="pf", switch_rx_ratio=switch_rx_ratio, init_vm_pu=v_start,
1092
                     init_va_degree=delta_start, enforce_q_lims=False, recycle=None,
1093
                     voltage_depend_loads=False, trafo3w_losses=trafo3w_losses)
1094 1
    _add_pf_options(net, tolerance_mva="1e-8", trafo_loading="power",
1095
                    numba=False, ac=True, algorithm="nr", max_iteration="auto",
1096
                    only_v_results=False)
1097

1098

1099 1
def _internal_stored(net):
1100
    """
1101

1102
    The function newtonpf() needs these variables as inputs:
1103
    Ybus, Sbus, V0, pv, pq, ppci, options
1104

1105
    Parameters
1106
    ----------
1107
    net - the pandapower net
1108

1109
    Returns
1110
    -------
1111
    True if all variables are stored False otherwise
1112

1113
    """
1114
    # checks if all internal variables are stored in net, which are needed for a power flow
1115

1116 1
    if net["_ppc"] is None:
1117 1
        return False
1118

1119 1
    mandatory_pf_variables = ["J", "bus", "gen", "branch", "baseMVA", "V", "pv", "pq", "ref",
1120
                              "Ybus", "Yf", "Yt", "Sbus", "ref_gens"]
1121 1
    for var in mandatory_pf_variables:
1122 1
        if "internal" not in net["_ppc"] or var not in net["_ppc"]["internal"]:
1123 1
            logger.warning("recycle is set to True, but internal variables are missing")
1124 1
            return False
1125 1
    return True

Read our documentation on viewing source code .

Loading