Esempio n. 1
0
def _prepare_busmap(bus_info_and_emission,
                    color_ng,
                    color_coal,
                    agg,
                    type1="natural gas",
                    type2="coal"):
    """Prepare data with amount of emissions and type for hover tips

    :param pandas.DataFrame bus_info_and_emission: info and emission of buses
        as returned by :func:`combine_bus_info_and_emission`.
    :param str color_ng: color assigned for ng, default to BE purple
    :param str color_coal: color associated with coal, default to black/gray
        :param boolean agg: if true, aggregates points by lat lon within a given radius
    :param str type1: label for hover over tool tips, first color/type
        (usual choices: natural gas or increase if making a diff map)
    :param str type2: label for hover over tool tips, second color/type
        (usual choices: coal or decrease if making diff map)
    :return: (pandas.DataFrame) -- data frame with amount and tye fields
    """
    bus_map = bus_info_and_emission
    bus_map["color"] = ""
    bus_map["type"] = ""
    bus_map.loc[(bus_map["ng"] > 0), "color"] = color_ng
    bus_map.loc[(bus_map["coal"] > 0), "color"] = color_coal
    bus_map.loc[(bus_map["ng"] > 0), "type"] = type1
    bus_map.loc[(bus_map["coal"] > 0), "type"] = type2

    bus_map = project_bus(bus_map)
    bus_map1 = group_lat_lon(bus_map, agg=agg)
    bus_map1["amount"] = bus_map1["ng"] + bus_map1["coal"]
    return bus_map1
def _get_shadowprice_data(scenario_id):
    """Gets data necessary for plotting shadow price

    :param str/int scenario_id: scenario id
    :return: (*tuple*) -- interconnect as a str, bus data as a data frame, lmp data
        as a data frame, branch data as a data frame and congestion data as a data
        frame
    """
    s = Scenario(scenario_id)

    interconnect = s.info["interconnect"]
    interconnect = " ".join(interconnect.split("_"))

    s_grid = s.state.get_grid()

    # Get bus and add location data
    bus_map = project_bus(s_grid.bus)

    # get branch and add location data
    branch_map = project_branch(s_grid.branch)

    # get congestion
    congu = s.state.get_congu()
    congl = s.state.get_congl()
    cong_abs = pd.DataFrame(
        np.maximum(congu.to_numpy(), congl.to_numpy()),
        columns=congu.columns,
        index=congu.index,
    )

    return interconnect, bus_map, s.state.get_lmp(), branch_map, cong_abs
Esempio n. 3
0
def aggregate_bus_lmp(bus, coordinate_rounding):
    """Aggregate LMP for buses based on similar lat/lon coordinates.

    :param pandas.DataFrame bus: data frame containing 'lat', 'lon', 'lmp' columns.
    :param int coordinate_rounding: number of digits to round lat/lon for aggregation.
    :return: (*pandas.DataFrame*) -- aggregated data frame.
    """
    bus_w_xy = project_bus(bus)
    bus_w_xy["lat"] = bus_w_xy["lat"].round(coordinate_rounding)
    bus_w_xy["lon"] = bus_w_xy["lon"].round(coordinate_rounding)
    aggregated = bus_w_xy.groupby(["lat", "lon"]).agg(
        {"lmp": "mean", "x": "mean", "y": "mean"}
    )

    return aggregated
def aggregate_plant_generation(plant, coordinate_rounding=0):
    """Aggregate generation for plants based on similar lat/lon coordinates.

    :param int coordinate_rounding: number of digits to round lat & lon for aggregation.
    :param pandas.DataFrame plant: data frame containing: 'lat', 'lon', 'x', 'y', 'pg'.
    :return: (*pandas.DataFrame*) -- data frame aggregated to the desired precision.
    """
    plant_w_xy = project_bus(plant)
    plant_w_xy["lat"] = plant_w_xy["lat"].round(coordinate_rounding)
    plant_w_xy["lon"] = plant_w_xy["lon"].round(coordinate_rounding)
    aggregated = plant_w_xy.groupby(["lat", "lon"]).agg({
        "pg": "sum",
        "x": "mean",
        "y": "mean"
    })
    return aggregated
