Instruction:
Can you tell what is the following Python 3 function doing
def graph_nodes_from_subtree(self, node_source, include_root_node=False):
        if node_source in self._graph.nodes():

            # get all nodes that are member of a ring
            node_ring = []
            for ring in self.rings_nodes(include_root_node=include_root_node):
                if node_source in ring:
                    node_ring = ring
                    break

            # result set
            nodes_subtree = set()

            # get nodes from subtree
            if node_source in node_ring:
                for path in nx.shortest_path(self._graph, node_source).values():
                    if len(path)>1:
                        if (path[1] not in node_ring) and (path[1] is not self.station()):
                            nodes_subtree.update(path[1:len(path)])
            else:
                raise ValueError(node_source, 'is not member of ring.')

        else:
            raise ValueError(node_source, 'is not member of graph.')

        return list(nodes_subtree)

Response:
Returns a list of nodes that are connected to node_source and are not part of the
        ring of node_source.


Instruction:
Explain what the following Python 3 code does
def set_branch_ids(self):

        # MV grid:
        ctr = 1
        for branch in self.graph_edges():
            branch['branch'].id_db = self.grid_district.id_db * 10**4 + ctr
            ctr += 1

        # LV grid:
        for lv_load_area in self.grid_district.lv_load_areas():
            for lv_grid_district in lv_load_area.lv_grid_districts():
                ctr = 1
                for branch in lv_grid_district.lv_grid.graph_edges():
                    branch['branch'].id_db = lv_grid_district.id_db * 10**7 + ctr
                    ctr += 1

Response: Generates and sets ids of branches for MV and LV grids.


Instruction:
Write a Python 3 function for
performing the routing on load area centres and creates MV grid with the grid topology.

Response:
def routing(self, debug=False, anim=None):
        """ Performs routing on Load Area centres to build MV grid with ring topology.

        Args
        ----
        debug: bool, defaults to False
            If True, information is printed while routing
        anim: type, defaults to None
            Descr #TODO
        """

        # do the routing
        self._graph = mv_routing.solve(graph=self._graph,
                                       debug=debug,
                                       anim=anim)
        logger.info('==> MV Routing for {} done'.format(repr(self)))

        # connect satellites (step 1, with restrictions like max. string length, max peak load per string)
        self._graph = mv_connect.mv_connect_satellites(mv_grid=self,
                                                       graph=self._graph,
                                                       mode='normal',
                                                       debug=debug)
        logger.info('==> MV Sat1 for {} done'.format(repr(self)))

        # connect satellites to closest line/station on a MV ring that have not been connected in step 1
        self._graph = mv_connect.mv_connect_satellites(mv_grid=self,
                                                       graph=self._graph,
                                                       mode='isolated',
                                                       debug=debug)
        logger.info('==> MV Sat2 for {} done'.format(repr(self)))

        # connect stations
        self._graph = mv_connect.mv_connect_stations(mv_grid_district=self.grid_district,
                                                     graph=self._graph,
                                                     debug=debug)
        logger.info('==> MV Stations for {} done'.format(repr(self)))


Instruction:
Make a summary of the following Python 3 code
def connect_generators(self, debug=False):

        self._graph = mv_connect.mv_connect_generators(self.grid_district, self._graph, debug)

Response: Connects MV generators to grid


Instruction:
Can you write a function in Python 3 where it
performs the parametrization of the grid equipment for this MV station and the current grid.

Response:
def parametrize_grid(self, debug=False):
        """ Performs Parametrization of grid equipment:

            i) Sets voltage level of MV grid,
            ii) Operation voltage level and transformer of HV/MV station,
            iii) Default branch types (normal, aggregated, settlement)

        Args
        ----
        debug: bool, defaults to False
            If True, information is printed during process.

        Notes
        -----
        It is assumed that only cables are used within settlements.
        """
        # TODO: Add more detailed description

        # set grid's voltage level
        self.set_voltage_level()

        # set MV station's voltage level
        self._station.set_operation_voltage_level()

        # set default branch types (normal, aggregated areas and within settlements)
        self.default_branch_type,\
        self.default_branch_type_aggregated,\
        self.default_branch_type_settle = self.set_default_branch_type(debug)

        # set default branch kinds
        self.default_branch_kind_aggregated = self.default_branch_kind
        self.default_branch_kind_settle = 'cable'

        # choose appropriate transformers for each HV/MV sub-station
        self._station.select_transformers()


Instruction:
Make a summary of the following Python 3 code
def set_voltage_level(self, mode='distance'):

        if mode == 'load_density':

            # get power factor for loads
            cos_phi_load = cfg_ding0.get('assumptions', 'cos_phi_load')

            # get load density
            load_density_threshold = float(cfg_ding0.get('assumptions',
                                                         'load_density_threshold'))

            # transform MVGD's area to epsg 3035
            # to achieve correct area calculation
            projection = partial(
                pyproj.transform,
                pyproj.Proj(init='epsg:4326'),  # source coordinate system
                pyproj.Proj(init='epsg:3035'))  # destination coordinate system

            # calculate load density
            kw2mw = 1e-3
            sqm2sqkm = 1e6
            load_density = ((self.grid_district.peak_load * kw2mw / cos_phi_load) /
                            (transform(projection, self.grid_district.geo_data).area / sqm2sqkm)) # unit MVA/km^2

            # identify voltage level
            if load_density < load_density_threshold:
                self.v_level = 20
            elif load_density >= load_density_threshold:
                self.v_level = 10
            else:
                raise ValueError('load_density is invalid!')

        elif mode == 'distance':

            # get threshold for 20/10kV disambiguation
            voltage_per_km_threshold = float(cfg_ding0.get('assumptions',
                                                           'voltage_per_km_threshold'))

            # initial distance
            dist_max = 0
            import time
            start = time.time()
            for node in self.graph_nodes_sorted():
                if isinstance(node, LVLoadAreaCentreDing0):
                    # calc distance from MV-LV station to LA centre
                    dist_node = calc_geo_dist_vincenty(self.station(), node) / 1e3
                    if dist_node > dist_max:
                        dist_max = dist_node

            # max. occurring distance to a Load Area exceeds threshold => grid operates at 20kV
            if dist_max >= voltage_per_km_threshold:
                self.v_level = 20
            # not: grid operates at 10kV
            else:
                self.v_level = 10

        else:
            raise ValueError('parameter \'mode\' is invalid!')

Response: Sets voltage level of MV grid according to load density and distance.


Instruction:
Write a Python 3 function that can
set the default branch type according to grid district s peak load and standard equipment.

