Instruction:
How would you explain what the following Python 3 function does
def calculate_mvgd_stats(nw):
    ##############################

    omega = 2 * pi * 50

    # close circuit breakers
    nw.control_circuit_breakers(mode='close')
    ##############################
    # Collect info from nw into dataframes
    # define dictionaries for collection
    trafos_dict = {}
    generators_dict = {}
    branches_dict = {}
    ring_dict = {}
    LA_dict = {}
    other_nodes_dict = {}
    lv_branches_dict = {}
    # initiate indexes
    trafos_idx = 0
    gen_idx = 0
    branch_idx = 0
    ring_idx = 0
    LA_idx = 0
    lv_branches_idx = 0

    # loop over MV grid districts
    for district in nw.mv_grid_districts():

        # node of MV station
        root = district.mv_grid.station()

        ###################################
        # get impedance of path to each terminal node
        # and get thermal capacity of first segment of path to each terminal node

        # store properties of terminal nodes in dictionaries
        # properties are e.g. impedance of path, length of path, thermal limit of first segment of path
        mv_impedances = {}
        mvlv_impedances = {}

        mv_path_lengths = {}
        mvlv_path_lengths = {}

        mv_thermal_limits = {}  # I_max of first segment on MV for each MV path
        lv_thermal_limits = {}  # I_max of first segment on LV for each LV path
        mvlv_thermal_limits = {}  # I_max of first segment on MV for each MVLV path

        n_outgoing_LV = 0
        n_stations_LV = 0

        n_outgoing_MV = 0

        G = district.mv_grid._graph

        for node in G.nodes():
            if isinstance(node, MVStationDing0):
                n_outgoing_MV += len(list(G.neighbors(node)))
                continue
            mv_impedance = 0
            mv_path_length = 0
            if not isinstance(node, MVCableDistributorDing0) and not isinstance(node, CircuitBreakerDing0):
                if not nx.has_path(G, root, node):
                    continue
                    #print(node, node.lv_load_area.is_aggregated) # only debug
                else:
                    path = nx.shortest_path(G, root, node)
                    for i in range(len(path) - 1):
                        mv_impedance += np.sqrt(
                            (G.adj[path[i]][path[i + 1]]['branch'].type[
                                 'L'] * 1e-3 * omega * \
                             G.adj[path[i]][path[i + 1]][
                                 'branch'].length) ** 2. + \
                            (G.adj[path[i]][path[i + 1]]['branch'].type[
                                 'R'] * \
                             G.adj[path[i]][path[i + 1]][
                                 'branch'].length) ** 2.)
                        mv_path_length += G.adj[path[i]][path[i + 1]][
                            'branch'].length

                    mv_impedances[node] = mv_impedance
                    mv_path_lengths[node] = mv_path_length
                    mv_thermal_limit = G.adj[path[0]][path[1]]['branch'].type['I_max_th']
                    mv_thermal_limits[node] = mv_thermal_limit

                    if isinstance(node, LVStationDing0):
                        # add impedance of transformers in LV station
                        lvstation_impedance = 0.
                        for trafo in node.transformers():
                            lvstation_impedance += 1. / np.hypot(trafo.r,trafo.x)  # transformers operating in parallel
                        if lvstation_impedance > 0.:  # avoid dividing by zero
                            lvstation_impedance = 1. / lvstation_impedance
                        else:
                            lvstation_impedance = 0.
                        # identify LV nodes belonging to LV station
                        for lv_LA in district.lv_load_areas():
                            for lv_dist in lv_LA.lv_grid_districts():
                                if lv_dist.lv_grid._station == node:
                                    G_lv = lv_dist.lv_grid._graph
                                    # loop over all LV terminal nodes belonging to LV station
                                    for lv_node in G_lv.nodes():
                                        if isinstance(lv_node, GeneratorDing0) or isinstance(lv_node, LVLoadDing0):
                                            path = nx.shortest_path(G_lv, node, lv_node)
                                            lv_impedance = lvstation_impedance
                                            lv_path_length = 0.
                                            for i in range(len(path)-1):
                                                lv_impedance += np.sqrt((G_lv.adj[path[i]][path[i+1]]['branch'].type['L'] * 1e-3 * omega * \
                                                                          G_lv.adj[path[i]][path[i+1]]['branch'].length)**2. + \
                                                                         (G_lv.adj[path[i]][path[i+1]]['branch'].type['R'] * \
                                                                          G_lv.adj[path[i]][path[i+1]]['branch'].length)**2.)
                                                lv_path_length += G_lv.adj[path[i]][path[i+1]]['branch'].length
                                            lv_thermal_limit = G_lv.adj[path[0]][path[1]]['branch'].type['I_max_th']

                                            mvlv_impedances[lv_node] = mv_impedance + lv_impedance
                                            mvlv_path_lengths[lv_node] = mv_path_length + lv_path_length
                                            lv_thermal_limits[lv_node] = lv_thermal_limit
                                            mvlv_thermal_limits[lv_node] = mv_thermal_limit

                                        elif isinstance(lv_node, LVStationDing0):
                                            n_outgoing_LV += len(list(G_lv.neighbors(lv_node)))
                                            n_stations_LV += 1

        # compute mean values by looping over terminal nodes
        sum_impedances = 0.
        sum_thermal_limits = 0.
        sum_path_lengths = 0.
        n_terminal_nodes_MV = 0

        # terminal nodes on MV
        for terminal_node in mv_impedances.keys():  # neglect LVStations here because already part of MVLV paths below
            if not isinstance(terminal_node, LVStationDing0) and not isinstance(terminal_node, MVStationDing0):
                sum_impedances += mv_impedances[terminal_node]
                sum_thermal_limits += mv_thermal_limits[terminal_node]
                sum_path_lengths += mv_path_lengths[terminal_node]
                n_terminal_nodes_MV += 1

        sum_thermal_limits_LV = 0.
        n_terminal_nodes_LV = 0

        # terminal nodes on LV
        for terminal_node in mvlv_impedances.keys():
            sum_impedances += mvlv_impedances[terminal_node]
            sum_thermal_limits += mvlv_thermal_limits[terminal_node]
            sum_thermal_limits_LV += lv_thermal_limits[terminal_node]
            sum_path_lengths += mvlv_path_lengths[terminal_node]
            n_terminal_nodes_LV += 1

        n_terminal_nodes = n_terminal_nodes_MV + n_terminal_nodes_LV

        if n_terminal_nodes < 1:
            mean_impedance = np.nan
            mean_thermal_limit = np.nan
            mean_path_length = np.nan
        else:
            mean_impedance = sum_impedances / n_terminal_nodes
            mean_thermal_limit = sum_thermal_limits / n_terminal_nodes
            mean_path_length = sum_path_lengths / n_terminal_nodes
        if n_terminal_nodes_LV < 1:
            mean_thermal_limit_LV = np.nan
        else:
            mean_thermal_limit_LV = sum_thermal_limits_LV / n_terminal_nodes_LV
        number_outgoing_LV = n_outgoing_LV  # / n_stations_LV
        number_outgoing_MV = n_outgoing_MV

        ###################################
        # compute path lengths (written by Miguel)
        max_mv_path = 0
        max_mvlv_path = 0

        # rings
        nodes_in_rings = []
        branches_in_rings = []
        for ring in district.mv_grid.rings_full_data():
            ring_idx += 1

            # generation cap
            ring_gen = 0
            for node in ring[2]:
                nodes_in_rings.append(node)
                if isinstance(node, GeneratorDing0):
                    ring_gen += node.capacity
            # length
            ring_length = 0
            for branch in ring[1]:
                branches_in_rings.append(branch)
                ring_length += branch.length / 1e3

            ring_dict[ring_idx] = {
                'grid_id': district.mv_grid.id_db,
                'ring_length': ring_length,
                'ring_capacity': ring_gen,
            }

        # transformers in main station
        for trafo in district.mv_grid.station().transformers():
            trafos_idx += 1
            trafos_dict[trafos_idx] = {
                'grid_id': district.mv_grid.id_db,
                's_max_a': trafo.s_max_a}

        # Generators and other MV special nodes
        cd_count = 0
        LVs_count = 0
        cb_count = 0
        lv_trafo_count = 0
        lv_trafo_cap = 0

        for node in district.mv_grid._graph.nodes():
            mv_path_length = 0
            mvlv_path_length = 0

            if isinstance(node, GeneratorDing0):
                gen_idx += 1
                isolation = not node in nodes_in_rings
                subtype = node.subtype
                if subtype == None:
                    subtype = 'other'
                generators_dict[gen_idx] = {
                    'grid_id': district.mv_grid.id_db,
                    'type': node.type,
                    'sub_type': node.type + '/' + subtype,
                    'gen_cap': node.capacity,
                    'v_level': node.v_level,
                    'isolation': isolation,
                }
                mv_path_length = district.mv_grid.graph_path_length(
                    node_source=root,
                    node_target=node)

            elif isinstance(node, MVCableDistributorDing0):
                cd_count += 1
            elif isinstance(node, LVStationDing0):
                LVs_count += 1
                lv_trafo_count += len([trafo for trafo in node.transformers()])
                lv_trafo_cap += np.sum([trafo.s_max_a for trafo in node.transformers()])

                if not node.lv_load_area.is_aggregated:
                    mv_path_length = district.mv_grid.graph_path_length(
                        node_source=root,
                        node_target=node)
                    max_lv_path = 0
                    for lv_LA in district.lv_load_areas():
                        for lv_dist in lv_LA.lv_grid_districts():
                            if lv_dist.lv_grid._station == node:
                                for lv_node in lv_dist.lv_grid._graph.nodes():
                                    lv_path_length = lv_dist.lv_grid.graph_path_length(
                                        node_source=node,
                                        node_target=lv_node)
                                    max_lv_path = max(max_lv_path, lv_path_length)
                    mvlv_path_length = mv_path_length + max_lv_path

            elif isinstance(node, CircuitBreakerDing0):
                cb_count += 1

            max_mv_path = max(max_mv_path, mv_path_length / 1000)
            max_mvlv_path = max(max_mvlv_path, mvlv_path_length / 1000)

        other_nodes_dict[district.mv_grid.id_db] = {
            'CD_count': cd_count,
            'LV_count': LVs_count,
            'CB_count': cb_count,
            'MVLV_trafo_count': lv_trafo_count,
            'MVLV_trafo_cap': lv_trafo_cap,
            'max_mv_path': max_mv_path,
            'max_mvlv_path': max_mvlv_path,
            'mean_impedance': mean_impedance,
            'mean_thermal_limit': mean_thermal_limit,
            'mean_thermal_limit_LV': mean_thermal_limit_LV,
            'mean_path_length': mean_path_length / 1.e3,
            'number_outgoing_LV': number_outgoing_LV,
            'number_outgoing_MV': number_outgoing_MV
        }

        # branches
        for branch in district.mv_grid.graph_edges():
            branch_idx += 1
            br_in_ring = branch['branch'] in branches_in_rings
            branches_dict[branch_idx] = {
                'grid_id': district.mv_grid.id_db,
                'length': branch['branch'].length / 1e3,
                'type_name': branch['branch'].type['name'],
                'type_kind': branch['branch'].kind,
                'in_ring': br_in_ring,
            }
        # Load Areas
        for LA in district.lv_load_areas():
            LA_idx += 1
            LA_dict[LA_idx] = {
                'grid_id': district.mv_grid.id_db,
                'is_agg': LA.is_aggregated,
                'is_sat': LA.is_satellite,
                # 'peak_gen':LA.peak_generation,
            }
            LA_pop = 0
            residential_peak_load = 0
            retail_peak_load = 0
            industrial_peak_load = 0
            agricultural_peak_load = 0
            lv_gen_level_6 = 0
            lv_gen_level_7 = 0
            for lv_district in LA.lv_grid_districts():
                LA_pop = + lv_district.population
                residential_peak_load += lv_district.peak_load_residential
                retail_peak_load += lv_district.peak_load_retail
                industrial_peak_load += lv_district.peak_load_industrial
                agricultural_peak_load += lv_district.peak_load_agricultural

                # generation capacity
                for g in lv_district.lv_grid.generators():
                    if g.v_level == 6:
                        lv_gen_level_6 += g.capacity
                    elif g.v_level == 7:
                        lv_gen_level_7 += g.capacity

                # branches lengths
                for br in lv_district.lv_grid.graph_edges():
                    lv_branches_idx += 1
                    lv_branches_dict[lv_branches_idx] = {
                        'grid_id': district.mv_grid.id_db,
                        'length': br['branch'].length / 1e3,
                        'type_name': br['branch'].type.to_frame().columns[0],  # why is it different as for MV grids?
                        'type_kind': br['branch'].kind,
                    }

            LA_dict[LA_idx].update({
                'population': LA_pop,
                'residential_peak_load': residential_peak_load,
                'retail_peak_load': retail_peak_load,
                'industrial_peak_load': industrial_peak_load,
                'agricultural_peak_load': agricultural_peak_load,
                'total_peak_load': residential_peak_load + retail_peak_load + \
                                   industrial_peak_load + agricultural_peak_load,
                'lv_generation': lv_gen_level_6 + lv_gen_level_7,
                'lv_gens_lvl_6': lv_gen_level_6,
                'lv_gens_lvl_7': lv_gen_level_7,
            })

        # geographic
        #  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
        district_geo = transform(proj, district.geo_data)
        other_nodes_dict[district.mv_grid.id_db].update({'Dist_area': district_geo.area})

    mvgd_stats = pd.DataFrame.from_dict({}, orient='index')
    ###################################
    # built dataframes from dictionaries
    trafos_df = pd.DataFrame.from_dict(trafos_dict, orient='index')
    generators_df = pd.DataFrame.from_dict(generators_dict, orient='index')
    other_nodes_df = pd.DataFrame.from_dict(other_nodes_dict, orient='index')
    branches_df = pd.DataFrame.from_dict(branches_dict, orient='index')
    lv_branches_df = pd.DataFrame.from_dict(lv_branches_dict, orient='index')
    ring_df = pd.DataFrame.from_dict(ring_dict, orient='index')
    LA_df = pd.DataFrame.from_dict(LA_dict, orient='index')

    ###################################
    # Aggregated data HV/MV Trafos
    if not trafos_df.empty:
        mvgd_stats = pd.concat([mvgd_stats, trafos_df.groupby('grid_id').count()['s_max_a']], axis=1)
        mvgd_stats = pd.concat([mvgd_stats, trafos_df.groupby('grid_id').sum()[['s_max_a']]], axis=1)
        mvgd_stats.columns = ['N° of HV/MV Trafos', 'Trafos HV/MV Acc s_max_a']

    ###################################
    # Aggregated data Generators
    if not generators_df.empty:
        # MV generation per sub_type
        mv_generation = generators_df.groupby(['grid_id', 'sub_type'])['gen_cap'].sum().to_frame().unstack(level=-1)
        mv_generation.columns = ['Gen. Cap. of MV ' + _[1] if isinstance(_, tuple) else _
                                 for _ in mv_generation.columns]
        mvgd_stats = pd.concat([mvgd_stats, mv_generation], axis=1)

        # MV generation at V levels
        mv_generation = generators_df.groupby(
            ['grid_id', 'v_level'])['gen_cap'].sum().to_frame().unstack(level=-1)
        mv_generation.columns = ['Gen. Cap. of MV at v_level ' + str(_[1])
                                 if isinstance(_, tuple) else _
                                 for _ in mv_generation.columns]
        mvgd_stats = pd.concat([mvgd_stats, mv_generation], axis=1)
        # Isolated generators
        mv_generation = generators_df[generators_df['isolation']].groupby(
            ['grid_id'])['gen_cap'].count().to_frame()  # .unstack(level=-1)
        mv_generation.columns = ['N° of isolated MV Generators']
        mvgd_stats = pd.concat([mvgd_stats, mv_generation], axis=1)

    ###################################
    # Aggregated data of other nodes
    if not other_nodes_df.empty:
        # print(other_nodes_df['CD_count'].to_frame())
        mvgd_stats['N° of Cable Distr'] = other_nodes_df['CD_count'].to_frame().astype(int)
        mvgd_stats['N° of LV Stations'] = other_nodes_df['LV_count'].to_frame().astype(int)
        mvgd_stats['N° of Circuit Breakers'] = other_nodes_df['CB_count'].to_frame().astype(int)
        mvgd_stats['District Area'] = other_nodes_df['Dist_area'].to_frame()
        mvgd_stats['N° of MV/LV Trafos'] = other_nodes_df['MVLV_trafo_count'].to_frame().astype(int)
        mvgd_stats['Trafos MV/LV Acc s_max_a'] = other_nodes_df['MVLV_trafo_cap'].to_frame()
        mvgd_stats['Length of MV max path'] = other_nodes_df['max_mv_path'].to_frame()
        mvgd_stats['Length of MVLV max path'] = other_nodes_df['max_mvlv_path'].to_frame()
        mvgd_stats['Impedance Z of path to terminal node (mean value)'] = \
            other_nodes_df['mean_impedance'].to_frame()
        mvgd_stats['I_max of first segment of path from MV station to terminal node (mean value)'] = \
            other_nodes_df['mean_thermal_limit'].to_frame()
        mvgd_stats['I_max of first segment of path from LV station to terminal node (mean value)'] = \
            other_nodes_df['mean_thermal_limit_LV'].to_frame()
        mvgd_stats['Length of path from MV station to terminal node (mean value)'] = \
            other_nodes_df['mean_path_length'].to_frame()
        mvgd_stats['Number of lines and cables going out from LV stations'] = \
            other_nodes_df['number_outgoing_LV'].to_frame()
        mvgd_stats['Number of lines and cables going out from MV stations'] = \
            other_nodes_df['number_outgoing_MV'].to_frame()

    ###################################
    # Aggregated data of MV Branches
    if not branches_df.empty:
        # km of underground cable
        branches_data = branches_df[branches_df['type_kind'] == 'cable'].groupby(
            ['grid_id'])['length'].sum().to_frame()
        branches_data.columns = ['Length of MV underground cables']
        mvgd_stats = pd.concat([mvgd_stats, branches_data], axis=1)

        # km of overhead lines
        branches_data = branches_df[branches_df['type_kind'] == 'line'].groupby(
            ['grid_id'])['length'].sum().to_frame()
        branches_data.columns = ['Length of MV overhead lines']
        mvgd_stats = pd.concat([mvgd_stats, branches_data], axis=1)

        # km of different wire types
        branches_data = branches_df.groupby(
            ['grid_id', 'type_name'])['length'].sum().to_frame().unstack(level=-1)
        branches_data.columns = ['Length of MV type ' + _[1] if isinstance(_, tuple) else _
                                 for _ in branches_data.columns]
        mvgd_stats = pd.concat([mvgd_stats, branches_data], axis=1)

        # branches not in ring
        total_br = branches_df.groupby(['grid_id'])['length'].count().to_frame()
        ring_br = branches_df[branches_df['in_ring']].groupby(
            ['grid_id'])['length'].count().to_frame()
        branches_data = total_br - ring_br
        total_br.columns = ['N° of MV branches']
        mvgd_stats = pd.concat([mvgd_stats, total_br], axis=1)
        branches_data.columns = ['N° of MV branches not in a ring']
        mvgd_stats = pd.concat([mvgd_stats, branches_data], axis=1)

    ###################################
    # Aggregated data of LV Branches
    if not lv_branches_df.empty:
        # km of underground cable
        lv_branches_data = lv_branches_df[lv_branches_df['type_kind'] == 'cable'].groupby(
            ['grid_id'])['length'].sum().to_frame()
        lv_branches_data.columns = ['Length of LV underground cables']
        mvgd_stats = pd.concat([mvgd_stats, lv_branches_data], axis=1)

        # km of overhead lines
        lv_branches_data = lv_branches_df[lv_branches_df['type_kind'] == 'line'].groupby(
            ['grid_id'])['length'].sum().to_frame()
        lv_branches_data.columns = ['Length of LV overhead lines']
        mvgd_stats = pd.concat([mvgd_stats, lv_branches_data], axis=1)

        # km of different wire types
        lv_branches_data = lv_branches_df.groupby(
            ['grid_id', 'type_name'])['length'].sum().to_frame().unstack(level=-1)
        lv_branches_data.columns = ['Length of LV type ' + _[1] if isinstance(_, tuple) else _
                                    for _ in lv_branches_data.columns]
        mvgd_stats = pd.concat([mvgd_stats, lv_branches_data], axis=1)

        # n° of branches
        total_lv_br = lv_branches_df.groupby(['grid_id'])['length'].count().to_frame()
        total_lv_br.columns = ['N° of LV branches']
        mvgd_stats = pd.concat([mvgd_stats, total_lv_br], axis=1)

    ###################################
    # Aggregated data of Rings
    if not ring_df.empty:
        # N° of rings
        ring_data = ring_df.groupby(['grid_id'])['grid_id'].count().to_frame()
        ring_data.columns = ['N° of MV Rings']
        mvgd_stats = pd.concat([mvgd_stats, ring_data], axis=1)

        # min,max,mean km of all rings
        ring_data = ring_df.groupby(['grid_id'])['ring_length'].min().to_frame()
        ring_data.columns = ['Length of MV Ring min']
        mvgd_stats = pd.concat([mvgd_stats, ring_data], axis=1)
        ring_data = ring_df.groupby(['grid_id'])['ring_length'].max().to_frame()
        ring_data.columns = ['Length of MV Ring max']
        mvgd_stats = pd.concat([mvgd_stats, ring_data], axis=1)
        ring_data = ring_df.groupby(['grid_id'])['ring_length'].mean().to_frame()
        ring_data.columns = ['Length of MV Ring mean']
        mvgd_stats = pd.concat([mvgd_stats, ring_data], axis=1)

        # km of all rings
        ring_data = ring_df.groupby(['grid_id'])['ring_length'].sum().to_frame()
        ring_data.columns = ['Length of MV Rings total']
        mvgd_stats = pd.concat([mvgd_stats, ring_data], axis=1)

        # km of non-ring
        non_ring_data = branches_df.groupby(['grid_id'])['length'].sum().to_frame()
        non_ring_data.columns = ['Length of MV Rings total']
        ring_data = non_ring_data - ring_data
        ring_data.columns = ['Length of MV Non-Rings total']
        mvgd_stats = pd.concat([mvgd_stats, ring_data.round(1).abs()], axis=1)

        # rings generation capacity
        ring_data = ring_df.groupby(['grid_id'])['ring_capacity'].sum().to_frame()
        ring_data.columns = ['Gen. Cap. Connected to MV Rings']
        mvgd_stats = pd.concat([mvgd_stats, ring_data], axis=1)
    ###################################
    # Aggregated data of Load Areas
    if not LA_df.empty:
        LA_data = LA_df.groupby(['grid_id'])['population'].count().to_frame()
        LA_data.columns = ['N° of Load Areas']

        mvgd_stats = pd.concat([mvgd_stats, LA_data], axis=1)

        LA_data = LA_df.groupby(['grid_id'])['population',
                                             'residential_peak_load',
                                             'retail_peak_load',
                                             'industrial_peak_load',
                                             'agricultural_peak_load',
                                             'total_peak_load',
                                             'lv_generation',
                                             'lv_gens_lvl_6',
                                             'lv_gens_lvl_7'
        ].sum()
        LA_data.columns = ['LA Total Population',
                           'LA Total LV Peak Load Residential',
                           'LA Total LV Peak Load Retail',
                           'LA Total LV Peak Load Industrial',
                           'LA Total LV Peak Load Agricultural',
                           'LA Total LV Peak Load total',
                           'LA Total LV Gen. Cap.',
                           'Gen. Cap. of LV at v_level 6',
                           'Gen. Cap. of LV at v_level 7',
                           ]
        mvgd_stats = pd.concat([mvgd_stats, LA_data], axis=1)

    ###################################
    # Aggregated data of Aggregated Load Areas
    if not LA_df.empty:
        agg_LA_data = LA_df[LA_df['is_agg']].groupby(
            ['grid_id'])['population'].count().to_frame()
        agg_LA_data.columns = ['N° of Load Areas - Aggregated']
        mvgd_stats = pd.concat([mvgd_stats, agg_LA_data], axis=1)

        sat_LA_data = LA_df[LA_df['is_sat']].groupby(
            ['grid_id'])['population'].count().to_frame()
        sat_LA_data.columns = ['N° of Load Areas - Satellite']
        mvgd_stats = pd.concat([mvgd_stats, sat_LA_data], axis=1)

        agg_LA_data = LA_df[LA_df['is_agg']].groupby(['grid_id'])['population',
                                                                  'lv_generation',
                                                                  'total_peak_load'].sum()
        agg_LA_data.columns = ['LA Aggregated Population',
                               'LA Aggregated LV Gen. Cap.', 'LA Aggregated LV Peak Load total'
                               ]
        mvgd_stats = pd.concat([mvgd_stats, agg_LA_data], axis=1)

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