Esempio n. 5
0
def aggregate_bus_emission_difference(bus, coordinate_rounding):
    """Aggregate emission for buses based on similar lat/lon coordinates

    :param pandas.DataFrame bus: bus data frame containing 'coal', 'ng', 'lat', 'lon'
        'type' and 'color' columns.
    :param int coordinate_rounding: number of digits to round lat/lon for aggregation.
    :return: (*pandas.DataFrame*) -- aggregated data frame.
    """
    bus_w_xy = project_bus(bus)
    bus_w_xy["lat"] = bus_w_xy["lat"].round(coordinate_rounding)
    bus_w_xy["lon"] = bus_w_xy["lon"].round(coordinate_rounding)

    aggregated = bus_w_xy.groupby(["lat", "lon"]).agg({
        "amount": "sum",
        "x": "mean",
        "y": "mean"
    })
    aggregated = aggregated.reset_index()
    return aggregated
Esempio n. 6
0
def map_lmp(s_grid, lmp, us_states_dat=None, lmp_min=20, lmp_max=45, is_website=False):
    """Plots average LMP by color coding buses

    :param powersimdata.input.grid.Grid s_grid: scenario grid
    :param pandas.DataFrame lmp: locational marginal prices calculated for the scenario
    :param dict us_states_dat: dictionary of state border lats/lons. If None, get
        from :func:`postreise.plot.plot_states.get_state_borders`.
    :param inf/float lmp_min: minimum LMP to clamp plot range to.
    :param inf/float lmp_max: maximum LMP to clamp plot range to.
    :param bool is_website: changes text/legend formatting to look better on the website
    :return: (*bokeh.models.layout.Row*) bokeh map visual in row layout
    """
    if us_states_dat is None:
        us_states_dat = get_state_borders()

    bus = project_bus(s_grid.bus)
    bus_segments = _construct_bus_data(bus, lmp, lmp_min, lmp_max)
    return _construct_shadowprice_visuals(
        bus_segments, us_states_dat, lmp_min, lmp_max, is_website
    )