Response:
def set_default_branch_type(self, debug=False):
        """ Determines default branch type according to grid district's peak load and standard equipment.

        Args
        ----
        debug: bool, defaults to False
            If True, information is printed during process

        Returns
        -------
        :pandas:`pandas.Series<series>`
            default branch type: pandas Series object. If no appropriate type is found, return largest possible one.
        :pandas:`pandas.Series<series>`
            default branch type max: pandas Series object. Largest available line/cable type

        Notes
        -----
        Parameter values for cables and lines are taken from [#]_, [#]_ and [#]_.

        Lines are chosen to have 60 % load relative to their nominal capacity according to [#]_.

        Decision on usage of overhead lines vs. cables is determined by load density of the considered region. Urban
        areas usually are equipped with underground cables whereas rural areas often have overhead lines as MV
        distribution system [#]_.

        References
        ----------
        .. [#] Klaus Heuck et al., "Elektrische Energieversorgung", Vieweg+Teubner, Wiesbaden, 2007
        .. [#] René Flosdorff et al., "Elektrische Energieverteilung", Vieweg+Teubner, 2005
        .. [#] Südkabel GmbH, "Einadrige VPE-isolierte Mittelspannungskabel",
            http://www.suedkabel.de/cms/upload/pdf/Garnituren/Einadrige_VPE-isolierte_Mittelspannungskabel.pdf, 2017
        .. [#] Deutsche Energie-Agentur GmbH (dena), "dena-Verteilnetzstudie. Ausbau- und Innovationsbedarf der
            Stromverteilnetze in Deutschland bis 2030.", 2012
        .. [#] Tao, X., "Automatisierte Grundsatzplanung von
            Mittelspannungsnetzen", Dissertation, RWTH Aachen, 2007
        """

        # decide whether cable or line is used (initially for entire grid) and set grid's attribute
        if self.v_level == 20:
            self.default_branch_kind = 'line'
        elif self.v_level == 10:
            self.default_branch_kind = 'cable'

        # get power factor for loads
        cos_phi_load = cfg_ding0.get('assumptions', 'cos_phi_load')

        # get max. count of half rings per MV grid district
        mv_half_ring_count_max = int(cfg_ding0.get('mv_routing_tech_constraints',
                                                   'mv_half_ring_count_max'))
        #mv_half_ring_count_max=20

        # load cable/line assumptions, file_names and parameter
        if self.default_branch_kind == 'line':
            load_factor_normal = float(cfg_ding0.get('assumptions',
                                                     'load_factor_mv_line_lc_normal'))
            branch_parameters = self.network.static_data['MV_overhead_lines']

            # load cables as well to use it within settlements
            branch_parameters_settle = self.network.static_data['MV_cables']
            # select types with appropriate voltage level
            branch_parameters_settle = branch_parameters_settle[branch_parameters_settle['U_n'] == self.v_level]

        elif self.default_branch_kind == 'cable':
            load_factor_normal = float(cfg_ding0.get('assumptions',
                                                     'load_factor_mv_cable_lc_normal'))
            branch_parameters = self.network.static_data['MV_cables']
        else:
            raise ValueError('Grid\'s default_branch_kind is invalid, could not set branch parameters.')

        # select appropriate branch params according to voltage level, sorted ascending by max. current
        # use <240mm2 only (ca. 420A) for initial rings and for disambiguation of agg. LA
        branch_parameters = branch_parameters[branch_parameters['U_n'] == self.v_level]
        branch_parameters = branch_parameters[branch_parameters['reinforce_only'] == 0].sort_values('I_max_th')

        # get largest line/cable type
        branch_type_max = branch_parameters.loc[branch_parameters['I_max_th'].idxmax()]

        # set aggregation flag using largest available line/cable
        self.set_nodes_aggregation_flag(branch_type_max['I_max_th'] * load_factor_normal)

        # calc peak current sum (= "virtual" current) of whole grid (I = S / sqrt(3) / U) excluding load areas of type
        # satellite and aggregated
        peak_current_sum = ((self.grid_district.peak_load -
                             self.grid_district.peak_load_satellites -
                             self.grid_district.peak_load_aggregated) /
                            cos_phi_load /
                            (3**0.5) / self.v_level)  # units: kVA / kV = A

        branch_type_settle = branch_type_settle_max = None

        # search the smallest possible line/cable for MV grid district in equipment datasets for all load areas
        # excluding those of type satellite and aggregated
        for idx, row in branch_parameters.iterrows():
            # calc number of required rings using peak current sum of grid district,
            # load factor and max. current of line/cable
            half_ring_count = round(peak_current_sum / (row['I_max_th'] * load_factor_normal))

            if debug:
                logger.debug('=== Selection of default branch type in {} ==='.format(self))
                logger.debug('Peak load= {} kVA'.format(self.grid_district.peak_load))
                logger.debug('Peak current={}'.format(peak_current_sum))
                logger.debug('I_max_th={}'.format(row['I_max_th']))
                logger.debug('Half ring count={}'.format(half_ring_count))

            # if count of half rings is below or equal max. allowed count, use current branch type as default
            if half_ring_count <= mv_half_ring_count_max:
                if self.default_branch_kind == 'line':

                    # take only cables that can handle at least the current of the line
                    branch_parameters_settle_filter = branch_parameters_settle[\
                                                      branch_parameters_settle['I_max_th'] - row['I_max_th'] > 0]

                    # get cable type with similar (but greater) I_max_th
                    # note: only grids with lines as default branch kind get cables in settlements
                    # (not required in grids with cables as default branch kind)
                    branch_type_settle = branch_parameters_settle_filter.loc[\
                                         branch_parameters_settle_filter['I_max_th'].idxmin()]

                return row, branch_type_max, branch_type_settle

        # no equipment was found, return largest available line/cable

        if debug:
            logger.debug('No appropriate line/cable type could be found for '
                         '{}, declare some load areas as aggregated.'.format(self))

        if self.default_branch_kind == 'line':
            branch_type_settle_max = branch_parameters_settle.loc[branch_parameters_settle['I_max_th'].idxmax()]

        return branch_type_max, branch_type_max, branch_type_settle_max


Instruction:
How would you implement a function in Python 3 that
sets the load areas to be aggregated if they are too high demand.

Response:
def set_nodes_aggregation_flag(self, peak_current_branch_max):
        """ Set Load Areas with too high demand to aggregated type.

        Args
        ----
        peak_current_branch_max: float
            Max. allowed current for line/cable

        """

        for lv_load_area in self.grid_district.lv_load_areas():
            peak_current_node = (lv_load_area.peak_load / (3**0.5) / self.v_level)  # units: kVA / kV = A
            if peak_current_node > peak_current_branch_max:
                lv_load_area.is_aggregated = True

        # add peak demand for all Load Areas of aggregation type
        self.grid_district.add_aggregated_peak_demand()


Instruction:
Write a Python 3 script to
export MVGridDing0 grid to PyPSA database tables.

Response:
def export_to_pypsa(self, session, method='onthefly'):
        """Exports MVGridDing0 grid to PyPSA database tables

        Peculiarities of MV grids are implemented here. Derive general export
        method from this and adapt to needs of LVGridDing0

        Parameters
        ----------
        session: :sqlalchemy:`SQLAlchemy session object<orm/session_basics.html>`
            Description
        method: str
            Specify export method::

            'db': grid data will be exported to database
            'onthefly': grid data will be passed to PyPSA directly (default)

        Notes
        -----
        It has to be proven that this method works for LV grids as well!

        Ding0 treats two stationary case of powerflow:
        1) Full load: We assume no generation and loads to be set to peak load
        2) Generation worst case:
        """

        # definitions for temp_resolution table
        temp_id = 1
        timesteps = 2
        start_time = datetime(1970, 1, 1, 00, 00, 0)
        resolution = 'H'

        nodes = self._graph.nodes()

        edges = [edge for edge in list(self.graph_edges())
                 if (edge['adj_nodes'][0] in nodes and not isinstance(
                edge['adj_nodes'][0], LVLoadAreaCentreDing0))
                 and (edge['adj_nodes'][1] in nodes and not isinstance(
                edge['adj_nodes'][1], LVLoadAreaCentreDing0))]

        if method == 'db':

            # Export node objects: Busses, Loads, Generators
            pypsa_io.export_nodes(self,
                                  session,
                                  nodes,
                                  temp_id,
                                  lv_transformer=False)

            # Export edges
            pypsa_io.export_edges(self, session, edges)

            # Create table about temporal coverage of PF analysis
            pypsa_io.create_temp_resolution_table(session,
                                                  timesteps=timesteps,
                                                  resolution=resolution,
                                                  start_time=start_time)
        elif method == 'onthefly':

            nodes_dict, components_data = pypsa_io.nodes_to_dict_of_dataframes(
                self,
                nodes,
                lv_transformer=False)
            edges_dict = pypsa_io.edges_to_dict_of_dataframes(self, edges)
            components = tools.merge_two_dicts(nodes_dict, edges_dict)

            return components, components_data
        else:
            raise ValueError('Sorry, this export method does not exist!')


Instruction:
Explain what the following Python 3 code does
def run_powerflow(self, session, export_pypsa_dir=None,  method='onthefly', debug=False):

        if method == 'db':
            raise NotImplementedError("Please use 'onthefly'.")

        elif method == 'onthefly':
            components, components_data = self.export_to_pypsa(session, method)
            pypsa_io.run_powerflow_onthefly(components,
                                            components_data,
                                            self,
                                            export_pypsa_dir=export_pypsa_dir,
                                            debug=debug)

Response: This method runs the power flow for all MV grids in the current database and returns the result.


Instruction:
Make a summary of the following Python 3 code
def import_powerflow_results(self, session):

        # bus data
        pypsa_io.import_pfa_bus_results(session, self)

        # line data
        pypsa_io.import_pfa_line_results(session, self)

Response:
Assign results from power flow analysis to edges and nodes
 Arcs and set them as attributes of the object.


Instruction:
Can you implement a function in Python 3 that
adds a LV station to _station and grid graph if not already existing.

Response:
def add_station(self, lv_station):
        """Adds a LV station to _station and grid graph if not already existing"""
        if not isinstance(lv_station, LVStationDing0):
            raise Exception('Given LV station is not a LVStationDing0 object.')
        if self._station is None:
            self._station = lv_station
            self.graph_add_node(lv_station)
            self.grid_district.lv_load_area.mv_grid_district.mv_grid.graph_add_node(lv_station)


Instruction:
Explain what the following Python 3 code does
def loads_sector(self, sector='res'):

        for load in self._loads:
            if (sector == 'res') and (load.string_id is not None):
                yield load
            elif (sector == 'ria') and (load.string_id is None):
                yield load

Response: Returns a generator for iterating over loads of the specified sector.


Instruction:
Can you generate the documentation for the following Python 3 function
def add_load(self, lv_load):
        if lv_load not in self._loads and isinstance(lv_load,
                                                     LVLoadDing0):
            self._loads.append(lv_load)
            self.graph_add_node(lv_load)

Response: Adds a LV load to _loads and grid graph if not already existing


Instruction:
Explain what the following Python 3 code does
def add_cable_dist(self, lv_cable_dist):
        if lv_cable_dist not in self._cable_distributors and isinstance(lv_cable_dist,
                                                                        LVCableDistributorDing0):
            self._cable_distributors.append(lv_cable_dist)
            self.graph_add_node(lv_cable_dist)

Response: Adds a LV cable_dist to the _cable_distributors and the grid graph if not already existing.


Instruction:
Make a summary of the following Python 3 code
def build_grid(self):

        # add required transformers
        build_grid.transformer(self)

        # add branches of sectors retail/industrial and agricultural
        build_grid.build_ret_ind_agr_branches(self.grid_district)

        # add branches of sector residential
        build_grid.build_residential_branches(self.grid_district)

Response: Create LV grid graph


Instruction:
Create a Python 3 function for
connecting LV generators to grid