Response: Calculates statistics about the MV grids in a single network and stores them in a pandas. DataFrame containing several statistical numbers about the MV grids in the MV grid.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def calculate_mvgd_voltage_current_stats(nw):
    ##############################
    # close circuit breakers
    nw.control_circuit_breakers(mode='close')
    ##############################
    nodes_idx = 0
    nodes_dict = {}
    branches_idx = 0
    branches_dict = {}
    for district in nw.mv_grid_districts():
        # nodes voltage
        for node in district.mv_grid.graph_nodes_sorted():
            nodes_idx += 1
            if hasattr(node, 'voltage_res'):
                Vres0 = node.voltage_res[0]
                Vres1 = node.voltage_res[1]
            else:
                Vres0 = 'Not available'
                Vres1 = 'Not available'
            nodes_dict[nodes_idx] = {'MV_grid_id': district.mv_grid.id_db,
                                     'node id': node.__repr__(),
                                     'V_res_0': Vres0,
                                     'V_res_1': Vres1,
                                     'V nominal': district.mv_grid.v_level}
        # branches currents
        for branch in district.mv_grid.graph_edges():
            branches_idx += 1
            if hasattr(branch['branch'], 's_res'):
                s_res0 = branch['branch'].s_res[0]
                s_res1 = branch['branch'].s_res[1]
            else:
                s_res0 = 'Not available'
                s_res1 = 'Not available'

            branches_dict[branches_idx] = {
                'MV_grid_id': district.mv_grid.id_db,
                'branch id': branch['branch'].__repr__(),  # .id_db
                's_res_0': s_res0,
                's_res_1': s_res1,
                # 'length': branch['branch'].length / 1e3,
            }
    nodes_df = pd.DataFrame.from_dict(nodes_dict, orient='index')
    branches_df = pd.DataFrame.from_dict(branches_dict, orient='index')

    if not nodes_df.empty:
        nodes_df = nodes_df.set_index('node id')
        nodes_df = nodes_df.fillna(0)
        nodes_df = nodes_df[sorted(nodes_df.columns.tolist())]
        nodes_df.sort_index(inplace=True)

    if not branches_df.empty:
        branches_df = branches_df.set_index('branch id')
        branches_df = branches_df.fillna(0)
        branches_df = branches_df[sorted(branches_df.columns.tolist())]
        branches_df.sort_index(inplace=True)

    return (nodes_df, branches_df)