def plot_powerflow_snapshot(
    scenario,
    hour,
    b2b_dclines=None,
    demand_centers=None,
    ac_branch_color="#8B36FF",
    dc_branch_color="#01D4ED",
    solar_color="#FFBB45",
    wind_color="#78D911",
    demand_color="gray",
    figsize=(1400, 800),
    circle_scale_factor=0.25,
    bg_width_scale_factor=0.001,
    pf_width_scale_factor=0.00125,
    arrow_pf_threshold=3000,
    arrow_dist_threshold=20,
    num_ac_arrows=1,
    num_dc_arrows=1,
    min_arrow_size=5,
    branch_alpha=0.5,
    state_borders_kwargs=None,
    x_range=None,
    y_range=None,
    legend_font_size=None,
):
    """Plot a snapshot of powerflow.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario to plot.
    :param pandas.Timestamp/numpy.datetime64/datetime.datetime hour: snapshot interval.
    :param dict b2b_dclines: which DC lines are actually B2B facilities. Keys are:
        {"from", "to"}, values are iterables of DC line indices to plot (indices in
        "from" get plotted at the "from" end, and vice versa).
    :param pandas.DataFrame demand_centers: lat/lon centers at which to plot the demand
        from each load zone.
    :param str ac_branch_color: color to plot AC branches.
    :param str dc_branch_color: color to plot DC branches.
    :param str solar_color: color to plot solar generation.
    :param str wind_color: color to plot wind generation.
    :param str demand_color: color to plot demand.
    :param tuple figsize: size of the bokeh figure (in pixels).
    :param int/float circle_scale_factor: scale factor for demand/solar/wind circles.
    :param int/float bg_width_scale_factor: scale factor for grid capacities.
    :param int/float pf_width_scale_factor: scale factor for power flows.
    :param int/float arrow_pf_threshold: minimum power flow (MW) for adding arrows.
    :param int/float arrow_dist_threshold: minimum distance (miles) for adding arrows.
    :param int num_ac_arrows: number of arrows for each AC branch.
    :param int num_dc_arrows: number of arrows for each DC branch.
    :param int/float min_arrow_size: minimum arrow size.
    :param int/float branch_alpha: opaqueness of branches.
    :param dict state_borders_kwargs: keyword arguments to be passed to
        :func:`postreise.plot.plot_states.add_state_borders`.
    :param tuple(float, float) x_range: x range to zoom plot to (EPSG:3857).
    :param tuple(float, float) y_range: y range to zoom plot to (EPSG:3857).
    :param int/str legend_font_size: size to display legend specified as e.g. 12/'12pt'.
    :return: (*bokeh.plotting.figure*) -- power flow snapshot map.
    """
    _check_scenario_is_in_analyze_state(scenario)
    _check_date_range_in_scenario(scenario, hour, hour)
    # Get scenario data
    grid = scenario.state.get_grid()
    bus = grid.bus
    plant = grid.plant
    # Augment the branch dataframe with extra info needed for plotting
    branch = grid.branch
    branch["pf"] = scenario.state.get_pf().loc[hour]
    branch = branch.query("pf != 0").copy()
    branch["dist"] = branch.apply(lambda x: haversine((x.from_lat, x.from_lon),
                                                      (x.to_lat, x.to_lon)),
                                  axis=1)
    branch["arrow_size"] = branch["pf"].abs(
    ) * pf_width_scale_factor + min_arrow_size
    branch = project_branch(branch)
    # Augment the dcline dataframe with extra info needed for plotting
    dcline = grid.dcline
    dcline["pf"] = scenario.state.get_dcline_pf().loc[hour]
    dcline["from_lat"] = dcline.apply(lambda x: bus.loc[x.from_bus_id, "lat"],
                                      axis=1)
    dcline["from_lon"] = dcline.apply(lambda x: bus.loc[x.from_bus_id, "lon"],
                                      axis=1)
    dcline["to_lat"] = dcline.apply(lambda x: bus.loc[x.to_bus_id, "lat"],
                                    axis=1)
    dcline["to_lon"] = dcline.apply(lambda x: bus.loc[x.to_bus_id, "lon"],
                                    axis=1)
    dcline["dist"] = dcline.apply(lambda x: haversine((x.from_lat, x.from_lon),
                                                      (x.to_lat, x.to_lon)),
                                  axis=1)
    dcline["arrow_size"] = dcline["pf"].abs(
    ) * pf_width_scale_factor + min_arrow_size
    dcline = project_branch(dcline)
    # Create a dataframe for demand plotting, if necessary
    if demand_centers is not None:
        demand = scenario.state.get_demand()
        demand_centers["demand"] = demand.loc[hour]
        demand_centers = project_bus(demand_centers)

    # create canvas
    canvas = create_map_canvas(figsize=figsize,
                               x_range=x_range,
                               y_range=y_range)

    # Add state borders
    default_state_borders_kwargs = {"fill_alpha": 0.0, "background_map": False}
    all_state_borders_kwargs = ({
        **default_state_borders_kwargs,
        **state_borders_kwargs
    } if state_borders_kwargs is not None else default_state_borders_kwargs)
    _check_func_kwargs(add_state_borders, set(all_state_borders_kwargs),
                       "state_borders_kwargs")
    canvas = add_state_borders(canvas, **all_state_borders_kwargs)

    if b2b_dclines is not None:
        # Append the pseudo AC lines to the branch dataframe, remove from dcline
        all_b2b_dclines = list(b2b_dclines["to"]) + list(b2b_dclines["from"])
        pseudo_ac_lines = dcline.loc[all_b2b_dclines]
        pseudo_ac_lines["rateA"] = pseudo_ac_lines[["Pmin",
                                                    "Pmax"]].abs().max(axis=1)
        branch = branch.append(pseudo_ac_lines)
        # Construct b2b dataframe so that all get plotted at their 'from' x/y
        b2b_from = dcline.loc[b2b_dclines["from"]]
        b2b_to = dcline.loc[b2b_dclines["to"]].rename(
            {
                "from_x": "to_x",
                "from_y": "to_y",
                "to_x": "from_x",
                "to_y": "from_y"
            },
            axis=1,
        )
        b2b = pd.concat([b2b_from, b2b_to])
        dcline = dcline.loc[~dcline.index.isin(all_b2b_dclines)]

    # Plot grid background in grey
    canvas.multi_line(
        branch[["from_x", "to_x"]].to_numpy().tolist(),
        branch[["from_y", "to_y"]].to_numpy().tolist(),
        color="gray",
        alpha=branch_alpha,
        line_width=(branch["rateA"].abs() * bg_width_scale_factor),
    )
    canvas.multi_line(
        dcline[["from_x", "to_x"]].to_numpy().tolist(),
        dcline[["from_y", "to_y"]].to_numpy().tolist(),
        color="gray",
        alpha=branch_alpha,
        line_width=(dcline[["Pmin", "Pmax"]].abs().max(axis=1) *
                    bg_width_scale_factor),
    )
    if b2b_dclines is not None:
        canvas.scatter(
            x=b2b.from_x,
            y=b2b.from_y,
            color="gray",
            alpha=0.5,
            marker="triangle",
            size=(b2b[["Pmin", "Pmax"]].abs().max(axis=1) *
                  bg_width_scale_factor),
        )

    fake_location = branch.iloc[0].drop("x").rename({
        "from_x": "x",
        "from_y": "y"
    })
    # Legend entries
    canvas.multi_line(
        (fake_location.x, fake_location.x),
        (fake_location.y, fake_location.y),
        color=dc_branch_color,
        alpha=branch_alpha,
        line_width=10,
        legend_label="HVDC powerflow",
        visible=False,
    )
    canvas.multi_line(
        (fake_location.x, fake_location.x),
        (fake_location.y, fake_location.y),
        color=ac_branch_color,
        alpha=branch_alpha,
        line_width=10,
        legend_label="AC powerflow",
        visible=False,
    )
    canvas.circle(
        fake_location.x,
        fake_location.y,
        color=solar_color,
        alpha=0.6,
        size=5,
        legend_label="Solar Gen.",
        visible=False,
    )
    canvas.circle(
        fake_location.x,
        fake_location.y,
        color=wind_color,
        alpha=0.6,
        size=5,
        legend_label="Wind Gen.",
        visible=False,
    )

    # Plot demand
    if demand_centers is not None:
        canvas.circle(
            fake_location.x,
            fake_location.y,
            color=demand_color,
            alpha=0.3,
            size=5,
            legend_label="Demand",
            visible=False,
        )
        canvas.circle(
            demand_centers.x,
            demand_centers.y,
            color=demand_color,
            alpha=0.3,
            size=(demand_centers.demand * circle_scale_factor)**0.5,
        )

    # Aggregate solar and wind for plotting
    plant_with_pg = plant.copy()
    plant_with_pg["pg"] = scenario.state.get_pg().loc[hour]
    grouped_solar = aggregate_plant_generation(
        plant_with_pg.query("type == 'solar'"))
    grouped_wind = aggregate_plant_generation(
        plant_with_pg.query("type == 'wind'"))
    # Plot solar, wind
    canvas.circle(
        grouped_solar.x,
        grouped_solar.y,
        color=solar_color,
        alpha=0.6,
        size=(grouped_solar.pg * circle_scale_factor)**0.5,
    )
    canvas.circle(
        grouped_wind.x,
        grouped_wind.y,
        color=wind_color,
        alpha=0.6,
        size=(grouped_wind.pg * circle_scale_factor)**0.5,
    )

    # Plot powerflow on AC branches
    canvas.multi_line(
        branch[["from_x", "to_x"]].to_numpy().tolist(),
        branch[["from_y", "to_y"]].to_numpy().tolist(),
        color=ac_branch_color,
        alpha=branch_alpha,
        line_width=(branch["pf"].abs() * pf_width_scale_factor),
    )
    add_arrows(
        canvas,
        branch,
        color=ac_branch_color,
        pf_threshold=arrow_pf_threshold,
        dist_threshold=arrow_dist_threshold,
        n=num_ac_arrows,
    )

    # Plot powerflow on DC lines
    canvas.multi_line(
        dcline[["from_x", "to_x"]].to_numpy().tolist(),
        dcline[["from_y", "to_y"]].to_numpy().tolist(),
        color=dc_branch_color,
        alpha=branch_alpha,
        line_width=(dcline["pf"].abs() * pf_width_scale_factor),
    )
    add_arrows(
        canvas,
        dcline,
        color=dc_branch_color,
        pf_threshold=0,
        dist_threshold=0,
        n=num_dc_arrows,
    )
    # B2Bs
    if b2b_dclines is not None:
        canvas.scatter(
            x=b2b.from_x,
            y=b2b.from_y,
            color=dc_branch_color,
            alpha=0.5,
            marker="triangle",
            size=(b2b["pf"].abs() * pf_width_scale_factor * 5),
        )

    canvas.legend.location = "bottom_left"
    if legend_font_size is not None:
        if isinstance(legend_font_size, (int, float)):
            legend_font_size = f"{legend_font_size}pt"
        canvas.legend.label_text_font_size = legend_font_size

    return canvas