Response:
def connect_generators(self, debug=False):
        """ Connects LV generators (graph nodes) to grid (graph)

        Args
        ----
        debug: bool, defaults to False
             If True, information is printed during process
        """

        self._graph = lv_connect.lv_connect_generators(self.grid_district, self._graph, debug)


Instruction:
Write a Python 3 script for
returning a deep copy of self

Response:
def clone(self):
        """Returns a deep copy of self

        Function clones:

        * allocation
        * nodes

        Returns
        -------
        type
            Deep copy of self
        """

        new_route = self.__class__(self._problem)
        for node in self.nodes():
            # Insere new node on new route
            new_node = node.__class__(node._name, node._demand)
            new_route.allocate([new_node])

        return new_route


Instruction:
Can you tell what is the following Python 3 function doing
def length(self):
        cost = 0
        depot = self._problem.depot()

        last = depot
        for i in self._nodes:
            a, b = last, i
            if a.name() > b.name():
                a, b = b, a

            cost = cost + self._problem.distance(a, b)
            last = i

        cost = cost + self._problem.distance(depot, last)

        return cost

Response: Returns the route length


Instruction:
Explain what the following Python 3 code does
def length_from_nodelist(self, nodelist):
        cost = 0

        for n1, n2 in zip(nodelist[0:len(nodelist) - 1], nodelist[1:len(nodelist)]):
            cost += self._problem.distance(n1, n2)

        return cost

Response: Returns the length of the route from the first to the last node in nodelist


Instruction:
Can you generate a brief explanation for the following Python 3 code
def can_allocate(self, nodes, pos=None):
        # TODO: check docstring
        """Returns True if this route can allocate nodes in `nodes` list

        Parameters
        ----------
        nodes : type
            Desc
        pos : type, defaults to None
            Desc

        Returns
        -------
        bool
            True if this route can allocate nodes in `nodes` list
        """

        # clone route and nodes
        new_route = self.clone()
        new_nodes = [node.clone() for node in nodes]
        if pos is None:
            pos = len(self._nodes)
        new_route._nodes = new_route._nodes[:pos] + new_nodes + new_route._nodes[pos:]
        new_route._demand = sum([node.demand() for node in new_route._nodes])

        if new_route.tech_constraints_satisfied():
            return True

        return False

Response:
Returns True if this route can allocate nodes in nodes list
ecords


Instruction:
Write a Python 3 script to
allocate all nodes from nodes list in this route.

Response:
def allocate(self, nodes, append=True):
        # TODO: check docstring
        """Allocates all nodes from `nodes` list in this route

        Parameters
        ----------
        nodes : type
            Desc
        append : bool, defaults to True
            Desc

        """

        nodes_demand = 0
        for node in [node for node in nodes]:
            if node._allocation:
                node._allocation.deallocate([node])

            node._allocation = self
            nodes_demand = nodes_demand + node.demand()
            if append:
                self._nodes.append(node)
            else:
                self._nodes.insert(0, node)

        self._demand = self._demand + nodes_demand


Instruction:
Make a summary of the following Python 3 code
def deallocate(self, nodes):
        # TODO: check docstring
        """Deallocates all nodes from `nodes` list from this route

        Parameters
        ----------
        nodes : type
            Desc
        """

        nodes_demand = 0
        for node in nodes:
            self._nodes.remove(node)
            node._allocation = None
            nodes_demand = nodes_demand + node.demand()

        self._demand = self._demand - nodes_demand

        if self._demand < 0:
            raise Exception('Trying to deallocate more than previously allocated')

Response: Deallocates all nodes from nodes list from this route


Instruction:
Implement a function in Python 3 to
insert all nodes from nodes list into this route at position pos.

Response:
def insert(self, nodes, pos):
        # TODO: check docstring
        """Inserts all nodes from `nodes` list into this route at position `pos`

        Parameters
        ----------
        nodes : type
            Desc
        pos : type
            Desc

        """

        node_list = []
        nodes_demand = 0
        for node in [node for node in nodes]:
            if node._allocation:
                node._allocation.deallocate([node])
            node_list.append(node)
            node._allocation = self
            nodes_demand = nodes_demand + node.demand()

        self._nodes = self._nodes[:pos] + node_list + self._nodes[pos:]
        self._demand += nodes_demand


Instruction:
Can you tell what is the following Python 3 function doing
def is_interior(self, node):
        # TODO: check docstring
        """Returns True if node is interior to the route, i.e., not adjascent to depot

        Parameters
        ----------
        nodes : type
            Desc

        Returns
        -------
        bool
            True if node is interior to the route

        """
        return self._nodes.index(node) != 0 and self._nodes.index(node) != len(self._nodes) - 1

Response: Returns True if node is interior to the route False otherwise.


Instruction:
Write a Python 3 script for
returning True if the node is the last node in the route ecords.

Response:
def last(self, node):
        # TODO: check docstring
        """Returns True if node is the last node in the route

        Parameters
        ----------
        nodes : type
            Desc

        Returns
        -------
        bool
            True if node is the last node in the route
        """
        return self._nodes.index(node) == len(self._nodes) - 1


Instruction:
Can you create a Python 3 function that
calculates the optimal position of a circuit breaker on a route.

Response:
def calc_circuit_breaker_position(self, debug=False):
        """ Calculates the optimal position of a circuit breaker on route.

        Parameters
        ----------
        debug: bool, defaults to False
            If True, prints process information.

        Returns
        -------
        int
            position of circuit breaker on route (index of last node on 1st half-ring preceding the circuit breaker)

        Notes
        -----
        According to planning principles of MV grids, a MV ring is run as two strings (half-rings) separated by a
        circuit breaker which is open at normal operation.
        Assuming a ring (route which is connected to the root node at either sides), the optimal position of a circuit
        breaker is defined as the position (virtual cable) between two nodes where the conveyed current is minimal on
        the route. Instead of the peak current, the peak load is used here (assuming a constant voltage).

        The circuit breakers are used here for checking tech. constraints only and will be re-located after connection
        of satellites and stations in ding0.grid.mv_grid.tools.set_circuit_breakers

        References
        ----------

        See Also
        --------
        ding0.grid.mv_grid.tools.set_circuit_breakers

        """
        # TODO: add references (Tao)

        # set init value
        demand_diff_min = 10e6

        # check possible positions in route
        for ctr in range(len(self._nodes)):
            # split route and calc demand difference
            route_demand_part1 = sum([node.demand() for node in self._nodes[0:ctr]])
            route_demand_part2 = sum([node.demand() for node in self._nodes[ctr:len(self._nodes)]])
            demand_diff = abs(route_demand_part1 - route_demand_part2)

            if demand_diff < demand_diff_min:
                demand_diff_min = demand_diff
                position = ctr

        if debug:
            logger.debug('sum 1={}'.format(
                sum([node.demand() for node in self._nodes[0:position]])))
            logger.debug('sum 2={}'.format(sum([node.demand() for node in
                                                self._nodes[
                                                position:len(self._nodes)]])))
            logger.debug(
                'Position of circuit breaker: {0}-{1} (sumdiff={2})'.format(
                    self._nodes[position - 1], self._nodes[position],
                    demand_diff_min))

        return position