Response: Calculates the MV Voltage and Current Statistics for a given network and returns a pandas. DataFrame containing the voltage statistics for each node in the MVGD and every edge in the MVGD.


Instruction:
Make a summary of the following Python 3 code
def calculate_lvgd_voltage_current_stats(nw):
    ##############################
    # close circuit breakers
    nw.control_circuit_breakers(mode='close')
    ##############################
    nodes_idx = 0
    nodes_dict = {}
    branches_idx = 0
    branches_dict = {}
    for mv_district in nw.mv_grid_districts():
        for LA in mv_district.lv_load_areas():
            if not LA.is_aggregated:
                for lv_district in LA.lv_grid_districts():
                    # nodes voltage
                    crit_nodes = get_critical_voltage_at_nodes(lv_district.lv_grid)
                    for node in crit_nodes:
                        nodes_idx += 1
                        nodes_dict[nodes_idx] = {
                            'MV_grid_id': mv_district.mv_grid.id_db,
                            'LV_grid_id': lv_district.lv_grid.id_db,
                            'LA_id': LA.id_db,
                            'node id': node['node'].__repr__(),
                            'v_diff_0': node['v_diff'][0],
                            'v_diff_1': node['v_diff'][1],
                            's_max_0': 'NA',
                            's_max_1': 'NA',
                            'V nominal': lv_district.lv_grid.v_level,
                        }
                    # branches currents
                    critical_branches, critical_stations = get_critical_line_loading(lv_district.lv_grid)
                    for branch in critical_branches:
                        branches_idx += 1
                        branches_dict[branches_idx] = {
                            'MV_grid_id': mv_district.mv_grid.id_db,
                            'LV_grid_id': lv_district.lv_grid.id_db,
                            'LA_id': LA.id_db,
                            'branch id': branch['branch'].__repr__(),
                            's_max_0': branch['s_max'][0],
                            's_max_1': branch['s_max'][1],
                        }
                    # stations
                    for node in critical_stations:
                        nodes_idx += 1
                        nodes_dict[nodes_idx] = {
                            'MV_grid_id': mv_district.mv_grid.id_db,
                            'LV_grid_id': lv_district.lv_grid.id_db,
                            'LA_id': LA.id_db,
                            'node id': node['station'].__repr__(),
                            's_max_0': node['s_max'][0],
                            's_max_1': node['s_max'][1],
                            'v_diff_0': 'NA',
                            'v_diff_1': 'NA',
                        }
    nodes_df = pd.DataFrame.from_dict(nodes_dict, orient='index')
    branches_df = pd.DataFrame.from_dict(branches_dict, orient='index')

    if not nodes_df.empty:
        nodes_df = nodes_df.set_index('node id')
        nodes_df = nodes_df.fillna(0)
        nodes_df = nodes_df[sorted(nodes_df.columns.tolist())]
        nodes_df.sort_index(inplace=True)

    if not branches_df.empty:
        branches_df = branches_df.set_index('branch id')
        branches_df = branches_df.fillna(0)
        branches_df = branches_df[sorted(branches_df.columns.tolist())]
        branches_df.sort_index(inplace=True)

    return nodes_df, branches_df

Response: Calculates LV Voltage and Current Statistics for a given network.


Instruction:
Write a Python 3 function that can
create a MV grid with ding0 over the selected MV Grid Districts and saves the result in pickle file.

Response:
def init_mv_grid(mv_grid_districts=[3545], filename='ding0_tests_grids_1.pkl'):
    '''Runs ding0 over the districtis selected in mv_grid_districts

    It also writes the result in filename. If filename = False,
    then the network is not saved.

    Parameters
    ----------
    mv_grid_districts: :any:`list` of :obj:`int`
        Districts IDs: Defaults to [3545]
    filename: str
        Defaults to 'ding0_tests_grids_1.pkl'
        If filename=False, then the network is not saved

    Returns
    -------
    NetworkDing0
        The created MV network.

    '''
    print('\n########################################')
    print('  Running ding0 for district', mv_grid_districts)
    # database connection/ session
    engine = db.connection(section='oedb')
    session = sessionmaker(bind=engine)()

    # instantiate new ding0 network object
    nd = NetworkDing0(name='network')

    # run DINGO on selected MV Grid District
    nd.run_ding0(session=session, mv_grid_districts_no=mv_grid_districts)

    # export grid to file (pickle)
    if filename:
        print('\n########################################')
        print('  Saving result in ', filename)
        save_nd_to_pickle(nd, filename=filename)

    print('\n########################################')
    return nd