def map_plant_capacity(scenario, us_states_dat=None, size_factor=1):
    """Makes map of renewables from change table 'new plants'. Size/area
    indicates capacity.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :param dict us_states_dat: dictionary of state border lats/lons. If None, get
        from :func:`postreise.plot.plot_states.get_state_borders`.
    :param float/int size_factor: scale size of glyphs.
    """

    _check_scenario_is_in_analyze_state(scenario)

    # prepare data from the change table to select new plants
    ct = scenario.state.get_ct()
    # check that there are new plants, check scenario is in analyze state
    if "new_plant" not in ct.keys():
        raise ValueError(
            "There are no new plants added in the selected scenario. Please choose a different scenario."
        )
    if us_states_dat is None:
        us_states_dat = get_state_borders()

    newplant = pd.DataFrame(ct["new_plant"])
    newplant = newplant.set_index("bus_id")
    newplant = newplant[newplant.Pmax > 0]

    # merge with bus info to get coordinates
    gridscen = scenario.state.get_grid()
    bus_of_interest = gridscen.bus.loc[list(set(newplant.index))]
    bus_capacity = bus_of_interest.merge(newplant,
                                         right_index=True,
                                         left_index=True)

    bus_map = project_bus(bus_capacity)
    bus_map1 = bus_map.loc[bus_map.Pmax > 1]
    rar_df = bus_map1.loc[(bus_map1.type_y == "solar") |
                          (bus_map1.type_y == "wind")]

    # group by coordinates
    rar_df = rar_df.groupby(["lat", "lon"]).agg({
        "Pmax": "sum",
        "x": "mean",
        "y": "mean"
    })

    a, b = project_borders(us_states_dat)

    rar_source = ColumnDataSource({
        "x": rar_df["x"],
        "y": rar_df["y"],
        "capacity": (rar_df["Pmax"] * size_factor)**0.5,
        "capacitymw": rar_df["Pmax"],
    })

    tools: str = "pan,wheel_zoom,reset,save"
    p = figure(
        tools=tools,
        x_axis_location=None,
        y_axis_location=None,
        plot_width=800,
        plot_height=800,
        output_backend="webgl",
        sizing_mode="stretch_both",
        match_aspect=True,
    )

    # for legend, plot behind tiles
    p.circle(
        -8.1e6,
        5.2e6,
        fill_color=be_green,
        color=be_green,
        alpha=0.5,
        size=50,
        legend_label="Renewable Capacity Added",
    )

    p.add_tile(get_provider(Vendors.CARTODBPOSITRON_RETINA))

    # state borders
    p.patches(a, b, fill_alpha=0.0, line_color="gray", line_width=2)

    # capacity circles
    circle = p.circle(
        "x",
        "y",
        fill_color=be_green,
        color=be_green,
        alpha=0.8,
        size="capacity",
        source=rar_source,
    )
    p.legend.label_text_font_size = "12pt"
    p.legend.location = "bottom_right"

    hover = HoverTool(
        tooltips=[
            ("Capacity (MW)", "@capacitymw"),
        ],
        renderers=[circle],
    )

    p.add_tools(hover)

    return p
