def calculate_dcline_difference(grid1, grid2):
    """Calculate capacity differences between dcline tables, and add to/from lat/lon.

    :param powersimdata.input.grid.Grid grid1: first grid instance.
    :param powersimdata.input.grid.Grid grid2: second grid instance.
    :return: (*pandas.DataFrame*) -- data frame with all indices, plus new columns:
        diff, from_lat, from_lon, to_lat, to_lon.
    """
    _check_grid_type(grid1)
    _check_grid_type(grid2)
    dcline1, dcline2 = _reindex_as_necessary(
        grid1.dcline, grid2.dcline, ["from_bus_id", "to_bus_id"]
    )
    # Get latitudes and longitudes for to & from buses
    for dcline, grid in [(dcline1, grid1), (dcline2, grid2)]:
        dcline["from_lat"] = grid.bus.loc[dcline.from_bus_id, "lat"].to_numpy()
        dcline["from_lon"] = grid.bus.loc[dcline.from_bus_id, "lon"].to_numpy()
        dcline["to_lat"] = grid.bus.loc[dcline.to_bus_id, "lat"].to_numpy()
        dcline["to_lon"] = grid.bus.loc[dcline.to_bus_id, "lon"].to_numpy()
    dc_merge = dcline1.merge(
        dcline2, how="outer", right_index=True, left_index=True, suffixes=(None, "_2")
    )
    dc_merge["diff"] = dc_merge.Pmax_2.fillna(0) - dc_merge.Pmax.fillna(0)
    # Ensure that lats & lons get filled in as necessary from grid2.dcline entries
    for l in ["from_lat", "from_lon", "to_lat", "to_lon"]:
        dc_merge[l].fillna(dc_merge[f"{l}_2"], inplace=True)

    return dc_merge
Exemple #2
0
def get_resources_in_grid(grid):
    """Get resources in grid.

    :param powersimdata.input.grid.Grid grid: a Grid instance.
    :return: (*set*) -- name of all resources in grid.
    """
    _check_grid_type(grid)
    resources = set(grid.plant["type"].unique())
    return resources
Exemple #3
0
def get_active_resources_in_grid(grid):
    """Get active resources in grid.

    :param powersimdata.input.grid.Grid grid: a Grid instance.
    :return: (*set*) -- name of active resources in grid.
    """
    _check_grid_type(grid)
    active_resources = set(grid.plant.loc[grid.plant["Pmax"] > 0].type.unique())
    return active_resources
Exemple #4
0
def summarize_plant_to_location(df, grid):
    """Take a plant-column data frame and sum to a location-column data frame.

    :param pandas.DataFrame df: dataframe, columns are plant id in grid.
    :param powersimdata.input.grid.Grid grid: Grid instance.
    :return: (*pandas.DataFrame*) -- index: df index, columns: location tuples.
    """
    _check_data_frame(df, "PG")
    _check_grid_type(grid)
    _check_plants_are_in_grid(df.columns.to_list(), grid)

    all_locations = grid.plant[["lat", "lon"]]
    locations_in_df = all_locations.loc[df.columns].to_records(index=False)
    location_data = df.groupby(locations_in_df, axis=1).sum()

    return location_data
Exemple #5
0
def summarize_plant_to_bus(df, grid, all_buses=False):
    """Take a plant-column data frame and sum to a bus-column data frame.

    :param pandas.DataFrame df: dataframe, columns are plant id in grid.
    :param powersimdata.input.grid.Grid grid: Grid instance.
    :param boolean all_buses: return all buses in grid, not just plant buses.
    :return: (*pandas.DataFrame*) -- index as df input, columns are buses.
    """
    _check_data_frame(df, "PG")
    _check_grid_type(grid)
    _check_plants_are_in_grid(df.columns.to_list(), grid)

    all_buses_in_grid = grid.plant["bus_id"]
    buses_in_df = all_buses_in_grid.loc[df.columns]
    bus_data = df.T.groupby(buses_in_df).sum().T
    if all_buses:
        bus_data = pd.DataFrame(bus_data,
                                columns=grid.bus.index,
                                index=df.index).fillna(0.0)

    return bus_data