Instruction:
Make a summary of the following Python 3 code
def process_stats(mv_districts,
                  n_of_districts,
                  source,
                  mode,
                  critical,
                  filename,
                  output):
    '''Generates stats dataframes for districts in mv_districts.

    If source=='ding0', then runned districts are saved to a pickle named
    filename+str(n_of_districts[0])+'_to_'+str(n_of_districts[-1])+'.pkl'

    Parameters
    ----------
    districts_list: list of int
        List with all districts to be run.
    n_of_districts: int
        Number of districts to be run in each cluster
    source: str
        If 'pkl', pickle files are read.
        If 'ding0', ding0 is run over the districts.
    mode: str
        If 'MV', medium voltage stats are calculated.
        If 'LV', low voltage stats are calculated.
        If empty, medium and low voltage stats are calculated.
    critical: bool
        If True, critical nodes and branches are returned
    filename: str
        filename prefix for saving pickles
    output:
        outer variable where the output is stored as a tuple of 6 lists::

        * mv_stats: MV stats DataFrames.
          If mode=='LV', then DataFrame is empty.

        * lv_stats: LV stats DataFrames.
          If mode=='MV', then DataFrame is empty.

        * mv_crit_nodes: MV critical nodes stats DataFrames.
          If mode=='LV', then DataFrame is empty.
          If critical==False, then DataFrame is empty.

        * mv_crit_edges: MV critical edges stats DataFrames.
          If mode=='LV', then DataFrame is empty.
          If critical==False, then DataFrame is empty.

        * lv_crit_nodes: LV critical nodes stats DataFrames.
          If mode=='MV', then DataFrame is empty.
          If critical==False, then DataFrame is empty.

        * lv_crit_edges: LV critical edges stats DataFrames.
          If mode=='MV', then DataFrame is empty.
          If critical==False, then DataFrame is empty.
    '''
    #######################################################################
    # decide what exactly to do with MV LV
    if mode == 'MV':
        calc_mv = True
        calc_lv = False
    elif mode == 'LV':
        calc_mv = False
        calc_lv = True
    else:
        calc_mv = True
        calc_lv = True
    #######################################################################
    clusters = [mv_districts[x:x + n_of_districts] for x in range(0, len(mv_districts), n_of_districts)]

    mv_stats      = []
    lv_stats      = []
    mv_crit_nodes = []
    mv_crit_edges = []
    lv_crit_nodes = []
    lv_crit_edges = []
    #######################################################################
    for cl in clusters:
        nw_name = filename + str(cl[0])
        if not cl[0] == cl[-1]:
            nw_name = nw_name + '_to_' + str(cl[-1])

        nw = NetworkDing0(name=nw_name)
        if source == 'pkl':
            print('\n########################################')
            print('  Reading data from pickle district', cl)
            print('########################################')
            try:
                nw = load_nd_from_pickle(nw_name + '.pkl')
            except Exception:
                continue
        else:
            # database connection/ session
            engine = db.connection(section='oedb')
            session = sessionmaker(bind=engine)()

            print('\n########################################')
            print('  Running ding0 for district', cl)
            print('########################################')
            try:
                nw.run_ding0(session=session, mv_grid_districts_no=cl)
                try:
                    save_nd_to_pickle(nw, filename=nw_name + '.pkl')
                except Exception:
                    continue
            except Exception:
                continue

            # Close database connection
        if calc_mv:
            stats = calculate_mvgd_stats(nw)
            mv_stats.append(stats)
        if calc_lv:
            stats = calculate_lvgd_stats(nw)
            lv_stats.append(stats)
        if critical and calc_mv:
            stats = calculate_mvgd_voltage_current_stats(nw)
            mv_crit_nodes.append(stats[0])
            mv_crit_edges.append(stats[1])
        if critical and calc_lv:
            stats = calculate_lvgd_voltage_current_stats(nw)
            lv_crit_nodes.append(stats[0])
            lv_crit_edges.append(stats[1])
    #######################################################################
    salida = (mv_stats, lv_stats, mv_crit_nodes, mv_crit_edges, lv_crit_nodes, lv_crit_edges)
    output.put(salida)

Response: Generates stats dataframes for the given set of districts.


Instruction:
Explain what the following Python 3 code does
def parallel_running_stats(districts_list,
                           n_of_processes,
                           n_of_districts=1,
                           source='pkl',
                           mode='',
                           critical=False,
                           save_csv=False,
                           save_path=''):
    '''Organize parallel runs of ding0 to calculate stats

    The function take all districts in a list and divide them into
    n_of_processes parallel processes. For each process, the assigned districts
    are given to the function process_runs() with arguments n_of_districts,
    source, mode, and critical

    Parameters
    ----------
    districts_list: list of int
        List with all districts to be run.
    n_of_processes: int
        Number of processes to run in parallel
    n_of_districts: int
        Number of districts to be run in each cluster given as argument to
        process_stats()
    source: str
        If 'pkl', pickle files are read. Otherwise, ding0 is run over the districts.
    mode: str
        If 'MV', medium voltage stats are calculated.
        If 'LV', low voltage stats are calculated.
        If empty, medium and low voltage stats are calculated.
    critical: bool
        If True, critical nodes and branches are returned
    path: str
        path to save the pkl and csv files

    Returns
    -------
    DataFrame
        mv_stats: MV stats in a DataFrame.
        If mode=='LV', then DataFrame is empty.
    DataFrame
        lv_stats: LV stats in a DataFrame.
        If mode=='MV', then DataFrame is empty.
    DataFrame
        mv_crit_nodes: MV critical nodes stats in a DataFrame.
        If mode=='LV', then DataFrame is empty.
        If critical==False, then DataFrame is empty.
    DataFrame
        mv_crit_edges: MV critical edges stats in a DataFrame.
        If mode=='LV', then DataFrame is empty.
        If critical==False, then DataFrame is empty.
    DataFrame
        lv_crit_nodes: LV critical nodes stats in a DataFrame.
        If mode=='MV', then DataFrame is empty.
        If critical==False, then DataFrame is empty.
    DataFrame
        lv_crit_edges: LV critical edges stats in a DataFrame.
        If mode=='MV', then DataFrame is empty.
        If critical==False, then DataFrame is empty.

    See Also
    --------
    process_stats
    '''
    start = time.time()

    nw_name = os.path.join(save_path, 'ding0_grids__')  # name of files prefix

    #######################################################################
    # Define an output queue
    output_stats = mp.Queue()
    #######################################################################
    # Setup a list of processes that we want to run
    max_dist = len(districts_list)
    threat_long = floor(max_dist / n_of_processes)

    if threat_long == 0:
        threat_long = 1

    threats = [districts_list[x:x + threat_long] for x in
               range(0, len(districts_list), threat_long)]

    processes = []
    for districts in threats:
        args = (districts, n_of_districts, source, mode, critical, nw_name, output_stats)
        processes.append(mp.Process(target=process_stats, args=args))
    #######################################################################
    # Run processes
    for p in processes:
        p.start()
    # Resque output_stats from processes
    output = [output_stats.get() for p in processes]
    # Exit the completed processes
    for p in processes:
        p.join()
    #######################################################################
    # create outputs
    # Name of files
    if save_csv:
        nw_name = nw_name + str(districts_list[0])
        if not districts_list[0] == districts_list[-1]:
            nw_name = nw_name + '_to_' + str(districts_list[-1])

    # concatenate all dataframes
    try:
        mv_stats = pd.concat(
            [df for p in range(0, len(processes)) for df in output[p][0]],
            axis=0)
    except:
        mv_stats = pd.DataFrame.from_dict({})
    try:
        lv_stats = pd.concat(
            [df for p in range(0, len(processes)) for df in output[p][1]],
            axis=0)
    except:
        lv_stats = pd.DataFrame.from_dict({})
    try:
        mv_crit_nodes = pd.concat(
            [df for p in range(0, len(processes)) for df in output[p][2]],
            axis=0)
    except:
        mv_crit_nodes = pd.DataFrame.from_dict({})
    try:
        mv_crit_edges = pd.concat(
            [df for p in range(0, len(processes)) for df in output[p][3]],
            axis=0)
    except:
        mv_crit_edges = pd.DataFrame.from_dict({})
    try:
        lv_crit_nodes = pd.concat(
            [df for p in range(0, len(processes)) for df in output[p][4]],
            axis=0)
    except:
        lv_crit_nodes = pd.DataFrame.from_dict({})
    try:
        lv_crit_edges = pd.concat(
            [df for p in range(0, len(processes)) for df in output[p][5]],
            axis=0)
    except:
        lv_crit_edges = pd.DataFrame.from_dict({})

    # format concatenated Dataframes
    if not mv_stats.empty:
        mv_stats = mv_stats.fillna(0)
        mv_stats = mv_stats[sorted(mv_stats.columns.tolist())]
        mv_stats.sort_index(inplace=True)
        if save_csv:
            mv_stats.to_csv(nw_name + '_mv_stats.csv')

    if not lv_stats.empty:
        lv_stats = lv_stats.fillna(0)
        lv_stats = lv_stats[sorted(lv_stats.columns.tolist())]
        lv_stats.sort_index(inplace=True)
        if save_csv:
            lv_stats.to_csv(nw_name + '_lv_stats.csv')

    if not mv_crit_nodes.empty:
        mv_crit_nodes = mv_crit_nodes.fillna(0)
        mv_crit_nodes = mv_crit_nodes[sorted(mv_crit_nodes.columns.tolist())]
        mv_crit_nodes.sort_index(inplace=True)
        if save_csv:
            mv_crit_nodes.to_csv(nw_name + '_mv_crit_nodes.csv')

    if not mv_crit_edges.empty:
        mv_crit_edges = mv_crit_edges.fillna(0)
        mv_crit_edges = mv_crit_edges[sorted(mv_crit_edges.columns.tolist())]
        mv_crit_edges.sort_index(inplace=True)
        if save_csv:
            mv_crit_edges.to_csv(nw_name + '_mv_crit_edges.csv')

    if not lv_crit_nodes.empty:
        lv_crit_nodes = lv_crit_nodes.fillna(0)
        lv_crit_nodes = lv_crit_nodes[sorted(lv_crit_nodes.columns.tolist())]
        lv_crit_nodes.sort_index(inplace=True)
        if save_csv:
            lv_crit_nodes.to_csv(nw_name + '_lv_crit_nodes.csv')

    if not lv_crit_edges.empty:
        lv_crit_edges = lv_crit_edges.fillna(0)
        lv_crit_edges = lv_crit_edges[sorted(lv_crit_edges.columns.tolist())]
        lv_crit_edges.sort_index(inplace=True)
        if save_csv:
            lv_crit_edges.to_csv(nw_name + '_lv_crit_edges.csv')

    #######################################################################
    print('\n########################################')
    print('  Elapsed time for', str(max_dist),
          'MV grid districts (seconds): {}'.format(time.time() - start))
    print('\n########################################')
    #######################################################################
    return mv_stats, lv_stats, mv_crit_nodes, mv_crit_edges, lv_crit_nodes, lv_crit_edges

Response: This function calculates parallel stats for the given list of districts in a single process.