Esempio n. 9
0
def map_carbon_emission_bar(
    bus_info_and_emission,
    scenario_name,
    color_coal="black",
    color_ng="purple",
    us_states_dat=None,
    size_factor=1.0,
):
    """Makes map of carbon emissions, color code by fuel type. Size/area
    indicates emissions.

    :param dict us_states_dat: dictionary of state border lats/lons. If None, get
        from :func:`postreise.plot.plot_states.get_state_borders`.
    :param pandas.DataFrame bus_info_and_emission: info and
        emission of buses by :func:`combine_bus_info_and_emission`.
    :param str scenario_name: name of scenario for labeling.
    :param str color_coal: color assigned for coal
    :param str color_ng: color assigned for natural gas
    :param float size_factor: height scale for bars
    """

    if us_states_dat is None:
        us_states_dat = get_state_borders()

    bus_map = project_bus(bus_info_and_emission)
    bus_map = group_zone(bus_map)

    a, b = project_borders(us_states_dat)

    # plotting adjustment constants
    ha = 85000
    size_factor = size_factor * 0.02

    bus_source = ColumnDataSource({
        "x":
        bus_map["x"],
        "x2":
        bus_map["x"] + ha * 2,
        "y":
        bus_map["y"],
        "coaly":
        bus_map["y"] + bus_map["coal"] * size_factor,
        "ngy":
        bus_map["y"] + bus_map["ng"] * size_factor,
    })

    # Set up figure
    tools: str = "pan,wheel_zoom,reset,hover,save"
    p = figure(
        title=scenario_name,
        tools=tools,
        x_axis_location=None,
        y_axis_location=None,
        plot_width=800,
        plot_height=800,
    )
    p_legend = figure(
        x_axis_location=None,
        y_axis_location=None,
        toolbar_location=None,
        plot_width=200,
        plot_height=200,
        y_range=(0, 2),
        x_range=(0, 2),
    )
    p.add_tile(get_provider(Vendors.CARTODBPOSITRON_RETINA))
    # state borders
    p.patches(a, b, fill_alpha=0.0, line_color="black", line_width=2)

    # emissions bars
    width = 150000.0
    alpha = 0.7
    p.vbar(
        x="x2",
        bottom="y",
        top="coaly",
        width=width,
        color=color_coal,
        source=bus_source,
        alpha=alpha,
    )
    p.vbar(
        x="x",
        bottom="y",
        top="ngy",
        width=width,
        color=color_ng,
        source=bus_source,
        alpha=alpha,
    )
    bus_map = pd.DataFrame(bus_map)

    # citation fields
    cit_x = []
    cit_y = []
    cit_text = []

    # constants for adjustments of text labels
    va = 1000
    m = 1000000

    # x values are offset so vbars are side by side.
    cit_x.append([i - ha * 1.5 for i in bus_map.x.values.tolist()])
    cit_x.append([i + ha for i in bus_map.x.values.tolist()])
    cit_y.append(
        np.add(
            [i + va * 2 for i in bus_map.y.values.tolist()],
            [i * size_factor for i in bus_map.ng.values.tolist()],
        ))
    cit_y.append(
        np.add(
            [i + va * 2 for i in bus_map.y.values.tolist()],
            [i * size_factor for i in bus_map.coal.values.tolist()],
        ))
    cit_text.append(
        [round(num, 1) for num in [i / m for i in bus_map.ng.values.tolist()]])
    cit_text.append([
        round(num, 1) for num in [i / m for i in bus_map.coal.values.tolist()]
    ])

    for j in range(0, len(cit_x)):
        for i in range(0, len(cit_x[j])):
            citation = Label(
                x=cit_x[j][i],
                y=cit_y[j][i],
                x_units="data",
                y_units="data",
                text_font_size="20pt",
                text=str(cit_text[j][i]),
                render_mode="css",
                border_line_color="black",
                border_line_alpha=0,
                background_fill_color="white",
                background_fill_alpha=0.8,
            )
            p.add_layout(citation)

    # custom legend
    p_legend.square(1, 1, fill_color="white", color="white", size=600)
    p_legend.square(1, 1, fill_color="white", color="black", size=400)
    p_legend.square(0.4,
                    0.8,
                    fill_color="black",
                    color="black",
                    size=40,
                    alpha=0.7)
    p_legend.square(0.4,
                    0.2,
                    fill_color="purple",
                    color="purple",
                    size=40,
                    alpha=0.7)
    source = ColumnDataSource(data=dict(
        x=[0.7, 0.7, 0.05],
        y=[0.6, 0.1, 1.5],
        text=["Coal", "Natural Gas", "Emissions (Million Tons)"],
    ))
    labels = LabelSet(
        x="x",
        y="y",
        text="text",
        source=source,
        level="glyph",
        x_offset=0,
        y_offset=0,
        render_mode="css",
        text_font_size="20pt",
    )
    p_legend.add_layout(labels)

    return row(p_legend, p)
