Source code for ego.tools.plots

# -*- coding: utf-8 -*-
# Copyright 2016-2018 Europa-Universität Flensburg,
# Flensburg University of Applied Sciences,
# Centre for Sustainable Energy Systems
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# File description
"""Module which collects useful functions for plotting eTraGo, eDisGo and
eGo results.
"""

import numpy as np
import pandas as pd
import os
geopandas = True

if not 'READTHEDOCS' in os.environ:
    from etrago.tools.plot import (plot_line_loading, plot_stacked_gen,
                                   add_coordinates, curtailment, gen_dist,
                                   storage_distribution,
                                   plot_voltage, plot_residual_load, coloring)
    from ego.tools.economics import etrago_convert_overnight_cost
    from ego.tools.utilities import open_oedb_session
    from pypsa import Network as PyPSANetwork
    import pyproj as proj
    from math import sqrt, log10
    from shapely.geometry import Polygon, Point, MultiPolygon
    from geoalchemy2 import *
    try:
        import geopandas as gpd
        import folium
        from folium import plugins
        from folium.plugins import FloatImage
        from folium.features import CustomIcon
        import branca.colormap as cm
    except:
        geopandas = False
    import oedialect
    import webbrowser
    import subprocess
    from egoio.db_tables.model_draft import (
        EgoGridMvGriddistrict, RenpassGisParameterRegion)
    from egoio.db_tables.grid import EgoDpMvGriddistrict
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    import matplotlib.colors as mcolors

import logging
logger = logging.getLogger('ego')

__copyright__ = "Flensburg University of Applied Sciences, Europa-Universität"\
    "Flensburg, Centre for Sustainable Energy Systems"
__license__ = "GNU Affero General Public License Version 3 (AGPL-3.0)"
__author__ = "wolfbunke"