Instruction:
Given the following Python 3 function, write the documentation
def export_network(nw, mode=''):

    # close circuit breakers
    nw.control_circuit_breakers(mode='close')
    # srid
    srid = str(int(nw.config['geo']['srid']))
    ##############################
    # check what to do
    lv_info = True
    mv_info = True
    if mode == 'LV':
        mv_info = False
    if mode == 'MV':
        lv_info = False
    ##############################
    # from datetime import datetime
    run_id = nw.metadata['run_id']  # datetime.now().strftime("%Y%m%d%H%M%S")
    ##############################
    #############################
    # go through the grid collecting info
    lvgrid_idx = 0
    lv_grid_dict = {}
    lvloads_idx = 0
    lv_loads_dict = {}
    mvgrid_idx = 0
    mv_grid_dict = {}
    mvloads_idx = 0
    mv_loads_dict = {}
    mvgen_idx = 0
    mv_gen_dict = {}
    mvcb_idx = 0
    mvcb_dict = {}
    mvcd_idx = 0
    mv_cd_dict = {}
    mvstations_idx = 0
    hvmv_stations_dict = {}
    mvtrafos_idx = 0
    hvmv_trafos_dict = {}
    lvgen_idx = 0
    lv_gen_dict = {}
    lvcd_idx = 0
    lv_cd_dict = {}
    lvstations_idx = 0
    mvlv_stations_dict = {}
    lvtrafos_idx = 0
    mvlv_trafos_dict = {}
    areacenter_idx = 0
    areacenter_dict = {}
    lines_idx = 0
    lines_dict = {}
    LVMVmapping_idx = 0
    mvlv_mapping_dict = {}

    def aggregate_generators(gen, aggr):
        """Aggregate generation capacity per voltage level
        Parameters
        ----------
        gen: ding0.core.GeneratorDing0
            Ding0 Generator object
        aggr: dict
            Aggregated generation capacity. For structure see
            `_determine_aggregated_nodes()`.
        Returns
        -------
        """

        if gen.v_level not in aggr['generation']:
            aggr['generation'][gen.v_level] = {}
        if gen.type not in aggr['generation'][gen.v_level]:
            aggr['generation'][gen.v_level][gen.type] = {}
        if gen.subtype not in aggr['generation'][gen.v_level][gen.type]:
            aggr['generation'][gen.v_level][gen.type].update(
                {gen.subtype: {'ids': [gen.id_db],
                               'capacity': gen.capacity}})
        else:
            aggr['generation'][gen.v_level][gen.type][gen.subtype][
                'ids'].append(gen.id_db)
            aggr['generation'][gen.v_level][gen.type][gen.subtype][
                'capacity'] += gen.capacity

        return aggr

    def aggregate_loads(la_center, aggr):
        """Aggregate consumption in load area per sector
        Parameters
        ----------
        la_center: LVLoadAreaCentreDing0
            Load area center object from Ding0
        Returns
        -------
        """
        for s in ['retail', 'industrial', 'agricultural', 'residential']:
            if s not in aggr['load']:
                aggr['load'][s] = {}

            for t in ['nominal','peak']:
                if t not in aggr['load'][s]:
                    aggr['load'][s][t] = 0

        aggr['load']['retail']['nominal'] += sum(
            [_.sector_consumption_retail
             for _ in la_center.lv_load_area._lv_grid_districts])
        aggr['load']['industrial']['nominal'] += sum(
            [_.sector_consumption_industrial
             for _ in la_center.lv_load_area._lv_grid_districts])
        aggr['load']['agricultural']['nominal'] += sum(
            [_.sector_consumption_agricultural
             for _ in la_center.lv_load_area._lv_grid_districts])
        aggr['load']['residential']['nominal'] += sum(
            [_.sector_consumption_residential
             for _ in la_center.lv_load_area._lv_grid_districts])

        aggr['load']['retail']['peak'] += sum(
            [_.peak_load_retail
             for _ in la_center.lv_load_area._lv_grid_districts])
        aggr['load']['industrial']['peak'] += sum(
            [_.peak_load_industrial
             for _ in la_center.lv_load_area._lv_grid_districts])
        aggr['load']['agricultural']['peak'] += sum(
            [_.peak_load_agricultural
             for _ in la_center.lv_load_area._lv_grid_districts])
        aggr['load']['residential']['peak'] += sum(
            [_.peak_load_residential
             for _ in la_center.lv_load_area._lv_grid_districts])

        return aggr

    for mv_district in nw.mv_grid_districts():

        mv_grid_id = mv_district.mv_grid.id_db
        mv_grid_id_db = '_'.join(
            [str(mv_district.mv_grid.__class__.__name__), 'MV', str(mv_grid_id), str(mv_district.mv_grid.id_db)])

        if mv_info:
            lv_grid_id = 0

            # MV-grid
            # ToDo: geom <- Polygon
            mvgrid_idx += 1
            mv_grid_dict[mvgrid_idx] = {
                'MV_grid_id': mv_district.mv_grid.id_db,
                'id_db': '_'.join([str(mv_district.mv_grid.__class__.__name__), 'MV', str(mv_grid_id),
                                   str(mv_district.mv_grid.id_db)]),
                # 'network': mv_district.mv_grid.network,
                'geom': wkt_dumps(mv_district.geo_data),
                'population':  # None,
                    sum([_.zensus_sum
                         for _ in
                         mv_district._lv_load_areas  # ding0_grid.grid_district._lv_load_areas
                         if not np.isnan(_.zensus_sum)]),
                'voltage_nom': mv_district.mv_grid.v_level,  # in kV
                'run_id': run_id
            }

            # id_db: Classname_MV/LV_mvgridid/lvgridid_id
            # excemptions: class LVStations: LVStationDing0_MV_mvgridid_id(=lvgridid)

            # MVGrid
            for node in mv_district.mv_grid.graph_nodes_sorted():
                geom = wkt_dumps(node.geo_data)
                # geom = from_shape(Point(node.geo_data), srid=srid)
                db_id = node.id_db

                # LVStation
                if isinstance(node, LVStationDing0):
                    if not node.lv_load_area.is_aggregated:
                        lvstations_idx += 1
                        mvlv_stations_dict[lvstations_idx] = {
                            'id_db': '_'.join([str(node.__class__.__name__), 'MV', str(mv_grid_id), str(node.id_db)]),
                            'LV_grid_id_db': '_'.join(['LVGridDing0', 'LV', str(node.id_db), str(node.id_db)]),
                            'geom': geom,
                            'run_id': run_id,
                        }

                        # LV-MV mapping
                        LVMVmapping_idx += 1
                        mvlv_mapping_dict[LVMVmapping_idx] = {
                            'MV_grid_id': mv_grid_id,
                            'MV_grid_id_db': mv_grid_id_db,
                            'LV_grid_id': node.id_db,
                            'LV_grid_id_db': '_'.join(['LVGridDing0', 'LV', str(node.id_db), str(node.id_db)]),
                            'run_id': run_id,
                        }

                        # Trafos LV
                        for t in node.transformers():
                            lvtrafos_idx += 1
                            mvlv_trafos_dict[lvtrafos_idx] = {
                                'id_db': '_'.join([str(t.__class__.__name__), 'LV', str(mv_grid_id), str(node.id_db)]),
                                'geom': geom,
                                'LV_grid_id_db': '_'.join(['LVGridDing0', 'LV', str(node.id_db), str(node.id_db)]),
                                'voltage_op': t.v_level,
                                'S_nom': t.s_max_a,
                                'X': t.x,
                                'R': t.r,
                                'run_id': run_id,
                            }

                # MVStation
                elif isinstance(node, MVStationDing0):
                    mvstations_idx += 1
                    hvmv_stations_dict[mvstations_idx] = {
                        'id_db': '_'.join([str(node.__class__.__name__), 'MV', str(mv_grid_id), str(node.id_db)]),
                        'MV_grid_id_db': mv_grid_id_db,
                        'geom': geom,
                        'run_id': run_id,
                    }

                    # Trafos MV
                    for t in node.transformers():
                        mvtrafos_idx += 1
                        hvmv_trafos_dict[mvtrafos_idx] = {
                            'id_db': '_'.join([str(t.__class__.__name__), 'MV', str(mv_grid_id), str(node.id_db)]),
                            'geom': geom,
                            'MV_grid_id_db': mv_grid_id_db,
                            'voltage_op': t.v_level,
                            'S_nom': t.s_max_a,
                            'X': t.x,
                            'R': t.r,
                            'run_id': run_id,
                        }

                # MVGenerator
                elif isinstance(node, GeneratorDing0):
                    if node.subtype == None:
                        subtype = 'other'
                    else:
                        subtype = node.subtype
                    type = node.type
                    mvgen_idx += 1
                    mv_gen_dict[mvgen_idx] = {
                        'id_db': '_'.join([str(node.__class__.__name__), 'MV', str(mv_grid_id), str(node.id_db)]),
                        'MV_grid_id_db': mv_grid_id_db,
                        'geom': geom,
                        'type': type,
                        'subtype': subtype,
                        'v_level': node.v_level,
                        'nominal_capacity': node.capacity,
                        'run_id': run_id,
                        'is_aggregated': False,
                    }

                # MVBranchTees
                elif isinstance(node, MVCableDistributorDing0):
                    mvcd_idx += 1
                    mv_cd_dict[mvcd_idx] = {
                        'id_db': '_'.join([str(node.__class__.__name__), 'MV', str(mv_grid_id), str(node.id_db)]),
                        'MV_grid_id_db': mv_grid_id_db,
                        'geom': geom,
                        'run_id': run_id,
                    }

                # LoadAreaCentre
                elif isinstance(node, LVLoadAreaCentreDing0):

                    # type = 'Load area center of aggregated load area'

                    areacenter_idx += 1
                    aggr_lines = 0

                    aggr = {'generation': {}, 'load': {}, 'aggregates': []}

                    # Determine aggregated generation in LV grid
                    for lvgd in node.lv_load_area._lv_grid_districts:

                        for aggr_gen in lvgd.lv_grid.generators():
                            aggr = aggregate_generators(aggr_gen, aggr)

                            if aggr_gen.subtype == None:
                                subtype = 'other'
                            else:
                                subtype = aggr_gen.subtype
                            type = aggr_gen.type

                    # Determine aggregated load in MV grid
                    # -> Implement once loads in Ding0 MV grids exist

                    # Determine aggregated load in LV grid
                    aggr = aggregate_loads(node, aggr)

                    # Collect metadata of aggregated load areas
                    aggr['aggregates'] = {
                        'population': node.lv_load_area.zensus_sum,
                        'geom': node.lv_load_area.geo_area}
                    aggr_line_type = nw._static_data['MV_cables'].iloc[
                        nw._static_data['MV_cables']['I_max_th'].idxmax()]
                    geom = wkt_dumps(node.lv_load_area.geo_area)

                    for aggr_node in aggr:
                        if aggr_node == 'generation':
                            mvgenaggr_idx = 0

                            for v_level in aggr['generation']:
                                for type in aggr['generation'][v_level]:
                                    for subtype in aggr['generation'][v_level][type]:
                                        mvgen_idx += 1
                                        mvgenaggr_idx += 1
                                        mv_gen_dict[mvgen_idx] = {
                                            'id_db': '_'.join(
                                                [str(aggr_gen.__class__.__name__), 'MV', str(mv_grid_id),
                                                 str(aggr_gen.id_db), str(mvgenaggr_idx)]),  # , str(mvgen_idx)
                                            'MV_grid_id_db': mv_grid_id_db,
                                            'geom': geom,#from_shape(Point(mv_district.mv_grid.station().geo_data), srid=srid),#lv_load_area.geo_area,#geom, #?? Polygon # hvmv_stations_dict[mvstations_idx]['geom'], #
                                            'type': type,
                                            'subtype': subtype,
                                            'v_level': v_level,
                                            'nominal_capacity': aggr['generation'][v_level][type][subtype]['capacity'],
                                            'is_aggregated': True,
                                            'run_id': run_id,
                                        }

                                        lines_idx += 1
                                        aggr_lines += 1
                                        lines_dict[lines_idx] = {
                                            # ToDo: Rename edge_name
                                            'edge_name': '_'.join(
                                                [str(mv_grid_id), 'aggr', str(node.lv_load_area.id_db),
                                                 str(aggr_lines)]),
                                            # , 'vlevel', str(v_level), 'subtype', str(subtype)]),#}'.format(v_level=v_level, subtype=subtype),
                                            'grid_id_db': mv_grid_id_db,
                                            # ToDo: read type_name from aggr_line_type
                                            'type_name': 'NA2XS2Y 3x1x500 RM/35',  # aggr_line_type.name,
                                            'type_kind': 'cable',  # branch['branch'].kind,
                                            'length': 1,
                                            'U_n': aggr_line_type.U_n,
                                            'I_max_th': aggr_line_type.I_max_th,
                                            'R': aggr_line_type.R,
                                            'L': aggr_line_type.L,
                                            'C': aggr_line_type.C,
                                            'node1': '_'.join(
                                                [str(aggr_gen.__class__.__name__), 'MV', str(mv_grid_id),
                                                 str(aggr_gen.id_db), str(mvgenaggr_idx)]),
                                            'node2': '_'.join([
                                                'MVStationDing0', 'MV', str(mv_grid_id), str(mv_grid_id)]),
                                            'run_id': run_id,
                                        }

                        elif aggr_node == 'load':
                            for type in aggr['load']:
                                mvloads_idx += 1
                                mv_loads_dict[mvloads_idx] = {
                                    'id_db': '_'.join(
                                        ['AggregatedLoad', 'MV', str(mv_grid_id), str(mvloads_idx)]),
                                    'MV_grid_id_db': mv_grid_id_db,
                                    'geom': geom,
                                    # from_shape(Point(mv_district.mv_grid.station().geo_data), srid=srid),
                                    'consumption_{}'.format(type): aggr['load'][type]['nominal'],
                                    'is_aggregated': True,
                                    'run_id': run_id,
                                }

                                lines_idx += 1
                                aggr_lines += 1
                                lines_dict[lines_idx] = {
                                    # ToDo: Rename edge_name
                                    'edge_name': '_'.join(
                                        [str(mv_grid_id), 'aggr', str(node.lv_load_area.id_db), str(aggr_lines)]),
                                    # 'edge_name': '_'.join(
                                    #    ['line_aggr_load', str(node.lv_load_area), 'vlevel', str(v_level),
                                    #     'subtype', str(subtype)]),  # }'.format(v_level=v_level, subtype=subtype),
                                    'grid_id_db': mv_grid_id_db,
                                    # ToDo: read type_name from aggr_line_type
                                    'type_name': 'NA2XS2Y 3x1x500 RM/35',  # aggr_line_type.name,
                                    'type_kind': 'cable',  # branch['branch'].kind,
                                    # 'type': aggr_line_type,
                                    'length': 1e-3,  # in km
                                    'U_n': aggr_line_type.U_n,
                                    'I_max_th': aggr_line_type.I_max_th,
                                    'R': aggr_line_type.R,
                                    'L': aggr_line_type.L,
                                    'C': aggr_line_type.C,
                                    'node1': '_'.join(
                                        ['AggregatedLoad', 'MV', str(mv_grid_id), str(mvloads_idx)]),
                                    'node2': '_'.join([
                                        'MVStationDing0', 'MV', str(mv_grid_id), str(mv_grid_id)]),
                                    'run_id': run_id,
                                }

                    # areacenter_dict[areacenter_idx] = {
                    #    'id_db': '_'.join([str(node.__class__.__name__), 'MV', str(mv_grid_id), str(node.id_db)]),#node.id_db,
                    #    'MV_grid_id':node.grid,
                    #    'geom':node.geo_data,
                    #    'lv_load_area': node.lv_load_area,
                    #    'run_id': run_id,#

                    # }

                # DisconnectingPoints
                elif isinstance(node, CircuitBreakerDing0):
                    mvcb_idx += 1
                    mvcb_dict[mvcb_idx] = {
                        'id_db': '_'.join([str(node.__class__.__name__), 'MV', str(mv_grid_id), str(node.id_db)]),
                        'MV_grid_id': mv_grid_id,
                        'MV_grid_id_db': mv_grid_id_db,
                        'geom': geom,
                        'status': node.status,
                        'run_id': run_id,
                    }
                else:
                    type = 'Unknown'

            # MVedges
            for branch in mv_district.mv_grid.graph_edges():
                # geom = wkt_dumps(node.geo_data)
                geom = from_shape(LineString([branch['adj_nodes'][0].geo_data, branch['adj_nodes'][1].geo_data]),
                                  srid=srid)
                if not any([isinstance(branch['adj_nodes'][0], LVLoadAreaCentreDing0),
                            isinstance(branch['adj_nodes'][1], LVLoadAreaCentreDing0)]):
                    lines_idx += 1
                    lines_dict[lines_idx] = {
                        'edge_name': branch['branch'].id_db,
                        'grid_id_db': mv_grid_id_db,
                        'type_name': branch['branch'].type['name'],
                        'type_kind': branch['branch'].kind,
                        'length': branch['branch'].length / 1e3,
                        'U_n': branch['branch'].type['U_n'],
                        'I_max_th': branch['branch'].type['I_max_th'],
                        'R': branch['branch'].type['R'],
                        'L': branch['branch'].type['L'],
                        'C': branch['branch'].type['C'],
                        'node1': '_'.join([str(branch['adj_nodes'][0].__class__.__name__), 'MV', str(mv_grid_id),
                                           str(branch['adj_nodes'][0].id_db)]),
                        'node2': '_'.join([str(branch['adj_nodes'][1].__class__.__name__), 'MV', str(mv_grid_id),
                                           str(branch['adj_nodes'][1].id_db)]),
                        'run_id': run_id,
                    }

        if lv_info:
            for LA in mv_district.lv_load_areas():
                for lv_district in LA.lv_grid_districts():
                    if not lv_district.lv_grid.grid_district.lv_load_area.is_aggregated:

                        # ding0_grid.grid_district._lv_load_areas._lv_grid_districts    _.lv_grid
                        # LV-grid
                        # ToDo: geom <- Polygon
                        lvgrid_idx += 1
                        lv_grid_dict[lvgrid_idx] = {
                            'LV_grid_id': lv_district.lv_grid.id_db,
                            'id_db': '_'.join(
                                [str(lv_district.lv_grid.__class__.__name__), 'LV', str(lv_district.lv_grid.id_db),
                                 str(lv_district.lv_grid.id_db)]),
                            'geom': wkt_dumps(lv_district.geo_data),
                            'population': lv_district.population,
                            'voltage_nom': lv_district.lv_grid.v_level / 1e3,
                            'run_id': run_id
                        }

                        lv_grid_id = lv_district.lv_grid.id_db
                        lv_grid_id_db = '_'.join(
                            [str(lv_district.lv_grid.__class__.__name__), 'LV', str(lv_district.lv_grid.id_db),
                             str(lv_district.lv_grid.id_db)])

                        # geom = from_shape(Point(lv_district.lv_grid.station().geo_data), srid=srid)
                        # geom = wkt_dumps(lv_district.geo_data)# lv_grid.station() #ding0_lv_grid.grid_district.geo_data
                        for node in lv_district.lv_grid.graph_nodes_sorted():
                            # geom = wkt_dumps(node.geo_data)

                            # LVGenerator
                            if isinstance(node, GeneratorDing0):
                                if node.subtype == None:
                                    subtype = 'other'
                                else:
                                    subtype = node.subtype
                                type = node.type
                                lvgen_idx += 1
                                lv_gen_dict[lvgen_idx] = {
                                    'id_db': '_'.join(
                                        [str(node.__class__.__name__), 'LV', str(lv_grid_id), str(node.id_db)]),
                                    'LV_grid_id_db': lv_grid_id_db,
                                    'geom': wkt_dumps(node.geo_data),
                                    'type': type,
                                    'subtype': subtype,
                                    'v_level': node.v_level,
                                    'nominal_capacity': node.capacity,
                                    'run_id': run_id,
                                }

                            # LVcd
                            elif isinstance(node, LVCableDistributorDing0):
                                lvcd_idx += 1
                                lv_cd_dict[lvcd_idx] = {
                                    'id_db': '_'.join(
                                        [str(node.__class__.__name__), 'LV', str(lv_grid_id), str(node.id_db)]),
                                    'LV_grid_id_db': lv_grid_id_db,
                                    'geom': None,
                                    # wkt_dumps(lv_district.geo_data),#wkt_dumps(node.geo_data), Todo: why no geo_data?
                                    'run_id': run_id,
                                }

                            # LVload
                            elif isinstance(node, LVLoadDing0):
                                consumption_dict = {}
                                for k in ['residential', 'retail', 'agricultural', 'industrial']:
                                    if k in node.consumption.keys():
                                        consumption_dict[k] = node.consumption[k]
                                    else:
                                        consumption_dict[k] = None
                                lvloads_idx += 1
                                lv_loads_dict[lvloads_idx] = {
                                    'id_db': '_'.join(
                                        [str(node.__class__.__name__), 'LV', str(lv_grid_id), str(node.id_db)]),
                                    'LV_grid_id_db': lv_grid_id_db,
                                    'geom': None,#wkt_dumps(lv_district.geo_data),#wkt_dumps(node.geo_data), Todo: why no geo_data?
                                    # 'consumption': json.dumps(node.consumption),
                                    'consumption_residential': consumption_dict['residential'],
                                    'consumption_retail': consumption_dict['retail'],
                                    'consumption_agricultural': consumption_dict['agricultural'],
                                    'consumption_industrial': consumption_dict['industrial'],
                                    'run_id': run_id,
                                }
                                del consumption_dict

                            else:
                                type = 'Unknown'

                        # LVedges
                        for branch in lv_district.lv_grid.graph_edges():
    #                        geom = from_shape(
    #                            LineString([branch['adj_nodes'][0].geo_data, branch['adj_nodes'][1].geo_data]), srid=srid)
                            if not any([isinstance(branch['adj_nodes'][0], LVLoadAreaCentreDing0),
                                        isinstance(branch['adj_nodes'][1], LVLoadAreaCentreDing0)]):
                                lines_idx += 1
                                lines_dict[lines_idx] = {
                                    'edge_name': branch['branch'].id_db,
                                    'grid_id_db': lv_grid_id_db,
                                    'type_name': branch['branch'].type.to_frame().columns[0],
                                    'type_kind': branch['branch'].kind,
                                    'length': branch['branch'].length / 1e3,  # length in km
                                    'U_n': branch['branch'].type['U_n'] / 1e3,  # U_n in kV
                                    'I_max_th': branch['branch'].type['I_max_th'],
                                    'R': branch['branch'].type['R'],
                                    'L': branch['branch'].type['L'],
                                    'C': branch['branch'].type['C'],
                                    'node1': '_'.join(
                                        [str(branch['adj_nodes'][0].__class__.__name__), 'LV', str(lv_grid_id),
                                         str(branch['adj_nodes'][0].id_db)])
                                    if not isinstance(branch['adj_nodes'][0], LVStationDing0) else '_'.join(
                                        [str(branch['adj_nodes'][0].__class__.__name__), 'MV', str(mv_grid_id),
                                         str(branch['adj_nodes'][0].id_db)]),
                                    'node2': '_'.join(
                                        [str(branch['adj_nodes'][1].__class__.__name__), 'LV', str(lv_grid_id),
                                         str(branch['adj_nodes'][1].id_db)])
                                    if not isinstance(branch['adj_nodes'][1], LVStationDing0) else '_'.join(
                                        [str(branch['adj_nodes'][1].__class__.__name__), 'MV', str(mv_grid_id),
                                         str(branch['adj_nodes'][1].id_db)]),
                                    'run_id': run_id,
                                }

    lv_grid       = pd.DataFrame.from_dict(lv_grid_dict, orient='index')
    lv_gen        = pd.DataFrame.from_dict(lv_gen_dict, orient='index')
    lv_cd         = pd.DataFrame.from_dict(lv_cd_dict, orient='index')
    mvlv_stations = pd.DataFrame.from_dict(mvlv_stations_dict, orient='index')
    mvlv_trafos   = pd.DataFrame.from_dict(mvlv_trafos_dict, orient='index')
    lv_loads      = pd.DataFrame.from_dict(lv_loads_dict, orient='index')
    mv_grid       = pd.DataFrame.from_dict(mv_grid_dict, orient='index')
    mv_gen        = pd.DataFrame.from_dict(mv_gen_dict, orient='index')
    # mv_cb         = pd.DataFrame.from_dict(mvcb_dict, orient='index')
    mv_cd         = pd.DataFrame.from_dict(mv_cd_dict, orient='index')
    hvmv_stations = pd.DataFrame.from_dict(hvmv_stations_dict, orient='index')
    # mv_areacenter= pd.DataFrame.from_dict(areacenter_dict, orient='index')
    hvmv_trafos   = pd.DataFrame.from_dict(hvmv_trafos_dict, orient='index')
    mv_loads      = pd.DataFrame.from_dict(mv_loads_dict, orient='index')
    lines         = pd.DataFrame.from_dict(lines_dict, orient='index')
    mvlv_mapping  = pd.DataFrame.from_dict(mvlv_mapping_dict, orient='index')

    lines = lines[sorted(lines.columns.tolist())]

    return run_id, lv_grid, lv_gen, lv_cd, mvlv_stations, mvlv_trafos, lv_loads, mv_grid, mv_gen, mv_cd, \
           hvmv_stations, hvmv_trafos, mv_loads, lines, mvlv_mapping