def summarize_emissions_by_bus(emissions, grid):
    """Calculate total emissions by generator type and bus.

    :param pandas.DataFrame emissions: hourly emissions by generator as returned by
        :func:`generate_emissions_stats`.
    :param powersimdata.input.grid.Grid grid: grid object.
    :return: (*dict*) -- annual emissions by fuel and bus.
    """

    _check_time_series(emissions, "emissions")
    if (emissions < -1e-3).any(axis=None):
        raise ValueError("emissions must be non-negative")

    _check_grid_type(grid)
    plant = grid.plant

    # sum by generator
    plant_totals = emissions.sum()
    # set up output data structure
    plant_buses = plant["bus_id"].unique()
    bus_totals_by_type = {
        f: {b: 0
            for b in plant_buses}
        for f in grid.model_immutables.plants["carbon_resources"]
    }
    # sum by fuel by bus
    for p in plant_totals.index:
        plant_type = plant.loc[p, "type"]
        if plant_type not in grid.model_immutables.plants["carbon_resources"]:
            continue
        plant_bus = plant.loc[p, "bus_id"]
        bus_totals_by_type[plant_type][plant_bus] += plant_totals.loc[p]
    # filter out buses whose emissions are zero
    bus_totals_by_type = {
        r: {b: v
            for b, v in bus_totals_by_type[r].items() if v > 0}
        for r in grid.model_immutables.plants["carbon_resources"]
    }

    return bus_totals_by_type
