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

604

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

615

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

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

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

646

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

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

657

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

664

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

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

682 1
    return numba
683

684

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

689

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

710 1
    return lightsim2grid
711

712

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

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

721

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

725

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

729

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

733

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

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

742

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

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

752

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

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

769

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

773

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

777

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

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

791

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

801

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

809

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

817

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

821

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

828

829
# =============================================================================
830
# Calculating Power
831
# =============================================================================
832

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

836

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

840

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

853

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

858

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

874

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

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

885

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

899 1
    kwargs.update(overrule_options)
900

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

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

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

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

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

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

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

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

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

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

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

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

993

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

1002

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

1010 1
    numba = _check_if_numba_is_installed(numba)
1011

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

1029

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

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

1056

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

1080

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

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

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

1099

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

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

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

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

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

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

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

Read our documentation on viewing source code .

Loading