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
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
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
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
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
def test_check_grid_type_success(mock_grid): _check_grid_type(mock_grid)
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)