Esempio n. 10
0
def map_carbon_emission_comparison(bus_info_and_emission_1,
                                   bus_info_and_emission_2,
                                   web=True):
    """Makes map of carbon emissions, color code by fuel type, size/area
        indicates emissions Also, returns data frame enclosing emission
        released by thermal generators.

    :param pandas.DataFrame bus_info_and_emission_1: info and emission
        of buses for 1st scenario
        returned by :func:`combine_bus_info_and_emission`.
    :param pandas.DataFrame bus_info_and_emission_2: info and emission
        of buses for 2nd scenario
        as returned by :func:`combine_bus_info_and_emission`.
    :param boolean web: if true, optimizes figure for web-based presentation
    :return: (pandas.DataFrame) -- comparison map indicating increase or
        decrease in emission
    """
    # merge
    bus_info_and_emission_1 = bus_info_and_emission_1.fillna(0)
    bus_info_and_emission_2 = bus_info_and_emission_2.fillna(0)

    bus_info_and_emission_1["amt"] = (bus_info_and_emission_1["ng"] +
                                      bus_info_and_emission_1["coal"])
    bus_info_and_emission_2["amt"] = (bus_info_and_emission_2["ng"] +
                                      bus_info_and_emission_2["coal"])

    bus_map = bus_info_and_emission_1.merge(
        bus_info_and_emission_2,
        right_index=True,
        left_index=True,
        suffixes=("_x", "_y"),
        how="outer",
    )
    # fill 0s so we can subtract
    bus_map = bus_map.fillna(0)

    bus_map["amt_dif"] = bus_map["amt_x"] - bus_map["amt_y"]
    bus_map["lon"] = bus_map["lon_x"].fillna(bus_map["lon_y"])
    bus_map["lat"] = bus_map["lat_x"].fillna(bus_map["lat_y"])
    bus_map["coal"] = 0
    bus_map["ng"] = 0
    bus_map.loc[bus_map.amt_dif > 0, ["coal"]] = bus_map["amt_dif"]

    bus_map.loc[bus_map.amt_dif < 0, ["ng"]] = abs(bus_map["amt_dif"])

    bus_map2 = bus_map[bus_map.amt_dif != 0]
    bus_map2 = project_bus(bus_map2)
    bus_map2 = _prepare_busmap(bus_map2,
                               be_green,
                               be_red,
                               agg=False,
                               type1="increase",
                               type2="decrease")

    p = map_carbon_emission(
        bus_map2,
        be_green,
        be_red,
        u"Less tons CO\u2082",
        u"More tons CO\u2082",
        web=web,
        type1="increase",
        type2="decrease",
    )

    return p