Response: Exports all nodes and lines of a network nw as DataFrames


Instruction:
Can you generate a brief explanation for the following Python 3 code
def ding0_exemplary_plots(stats, base_path=BASEPATH):

    # make some plot
    plotpath = os.path.join(base_path, 'plots')
    results.plot_cable_length(stats, plotpath)
    plt.show()
    results.plot_generation_over_load(stats, plotpath)
    plt.show()

Response: A function that performs the actual Ding0 exemplary plotting of the grid districts.


Instruction:
Can you generate the documentation for the following Python 3 function
def nd_load_and_stats(filenames, base_path=BASEPATH):

    # load Ding0 data
    nds = []
    for filename in filenames:
        try:
            nd_load = results.load_nd_from_pickle(filename=
                                                  os.path.join(base_path,
                                                               'grids',
                                                               filename))

            nds.append(nd_load)
        except:
            print("File {mvgd} not found. It was maybe excluded by Ding0 or "
                  "just forgotten to generate by you...".format(mvgd=filename))

    nd = nds[0]

    for n in nds[1:]:
        nd.add_mv_grid_district(n._mv_grid_districts[0])

    # get statistical numbers about grid
    stats = results.calculate_mvgd_stats(nd)

    return stats

Response: Loads multiple files from disk and generates stats for each MV grid district.