# plot colore of Carriers
[docs]def carriers_colore(): """ Return matplotlib colore set per carrier (technologies of generators) of eTraGo. Returns ------- colors : :obj:`dict` List of carriers and matplotlib colores """ colors = {'biomass': 'green', 'coal': 'k', 'gas': 'orange', 'eeg_gas': 'olive', 'geothermal': 'purple', 'lignite': 'brown', 'oil': 'darkgrey', 'other_non_renewable': 'pink', 'reservoir': 'navy', 'run_of_river': 'aqua', 'pumped_storage': 'steelblue', 'solar': 'yellow', 'uranium': 'lime', 'waste': 'sienna', 'wind': 'skyblue', 'slack': 'pink', 'load shedding': 'red', 'nan': 'm', 'imports': 'salmon', '': 'm'} return colors
[docs]def ego_colore(): """ Get the four eGo colores Returns ------- colors : :obj:`dict` List of eGo matplotlib hex colores """ colors = {'egoblue1': '#1F567D', 'egoblue2': '#84A2B8', 'egoblue3': '#A3B9C9', 'egoblue4': '#C7D5DE' } return colors
[docs]def plot_storage_expansion(ego, filename=None, dpi=300, column='overnight_costs', scaling=1): """ Plot line expantion Parameters ---------- ego : :class:`ego.tools.io.eGo` eGo ``eGo`` inclueds eTraGo and eDisGo results filename: str Filename and/or path of location to store graphic dpi: int dpi value of graphic column: str column name of eTraGo's line costs. Default: ``overnight_costs`` in EURO. Also available ``s_nom_expansion`` in MVA or annualized ``investment_costs`` in EURO scaling: numeric Factor to scale storage size of bus_sizes Returns ------- plot :obj:`matplotlib.pyplot.show` https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show """ network = ego.etrago.network json_file = ego.json_file # get storage values if 'storage' in ego.json_file['eTraGo']['extendable']: storage_inv = network.storage_units[network.storage_units. capital_cost > 0.] storage_inv['investment_costs'] = (storage_inv.capital_cost * storage_inv.p_nom_opt) storage_inv['overnight_costs'] = etrago_convert_overnight_cost( storage_inv['investment_costs'], json_file) msd_max = storage_inv[column].max() msd_median = storage_inv[column].median() msd_min = storage_inv[column].min() if (msd_max - msd_min) > 1.e+5: if msd_max != 0: LabelVal = int(log10(msd_max)) else: LabelVal = 0 if LabelVal < 0: LabelUnit = '€' msd_max, msd_median, msd_min = msd_max * \ 1000, msd_median * 1000, msd_min * 1000 storage_inv[column] = storage_inv[column] * 1000 elif LabelVal < 3: LabelUnit = 'k €' else: LabelUnit = 'M €' msd_max, msd_median, msd_min = msd_max / \ 1000, msd_median / 1000, msd_min / 1000 storage_inv[column] = storage_inv[column] / 1000 else: LabelUnit = '€' # start plotting figsize = 6, 6 fig, ax = plt.subplots(1, 1, figsize=(figsize)) bus_sizes = storage_inv[column] * scaling if column == 'investment_costs': title = 'Annualized Storage costs per timestep' ltitel = 'Storage costs' if column == 'overnight_costs': title = 'Total Expansion Costs Overnight' ltitel = 'Storage costs' if column == 'p_nom_opt': title = 'Storage Expansion in MVA' ltitel = 'Storage size' LabelUnit = 'kW' if column not in ['investment_costs', 'overnight_costs', 'p_nom_opt']: title = 'unknown' ltitel = 'unknown' LabelUnit = 'unknown' if sum(storage_inv[column]) == 0: sc = network.plot(bus_sizes=0, ax=ax, title="No storage expantion") else: sc = network.plot( bus_sizes=bus_sizes, bus_colors='g', # bus_cmap= # line_colors='gray', title=title, line_widths=0.3 ) ax.set_alpha(0.4) # add legend for area in [msd_max, msd_median, msd_min]: plt.scatter([], [], c='white', s=area * scaling, label='= ' + str(round(area, 0)) + LabelUnit + ' ') plt.legend(scatterpoints=1, labelspacing=1, title=ltitel, loc='upper left', shadow=True, fontsize='x-large') ax.autoscale(tight=True) if filename is None: plt.show() else: fig = ax.get_figure() fig.set_size_inches(10, 8, forward=True) fig.savefig(filename, dpi=dpi) plt.close()
[docs]def plot_line_expansion(ego, filename=None, dpi=300, column='overnight_costs'): """ Plot line expantion Parameters ---------- ego : :class:`ego.tools.io.eGo` eGo ``eGo`` inclueds eTraGo and eDisGo results filename: str Filename and or path of location to store graphic dpi: int dpi value of graphic column: str column name of eTraGo's line costs. Default: ``overnight_costs`` in EUR. Also available ``s_nom_expansion`` in MVA or annualized ``investment_costs`` in EUR Returns ------- plot :obj:`matplotlib.pyplot.show` https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show """ network = ego.etrago.network json_file = ego.json_file # get values if 'network' in ego.json_file['eTraGo']['extendable']: network.lines['s_nom_expansion'] = network.lines.s_nom_opt.subtract( network.lines.s_nom, axis='index') network.lines['investment_costs'] = network.lines.s_nom_expansion.\ multiply(network.lines.capital_cost, axis='index') network.lines['overnight_costs'] = etrago_convert_overnight_cost( network.lines['investment_costs'], json_file) else: network.lines['s_nom_expansion'] = None network.lines['investment_costs'] = None network.lines['overnight_costs'] = None # start plotting figsize = 10, 8 fig, ax = plt.subplots(1, 1, figsize=(figsize)) cmap = plt.cm.jet if column == 's_nom_expansion': line_value = network.lines[column] title = "Line expansion in MVA" if column == 'overnight_costs': line_value = network.lines[column] title = "Total Expansion Costs in € per line" if column == 'investment_costs': line_value = network.lines[column] title = "Annualized Expansion Costs in € per line and time step" line_widths = (line_value/line_value.max()) lc = network.plot(ax=ax, line_colors=line_value, line_cmap=cmap, title=title, line_widths=line_widths) boundaries = [min(line_value), max(line_value)] v = np.linspace(boundaries[0], boundaries[1], 101) print(v.dtype.name) # colorbar cb = plt.colorbar(lc[1], boundaries=v, ticks=v[0:101:10], ax=ax) cb.set_clim(vmin=boundaries[0], vmax=boundaries[1]) if column == 's_nom_expansion': cb.set_label('Expansion in MVA per line') if column == 'overnight_costs': cb.set_label('Total Expansion Costs in € per line') if column == 'investment_costs': cb.set_label('Annualized Expansion Costs in € per line') ax.autoscale(tight=True) if filename is None: plt.show() else: fig = ax.get_figure() fig.set_size_inches(10, 8, forward=True) fig.savefig(filename, dpi=dpi) plt.close()
[docs]def plot_grid_storage_investment(costs_df, filename, display, var=None): """Plot total grid and storage investment. Parameters ---------- costs_df: :pandas:`pandas.DataFrame<dataframe>` Dataframe containing total_investment_costs of ego filename: str Filename and or path of location to store graphic display: bool Display plot var: str Cost variable of ``overnight_cost`` by default displays annualized costs of timesteps Returns ------- plot :obj:`matplotlib.pyplot.show` https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show """ colors = ego_colore() bar_width = 0.35 opacity = 0.4 if var == 'overnight_cost': tic = costs_df[['component', 'overnight_costs', 'voltage_level', 'differentiation']] tic.set_index(['voltage_level', 'component', 'differentiation'], inplace=True) ax = tic.unstack().plot(kind='bar', stacked=False, rot=0, color=([colors.get(key) for key in ['egoblue1', 'egoblue2', 'egoblue4']]), legend=False) ax.set_ylabel("Overnight costs of simulation") ax.set_title("Total costs of simulation, " "voltage level and component", y=1.08) else: tic = costs_df[['component', 'capital_cost', 'voltage_level', 'differentiation']] tic.set_index(['voltage_level', 'component', 'differentiation'], inplace=True) ax = tic.unstack().plot(kind='bar', rot=0, stacked=False, color=([colors.get(key) for key in ['egoblue1', 'egoblue2', 'egoblue3']]), legend=False) ax.set_ylabel("Annualized costs per simulation periods") ax.set_title("Annualized costs per simulation periods, " "voltage level and component", y=1.08) ax.set_xlabel('Voltage level and component') ax.set_yscale("symlog") ax.legend(('cross-border', 'domestic', 'foreign')) ax.autoscale() if display is True: plt.show() else: fig = ax.get_figure() fig.set_size_inches(10, 8, forward=True) fig.savefig(filename, dpi=100) plt.close()
[docs]def power_price_plot(ego, filename, display): """ Plot power price of calculated scenario of timesteps and carrier Parameters ---------- ego : :class:`ego.tools.io.eGo` eGo ``eGo`` inclueds eTraGo and eDisGo results filename: str Filename and or path of location to store graphic display: bool Display plot Returns ------- plot :obj:`matplotlib.pyplot.show` https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show """ plt.rcdefaults() # colors = ego_colore() carrier_colors = coloring() fig, ax = plt.subplots() # plot power_price prc = ego.etrago.generator['power_price'] bar_width = 0.35 opacity = 0.4 ind = np.arange(len(prc.index)) # the x locations for the groups width = 0.35 # the width of the bars: can also be len(x) sequence plt_colors = [carrier_colors[carrier] for carrier in prc.index] # plt_colors = colors['egoblue1'] ax.barh(ind, prc, align='center', color=plt_colors) ax.set_yticks(ind) ax.set_yticklabels(prc.index) ax.invert_yaxis() ax.set_xlabel('Power price in €/MWh') ax.set_title('Power Costs per Carrier') ax.autoscale(tight=True) if display is True: plt.show() else: fig = ax.get_figure() fig.set_size_inches(10, 8, forward=True) fig.savefig(filename, dpi=100)
[docs]def plot_storage_use(ego, filename, display): """Plot storage use by charge and discharge values Parameters ---------- ego : :class:`ego.tools.io.eGo` eGo ``eGo`` inclueds eTraGo and eDisGo results filename: str Filename and or path of location to store graphic display: bool Display plot Returns ------- plot :obj:`matplotlib.pyplot.show` https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show """ colors = ego_colore() ax = ego.etrago.\ storage_charges[['charge', 'discharge']].plot(kind='bar', title="Storage usage", stacked=True, color=([colors.get(key) for key in ['egoblue1', 'egoblue2']]), figsize=( 15, 10), legend=True, fontsize=12) ax.set_xlabel("Kind of Storage", fontsize=12) ax.set_ylabel("Charge and Discharge in MWh", fontsize=12) ax.autoscale(tight=False) if display is True: plt.show() else: fig = ax.get_figure() fig.set_size_inches(10, 8, forward=True) fig.subplots_adjust(bottom=0.25) fig.savefig(filename, dpi=100)
[docs]def get_country(session, region=None): """Get Geometries of scenario Countries. Parameters ---------- session : :sqlalchemy:`sqlalchemy.orm.session.Session<orm/session_basics.html>` SQLAlchemy session to the OEDB region: list List of background countries e.g. ['DE', 'DK'] Returns ------- country: ``geopandas.GeoDataFrame`` GeoDataFrame inclueds MultiPolygon of selected regions or countries """ if region is None: # Define regions 'FR', region = ['DE', 'DK', 'BE', 'LU', 'NO', 'PL', 'CH', 'CZ', 'SE', 'NL'] else: region # get database tabel query = session.query(RenpassGisParameterRegion.gid, RenpassGisParameterRegion.stat_level, RenpassGisParameterRegion.u_region_id, RenpassGisParameterRegion.geom, RenpassGisParameterRegion.geom_point) # get regions by query and filter Regions = [(gid, u_region_id, stat_level, shape.to_shape(geom), shape.to_shape(geom_point)) for gid, u_region_id, stat_level, geom, geom_point in query.filter( RenpassGisParameterRegion.u_region_id. in_(region)).all()] # define SRID crs = {'init': 'epsg:4326'} country = gpd.GeoDataFrame( Regions, columns=['gid', 'stat_level', 'u_region_id', 'geometry', 'point_geom'], crs=crs) return country
[docs]def prepareGD(session, subst_id=None, version=None): """ Get MV grid districts for plotting form oedb. Parameters ---------- session : :sqlalchemy:`sqlalchemy.orm.session.Session<orm/session_basics.html>` SQLAlchemy session to the OEDB subst_id: list List of integer ids of substation of the pf ehv/hv grid model_draft version: str Name of data version saved in the OEDB Returns ------- region: ``geopandas.GeoDataFrame`` GeoDataFrame inclueds MultiPolygon of selected MV grids """ if version: query = session.query(EgoDpMvGriddistrict.subst_id, EgoDpMvGriddistrict.geom) if isinstance(subst_id, list): Regions = [(subst_id, shape.to_shape(geom)) for subst_id, geom in query.filter(EgoDpMvGriddistrict.version == version, EgoDpMvGriddistrict.subst_id.in_( subst_id)).all()] elif subst_id == "all": Regions = [(subst_id, shape.to_shape(geom)) for subst_id, geom in query.filter(EgoDpMvGriddistrict.version == version).all()] else: # ToDo query doesn't looks stable Regions = [(subst_id, shape.to_shape(geom)) for subst_id, geom in query.filter(EgoDpMvGriddistrict.version == version).all()] # toDo add values of sub_id etc. to popup else: # from model_draft query = session.query(EgoGridMvGriddistrict.subst_id, EgoGridMvGriddistrict.geom) Regions = [(subst_id, shape.to_shape(geom)) for subst_id, geom in query.filter(EgoGridMvGriddistrict.subst_id.in_( subst_id)).all()] crs = {'init': 'epsg:3035'} region = gpd.GeoDataFrame( Regions, columns=['subst_id', 'geometry'], crs=crs) region = region.to_crs({'init': 'epsg:4326'}) return region
[docs]def plot_edisgo_cluster(ego, filename, region=['DE'], display=False, dpi=150, add_ehv_storage=False, grid_choice=None, title="", cmap="jet" ): """Plot the Clustering of selected Dingo networks Parameters ---------- ego : :class:`ego.tools.io.eGo` eGo ``eGo`` inclueds on eTraGo and eDisGo results filename: str file name for plot e.g. ``cluster_plot.pdf`` region: list List of background countries e.g. ['DE', 'DK'] display: bool True show plot false print plot as ``filename`` add_ehv_storage: bool Display eTraGo ehv/hv storage distribution grid_choice: str path to seperate mv/lv grid choice csv file title: str Title of Plot cmap: str Name of colormap from https://matplotlib.org/gallery/color/colormap_reference.html Returns ------- plot :obj:`matplotlib.pyplot.show` https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show """ session = ego.session version = ego.json_file['eTraGo']['gridversion'] # get cluster if grid_choice: cluster = pd.read_csv(grid_choice, index_col=0) cluster['represented_grids'] = cluster.apply( lambda x: eval(x['represented_grids']), axis=1) else: cluster = ego.edisgo.grid_choice cluster = cluster.rename( columns={"the_selected_network_id": "subst_id"}) cluster_id = list(cluster.subst_id) # get country Polygon cnty = get_country(session, region=region) # get grid districts singel if ego.json_file['eGo']['eDisGo'] is True: gridcluster = prepareGD(session, cluster_id, version) gridcluster = gridcluster.merge(cluster, on='subst_id') # add percentage of grid representation gridcluster['percentage'] = ((gridcluster.no_of_points_per_cluster / gridcluster.no_of_points_per_cluster.sum())*100) gridcluster['percentage'] = gridcluster['percentage'].astype( float).round(2) # get represented grids repre_grids = pd.DataFrame(columns=['subst_id', 'geometry', 'cluster_id', 'style']) for cluster in gridcluster.index: rep_id = gridcluster.represented_grids[cluster] # represented_grids repre_grid = prepareGD(session, rep_id, version) repre_grid['cluster_id'] = gridcluster.subst_id[cluster] repre_grids = repre_grids.append(repre_grid, ignore_index=True) # add common SRID crs = {'init': 'epsg:4326'} repre_grids = gpd.GeoDataFrame(repre_grids, crs=crs) # get all MV grids bus_id = "all" mvgrids = prepareGD(session, bus_id, version) # start plotting figsize = 5, 5 fig, ax = plt.subplots(1, 1, figsize=(figsize)) cnty.plot(ax=ax, color='white', edgecolor='whitesmoke', alpha=0.5, linewidth=0.1) mvgrids.plot(ax=ax, color='white', alpha=0.1, linewidth=0.1) if ego.json_file['eGo']['eDisGo'] is True: repre_grids.plot(ax=ax, column='cluster_id', cmap=cmap, edgecolor='whitesmoke', linewidth=0.005, alpha=1, legend=False) # subplot gridcluster.plot(ax=ax, column='percentage', cmap=cmap, edgecolor='black', linewidth=1, legend=True) # add storage distribution if add_ehv_storage: _storage_distribution(ego.etrago.network, scaling=1, filename=None, ax=ax, fig=fig) ax.set_title(title) # ax.legend(title="id of cluster representative") ax.tick_params(labelsize=14) # cb = plt.colorbar(ax) # cb.ax.tick_params(labelsize=17) ax.set_ylabel("weighting of MV grid cluster in %", fontsize=17, rotation=270) ax.yaxis.set_label_coords(1.2, 0.5) ax.autoscale(tight=True) if display is True: plt.show() else: fig = ax.get_figure() fig.set_size_inches(10, 8, forward=True) fig.savefig(filename, dpi=dpi) plt.close()
[docs]def igeoplot(ego, tiles=None, geoloc=None, save_image=False): """Plot function in order to display eGo results on leaflet OSM map. This function will open the results in your main web browser. Parameters ---------- ego : :class:`ego.tools.io.eGo` eGo ``eGo`` inclueds eTraGo and eDisGo results tiles: str Folium background map style `None` as OSM or `Nasa` geoloc: list List which define center of map as (lon, lat) save_image: bool save iplot map as image Returns ------- plot: html HTML file with .js plot """ network = ego.etrago.network session = open_oedb_session(ego) # get scenario name from args scn_name = ego.json_file['eTraGo']['scn_name'] version = ego.json_file['eTraGo']['gridversion'] # define SRID crs = {'init': 'epsg:4326'} if geoloc is None: geoloc = [network.buses.y.mean(), network.buses.x.mean()] mp = folium.Map(tiles=None, location=geoloc, control_scale=True, zoom_start=6) # add Nasa light background if tiles == 'Nasa': tiles = ("https://map1.vis.earthdata.nasa.gov/wmts-webmerc/" + "VIIRS_CityLights_2012/default/GoogleMapsCompatible_" + "Level8/{z}/{y}/{x}.jpg") attr = ('&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://cartodb.com/attributions">CartoDB</a>') folium.raster_layers.TileLayer(tiles=tiles, attr=attr).add_to(mp) else: attr = ('&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="https://openenergy-platform.org/">OpenEnergy-Platform</a>') folium.raster_layers.TileLayer('OpenStreetMap', attr=attr).add_to(mp) # Legend name bus_group = folium.FeatureGroup( name='Bus information (ehv/hv)', show=True) # create icon url = 'https://raw.githubusercontent.com/openego/eGo/master/doc/images/{}'.format icon_image = url('trafo.png') bus_icon = CustomIcon(icon_image, icon_size=(27, 47)) # add buses for name, row in network.buses.iterrows(): # get information of buses popup = """ <b> Bus: </b> {} <br> Scenario: {} <br> <hr> Carrier: {} <br> Control: {} <br> type: {} <br> v_nom: {} <br> v_mag_pu_set: {} <br> v_mag_pu_min: {} <br> v_mag_pu_max: {} <br> sub_network: {} <br> Version: {} <br> """.format(row.name, scn_name, row['carrier'], row['control'], row['type'], row['v_nom'], row['v_mag_pu_set'], row['v_mag_pu_min'], row['v_mag_pu_max'], row['sub_network'], version) # add Popup values use HTML for formating folium.Marker([row["y"], row["x"]], popup=popup, icon=bus_icon).add_to(bus_group) logger.info('Added Busses') def convert_to_hex(rgba_color): """Convert rgba colors to hex """ red = str(hex(int(rgba_color[0]*255)))[2:].capitalize() green = str(hex(int(rgba_color[1]*255)))[2:].capitalize() blue = str(hex(int(rgba_color[2]*255)))[2:].capitalize() if blue == '0': blue = '00' if red == '0': red = '00' if green == '0': green = '00' return '#' + red + green + blue # Prepare lines line_group = folium.FeatureGroup(name='Line Loading (ehv/hv)', show=False) # get line Coordinates x0 = network.lines.bus0.map(network.buses.x) x1 = network.lines.bus1.map(network.buses.x) y0 = network.lines.bus0.map(network.buses.y) y1 = network.lines.bus1.map(network.buses.y) # get content lines = network.lines cols = list(network.lines.columns) # color map lines colormap = cm.linear.YlOrRd_09.scale( lines.s_nom.min(), lines.s_nom.max()).to_step(6) # add parameter for line in network.lines.index: popup = """ <b>Line:</b> {} <br> version: {} <br>""".format(line, version) for col in cols: popup += """ {}: {} <br>""".format(col, lines[col][line]) # change colore function l_color = colormapper_lines( colormap, lines, line, column="s_nom") # ToDo make it more generic folium.PolyLine(([y0[line], x0[line]], [y1[line], x1[line]]), popup=popup, color=convert_to_hex(l_color)).\ add_to(line_group) # Add results # add expansion costs per line lines = network.lines if 'network' in ego.json_file['eTraGo']['extendable']: lines['s_nom_expansion'] = lines.s_nom_opt.subtract( lines.s_nom, axis='index') lines['annuity'] = lines.s_nom_expansion.multiply( lines.capital_cost, axis='index') lines['overnight_cost'] = etrago_convert_overnight_cost( lines['annuity'], ego.json_file, t=40, p=0.05) lines['overnight_cost'] = lines['overnight_cost'].astype(float).round(0) else: lines['s_nom_expansion'] = 0. lines['annuity'] = 0. lines['overnight_cost'] = 0. # Prepare lines line_results_group = folium.FeatureGroup( name='Line costs by annuity costs (ehv/hv)') # color map lines colormap2 = cm.linear.YlGn_09.scale( lines.annuity.min(), lines.annuity.max()).to_step(4) # add parameter cols = list(ego.etrago.network.lines.columns) res = ('overnight_cost', 's_nom_expansion', 'annuity') unit = ('EUR', 'MVA', 'EUR') cols = [x for x in cols if x not in res] for line in network.lines.index: popup = """ <b>Line: {} </b><br> version: {} </b><br> <hr> <b>Line parameter: </b><br>""".format(line, version) for col in cols: popup += """ {}: {} <br>""".format(col, lines[col][line]) popup += """<hr> <b> Results:</b> <br>""" for idx, val in enumerate(res): popup += """{}: {:,} in {}<br>""".format(val, lines[val][line], unit[idx]) # change colore function lr_color = colormapper_lines( colormap2, lines, line, column="annuity") # ToDo make it more generic folium.PolyLine(([y0[line], x0[line]], [y1[line], x1[line]]), popup=popup, color=convert_to_hex(lr_color) ).add_to(line_results_group) logger.info('Added Lines') # Create ehv/hv storage expantion plot store_group = folium.FeatureGroup( name='Storage expantion (ehv/hv)', show=True) stores = network.storage_units[network.storage_units.carrier == 'extendable_storage'] # differentiation of storage units batteries = stores[stores.max_hours == 6] hydrogen = stores[stores.max_hours == 168] # sum by type and bus storage_distribution = network.storage_units.p_nom_opt[stores.index].groupby( network.storage_units.bus).sum().reindex(network.buses.index, fill_value=0.) battery_distribution = network.storage_units.p_nom_opt[batteries.index].groupby( network.storage_units.bus).sum().reindex(network.buses.index, fill_value=0.) hydrogen_distribution = network.storage_units.p_nom_opt[hydrogen.index].groupby( network.storage_units.bus).sum().reindex(network.buses.index, fill_value=0.) # add Coordinates sto_x = stores.bus.map(network.buses.x) sto_y = stores.bus.map(network.buses.y) cols = list(network.storage_units.columns) sto_max = stores.p_nom_opt.max() for store in stores.index: popup = """ <b>Storage:</b> {} <br> version: {} <br> <hr> <b>Parameter: </b><br>""".format(store, version) for col in cols: popup += """ {}: {} <br>""".format(col, stores[col][store]) # get storage radius by p_nom_opt (MW) if lager as 1 KW if ((stores['p_nom_opt'][store] > 7.4e-04) & (stores['capital_cost'][store] > 10)): radius = (3**(1+stores['p_nom_opt'][store]/sto_max)) # add singel storage folium.CircleMarker( location=([sto_y[store], sto_x[store]]), radius=radius, popup=popup, color='#3186cc', fill=True, fill_color='#3186cc', weight=1).add_to(store_group) logger.info('Added storages') ###################### # add MV line loading # add grid districs if ego.json_file['eGo']['eDisGo'] is True: grid_group = folium.FeatureGroup( name='Represented MV Grid district', show=False) subst_id = list(ego.edisgo.grid_choice.the_selected_network_id) district = prepareGD(session, subst_id, version) # Add for loop crs = {'init': 'epsg:4326'} for name, row in district.iterrows(): mv_grid_id = row['subst_id'] if not isinstance(ego.edisgo.network[mv_grid_id], str): lv, mv = _get_mv_plot_res(ego, mv_grid_id) lv_col = lv.columns mv_col = mv.columns pop = """<b>Grid district:</b> {} <br> <hr> <b>MV results:</b><br> """.format(row['subst_id']) for idxs in mv.index: pop += """ {} : {} € <br> """.format(idxs, mv[0][idxs].astype( float).round(2)) pop += """<b>LV results:</b> <br> """ for idxs in lv.index: pop += """ {} : {} € <br> """.format(idxs, lv[0][idxs].astype( float).round(2)) else: pop = """<b>Grid district:</b> {} <br> <hr> """.format(row['subst_id']) # folium.GeoJson(row['geometry']).add_to( # grid_group).add_child(folium.Popup(pop)) geojson = folium.GeoJson(row['geometry']) popup = folium.Popup(pop) popup.add_to(geojson) geojson.add_to(grid_group) # Add cluster grids repgrid_group = folium.FeatureGroup( name='Represented MV Grids per Cluster', show=False) cluster = ego.edisgo.grid_choice cluster = cluster.rename( columns={"the_selected_network_id": "subst_id"}) repre_grids = pd.DataFrame(columns=['subst_id', 'geometry', 'cluster_id', 'color']) style_function = (lambda x: { 'fillColor': x['properties']['color'], 'weight': 0.5, 'color': 'black'}) # simplify MultiPolygon tolerance = 0.002 for idx in cluster.index: cluster_id = list(cluster.represented_grids[idx]) # represented_grids repre_grid = prepareGD(session, cluster_id, version) repre_grid['cluster_id'] = cluster.subst_id[idx] repre_grids = repre_grids.append(repre_grid, ignore_index=True) # prepare cluster colore normal = mpl.colors.Normalize(vmin=repre_grids.cluster_id.min(), vmax=repre_grids.cluster_id.max(), clip=True) mapper = plt.cm.ScalarMappable(norm=normal, cmap=plt.cm.viridis) # add colors to column repre_grids['color'] = repre_grids['cluster_id'].apply( lambda x: mcolors.to_hex(mapper.to_rgba(x))) repre_grids = gpd.GeoDataFrame( repre_grids, geometry='geometry', crs=crs) # simplify Polygon geometry repre_grids.geometry = repre_grids.geometry.simplify(tolerance) # add popup for name, row in repre_grids.iterrows(): pops = """<b>Represented Grid:</b> {} <br>""".format( row['cluster_id']) folium.GeoJson(repre_grids[name:name+1], style_function=style_function, name='represented grids' ).add_to(repgrid_group ).add_child(folium.Popup(pops)) logger.info('Added MV Grids') # Prepare MV lines mv_line_group = folium.FeatureGroup( name='MV Grids (>=10kV)', show=False) mv_list = ego.edisgo.grid_choice.the_selected_network_id for grid in mv_list: mv_grid_id = grid if not isinstance(ego.edisgo.network[mv_grid_id], str): mv_network = ego.edisgo.network[mv_grid_id].network.pypsa # get line Coordinates x0 = mv_network.lines.bus0.loc[mv_network.lines.v_nom >= 10].map( mv_network.buses.x) x1 = mv_network.lines.bus1.loc[mv_network.lines.v_nom >= 10].map( mv_network.buses.x) y0 = mv_network.lines.bus0.loc[mv_network.lines.v_nom >= 10].map( mv_network.buses.y) y1 = mv_network.lines.bus1.loc[mv_network.lines.v_nom >= 10].map( mv_network.buses.y) # get content grid_expansion_costs = ego.edisgo.network[ mv_grid_id].network.results.grid_expansion_costs lines = pd.concat([mv_network.lines, grid_expansion_costs], axis=1, join_axes=[mv_network.lines.index]) lines = lines.loc[mv_network.lines.v_nom >= 10] lines = lines.reindex() cols = list(lines.columns) res_mv = ('overnight_costs', 'capital_cost') unit = ('EUR', 'EUR/time step') cols = [x for x in cols if x not in res_mv] # save results as csv csv_print = False if csv_print == True: geo_lines2 = pd.concat([y0, x0, y1, x1], axis=1, join_axes=[y0.index]) line_export = pd.concat([lines, geo_lines2], axis=1, join_axes=[lines.index]) line_export.to_csv("results/mv_line_results_" + str(mv_grid_id)+".csv") # color map lines try: mv_colormap = cm.linear.YlGnBu_09.scale( lines.overnight_costs.min(), lines.overnight_costs.max()).to_step(6) except: mv_colormap = cm.linear.YlGnBu_09.scale( 0, 0).to_step(6) mv_colormap.caption = 'Line investment of overnight cost (mv)' # add parameter for line in lines.index: popup = """ <b>Line:</b> {} <br> version: {} <br> <hr>""".format(line, version) popup += """<b>MV line parameter:</b><br> """ for col in cols: try: popup += """ {}: {} <br>""".format(col, lines[col][line]) except: popup += """ """ popup += """<hr> <b> Results:</b> <br>""" for idx, val in enumerate(res_mv): try: popup += """{}: {} in {}<br>""".format(val, lines[val][line], unit[idx]) except: popup += """ """ # change colore function mv_color = colormapper_lines( mv_colormap, lines, line, column="overnight_costs") # ToDo make it more generic try: folium.PolyLine(([y0[line], x0[line]], [y1[line], x1[line]]), popup=popup, color=convert_to_hex( mv_color) ).add_to(mv_line_group) except: logger.disabled = True logger.info('Cound not find a geometry') logger.disabled = False else: logger.info(str(mv_grid_id)+" " + str(ego.edisgo.network[mv_grid_id])) mp.add_child(mv_colormap) # Add MV Storage # Legend name mv_sto_group = folium.FeatureGroup(name='MV storages', show=False) # add mv storages mv_grid_id = list(ego.edisgo.grid_choice.the_selected_network_id) for mv_id in mv_grid_id: if not isinstance(ego.edisgo.network[mv_id], str): pypsa_network = ego.edisgo.network[mv_id].network.pypsa # create pypsa network only containing MV buses and lines pypsa_plot = PyPSANetwork() pypsa_plot.buses = pypsa_network.buses.loc[pypsa_network.buses.v_nom >= 10] # add Coordinates sto_x = pypsa_plot.storage_units.bus.map(pypsa_plot.buses.x) sto_y = pypsa_plot.storage_units.bus.map(pypsa_plot.buses.y) # sto_x = pypsa_plot.buses.x # sto_y = pypsa_plot.buses.y sto_cols = list(pypsa_plot.storage_units.columns) for store in pypsa_plot.storage_units.index: popup = """ <b>Storage:</b> {} <br> <hr> <b>Parameter: </b><br>""".format(store,) for col in sto_cols: popup += """ {}: {} <br> """.format(col, pypsa_plot.storage_units[col][store]) folium.CircleMarker( location=([sto_y[store], sto_x[store]]), radius=pypsa_plot.storage_units['p_nom'], popup=popup, color='#3186cc', fill=True, fill_color='#3186cc', weight=1).add_to(mv_sto_group) logger.info('Added MV stores') # add layers and others colormap.caption = 'Line loading s_nom (ehv/hv)' colormap2.caption = 'Line investment of annuity costs (ehv/hv)' mp.add_child(colormap) mp.add_child(colormap2) # add legend # add layer groups if ego.json_file['eGo']['eDisGo'] is True: repgrid_group.add_to(mp) grid_group.add_to(mp) mv_line_group.add_to(mp) mv_sto_group.add_to(mp) bus_group.add_to(mp) line_group.add_to(mp) line_results_group.add_to(mp) store_group.add_to(mp) folium.LayerControl().add_to(mp) plugins.Fullscreen( position='topright', title='Fullscreen', title_cancel='Exit me', force_separate_button=True).add_to(mp) url = ('https://openego.readthedocs.io/en/release-v0.3.1/_images/open_ego_icon_web.png') FloatImage(url, bottom=0, left=5).add_to(mp) if ego.json_file['eGo']['eDisGo'] is True: mp = iplot_griddistrict_legend( mp=mp, repre_grids=repre_grids, start=True) mp = iplot_totalresults_legend(mp=mp, ego=ego, start=True) # Save Map html_dir = 'results/html' if not os.path.exists(html_dir): os.makedirs(html_dir) mp.save("results/html/iplot_map.html") # Display htm result from consol new = 2 # open in a new tab, if possible # open a public URL, in this case, the webbrowser docs path = os.getcwd() url = "results/html/iplot_map.html" webbrowser.open(url, new=new) # save screenshots if save_image: url2 = "file://{}/{}".format(os.getcwd(), url) outfn = os.path.join(html_dir, "outfig.png") subprocess.check_call(["cutycapt", "--url={}".format(url2), "--out={}".format(outfn)]) # close oedb session.close() logger.info('Done')
[docs]def colormapper_lines(colormap, lines, line, column="s_nom"): """ Make Colore Map for lines. """ # TODO: make it more generic l_color = [] if len(colormap.index) == 7: if colormap.index[6] >= lines[column][line] > colormap.index[5]: l_color = colormap.colors[5] elif colormap.index[5] >= lines[column][line] > colormap.index[4]: l_color = colormap.colors[4] elif colormap.index[4] >= lines[column][line] > colormap.index[3]: l_color = colormap.colors[3] elif colormap.index[3] >= lines[column][line] > colormap.index[2]: l_color = colormap.colors[2] elif colormap.index[2] >= lines[column][line] > colormap.index[1]: l_color = colormap.colors[1] elif colormap.index[1] >= lines[column][line] >= colormap.index[0]: l_color = colormap.colors[0] else: l_color = (0., 0., 0., 1.) if len(colormap.index) == 5: if colormap.index[4] >= lines[column][line] > colormap.index[3]: l_color = colormap.colors[3] elif colormap.index[3] >= lines[column][line] > colormap.index[2]: l_color = colormap.colors[2] elif colormap.index[2] >= lines[column][line] > colormap.index[1]: l_color = colormap.colors[1] elif colormap.index[1] >= lines[column][line] >= colormap.index[0]: l_color = colormap.colors[0] else: l_color = (0., 0., 0., 1.) return l_color
def _storage_distribution(network, ax, fig, scaling=1, filename=None): """ Plot storage distribution as circles on grid nodes Displays storage size and distribution in network. Parameters ---------- network : PyPSA network container Holds topology of grid including results from powerflow analysis filename : str Specify filename If not given, figure will be show directly """ stores = network.storage_units storage_distribution = network.storage_units.p_nom_opt[stores.index]\ .groupby(network.storage_units.bus)\ .sum().reindex(network.buses.index, fill_value=0.) msd_max = storage_distribution.max() msd_median = storage_distribution[storage_distribution != 0].median() msd_min = storage_distribution[storage_distribution > 1].min() if msd_max != 0: LabelVal = int(log10(msd_max)) else: LabelVal = 0 if LabelVal < 0: LabelUnit = 'kW' msd_max, msd_median, msd_min = msd_max * \ 1000, msd_median * 1000, msd_min * 1000 storage_distribution = storage_distribution * 1000 elif LabelVal < 3: LabelUnit = 'MW' else: LabelUnit = 'GW' msd_max, msd_median, msd_min = msd_max / \ 1000, msd_median / 1000, msd_min / 1000 storage_distribution = storage_distribution / 1000 if sum(storage_distribution) == 0: network.plot(bus_sizes=0, ax=ax) else: network.plot( bus_sizes=storage_distribution * scaling, ax=ax, line_widths=0.3 )
[docs]def iplot_griddistrict_legend(mp, repre_grids, start=False): """Add legend to iplot function of mv grids. """ # from branca.element import Template, MacroElement from string import Template if start: legends = [] for name, row in repre_grids.groupby(['cluster_id', 'color']).count().iterrows(): color = name[1] grid_no = name[0] entry = """<li><span style = 'background:{};opacity:0.7;' > </span > Represented by Grid {} </li>""".format(color, grid_no) legends.append(entry) legend = "\n" legend = legend.join(legends) temp_1 = """ <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>open_eGo interactiv result plot</title> <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> <script src="https://code.jquery.com/jquery-1.12.4.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> <script> $( function() { $( "#maplegend" ).draggable({ start: function (event, ui) { $(this).css({ right: "auto", top: "auto", bottom: "auto" }); } }); }); </script> <script> $( function() { $( "#map-results-legend" ).draggable({ start: function (event, ui) { $(this).css({ right: "auto", top: "auto", bottom: "auto" }); } }); $("#button_results").click(function(){ if($(this).html() == "open"){ $(this).html("close"); } else{ $(this).html("open"); } $("#box_results").slideToggle(); }); }); </script> </head> <body> """ temp_2 = """ <div id='maplegend' class='maplegend' style='position: absolute; z-index:9999; border:2px solid grey; background-color:rgba(255, 255, 255, 0.8); border-radius:6px; padding: 10px; font-size:14px; right: 20px; bottom: 20px;'> <div class='legend-title'>MV Grid districts</div> <div class='legend-scale'> <ul class='legend-labels'> $legend </ul> </div> </div> </body> </html> """ temp_3 = """ <style type='text/css'> .maplegend .legend-title { text-align: left; margin-bottom: 5px; font-weight: bold; font-size: 90%; } .maplegend .legend-scale ul { margin: 0; margin-bottom: 5px; padding: 0; float: left; list-style: none; } .maplegend .legend-scale ul li { font-size: 80%; list-style: none; margin-left: 0; line-height: 18px; margin-bottom: 2px; } .maplegend ul.legend-labels li span { display: block; float: left; height: 16px; width: 30px; margin-right: 5px; margin-left: 0; border: 1px solid #999; } .maplegend .legend-source { font-size: 80%; color: #777; clear: both; } .maplegend a { color: #777; } </style> <style type='text/css'> .map-results-legend .legend-title { text-align: left; margin-bottom: 15px; font-weight: bold; font-size: 90%; } .map-results-legend .legend-scale ul { margin: 0; margin-bottom: 15px; padding: 0; float: left; list-style: none; } .map-results-legend .legend-scale ul li { font-size: 80%; list-style: none; margin-left: 0; line-height: 18px; margin-bottom: 10px; } .map-results-legend ul.legend-labels li span { display: block; float: left; height: 16px; width: 30px; margin-right: 15px; margin-left: 20; border: 1px solid #999; } .map-results-legend .legend-source { font-size: 80%; color: #777; clear: both; } .map-results-legend a { color: #777; } </style> <style type='text/css'> # window_results{ width:400px; border:solid 1px; } # title_bar_results{ background: #A3B9C9; height: 25px; font-size:14px; width: 100%; } # button_results{ border:solid 1px; width: 25px; height: 23px; float:right; font-size:14px; cursor:pointer; } # box_results{ height: 25px; background: #A3B9C9; } </style> """ t = Template(temp_2) temp_2 = t.substitute(legend=legend) temps = temp_1+temp_2+temp_3 # macro = MacroElement(**leg) # macro._template = Template(template) # return mp.get_root().add_child(macro) return mp.get_root().html.add_child(folium.Element(temps))
[docs]def iplot_totalresults_legend(mp, ego, start=False): """ Add total results as legend to iplot function. """ from string import Template if start: # get data total = ego.total_investment_costs.rename( columns={"capital_cost": "annuity_costs"}) # change format total['overnight_costs'] = ( total['overnight_costs']/1000000).map('M€ {:,.2f}'.format) total['annuity_costs'] = (total['annuity_costs'] / 1000).map('T€ {:,.2f}'.format) total = total[['component', 'voltage_level', 'differentiation', 'overnight_costs', 'annuity_costs']].to_html(index=False) # inclued grafic filepath = "results/total_investment_costs_map.png" ego.plot_total_investment_costs(filename=filepath) url = "file://{}/{}".format(os.getcwd(), filepath) outfn = os.path.join(url) temp_tr = """ <div id='map-results-legend' class='map-results-legend' style='position: absolute; z-index:9999; border:2px solid grey; background-color:rgba(255, 255, 255, 0.8); border-radius:6px; padding: 10px; font-size:14px; left: 10px; bottom: 200px;'> <div id="window_results"> <div id="title_bar_results"> <div id="button_results" style"font-size: 300%; text-align:right; float: right;"> close </div> </div> <div id="box_results"> <div class='legend-title'>Total investment costs</div> <div id="plot" style="width: 400px; height: 400px"> <img src= $plot height="390" /> </div> <div class='legend-scale'> <ul class='legend-labels'> $total </ul> </div> </div> </div> </div> </body> </html> """ temp_tmp = """ """ t = Template(temp_tr) temp_tr = t.substitute(total=total, plot=outfn) temps = temp_tr # +temp_tmp return mp.get_root().html.add_child(folium.Element(temps))
def _get_mv_plot_res(ego, mv_grid_id): """ Prepare mv results. """ logger.disabled = True pypsa_network = ego.edisgo.network[mv_grid_id].network.pypsa # create pypsa network only containing MV buses and lines pypsa_plot = PyPSANetwork() pypsa_plot.buses = pypsa_network.buses.loc[pypsa_network.buses.v_nom >= 10] # filter buses of aggregated loads and generators pypsa_plot.buses = pypsa_plot.buses[ ~pypsa_plot.buses.index.str.contains("agg")] pypsa_plot.lines = pypsa_network.lines[ pypsa_network.lines.bus0.isin(pypsa_plot.buses.index)][ pypsa_network.lines.bus1.isin(pypsa_plot.buses.index)] grid_expansion_costs = ego.edisgo.network[mv_grid_id].network.results.grid_expansion_costs bus_cost = pd.concat([pypsa_plot.buses, grid_expansion_costs], axis=1, join_axes=[pypsa_plot.buses.index]) costs_lv_stations = grid_expansion_costs[ grid_expansion_costs.index.str.contains("LVStation")] costs_lv_stations['station'] = \ costs_lv_stations.reset_index()['index'].apply( lambda _: '_'.join(_.split('_')[0:2])).values costs_lv_stations = costs_lv_stations.groupby('station').sum() costs_mv_station = grid_expansion_costs[ grid_expansion_costs.index.str.contains("MVStation")] costs_mv_station['station'] = \ costs_mv_station.reset_index()['index'].apply( lambda _: '_'.join(_.split('_')[0:2])).values costs_mv_station = costs_mv_station.groupby('station').sum() costs_lv_stations_total = costs_lv_stations[['overnight_costs', 'capital_cost']].sum() costs_mv_station_total = costs_mv_station[['overnight_costs', 'capital_cost']].sum() costs_lv_stations_total = pd.DataFrame(costs_lv_stations_total) costs_mv_station_total = pd.DataFrame(costs_mv_station_total) logger.disabled = False return costs_lv_stations_total, costs_mv_station_total