Esempio n. 11
0
def add_plant_capacity(
    canvas,
    scenario,
    resources,
    disaggregation=None,
    min_capacity=1,
    size_factor=1,
    alpha=0.5,
):
    """Adds renewables capacity to a plot.

    :param bokeh.plotting.figure.Figure canvas: canvas to plot capacities onto.
    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :param iterable resources: which types of resources to plot.
    :param tuple figsize: size of the bokeh figure (in pixels).
    :param tuple x_range: x range to zoom plot to (EPSG:3857).
    :param tuple y_range: y range to zoom plot to (EPSG:3857).
    :param str disaggregation: method used to disaggregate plants:
        if "new_vs_existing_plants": separates plants into added vs. existing.
        if None, no disaggregation.
    :param float/int min_capacity: minimum bus capacity (MW) for markers to be plotted.
    :param float/int size_factor: scale size of glyphs.
    :param float/int alpha: opacity of circles (between 0 and 1).
    :raises ValueError: if ``disaggregation`` is not 'new_vs_existing_plants' or None.
    :return: (*bokeh.plotting.figure.Figure*) -- map with color-coded upgrades.
    """
    ct = scenario.get_ct()
    grid = scenario.get_grid()
    grid.plant["lat"] = grid.plant["lat"].round(3)
    grid.plant["lon"] = grid.plant["lon"].round(3)
    grid.plant = project_bus(grid.plant.query("type in @resources"))
    type_colors = grid.model_immutables.plants["type2color"]

    xy_capacity = {}
    if disaggregation is None:
        grouped_capacities = grid.plant.groupby(["x", "y", "type"]).sum().Pmax
        grouped_capacities = grouped_capacities.reset_index()
        xy_capacity["all"] = grouped_capacities.query("Pmax > 0")
    elif disaggregation == "new_vs_existing_plants":
        if "new_plant" in ct.keys():
            num_new_plants = len(ct["new_plant"])
            scaled_plants = grid.plant.iloc[:-num_new_plants]
            new_plants = grid.plant.iloc[-num_new_plants:]
            grouped_new_capacities = new_plants.groupby(["x", "y",
                                                         "type"]).sum().Pmax
            grouped_new_capacities = grouped_new_capacities.reset_index()
            xy_capacity["new"] = grouped_new_capacities.query("Pmax > 0")
        else:
            scaled_plants = grid.plant
            new_plants = pd.DataFrame(columns=grid.plant.columns)
            xy_capacity["new"] = pd.DataFrame(
                columns=["x", "y", "type", "Pmax"])
        grouped_capacities = scaled_plants.groupby(["x", "y",
                                                    "type"]).sum().Pmax
        grouped_capacities = grouped_capacities.reset_index()
        xy_capacity["existing"] = grouped_capacities.query("Pmax > 0")
    else:
        raise ValueError(f"Unknown disaggregation method: {disaggregation}")

    # capacity circles
    renderers = []
    for tranche, plants in xy_capacity.items():
        for resource in sorted(resources):
            if disaggregation is None:
                legend_label = f"{resource} capacity"
            elif disaggregation == "new_vs_existing_plants":
                legend_label = f"{resource} capacity of {tranche} plants"
            if resource not in plants.type.unique():
                print(f"no {resource} plants for grouping: {tranche}")
                continue
            matching_plants = plants.query("type == @resource")
            data = {
                "x": matching_plants["x"],
                "y": matching_plants["y"],
                "capacity": matching_plants["Pmax"],
                "radius": matching_plants["Pmax"]**0.5 * size_factor,
            }
            circle = canvas.circle(
                "x",
                "y",
                color=mcolors.to_hex(type_colors[resource]),
                alpha=0.8,
                size="radius",
                source=ColumnDataSource(data),
                legend_label=legend_label,
            )
            renderers.append(circle)

    hover = HoverTool(
        tooltips=[
            ("Capacity (MW)", "@capacity"),
        ],
        renderers=renderers,
    )

    canvas.add_tools(hover)

    return canvas