Instruction:
How would you code a function in Python 3 to
check route validity according to technical constraints (voltage and current rating) It considers constraints as * current rating of cable/line * voltage stability at all nodes Notes ----- The validation is done for every tested MV grid configuration during CVRP algorithm. The current rating is checked using load factors from [#]_. Due to the high amount of steps the voltage rating cannot be checked using load flow calculation. Therefore we use a simple method which determines the voltage change between two consecutive nodes according to [#]_. Furthermore it is checked if new route has got more nodes than allowed (typ. 2*10 according to [#]_). References ---------- .. [#] Deutsche Energie-Agentur GmbH (dena), "dena-Verteilnetzstudie. Ausbau- und Innovationsbedarf der Stromverteilnetze in Deutschland bis 2030.", 2012 .. [#] M. Sakulin, W. Hipp, "Netzaspekte von dezentralen Erzeugungseinheiten, Studie im Auftrag der E-Control GmbH", TU Graz, 2004 .. [#] Klaus Heuck et al., "Elektrische Energieversorgung", Vieweg+Teubner, Wiesbaden, 2007 .. [#] FGH e.V.: "Technischer Bericht 302: Ein Werkzeug zur Optimierung der Störungsbeseitigung für Planung und Betrieb von Mittelspannungsnetzen", Tech. rep., 2008

Response:
def tech_constraints_satisfied(self):
        """ Check route validity according to technical constraints (voltage and current rating)

        It considers constraints as

        * current rating of cable/line
        * voltage stability at all nodes

        Notes
        -----
            The validation is done for every tested MV grid configuration during CVRP algorithm. The current rating is
            checked using load factors from [#]_. Due to the high amount of steps the voltage rating cannot be checked
            using load flow calculation. Therefore we use a simple method which determines the voltage change between
            two consecutive nodes according to [#]_.
            Furthermore it is checked if new route has got more nodes than allowed (typ. 2*10 according to [#]_).

        References
        ----------

        .. [#] Deutsche Energie-Agentur GmbH (dena), "dena-Verteilnetzstudie. Ausbau- und Innovationsbedarf der
            Stromverteilnetze in Deutschland bis 2030.", 2012
        .. [#] M. Sakulin, W. Hipp, "Netzaspekte von dezentralen Erzeugungseinheiten,
            Studie im Auftrag der E-Control GmbH", TU Graz, 2004
        .. [#] Klaus Heuck et al., "Elektrische Energieversorgung", Vieweg+Teubner, Wiesbaden, 2007
        .. [#] FGH e.V.: "Technischer Bericht 302: Ein Werkzeug zur Optimierung der Störungsbeseitigung
            für Planung und Betrieb von Mittelspannungsnetzen", Tech. rep., 2008
        """

        # load parameters
        load_area_count_per_ring = float(cfg_ding0.get('mv_routing',
                                                       'load_area_count_per_ring'))

        max_half_ring_length = float(cfg_ding0.get('mv_routing',
                                                   'max_half_ring_length'))

        if self._problem._branch_kind == 'line':
            load_factor_normal = float(cfg_ding0.get('assumptions',
                                                     'load_factor_mv_line_lc_normal'))
            load_factor_malfunc = float(cfg_ding0.get('assumptions',
                                                      'load_factor_mv_line_lc_malfunc'))
        elif self._problem._branch_kind == 'cable':
            load_factor_normal = float(cfg_ding0.get('assumptions',
                                                     'load_factor_mv_cable_lc_normal'))
            load_factor_malfunc = float(cfg_ding0.get('assumptions',
                                                      'load_factor_mv_cable_lc_malfunc'))
        else:
            raise ValueError('Grid\'s _branch_kind is invalid, could not use branch parameters.')

        mv_max_v_level_lc_diff_normal = float(cfg_ding0.get('mv_routing_tech_constraints',
                                                            'mv_max_v_level_lc_diff_normal'))
        mv_max_v_level_lc_diff_malfunc = float(cfg_ding0.get('mv_routing_tech_constraints',
                                                             'mv_max_v_level_lc_diff_malfunc'))
        cos_phi_load = cfg_ding0.get('assumptions', 'cos_phi_load')

        # step 0: check if route has got more nodes than allowed
        if len(self._nodes) > load_area_count_per_ring:
            return False

        # step 1: calc circuit breaker position
        position = self.calc_circuit_breaker_position()

        # step 2: calc required values for checking current & voltage
        # get nodes of half-rings
        nodes_hring1 = [self._problem._depot] + self._nodes[0:position]
        nodes_hring2 = list(reversed(self._nodes[position:len(self._nodes)] + [self._problem._depot]))
        # get all nodes of full ring for both directions
        nodes_ring1 = [self._problem._depot] + self._nodes
        nodes_ring2 = list(reversed(self._nodes + [self._problem._depot]))
        # factor to calc reactive from active power
        Q_factor = tan(acos(cos_phi_load))
        # line/cable params per km
        r = self._problem._branch_type['R']  # unit for r: ohm/km
        x = self._problem._branch_type['L'] * 2*pi * 50 / 1e3  # unit for x: ohm/km

        # step 3: check if total lengths of half-rings exceed max. allowed distance
        if (self.length_from_nodelist(nodes_hring1) > max_half_ring_length or
            self.length_from_nodelist(nodes_hring2) > max_half_ring_length):
            return False

        # step 4a: check if current rating of default cable/line is violated
        # (for every of the 2 half-rings using load factor for normal operation)
        demand_hring_1 = sum([node.demand() for node in self._nodes[0:position]])
        demand_hring_2 = sum([node.demand() for node in self._nodes[position:len(self._nodes)]])
        peak_current_sum_hring1 = demand_hring_1 / (3**0.5) / self._problem._v_level  # units: kVA / kV = A
        peak_current_sum_hring2 = demand_hring_2 / (3**0.5) / self._problem._v_level  # units: kVA / kV = A

        if (peak_current_sum_hring1 > (self._problem._branch_type['I_max_th'] * load_factor_normal) or
            peak_current_sum_hring2 > (self._problem._branch_type['I_max_th'] * load_factor_normal)):
            return False

        # step 4b: check if current rating of default cable/line is violated
        # (for full ring using load factor for malfunction operation)
        peak_current_sum_ring = self._demand / (3**0.5) / self._problem._v_level  # units: kVA / kV = A
        if peak_current_sum_ring > (self._problem._branch_type['I_max_th'] * load_factor_malfunc):
            return False

        # step 5a: check voltage stability at all nodes
        # (for every of the 2 half-rings using max. voltage difference for normal operation)

        # get operation voltage level from station
        v_level_hring1 =\
            v_level_hring2 =\
            v_level_ring_dir1 =\
            v_level_ring_dir2 =\
            v_level_op =\
            self._problem._v_level * 1e3

        # set initial r and x
        r_hring1 =\
            r_hring2 =\
            x_hring1 =\
            x_hring2 =\
            r_ring_dir1 =\
            r_ring_dir2 =\
            x_ring_dir1 =\
            x_ring_dir2 = 0

        for n1, n2 in zip(nodes_hring1[0:len(nodes_hring1)-1], nodes_hring1[1:len(nodes_hring1)]):
            r_hring1 += self._problem.distance(n1, n2) * r
            x_hring1 += self._problem.distance(n1, n2) * x
            v_level_hring1 -= n2.demand() * 1e3 * (r_hring1 + x_hring1*Q_factor) / v_level_op
            if (v_level_op - v_level_hring1) > (v_level_op * mv_max_v_level_lc_diff_normal):
                return False

        for n1, n2 in zip(nodes_hring2[0:len(nodes_hring2)-1], nodes_hring2[1:len(nodes_hring2)]):
            r_hring2 += self._problem.distance(n1, n2) * r
            x_hring2 += self._problem.distance(n1, n2) * x
            v_level_hring2 -= n2.demand() * 1e3 * (r_hring2 + x_hring2 * Q_factor) / v_level_op
            if (v_level_op - v_level_hring2) > (v_level_op * mv_max_v_level_lc_diff_normal):
                return False

        # step 5b: check voltage stability at all nodes
        # (for full ring calculating both directions simultaneously using max. voltage diff. for malfunction operation)
        for (n1, n2), (n3, n4) in zip(zip(nodes_ring1[0:len(nodes_ring1)-1], nodes_ring1[1:len(nodes_ring1)]),
                                      zip(nodes_ring2[0:len(nodes_ring2)-1], nodes_ring2[1:len(nodes_ring2)])):
            r_ring_dir1 += self._problem.distance(n1, n2) * r
            r_ring_dir2 += self._problem.distance(n3, n4) * r
            x_ring_dir1 += self._problem.distance(n1, n2) * x
            x_ring_dir2 += self._problem.distance(n3, n4) * x
            v_level_ring_dir1 -= (n2.demand() * 1e3 * (r_ring_dir1 + x_ring_dir1 * Q_factor) / v_level_op)
            v_level_ring_dir2 -= (n4.demand() * 1e3 * (r_ring_dir2 + x_ring_dir2 * Q_factor) / v_level_op)
            if ((v_level_op - v_level_ring_dir1) > (v_level_op * mv_max_v_level_lc_diff_malfunc) or
                (v_level_op - v_level_ring_dir2) > (v_level_op * mv_max_v_level_lc_diff_malfunc)):
                return False

        return True


Instruction:
Can you implement a function in Python 3 that
returns a deep copy of self

Response:
def clone(self):
        # TODO: check docstring
        """Returns a deep copy of self

        Function clones:

        * allocation
        * nodes

        Returns
        -------
        type
            Deep copy of self
        """

        new_node = self.__class__(self._name, self._demand)

        return new_node


Instruction:
Implement a function in Python 3 to
return a generator for iterating over edges.

Response:
def edges(self):
        # TODO: check docstring
        """Returns a generator for iterating over edges

        Yields
        ------
        type
            Generator for iterating over edges.

        """
        for i in sorted(self._matrix.keys(), key=lambda x:x.name()):
            for j in sorted(self._matrix[i].keys(), key=lambda x:x.name()):
                if i != j:
                    yield (i, j)


Instruction:
Implement a Python 3 function for
returning the distance between node i and node j.

Response:
def distance(self, i, j):
        # TODO: check docstring
        """Returns the distance between node i and node j

        Parameters
        ----------
        i : type
            Descr
        j : type
            Desc

        Returns
        -------
        float
            Distance between node i and node j.
        """

        a, b = i, j

        if a.name() > b.name():
            a, b = b, a

        return self._matrix[self._nodes[a.name()]][self._nodes[b.name()]]


Instruction:
How would you explain what the following Python 3 function does
def select_transformers(grid, s_max=None):

    load_factor_lv_trans_lc_normal = cfg_ding0.get('assumptions',
                                                   'load_factor_lv_trans_lc_normal')
    load_factor_lv_trans_fc_normal = cfg_ding0.get('assumptions',
                                                   'load_factor_lv_trans_fc_normal')

    cos_phi_load = cfg_ding0.get('assumptions',
                                 'cos_phi_load')
    cos_phi_gen = cfg_ding0.get('assumptions',
                                'cos_phi_gen')

    # get equipment parameters of LV transformers
    trafo_parameters = grid.network.static_data['LV_trafos']

    # determine s_max from grid object if not provided via arguments
    if s_max is None:
        # get maximum from peak load and peak generation
        s_max_load = grid.grid_district.peak_load / cos_phi_load
        s_max_gen = grid.station().peak_generation / cos_phi_gen

        # check if load or generation is greater respecting corresponding load factor
        if s_max_load > s_max_gen:
            # use peak load and load factor from load case
            load_factor_lv_trans = load_factor_lv_trans_lc_normal
            s_max = s_max_load
        else:
            # use peak generation and load factor for feedin case
            load_factor_lv_trans = load_factor_lv_trans_fc_normal
            s_max = s_max_gen
    else:
        if s_max['case'] == 'load':
            load_factor_lv_trans = load_factor_lv_trans_lc_normal
        elif s_max['case'] == 'gen':
            load_factor_lv_trans = load_factor_lv_trans_fc_normal
        else:
            logger.error('No proper \'case\' provided for argument s_max')
            raise ValueError('Please provide proper \'case\' for argument '
                             '`s_max`.')
        s_max = s_max['s_max']

    # get max. trafo
    transformer_max = trafo_parameters.iloc[trafo_parameters['S_nom'].idxmax()]

    # peak load is smaller than max. available trafo
    if s_max < (transformer_max['S_nom'] * load_factor_lv_trans ):
        # choose trafo
        transformer = trafo_parameters.iloc[
            trafo_parameters[
                trafo_parameters['S_nom'] * load_factor_lv_trans > s_max][
                'S_nom'].idxmin()]
        transformer_cnt = 1
    # peak load is greater than max. available trafo -> use multiple trafos
    else:
        transformer_cnt = 2
        # increase no. of trafos until peak load can be supplied
        while not any(trafo_parameters['S_nom'] * load_factor_lv_trans > (
                    s_max / transformer_cnt)):
            transformer_cnt += 1
        transformer = trafo_parameters.iloc[
            trafo_parameters[
                trafo_parameters['S_nom'] * load_factor_lv_trans
                > (s_max / transformer_cnt)]['S_nom'].idxmin()]

    return transformer, transformer_cnt

Response: Selects LV transformers according to peak load of LV grid district.


Instruction:
How would you explain what the following Python 3 function does
def transformer(grid):

    # choose size and amount of transformers
    transformer, transformer_cnt = select_transformers(grid)

    # create transformers and add them to station of LVGD
    for t in range(0, transformer_cnt):
        lv_transformer = TransformerDing0(
            grid=grid,
            id_db=id,
            v_level=0.4,
            s_max_longterm=transformer['S_nom'],
            r=transformer['R'],
            x=transformer['X'])

        # add each transformer to its station
        grid._station.add_transformer(lv_transformer)

Response: Choose transformers and add them to LV grid s station


Instruction:
How would you code a function in Python 3 to
select a typified grid for retail and industrial and agricultural grids.

Response:
def select_grid_model_ria(lvgd, sector):
    """Select a typified grid for retail/industrial and agricultural

    Parameters
    ----------
    lvgd : ding0.core.structure.regions.LVGridDistrictDing0
        Low-voltage grid district object
    sector : str
        Either 'retail/industrial' or 'agricultural'. Depending on choice
        different parameters to grid topology apply

    Returns
    -------
    :obj:`dict`
        Parameters that describe branch lines of a sector
    """

    cable_lf = cfg_ding0.get('assumptions',
                             'load_factor_lv_cable_lc_normal')

    cos_phi_load = cfg_ding0.get('assumptions',
                                 'cos_phi_load')

    max_lv_branch_line_load = cfg_ding0.get('assumptions',
                                            'max_lv_branch_line')

    # make a distinction between sectors
    if sector == 'retail/industrial':
        max_branch_length = cfg_ding0.get(
            "assumptions",
            "branch_line_length_retail_industrial")
        peak_load = lvgd.peak_load_retail + \
                    lvgd.peak_load_industrial
        count_sector_areas = lvgd.sector_count_retail + \
                             lvgd.sector_count_industrial
    elif sector == 'agricultural':
        max_branch_length = cfg_ding0.get(
            "assumptions",
            "branch_line_length_agricultural")
        peak_load = lvgd.peak_load_agricultural
        count_sector_areas = lvgd.sector_count_agricultural
    else:
        raise ValueError('Sector {} does not exist!'.format(sector))

    # determine size of a single load
    single_peak_load = peak_load / count_sector_areas

    # if this single load exceeds threshold of 300 kVA it is splitted
    while single_peak_load > (max_lv_branch_line_load * (cable_lf * cos_phi_load)):
        single_peak_load = single_peak_load / 2
        count_sector_areas = count_sector_areas * 2

    grid_model = {}

    # determine parameters of branches and loads connected to the branch
    # line
    if 0 < single_peak_load:
        grid_model['max_loads_per_branch'] = math.floor(
            (max_lv_branch_line_load * (cable_lf * cos_phi_load)) / single_peak_load)
        grid_model['single_peak_load'] = single_peak_load
        grid_model['full_branches'] = math.floor(
            count_sector_areas / grid_model['max_loads_per_branch'])
        grid_model['remaining_loads'] = count_sector_areas - (
            grid_model['full_branches'] * grid_model['max_loads_per_branch']
        )
        grid_model['load_distance'] = max_branch_length / (
            grid_model['max_loads_per_branch'] + 1)
        grid_model['load_distance_remaining'] = max_branch_length / (
            grid_model['remaining_loads'] + 1)
    else:
        if count_sector_areas > 0:
            logger.warning(
                'LVGD {lvgd} has in sector {sector} no load but area count'
                'is {count}. This is maybe related to #153'.format(
                    lvgd=lvgd,
                    sector=sector,
                    count=count_sector_areas))
            grid_model = None

    # add consumption to grid_model for assigning it to the load object
    # consumption is given per sector and per individual load
    if sector == 'retail/industrial':
        grid_model['consumption'] = {
            'retail': lvgd.sector_consumption_retail / (
                grid_model['full_branches'] *
                grid_model['max_loads_per_branch'] +
                grid_model['remaining_loads']),
            'industrial': lvgd.sector_consumption_industrial / (
                grid_model['full_branches'] *
                grid_model['max_loads_per_branch'] +
                grid_model['remaining_loads'])}
    elif sector == 'agricultural':
        grid_model['consumption'] = {
            'agricultural': lvgd.sector_consumption_agricultural / (
                grid_model['full_branches'] *
                grid_model['max_loads_per_branch'] +
                grid_model['remaining_loads'])}

    return grid_model


Instruction:
Can you create a Python 3 function that
builds a graph of LV grids for LV grids retail and industrial and agricultural branches for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids for LV grids.

Response:
def build_lv_graph_ria(lvgd, grid_model_params):
    """Build graph for LV grid of sectors retail/industrial and agricultural

    Based on structural description of LV grid topology for sectors
    retail/industrial and agricultural (RIA) branches for these sectors are
    created and attached to the LV grid's MV-LV substation bus bar.

    LV loads of the sectors retail/industrial and agricultural are located
    in separat branches for each sector (in case of large load multiple of
    these).
    These loads are distributed across the branches by an equidistant
    distribution.

    This function accepts the dict `grid_model_params` with particular
    structure

    >>> grid_model_params = {
    >>> ... 'agricultural': {
    >>> ...     'max_loads_per_branch': 2
    >>> ...     'single_peak_load': 140,
    >>> ...     'full_branches': 2,
    >>> ...     'remaining_loads': 1,
    >>> ...     'load_distance': 800/3,
    >>> ...     'load_distance_remaining': 400}}

    Parameters
    ----------
    lvgd : LVGridDistrictDing0
        Low-voltage grid district object
    grid_model_params : dict
        Dict of structural information of sectoral LV grid branchwith particular
        structure, e.g.::

            grid_model_params = {
                'agricultural': {
                    'max_loads_per_branch': 2
                    'single_peak_load': 140,
                    'full_branches': 2,
                    'remaining_loads': 1,
                    'load_distance': 800/3,
                    'load_distance_remaining': 400
                }
            }

    Notes
    -----
    We assume a distance from the load to the branch it is connected to of
    30 m. This assumption is defined in the config files.
    """

    def lv_graph_attach_branch():
        """Attach a single branch including its equipment (cable dist, loads
        and line segments) to graph of `lv_grid`
        """

        # determine maximum current occuring due to peak load
        # of this load load_no
        I_max_load = val['single_peak_load'] / (3 ** 0.5 * 0.4) / cos_phi_load

        # determine suitable cable for this current
        suitable_cables_stub = lvgd.lv_grid.network.static_data['LV_cables'][
            (lvgd.lv_grid.network.static_data['LV_cables'][
                'I_max_th'] * cable_lf) > I_max_load]
        cable_type_stub = suitable_cables_stub.loc[
            suitable_cables_stub['I_max_th'].idxmin(), :
            ]

        # cable distributor to divert from main branch
        lv_cable_dist = LVCableDistributorDing0(
            grid=lvgd.lv_grid,
            branch_no=branch_no,
            load_no=load_no)
        # add lv_cable_dist to graph
        lvgd.lv_grid.add_cable_dist(lv_cable_dist)

        # cable distributor within building (to connect load+geno)
        lv_cable_dist_building = LVCableDistributorDing0(
            grid=lvgd.lv_grid,
            branch_no=branch_no,
            load_no=load_no,
            in_building=True)
        # add lv_cable_dist_building to graph
        lvgd.lv_grid.add_cable_dist(lv_cable_dist_building)

        # create an instance of Ding0 LV load
        lv_load = LVLoadDing0(grid=lvgd.lv_grid,
                              branch_no=branch_no,
                              load_no=load_no,
                              peak_load=val['single_peak_load'],
                              consumption=val['consumption'])

        # add lv_load to graph
        lvgd.lv_grid.add_load(lv_load)

        # create branch line segment between either (a) station
        # and cable distributor or (b) between neighboring cable
        # distributors
        if load_no == 1:
            # case a: cable dist <-> station
            lvgd.lv_grid._graph.add_edge(
                lvgd.lv_grid.station(),
                lv_cable_dist,
                branch=BranchDing0(
                    length=val['load_distance'],
                    kind='cable',
                    type=cable_type,
                    id_db='branch_{sector}{branch}_{load}'.format(
                        branch=branch_no,
                        load=load_no,
                        sector=sector_short)
                ))
        else:
            # case b: cable dist <-> cable dist
            lvgd.lv_grid._graph.add_edge(
                lvgd.lv_grid._cable_distributors[-4],
                lv_cable_dist,
                branch=BranchDing0(
                    length=val['load_distance'],
                    kind='cable',
                    type=cable_type,
                    id_db='branch_{sector}{branch}_{load}'.format(
                        branch=branch_no,
                        load=load_no,
                        sector=sector_short)))

        # create branch stub that connects the load to the
        # lv_cable_dist located in the branch line
        lvgd.lv_grid._graph.add_edge(
            lv_cable_dist,
            lv_cable_dist_building,
            branch=BranchDing0(
                length=cfg_ding0.get(
                    'assumptions',
                    'lv_ria_branch_connection_distance'),
                kind='cable',
                type=cable_type_stub,
                id_db='stub_{sector}{branch}_{load}'.format(
                    branch=branch_no,
                    load=load_no,
                    sector=sector_short))
        )

        lvgd.lv_grid._graph.add_edge(
            lv_cable_dist_building,
            lv_load,
            branch=BranchDing0(
                length=1,
                kind='cable',
                type=cable_type_stub,
                id_db='stub_{sector}{branch}_{load}'.format(
                    branch=branch_no,
                    load=load_no,
                    sector=sector_short))
        )

    cable_lf = cfg_ding0.get('assumptions',
                             'load_factor_lv_cable_lc_normal')
    cos_phi_load = cfg_ding0.get('assumptions',
                                 'cos_phi_load')

    # iterate over branches for sectors retail/industrial and agricultural
    for sector, val in grid_model_params.items():
        if sector == 'retail/industrial':
            sector_short = 'RETIND'
        elif sector == 'agricultural':
            sector_short = 'AGR'
        else:
            sector_short = ''
        if val is not None:
            for branch_no in list(range(1, val['full_branches'] + 1)):

                # determine maximum current occuring due to peak load of branch
                I_max_branch = (val['max_loads_per_branch'] *
                                val['single_peak_load']) / (3 ** 0.5 * 0.4) / (
                    cos_phi_load)

                # determine suitable cable for this current
                suitable_cables = lvgd.lv_grid.network.static_data['LV_cables'][
                    (lvgd.lv_grid.network.static_data['LV_cables'][
                        'I_max_th'] * cable_lf) > I_max_branch]
                cable_type = suitable_cables.loc[
                    suitable_cables['I_max_th'].idxmin(), :
                ]

                # create Ding0 grid objects and add to graph
                for load_no in list(range(1, val['max_loads_per_branch'] + 1)):
                    # create a LV grid string and attached to station
                    lv_graph_attach_branch()

            # add remaining branch
            if val['remaining_loads'] > 0:
                if 'branch_no' not in locals():
                    branch_no = 0
                # determine maximum current occuring due to peak load of branch
                I_max_branch = (val['max_loads_per_branch'] *
                                val['single_peak_load']) / (3 ** 0.5 * 0.4) / (
                    cos_phi_load)

                # determine suitable cable for this current
                suitable_cables = lvgd.lv_grid.network.static_data['LV_cables'][
                    (lvgd.lv_grid.network.static_data['LV_cables'][
                        'I_max_th'] * cable_lf) > I_max_branch]
                cable_type = suitable_cables.loc[
                    suitable_cables['I_max_th'].idxmin(), :
                ]

                branch_no += 1

                for load_no in list(range(1, val['remaining_loads'] + 1)):
                    # create a LV grid string and attach to station
                    lv_graph_attach_branch()


Instruction:
Given the following Python 3 function, write the documentation
def select_grid_model_residential(lvgd):

    # Load properties of LV typified model grids
    string_properties = lvgd.lv_grid.network.static_data['LV_model_grids_strings']
    # Load relational table of apartment count and strings of model grid
    apartment_string = lvgd.lv_grid.network.static_data[
        'LV_model_grids_strings_per_grid']

    # load assumtions
    apartment_house_branch_ratio = cfg_ding0.get("assumptions",
                                                 "apartment_house_branch_ratio")
    population_per_apartment = cfg_ding0.get("assumptions",
                                             "population_per_apartment")

    # calc count of apartments to select string types
    apartments = round(lvgd.population / population_per_apartment)

    if apartments > 196:
        apartments = 196

    # select set of strings that represent one type of model grid
    strings = apartment_string.loc[apartments]
    selected_strings = [int(s) for s in strings[strings >= 1].index.tolist()]

    # slice dataframe of string parameters
    selected_strings_df = string_properties.loc[selected_strings]

    # add number of occurences of each branch to df
    occurence_selector = [str(i) for i in selected_strings]
    selected_strings_df['occurence'] = strings.loc[occurence_selector].tolist()

    return selected_strings_df

Response: Selects typified model grid based on population and returns DataFrame of model grids.


Instruction:
Write a Python 3 script to
build the nxGraph based on the LV grid district and the selected string dataframe.

Response:
def build_lv_graph_residential(lvgd, selected_string_df):
    """Builds nxGraph based on the LV grid model

    Parameters
    ----------
    lvgd : LVGridDistrictDing0
        Low-voltage grid district object
    selected_string_df: :pandas:`pandas.DataFrame<dataframe>`
        Table of strings of the selected grid model

    Notes
    -----
    To understand what is happening in this method a few data table columns
    are explained here

    * `count house branch`: number of houses connected to a string
    * `distance house branch`: distance on a string between two house branches
    * `string length`: total length of a string
    * `length house branch A|B`: cable from string to connection point of a house

    A|B in general brings some variation in to the typified model grid and
    refer to different length of house branches and different cable types
    respectively different cable widths.
    """

    houses_connected = (
        selected_string_df['occurence'] * selected_string_df[
            'count house branch']).sum()

    average_load = lvgd.peak_load_residential / \
                   houses_connected

    average_consumption = lvgd.sector_consumption_residential / \
                   houses_connected

    hh_branch = 0

    # iterate over each type of branch
    for i, row in selected_string_df.iterrows():

        # get overall count of branches to set unique branch_no
        branch_count_sum = len(list(
            lvgd.lv_grid._graph.neighbors(lvgd.lv_grid.station())))

        # iterate over it's occurences
        for branch_no in range(1, int(row['occurence']) + 1):

            hh_branch += 1
            # iterate over house branches
            for house_branch in range(1, row['count house branch'] + 1):
                if house_branch % 2 == 0:
                    variant = 'B'
                else:
                    variant = 'A'

                # cable distributor to divert from main branch
                lv_cable_dist = LVCableDistributorDing0(
                    grid=lvgd.lv_grid,
                    string_id=i,
                    branch_no=branch_no + branch_count_sum,
                    load_no=house_branch)
                # add lv_cable_dist to graph
                lvgd.lv_grid.add_cable_dist(lv_cable_dist)

                # cable distributor within building (to connect load+geno)
                lv_cable_dist_building = LVCableDistributorDing0(
                    grid=lvgd.lv_grid,
                    string_id=i,
                    branch_no=branch_no + branch_count_sum,
                    load_no=house_branch,
                    in_building=True)
                # add lv_cable_dist_building to graph
                lvgd.lv_grid.add_cable_dist(lv_cable_dist_building)

                lv_load = LVLoadDing0(grid=lvgd.lv_grid,
                                      string_id=i,
                                      branch_no=branch_no + branch_count_sum,
                                      load_no=house_branch,
                                      peak_load=average_load,
                                      consumption={
                                          'residential': average_consumption})

                # add lv_load to graph
                lvgd.lv_grid.add_load(lv_load)

                cable_name = row['cable type'] + \
                             ' 4x1x{}'.format(row['cable width'])
                cable_type = lvgd.lv_grid.network.static_data[
                    'LV_cables'].loc[cable_name]

                # connect current lv_cable_dist to station
                if house_branch == 1:
                    # edge connect first house branch in branch with the station
                    lvgd.lv_grid._graph.add_edge(
                        lvgd.lv_grid.station(),
                        lv_cable_dist,
                        branch=BranchDing0(
                            length=row['distance house branch'],
                            kind='cable',
                            type=cable_type,
                            id_db='branch_{sector}{branch}_{load}'.format(
                                branch=hh_branch,
                                load=house_branch,
                                sector='HH')
                        ))
                # connect current lv_cable_dist to last one
                else:
                    lvgd.lv_grid._graph.add_edge(
                        lvgd.lv_grid._cable_distributors[-4],
                        lv_cable_dist,
                        branch=BranchDing0(
                            length=row['distance house branch'],
                            kind='cable',
                            type=lvgd.lv_grid.network.static_data[
                                'LV_cables'].loc[cable_name],
                            id_db='branch_{sector}{branch}_{load}'.format(
                                branch=hh_branch,
                                load=house_branch,
                                sector='HH')))

                # connect house to cable distributor
                house_cable_name = row['cable type {}'.format(variant)] + \
                                   ' 4x1x{}'.format(
                                       row['cable width {}'.format(variant)])
                lvgd.lv_grid._graph.add_edge(
                    lv_cable_dist,
                    lv_cable_dist_building,
                    branch=BranchDing0(
                        length=row['length house branch {}'.format(
                            variant)],
                        kind='cable',
                        type=lvgd.lv_grid.network.static_data['LV_cables']. \
                            loc[house_cable_name],
                        id_db='branch_{sector}{branch}_{load}'.format(
                            branch=hh_branch,
                            load=house_branch,
                            sector='HH'))
                )

                lvgd.lv_grid._graph.add_edge(
                    lv_cable_dist_building,
                    lv_load,
                    branch=BranchDing0(
                        length=1,
                        kind='cable',
                        type=lvgd.lv_grid.network.static_data['LV_cables']. \
                            loc[house_cable_name],
                        id_db='branch_{sector}{branch}_{load}'.format(
                            branch=hh_branch,
                            load=house_branch,
                            sector='HH'))
                )


Instruction:
Create a Python 3 function for
building the residential sector branches for lv grid district and LV grid districts.

Response:
def build_residential_branches(lvgd):
    """Based on population and identified peak load data, the according grid
    topology for residential sector is determined and attached to the grid graph

    Parameters
    ----------
    lvgd : LVGridDistrictDing0
        Low-voltage grid district object
    """

    # Choice of typified lv model grid depends on population within lv
    # grid district. If no population is given, lv grid is omitted and
    # load is represented by lv station's peak load
    if lvgd.population > 0 \
            and lvgd.peak_load_residential > 0:
        model_grid = select_grid_model_residential(lvgd)

        build_lv_graph_residential(lvgd, model_grid)

    # no residential load but population
    elif lvgd.population > 0 \
            and lvgd.peak_load_residential == 0:
        logger.warning(
            '{} has population but no residential load. '
            'No grid is created.'.format(
                repr(lvgd)))

    # residential load but no population
    elif lvgd.population == 0 \
            and lvgd.peak_load_residential > 0:
        logger.warning(
            '{} has no population but residential load. '
            'No grid is created and thus this load is '
            'missing in overall balance!'.format(
                repr(lvgd)))

    else:
        logger.info(
            '{} has got no residential load. '
            'No grid is created.'.format(
                repr(lvgd)))


Instruction:
Make a summary of the following Python 3 code
def lv_grid_generators_bus_bar(nd):

    lv_stats = {}

    for la in nd._mv_grid_districts[0].lv_load_areas():
        for lvgd in la.lv_grid_districts():
            station_neighbors = list(lvgd.lv_grid._graph[
                                         lvgd.lv_grid._station].keys())

            # check if nodes of a statio are members of list generators
            station_generators = [x for x in station_neighbors
                                  if x in lvgd.lv_grid.generators()]

            lv_stats[repr(lvgd.lv_grid._station)] = station_generators

    return lv_stats

Response: Calculate statistics about generators at bus bar in LV grids


Instruction:
How would you explain what the following Python 3 function does
def save_nd_to_pickle(nd, path='', filename=None):

    abs_path = os.path.abspath(path)

    if len(nd._mv_grid_districts) > 1:
        name_extension = '_{number}-{number2}'.format(
            number=nd._mv_grid_districts[0].id_db,
            number2=nd._mv_grid_districts[-1].id_db)
    else:
        name_extension = '_{number}'.format(number=nd._mv_grid_districts[0].id_db)

    if filename is None:
        filename = "ding0_grids_{ext}.pkl".format(
            ext=name_extension)

    # delete attributes of `nd` in order to make pickling work
    # del nd._config
    del nd._orm

    pickle.dump(nd, open(os.path.join(abs_path, filename), "wb"))

Response: Use pickle to save the whole nd - object to a file.


Instruction:
How would you code a function in Python 3 to
load nd - object from pickle file.

Response:
def load_nd_from_pickle(filename=None, path=''):
    """
    Use pickle to save the whole nd-object to disc

    Parameters
    ----------
    filename : str
        Filename of nd pickle
    path : str
        Absolute or relative path where pickle should be saved. Default is ''
        which means pickle is save to PWD

    Returns
    -------
    nd : NetworkDing0
        Ding0 grid container object
    """

    abs_path = os.path.abspath(path)

    if filename is None:
        raise NotImplementedError

    return pickle.load(open(os.path.join(abs_path, filename), "rb"))


Instruction:
Given the following Python 3 function, write the documentation
def plot_cable_length(stats, plotpath):

    # cable and line kilometer distribution
    f, axarr = plt.subplots(2, 2, sharex=True)
    stats.hist(column=['Length of MV overhead lines'], bins=5, alpha=0.5, ax=axarr[0, 0])
    stats.hist(column=['Length of MV underground cables'], bins=5, alpha=0.5, ax=axarr[0, 1])
    stats.hist(column=['Length of LV overhead lines'], bins=5, alpha=0.5, ax=axarr[1, 0])
    stats.hist(column=['Length of LV underground cables'], bins=5, alpha=0.5, ax=axarr[1, 1])

    plt.savefig(os.path.join(plotpath,
                             'Histogram_cable_line_length.pdf'))

Response: Plots the length of each cable in MV grid district


Instruction:
Can you write a function in Python 3 where it
plots of generation over load

Response:
def plot_generation_over_load(stats, plotpath):
    """
    Plot of generation over load
    """

    # Generation capacity vs. peak load
    sns.set_context("paper", font_scale=1.1)
    sns.set_style("ticks")

    # reformat to MW

    gen_cap_indexes = ["Gen. Cap. of MV at v_level 4",
                       "Gen. Cap. of MV at v_level 5",
                       "Gen. Cap. of LV at v_level 6",
                       "Gen. Cap. of LV at v_level 7"]
    peak_load_index = ["LA Total LV Peak Load total"]
    stats['generation_capacity'] = stats[gen_cap_indexes].sum(axis=1) / 1e3
    stats['peak_load'] = stats[peak_load_index] / 1e3

    sns.lmplot('generation_capacity', 'peak_load',
               data=stats,
               fit_reg=False,
               # hue='v_nom',
               # hue='Voltage level',
               scatter_kws={"marker": "D",
                            "s": 100},
               aspect=2)
    plt.title('Peak load vs. generation capacity')
    plt.xlabel('Generation capacity in MW')
    plt.ylabel('Peak load in MW')

    plt.savefig(os.path.join(plotpath,
                             'Scatter_generation_load.pdf'))


Instruction:
Given the following Python 3 function, write the documentation
def concat_nd_pickles(self, mv_grid_districts):

    pickle_name = cfg_ding0.get('output', 'nd_pickle')
    # self.nd = self.read_pickles_from_files(pickle_name)

    # TODO: instead of passing a list of mvgd's, pass list of filenames plus optionally a basth_path
    for mvgd in mv_grid_districts[1:]:

        filename = os.path.join(
            self.base_path,
            'results', pickle_name.format(mvgd))
        if os.path.isfile(filename):
            mvgd_pickle = pickle.load(open(filename, 'rb'))
            if mvgd_pickle._mv_grid_districts:
                mvgd.add_mv_grid_district(mvgd_pickle._mv_grid_districts[0])

    # save to concatenated pickle
    pickle.dump(mvgd,
                open(os.path.join(
                    self.base_path,
                    'results',
                    "ding0_grids_{0}-{1}.pkl".format(
                        mv_grid_districts[0],
                        mv_grid_districts[-1])),
                    "wb"))

    # save stats (edges and nodes data) to csv
    nodes, edges = mvgd.to_dataframe()
    nodes.to_csv(os.path.join(
        self.base_path,
        'results', 'mvgd_nodes_stats_{0}-{1}.csv'.format(
            mv_grid_districts[0], mv_grid_districts[-1])),
        index=False)
    edges.to_csv(os.path.join(
        self.base_path,
        'results', 'mvgd_edges_stats_{0}-{1}.csv'.format(
            mv_grid_districts[0], mv_grid_districts[-1])),
        index=False)

Response: Read multiple pickles and save to file


Instruction:
How would you code a function in Python 3 to
calculate statistics about the LV grids in a single network and returns a pandas. DataFrame containing several statistical numbers about the LV grids in the same network.

Response:
def calculate_lvgd_stats(nw):
    """
    LV Statistics for an arbitrary network

    Parameters
    ----------
    nw: :any:`list` of NetworkDing0
        The MV grid(s) to be studied

    Returns
    -------
    lvgd_stats : pandas.DataFrame
        Dataframe containing several statistical numbers about the LVGD
    """
    ##############################
    #  ETRS (equidistant) to WGS84 (conformal) projection
    proj = partial(
        pyproj.transform,
        # pyproj.Proj(init='epsg:3035'),  # source coordinate system
        #  pyproj.Proj(init='epsg:4326'))  # destination coordinate system
        pyproj.Proj(init='epsg:4326'),  # source coordinate system
        pyproj.Proj(init='epsg:3035'))  # destination coordinate system
    ##############################
    # close circuit breakers
    nw.control_circuit_breakers(mode='close')
    ##############################
    lv_dist_idx = 0
    lv_dist_dict = {}
    lv_gen_idx = 0
    lv_gen_dict = {}
    lv_load_idx = 0
    lv_load_dict = {}
    branch_idx = 0
    branches_dict = {}
    trafos_idx = 0
    trafos_dict = {}
    for mv_district in nw.mv_grid_districts():
        for LA in mv_district.lv_load_areas():
            for lv_district in LA.lv_grid_districts():
                lv_dist_idx += 1
                branches_from_station = len(lv_district.lv_grid.graph_branches_from_node(lv_district.lv_grid.station()))
                lv_dist_dict[lv_dist_idx] = {
                    'MV_grid_id': mv_district.mv_grid.id_db,
                    'LV_grid_id': lv_district.lv_grid.id_db,
                    'Load Area ID': LA.id_db,
                    'Population': lv_district.population,
                    'Peak Load Residential': lv_district.peak_load_residential,
                    'Peak Load Retail': lv_district.peak_load_retail,
                    'Peak Load Industrial': lv_district.peak_load_industrial,
                    'Peak Load Agricultural': lv_district.peak_load_agricultural,
                    'N° of Sector Residential': lv_district.sector_count_residential,
                    'N° of Sector Retail': lv_district.sector_count_retail,
                    'N° of Sector Industrial': lv_district.sector_count_industrial,
                    'N° of Sector Agricultural': lv_district.sector_count_agricultural,
                    'Accum. Consumption Residential': lv_district.sector_consumption_residential,
                    'Accum. Consumption Retail': lv_district.sector_consumption_retail,
                    'Accum. Consumption Industrial': lv_district.sector_consumption_industrial,
                    'Accum. Consumption Agricultural': lv_district.sector_consumption_agricultural,
                    'N° of branches from LV Station': branches_from_station,
                    'Load Area is Aggregated': LA.is_aggregated,
                    'Load Area is Satellite': LA.is_satellite,
                }
                # generation capacity
                for g in lv_district.lv_grid.generators():
                    lv_gen_idx += 1
                    subtype = g.subtype
                    if subtype == None:
                        subtype = 'other'
                    type = g.type
                    if type == None:
                        type = 'other'
                    lv_gen_dict[lv_gen_idx] = {
                        'LV_grid_id': lv_district.lv_grid.id_db,
                        'v_level': g.v_level,
                        'subtype': type + '/' + subtype,
                        'GenCap': g.capacity,
                    }
                # nodes bzw. LV loads
                for node in lv_district.lv_grid.graph_nodes_sorted():
                    if isinstance(node, LVLoadDing0):
                        lv_load_idx += 1
                        if 'agricultural' in node.consumption:
                            tipo = 'agricultural'
                        elif 'industrial' in node.consumption:
                            tipo = 'ind_ret'
                        elif 'residential' in node.consumption:
                            tipo = 'residential'
                        else:
                            tipo = 'none'
                        lv_load_dict[lv_load_idx] = {
                            'LV_grid_id': lv_district.lv_grid.id_db,
                            'load_type': tipo,
                        }
                # branches
                for branch in lv_district.lv_grid.graph_edges():
                    branch_idx += 1
                    branches_dict[branch_idx] = {
                        'LV_grid_id': lv_district.lv_grid.id_db,
                        'length': branch['branch'].length / 1e3,
                        'type_name': branch['branch'].type.to_frame().columns[0],
                        'type_kind': branch['branch'].kind,
                    }
                # Transformers
                for trafo in lv_district.lv_grid.station().transformers():
                    trafos_idx += 1
                    trafos_dict[trafos_idx] = {
                        'LV_grid_id': lv_district.lv_grid.id_db,
                        's_max_a': trafo.s_max_a,
                    }

                # geographic
                district_geo = transform(proj, lv_district.geo_data)
                lv_dist_dict[lv_dist_idx].update({'Area': district_geo.area})

    lvgd_stats = pd.DataFrame.from_dict(lv_dist_dict, orient='index').set_index('LV_grid_id')
    # generate partial dataframes
    gen_df = pd.DataFrame.from_dict(lv_gen_dict, orient='index')
    load_df = pd.DataFrame.from_dict(lv_load_dict, orient='index')
    branch_df = pd.DataFrame.from_dict(branches_dict, orient='index')
    trafos_df = pd.DataFrame.from_dict(trafos_dict, orient='index')

    # resque desired data
    if not gen_df.empty:
        # generation by voltage level
        lv_generation = gen_df.groupby(['LV_grid_id', 'v_level'])['GenCap'].sum().to_frame().unstack(level=-1)
        lv_generation.columns = ['Gen. Cap. v_level ' + str(_[1]) if isinstance(_, tuple) else str(_) for _ in
                                 lv_generation.columns]
        lvgd_stats = pd.concat([lvgd_stats, lv_generation], axis=1)
        # generation by type/subtype
        lv_generation = gen_df.groupby(['LV_grid_id', 'subtype'])['GenCap'].sum().to_frame().unstack(level=-1)
        lv_generation.columns = ['Gen. Cap. type ' + str(_[1]) if isinstance(_, tuple) else str(_) for _ in
                                 lv_generation.columns]
        lvgd_stats = pd.concat([lvgd_stats, lv_generation], axis=1)
    if not load_df.empty:
        # number of residential loads
        lv_loads = load_df[load_df['load_type'] == 'residential'].groupby(['LV_grid_id'])[
            'load_type'].count().to_frame()  # .unstack(level=-1)
        lv_loads.columns = ['N° of loads residential']
        lvgd_stats = pd.concat([lvgd_stats, lv_loads], axis=1)
        # number of agricultural loads
        lv_loads = load_df[load_df['load_type'] == 'agricultural'].groupby(['LV_grid_id'])[
            'load_type'].count().to_frame()  # .unstack(level=-1)
        lv_loads.columns = ['N° of loads agricultural']
        lvgd_stats = pd.concat([lvgd_stats, lv_loads], axis=1)
        # number of mixed industrial / retail loads
        lv_loads = load_df[load_df['load_type'] == 'ind_ret'].groupby(['LV_grid_id'])[
            'load_type'].count().to_frame()  # .unstack(level=-1)
        lv_loads.columns = ['N° of loads mixed industrial/retail']
        lvgd_stats = pd.concat([lvgd_stats, lv_loads], axis=1)

    if not branch_df.empty:
        # branches by type name
        lv_branches = branch_df.groupby(['LV_grid_id', 'type_name'])['length'].sum().to_frame().unstack(level=-1)
        lv_branches.columns = ['Length Type ' + _[1] if isinstance(_, tuple) else _ for _ in lv_branches.columns]
        lvgd_stats = pd.concat([lvgd_stats, lv_branches], axis=1)
        # branches by kind
        lv_branches = branch_df[branch_df['type_kind'] == 'line'].groupby(['LV_grid_id'])['length'].sum().to_frame()
        lv_branches.columns = ['Length of overhead lines']
        lvgd_stats = pd.concat([lvgd_stats, lv_branches], axis=1)
        lv_branches = branch_df[branch_df['type_kind'] == 'cable'].groupby(['LV_grid_id'])['length'].sum().to_frame()
        lv_branches.columns = ['Length of underground cables']
        lvgd_stats = pd.concat([lvgd_stats, lv_branches], axis=1)
        # N°of branches
        lv_branches = branch_df.groupby(['LV_grid_id', 'type_name'])['length'].count().to_frame().unstack(level=-1)
        lv_branches.columns = ['N° of branches Type ' + _[1] if isinstance(_, tuple) else _ for _ in
                               lv_branches.columns]
        lvgd_stats = pd.concat([lvgd_stats, lv_branches], axis=1)
        lv_branches = branch_df[branch_df['type_kind'] == 'line'].groupby(['LV_grid_id'])['length'].count().to_frame()
        lv_branches.columns = ['N° of branches overhead lines']
        lvgd_stats = pd.concat([lvgd_stats, lv_branches], axis=1)
        lv_branches = branch_df[branch_df['type_kind'] == 'cable'].groupby(['LV_grid_id'])['length'].count().to_frame()
        lv_branches.columns = ['N° of branches underground cables']
        lvgd_stats = pd.concat([lvgd_stats, lv_branches], axis=1)
    if not trafos_df.empty:
        # N of trafos
        lv_trafos = trafos_df.groupby(['LV_grid_id'])['s_max_a'].count().to_frame()
        lv_trafos.columns = ['N° of MV/LV Trafos']
        lvgd_stats = pd.concat([lvgd_stats, lv_trafos], axis=1)
        # Capacity of trafos
        lv_trafos = trafos_df.groupby(['LV_grid_id'])['s_max_a'].sum().to_frame()
        lv_trafos.columns = ['Accumulated s_max_a in MVLV trafos']
        lvgd_stats = pd.concat([lvgd_stats, lv_trafos], axis=1)

    lvgd_stats = lvgd_stats.fillna(0)
    lvgd_stats = lvgd_stats[sorted(lvgd_stats.columns.tolist())]
    return lvgd_stats