def map_interconnections(
    grid,
    branch_distance_cutoff=5,
    figsize=(1400, 800),
    branch_width_scale_factor=0.5,
    hvdc_width_scale_factor=1,
    b2b_size_scale_factor=50,
    state_borders_kwargs=None,
):
    """Map transmission lines color coded by interconnection.

    :param powersimdata.input.grid.Grid grid: grid object.
    :param int/float branch_distance_cutoff: distance cutoff for branch display.
    :param tuple figsize: size of the bokeh figure (in pixels).
    :param int/float branch_width_scale_factor: scale factor for branch capacities.
    :param int/float hvdc_width_scale_factor: scale factor for hvdc capacities.
    :param int/float b2b_size_scale_factor: scale factor for back-to_back capacities.
    :param dict state_borders_kwargs: keyword arguments to be passed to
        :func:`postreise.plot.plot_states.add_state_borders`.
    :return: (*bokeh.plotting.figure*) -- interconnection map with lines and nodes.
    :raises TypeError:
        if ``branch_device_cutoff`` is not ``float``.
        if ``branch_width_scale_factor`` is not ``int`` or ``float``.
        if ``hvdc_width_scale_factor`` is not ``int`` or ``float``.
        if ``b2b_size_scale_factor`` is not ``int`` or ``float``.
    :raises ValueError:
        if ``branch_device_cutoff`` is negative.
        if ``branch_width_scale_factor`` is negative.
        if ``hvdc_width_scale_factor`` is negative.
        if ``b2b_size_scale_factor`` is negative.
        if grid model is not supported.
    """
    _check_grid_type(grid)
    if not isinstance(branch_distance_cutoff, (int, float)):
        raise TypeError("branch_distance_cutoff must be an int")
    if branch_distance_cutoff <= 0:
        raise ValueError("branch_distance_cutoff must be strictly positive")
    if not isinstance(branch_width_scale_factor, (int, float)):
        raise TypeError("branch_width_scale_factor must be a int/float")
    if branch_width_scale_factor < 0:
        raise ValueError("branch_width_scale_factor must be positive")
    if not isinstance(hvdc_width_scale_factor, (int, float)):
        raise TypeError("hvdc_width_scale_factor must be a int/float")
    if hvdc_width_scale_factor < 0:
        raise ValueError("hvdc_width_scale_factor must be positive")
    if not isinstance(b2b_size_scale_factor, (int, float)):
        raise TypeError("b2b_size_scale_factor must be a int/float")
    if b2b_size_scale_factor < 0:
        raise ValueError("b2b_size_scale_factor must be positive")

    # branches
    branch = grid.branch.copy()
    branch["to_coord"] = list(zip(branch["to_lat"], branch["to_lon"]))
    branch["from_coord"] = list(zip(branch["from_lat"], branch["from_lon"]))
    branch["dist"] = branch.apply(
        lambda row: distance.haversine(row["to_coord"], row["from_coord"]),
        axis=1)
    branch = branch.loc[branch["dist"] > branch_distance_cutoff]
    branch = project_branch(branch)

    branch_west = branch.loc[branch["interconnect"] == "Western"]
    branch_east = branch.loc[branch["interconnect"] == "Eastern"]
    branch_tx = branch.loc[branch["interconnect"] == "Texas"]

    # HVDC lines
    all_dcline = grid.dcline.copy()
    all_dcline["from_lon"] = grid.bus.loc[all_dcline["from_bus_id"],
                                          "lon"].values
    all_dcline["from_lat"] = grid.bus.loc[all_dcline["from_bus_id"],
                                          "lat"].values
    all_dcline["to_lon"] = grid.bus.loc[all_dcline["to_bus_id"], "lon"].values
    all_dcline["to_lat"] = grid.bus.loc[all_dcline["to_bus_id"], "lat"].values
    all_dcline = project_branch(all_dcline)

    if grid.grid_model == "usa_tamu":
        b2b_id = range(9)
    else:
        raise ValueError("grid model is not supported")
    dcline = all_dcline.iloc[~all_dcline.index.isin(b2b_id)]
    b2b = all_dcline.iloc[b2b_id]

    # create canvas
    canvas = create_map_canvas(figsize=figsize)

    # add state borders
    default_state_borders_kwargs = {
        "line_width": 2,
        "fill_alpha": 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)

    # add state tooltips
    state_counts = count_nodes_per_state(grid)
    state2label = {
        s: c
        for s, c in zip(state_counts.index, state_counts.to_numpy())
    }
    canvas = add_state_tooltips(canvas, "nodes", state2label)

    canvas.multi_line(
        branch_west[["from_x", "to_x"]].to_numpy().tolist(),
        branch_west[["from_y", "to_y"]].to_numpy().tolist(),
        color="#006ff9",
        line_width=branch_west["rateA"].abs() * 1e-3 *
        branch_width_scale_factor,
        legend_label="Western",
    )

    canvas.multi_line(
        branch_east[["from_x", "to_x"]].to_numpy().tolist(),
        branch_east[["from_y", "to_y"]].to_numpy().tolist(),
        color="#8B36FF",
        line_width=branch_east["rateA"].abs() * 1e-3 *
        branch_width_scale_factor,
        legend_label="Eastern",
    )

    canvas.multi_line(
        branch_tx[["from_x", "to_x"]].to_numpy().tolist(),
        branch_tx[["from_y", "to_y"]].to_numpy().tolist(),
        color="#01D4ED",
        line_width=branch_tx["rateA"].abs() * 1e-3 * branch_width_scale_factor,
        legend_label="Texas",
    )

    canvas.multi_line(
        dcline[["from_x", "to_x"]].to_numpy().tolist(),
        dcline[["from_y", "to_y"]].to_numpy().tolist(),
        color="#FF2370",
        line_width=dcline["Pmax"] * 1e-3 * hvdc_width_scale_factor,
        legend_label="HVDC",
    )

    canvas.scatter(
        x=b2b["from_x"],
        y=b2b["from_y"],
        color="#FF2370",
        marker="triangle",
        size=b2b["Pmax"] * 1e-3 * b2b_size_scale_factor,
        legend_label="Back-to-Back",
    )

    canvas.legend.location = "bottom_left"
    canvas.legend.label_text_font_size = "12pt"

    return canvas
Exemple #8
0
def test_check_grid_type_success(mock_grid):
    _check_grid_type(mock_grid)
Exemple #9
0
def test_check_grid_type_failure():
    arg = (1, pd.DataFrame({"California": [1, 2, 3], "Texas": [4, 5, 6]}))
    for a in arg:
        with pytest.raises(TypeError):
            _check_grid_type(a)