Instruction:
Can you implement a function in Python 3 that
reinforces cables that are not in the current level.

Response:
def reinforce_branches_current(grid, crit_branches):
    #TODO: finish docstring
    """ Reinforce MV or LV grid by installing a new branch/line type

    Parameters
    ----------
    grid : GridDing0
        Grid identifier.
    crit_branches : dict
        Dict of critical branches with max. relative overloading.

    Notes
    -----
    The branch type to be installed is determined per branch using the rel. overloading. According to [#]_
    only cables are installed.

    References
    ----------
    .. [#] Ackermann et al. (RP VNS)

    See Also
    --------
    ding0.flexopt.check_tech_constraints.check_load :
    ding0.flexopt.reinforce_measures.reinforce_branches_voltage :
    """
    # load cable data, file_names and parameter
    branch_parameters = grid.network.static_data['MV_cables']
    branch_parameters = branch_parameters[branch_parameters['U_n'] == grid.v_level].sort_values('I_max_th')

    branch_ctr = 0

    for branch, rel_overload in crit_branches.items():
        try:
            type = branch_parameters.loc[
                branch_parameters[
                    branch_parameters['I_max_th'] >= branch['branch']
                    .type['I_max_th'] * rel_overload
                ].loc[
                    :, 'I_max_th'
                ].idxmin(), :
            ]
            branch['branch'].type = type
            branch_ctr += 1
        except:
            logger.warning('Branch {} could not be reinforced (current '
                           'issues) as there is no appropriate cable type '
                           'available. Original type is retained.'.format(
                branch))
            pass

    if branch_ctr:
        logger.info('==> {} branches were reinforced.'.format(str(branch_ctr)))


Instruction:
Can you write a function in Python 3 where it
reinforces cables that are at least voltage for a given grid level.

Response:
def reinforce_branches_voltage(grid, crit_branches, grid_level='MV'):
    #TODO: finish docstring
    """ Reinforce MV or LV grid by installing a new branch/line type

    Parameters
    ----------
    grid : GridDing0
        Grid identifier.
    crit_branches : :any:`list` of :obj:`int`
        List of critical branches. #TODO: check if a list or a dictionary
    grid_level : str
        Specifying either 'MV' for medium-voltage grid or 'LV' for
        low-voltage grid level.

    Notes
    -----
    The branch type to be installed is determined per branch - the next larger cable available is used.
    According to Ackermann only cables are installed.

    See Also
    --------
    ding0.flexopt.check_tech_constraints.check_load :
    ding0.flexopt.reinforce_measures.reinforce_branches_voltage :
    """

    # load cable data, file_names and parameter
    branch_parameters = grid.network.static_data['{gridlevel}_cables'.format(
        gridlevel=grid_level)]
    branch_parameters = branch_parameters[branch_parameters['U_n'] == grid.v_level].sort_values('I_max_th')

    branch_ctr = 0

    for branch in crit_branches:
        try:
            type = branch_parameters.loc[
                branch_parameters.loc[
                       branch_parameters['I_max_th'] > branch.type['I_max_th']
                ].loc[
                    :, 'I_max_th'
                ].idxmin(), :
            ]
            branch.type = type
            branch_ctr += 1
        except:
            logger.warning('Branch {} could not be reinforced (voltage '
                           'issues) as there is no appropriate cable type '
                           'available. Original type is retained.'.format(
                branch))
            pass

    if branch_ctr:
        logger.info('==> {} branches were reinforced.'.format(str(branch_ctr)))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def extend_substation(grid, critical_stations, grid_level):
    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')

    trafo_params = grid.network._static_data['{grid_level}_trafos'.format(
        grid_level=grid_level)]
    trafo_s_max_max = max(trafo_params['S_nom'])

    for station in critical_stations:
        # determine if load or generation case and apply load factor
        if station['s_max'][0] > station['s_max'][1]:
            case = 'load'
            lf_lv_trans_normal = load_factor_lv_trans_lc_normal
        else:
            case = 'gen'
            lf_lv_trans_normal = load_factor_lv_trans_fc_normal

        # cumulative maximum power of transformers installed
        s_max_trafos = sum([_.s_max_a
                            for _ in station['station']._transformers])

        # determine missing trafo power to solve overloading issue
        s_trafo_missing = max(station['s_max']) - (
            s_max_trafos * lf_lv_trans_normal)

        # list of trafos with rated apparent power below `trafo_s_max_max`
        extendable_trafos = [_ for _ in station['station']._transformers
                             if _.s_max_a < trafo_s_max_max]

        # try to extend power of existing trafos
        while (s_trafo_missing > 0) and extendable_trafos:
            # only work with first of potentially multiple trafos
            trafo = extendable_trafos[0]
            trafo_s_max_a_before = trafo.s_max_a

            # extend power of first trafo to next higher size available
            extend_trafo_power(extendable_trafos, trafo_params)

            # diminish missing trafo power by extended trafo power and update
            # extendable trafos list
            s_trafo_missing -= ((trafo.s_max_a * lf_lv_trans_normal) -
                                trafo_s_max_a_before)
            extendable_trafos = [_ for _ in station['station']._transformers
                                 if _.s_max_a < trafo_s_max_max]

        # build new trafos inside station until
        if s_trafo_missing > 0:
            trafo_type, trafo_cnt = select_transformers(grid, s_max={
                's_max': s_trafo_missing,
                'case': case
            })

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

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

    logger.info("{stations_cnt} have been reinforced due to overloading "
                "issues.".format(stations_cnt=len(critical_stations)))

Response: Extend existing substation by exchanging existing trafo and installing parallel one if necessary.


Instruction:
How would you explain what the following Python 3 function does
def extend_substation_voltage(crit_stations, grid_level='LV'):
    grid = crit_stations[0]['node'].grid
    trafo_params = grid.network._static_data['{grid_level}_trafos'.format(
        grid_level=grid_level)]
    trafo_s_max_max = max(trafo_params['S_nom'])
    trafo_min_size = trafo_params.loc[trafo_params['S_nom'].idxmin(), :]

    v_diff_max_fc = cfg_ding0.get('assumptions', 'lv_max_v_level_fc_diff_normal')
    v_diff_max_lc = cfg_ding0.get('assumptions', 'lv_max_v_level_lc_diff_normal')

    tree = nx.dfs_tree(grid._graph, grid._station)

    for station in crit_stations:
        v_delta = max(station['v_diff'])

        # get list of nodes of main branch in right order
        extendable_trafos = [_ for _ in station['node']._transformers
                             if _.s_max_a < trafo_s_max_max]

        v_delta_initially_lc = v_delta[0]
        v_delta_initially_fc = v_delta[1]

        new_transformers_cnt = 0

        # extend existing trafo power while voltage issues exist and larger trafos
        # are available
        while (v_delta[0] > v_diff_max_lc) or (v_delta[1] > v_diff_max_fc):
            if extendable_trafos:
                # extend power of first trafo to next higher size available
                extend_trafo_power(extendable_trafos, trafo_params)
            elif new_transformers_cnt < 2:
                # build a new transformer
                lv_transformer = TransformerDing0(
                    grid=grid,
                    id_db=id,
                    v_level=0.4,
                    s_max_longterm=trafo_min_size['S_nom'],
                    r=trafo_min_size['R'],
                    x=trafo_min_size['X'])

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

                new_transformers_cnt += 1

            # update break criteria
            v_delta = get_voltage_at_bus_bar(grid, tree)
            extendable_trafos = [_ for _ in station['node']._transformers
                                 if _.s_max_a < trafo_s_max_max]

            if (v_delta[0] == v_delta_initially_lc) or (
                v_delta[1] == v_delta_initially_fc):
                logger.warning("Extension of {station} has no effect on "
                               "voltage delta at bus bar. Transformation power "
                               "extension is halted.".format(
                    station=station['node']))
                break

Response: Extend substation if voltage issues at MV - LV substation occur.


Instruction:
Explain what the following Python 3 code does
def reinforce_lv_branches_overloading(grid, crit_branches):
    unsolved_branches = []

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

    cables = grid.network.static_data['LV_cables']

    # resolve overloading issues for each branch segment
    for branch in crit_branches:
        I_max_branch_load = branch['s_max'][0]
        I_max_branch_gen = branch['s_max'][1]
        I_max_branch = max([I_max_branch_load, I_max_branch_gen])

        suitable_cables = cables[(cables['I_max_th'] * cable_lf)
                          > I_max_branch]

        if not suitable_cables.empty:
            cable_type = suitable_cables.loc[suitable_cables['I_max_th'].idxmin(), :]
            branch['branch'].type = cable_type
            crit_branches.remove(branch)
        else:
            cable_type_max = cables.loc[cables['I_max_th'].idxmax(), :]
            unsolved_branches.append(branch)
            branch['branch'].type = cable_type_max
            logger.error("No suitable cable type could be found for {branch} "
                          "with I_th_max = {current}. "
                          "Cable of type {cable} is chosen during "
                          "reinforcement.".format(
                branch=branch['branch'],
                cable=cable_type_max.name,
                current=I_max_branch
            ))

    return unsolved_branches

Response: Reinforce LV branches with line overloading issues.


Instruction:
Implement a function in Python 3 to
extend power of first trafo in list of extendable trafos with rated power below maximum size available.

Response:
def extend_trafo_power(extendable_trafos, trafo_params):
    """
    Extend power of first trafo in list of extendable trafos

    Parameters
    ----------
    extendable_trafos : :any:`list`
        Trafos with rated power below maximum size available trafo
    trafo_params : :pandas:`pandas.DataFrame<dataframe>`
        Transformer parameters
    """
    trafo = extendable_trafos[0]
    trafo_s_max_a_before = trafo.s_max_a
    trafo_nearest_larger = trafo_params.loc[
        trafo_params.loc[
            trafo_params['S_nom'] > trafo_s_max_a_before
        ].loc[
            :, 'S_nom'
        ].idxmin(), :
    ]
    trafo.s_max_a = trafo_nearest_larger['S_nom']
    trafo.r = trafo_nearest_larger['R']
    trafo.x = trafo_nearest_larger['X']


Instruction:
Can you create a Python 3 function that
adds a generator to the _generators and grid graph if not already existing

Response:
def add_generator(self, generator):
        """Adds a generator to _generators and grid graph if not already existing

        Parameters
        ----------
        generator : GridDing0
            Description #TODO

        """
        if generator not in self._generators and isinstance(generator,
                                                            GeneratorDing0):
            self._generators.append(generator)
            self.graph_add_node(generator)


Instruction:
Implement a function in Python 3 to
add a station or cable distributor object to the grid graph if it does not already exist.

Response:
def graph_add_node(self, node_object):
        """Adds a station or cable distributor object to grid graph if not already existing

        Parameters
        ----------
        node_object : GridDing0
            Description #TODO

        """
        if ((node_object not in self._graph.nodes()) and
            (isinstance(node_object, (StationDing0,
                                      CableDistributorDing0,
                                      LVLoadAreaCentreDing0,
                                      CircuitBreakerDing0,
                                      GeneratorDing0)))):
            self._graph.add_node(node_object)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def graph_draw(self, mode):

        g = self._graph

        if mode == 'MV':
            # get draw params from nodes and edges (coordinates, colors, demands, etc.)
            nodes_pos = {}; demands = {}; demands_pos = {}
            nodes_color = []
            for node in g.nodes():
                if isinstance(node, (StationDing0,
                                     LVLoadAreaCentreDing0,
                                     CableDistributorDing0,
                                     GeneratorDing0,
                                     CircuitBreakerDing0)):
                    nodes_pos[node] = (node.geo_data.x, node.geo_data.y)
                    # TODO: MOVE draw/color settings to config
                if node == self.station():
                    nodes_color.append((1, 0.5, 0.5))
                else:
                    #demands[node] = 'd=' + '{:.3f}'.format(node.grid.region.peak_load_sum)
                    #demands_pos[node] = tuple([a+b for a, b in zip(nodes_pos[node], [0.003]*len(nodes_pos[node]))])
                    nodes_color.append((0.5, 0.5, 1))

            edges_color = []
            for edge in self.graph_edges():
                if edge['branch'].critical:
                    edges_color.append((1, 0, 0))
                else:
                    edges_color.append((0, 0, 0))

            plt.figure()
            nx.draw_networkx(g, nodes_pos, node_color=nodes_color, edge_color=edges_color, font_size=8)
            #nx.draw_networkx_labels(g, demands_pos, labels=demands, font_size=8)
            plt.show()

        elif mode == 'LV':
            nodes_pos = {}
            nodes_color = []

            for node in g.nodes():
                # get neighbors of station (=first node of each branch)
                station_neighbors = sorted(
                    g.neighbors(self.station()), key=lambda _: repr(_))

                # set x-offset according to count of branches
                if len(station_neighbors) % 2 == 0:
                    x_pos_start = -(len(station_neighbors) // 2 - 0.5)
                else:
                    x_pos_start = -(len(station_neighbors) // 2)

                # set positions
                if isinstance(node, CableDistributorDing0):
                    if node.in_building:
                        nodes_pos[node] = (x_pos_start + node.branch_no - 1 + 0.25, -node.load_no - 2)
                        nodes_color.append((0.5, 0.5, 0.5))
                    else:
                        nodes_pos[node] = (x_pos_start + node.branch_no - 1, -node.load_no - 2)
                        nodes_color.append((0.5, 0.5, 0.5))

                elif isinstance(node, LoadDing0):
                    nodes_pos[node] = (x_pos_start + node.branch_no - 1 + 0.5, -node.load_no - 2 - 0.25)
                    nodes_color.append((0.5, 0.5, 1))
                elif isinstance(node, GeneratorDing0):
                    # get neighbor of geno
                    neighbor = list(g.neighbors(node))[0]

                    # neighbor is cable distributor of building
                    if isinstance(neighbor, CableDistributorDing0):
                        nodes_pos[node] = (x_pos_start + neighbor.branch_no - 1 + 0.5, -neighbor.load_no - 2 + 0.25)
                    else:
                        nodes_pos[node] = (1,1)

                    nodes_color.append((0.5, 1, 0.5))
                elif isinstance(node, StationDing0):
                    nodes_pos[node] = (0, 0)
                    nodes_color.append((1, 0.5, 0.5))

            plt.figure()
            nx.draw_networkx(g, nodes_pos, node_color=nodes_color, font_size=8, node_size=100)
            plt.show()

Response: Draws the grid graph using networkx


Instruction:
Can you generate a brief explanation for the following Python 3 code
def graph_nodes_sorted(self):
        return sorted(self._graph.nodes(), key=lambda _: repr(_))

Response: Returns an ordered list of graph s nodes.


Instruction:
Write a Python 3 script for
returning a list of nodes that are connected by branch

Response:
def graph_nodes_from_branch(self, branch):
        """ Returns nodes that are connected by `branch`

        Args
        ----
        branch: BranchDing0
            Description #TODO

        Returns
        -------
        (:obj:`GridDing0`, :obj:`GridDing0`)
            2-tuple of nodes (Ding0 objects) #TODO:Check
        """
        edges = nx.get_edge_attributes(self._graph, 'branch')
        nodes = list(edges.keys())[list(edges.values()).index(branch)]
        return nodes


Instruction:
How would you implement a function in Python 3 that
returns a list of branches that are connected to a node.

Response:
def graph_branches_from_node(self, node):
        """ Returns branches that are connected to `node`

        Args
        ----
        node: GridDing0
            Ding0 object (member of graph)

        Returns
        -------
        :any:`list`
            List of tuples (node in :obj:`GridDing0`, branch in :obj:`BranchDing0`) ::

                (node , branch_0 ),
                ...,
                (node , branch_N ),

        """
        # TODO: This method can be replaced and speed up by using NetworkX' neighbors()

        branches = []
        branches_dict = self._graph.adj[node]
        for branch in branches_dict.items():
            branches.append(branch)
        return sorted(branches, key=lambda _: repr(_))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def graph_edges(self):

        # get edges with attributes
        edges = nx.get_edge_attributes(self._graph, 'branch').items()

        # sort them according to connected nodes
        edges_sorted = sorted(list(edges), key=lambda _: (''.join(sorted([repr(_[0][0]),repr(_[0][1])]))))

        for edge in edges_sorted:
            yield {'adj_nodes': edge[0], 'branch': edge[1]}

Response: Returns a generator for iterating over edges of a graph.


Instruction:
Write a Python 3 script to
determine the shortest path from node_source to node_target in _graph using networkx shortest path algorithm.

Response:
def find_path(self, node_source, node_target, type='nodes'):
        """Determines shortest path

        Determines the shortest path from `node_source` to
        `node_target` in _graph using networkx' shortest path
        algorithm.

        Args
        ----
        node_source: GridDing0
            source node, member of _graph
        node_target: GridDing0
            target node, member of _graph
        type : str
            Specify if nodes or edges should be returned. Default
            is `nodes`

        Returns
        -------
        :any:`list` of :obj:`GridDing0`
            path: shortest path from `node_source` to `node_target` (list of nodes in _graph)

        Notes
        -----
        WARNING: The shortest path is calculated using the count of hops, not the actual line lengths!
        As long as the circuit breakers are open, this works fine since there's only one path. But if
        they are closed, there are 2 possible paths. The result is a path which have min. count of hops
        but might have a longer total path length than the second sone.
        See networkx' function shortest_path() function for details on how the path is calculated.
        """
        if (node_source in self._graph.nodes()) and (node_target in self._graph.nodes()):
            path = nx.shortest_path(self._graph, node_source, node_target)
        else:
            raise Exception('At least one of the nodes is not a member of graph.')
        if type == 'nodes':
            return path
        elif type == 'edges':
            return [_ for _ in self._graph.edges(nbunch=path, data=True)
                    if (_[0] in path and _[1] in path)]
        else:
            raise ValueError('Please specify type as nodes or edges')