Exemplo n.º 1
0
def calculate_ac_inv_costs(scenario, sum_results=True, exclude_branches=None):
    """Calculate cost of upgrading AC lines and/or transformers in a scenario.
    NEEM regions are used to find regional multipliers.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :param bool sum_results: whether to sum data frame for each branch type. Defaults to
        True.
    :return: (*dict*) -- keys are {'line_cost', 'transformer_cost'}, values are either
        float if ``sum_results``, or pandas Series indexed by branch ID.
        Whether summed or not, values are $USD, inflation-adjusted to today.
    """

    base_grid = Grid(scenario.info["interconnect"].split("_"))
    grid = scenario.state.get_grid()

    # find upgraded AC lines
    grid_new = cp.deepcopy(grid)
    # Reindex so that we don't get NaN when calculating upgrades for new branches
    base_grid.branch = base_grid.branch.reindex(grid_new.branch.index).fillna(0)
    grid_new.branch.rateA = grid.branch.rateA - base_grid.branch.rateA
    grid_new.branch = grid_new.branch[grid_new.branch.rateA != 0.0]
    if exclude_branches is not None:
        present_exclude_branches = set(exclude_branches) & set(grid_new.branch.index)
        grid_new.branch.drop(index=present_exclude_branches, inplace=True)

    costs = _calculate_ac_inv_costs(grid_new, sum_results)
    return costs
Exemplo n.º 2
0
def calculate_ac_inv_costs(scenario, sum_results=True, exclude_branches=None):
    """Given a Scenario object, calculate the total cost of building that scenario's
    upgrades of lines and transformers.
    Currently uses NEEM regions to find regional multipliers.
    Currently ignores financials, but all values are in 2010 $-year.
    Need to test that there aren't any na values in regional multipliers
    (some empty parts of table)

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :param boolean sum_results: if True, sum dataframe for each category.
    :return: (*dict*) -- Total costs (line costs, transformer costs) (in $2010).
    """

    base_grid = Grid(scenario.info["interconnect"].split("_"))
    grid = scenario.state.get_grid()

    # find upgraded AC lines
    grid_new = cp.deepcopy(grid)
    # Reindex so that we don't get NaN when calculating upgrades for new branches
    base_grid.branch = base_grid.branch.reindex(
        grid_new.branch.index).fillna(0)
    grid_new.branch.rateA = grid.branch.rateA - base_grid.branch.rateA
    grid_new.branch = grid_new.branch[grid_new.branch.rateA != 0.0]
    if exclude_branches is not None:
        present_exclude_branches = set(exclude_branches) & set(
            grid_new.branch.index)
        grid_new.branch.drop(index=present_exclude_branches, inplace=True)

    costs = _calculate_ac_inv_costs(grid_new, sum_results)
    return costs
Exemplo n.º 3
0
def _identify_mesh_branch_upgrades(
    ref_scenario,
    upgrade_n=100,
    quantile=0.95,
    allow_list=None,
    deny_list=None,
    method="branches",
):
    """Identify the N most congested branches in a previous scenario, based on
    the quantile value of congestion duals. A quantile value of 0.95 obtains
    the branches with highest dual in top 5% of hours.

    :param powersimdata.scenario.scenario.Scenario ref_scenario: the reference
        scenario to be used to determine the most congested branches.
    :param int upgrade_n: the number of branches to upgrade.
    :param float quantile: the quantile to use to judge branch congestion.
    :param list/set/tuple/None allow_list: only select from these branch IDs.
    :param list/set/tuple/None deny_list: never select any of these branch IDs.
    :param str method: prioritization method: 'branches', 'MW', or 'MWmiles'.
    :raises ValueError: if 'method' not recognized, or not enough branches to
        upgrade.
    :return: (*set*) -- A set of ints representing branch indices.
    """

    # How big does a dual value have to be to be 'real' and not barrier cruft?
    cong_significance_cutoff = 1e-6  # $/MWh
    # If we rank by MW-miles, what 'length' do we give to zero-length branches?
    zero_length_value = 1  # miles

    # Validate method input
    allowed_methods = ("branches", "MW", "MWmiles", "cost")
    if method not in allowed_methods:
        allowed_list = ", ".join(allowed_methods)
        raise ValueError(f"method must be one of: {allowed_list}")

    # Get raw congestion dual values, add them
    ref_cong_abs = ref_scenario.state.get_congu(
    ) + ref_scenario.state.get_congl()
    all_branches = set(ref_cong_abs.columns.tolist())
    # Create validated composite allow list
    composite_allow_list = _construct_composite_allow_list(
        all_branches, allow_list, deny_list)

    # Parse 2-D array to vector of quantile values
    ref_cong_abs = ref_cong_abs.filter(items=composite_allow_list)
    quantile_cong_abs = ref_cong_abs.quantile(quantile)
    # Filter out insignificant values
    significance_bitmask = quantile_cong_abs > cong_significance_cutoff
    quantile_cong_abs = quantile_cong_abs.where(significance_bitmask).dropna()
    # Filter based on composite allow list
    congested_indices = list(quantile_cong_abs.index)

    # Ensure that we have enough congested branches to upgrade
    num_congested = len(quantile_cong_abs)
    if num_congested < upgrade_n:
        err_msg = "not enough congested branches: "
        err_msg += f"{upgrade_n} desired, but only {num_congested} congested."
        raise ValueError(err_msg)

    # Calculate selected metric for congested branches
    if method == "cost":
        # Calculate costs for an upgrade dataframe containing only composite_allow_list
        base_grid = Grid(ref_scenario.info["interconnect"],
                         ref_scenario.info["grid_model"])
        base_grid.branch = base_grid.branch.filter(items=congested_indices,
                                                   axis=0)
        upgrade_costs = _calculate_ac_inv_costs(base_grid, sum_results=False)
        # Merge the individual line/transformer data into a single Series
        merged_upgrade_costs = pd.concat([v for v in upgrade_costs.values()])
    if method in ("MW", "MWmiles"):
        ref_grid = ref_scenario.state.get_grid()
        branch_ratings = ref_grid.branch.loc[congested_indices, "rateA"]
        # Calculate 'original' branch capacities, since that's our increment
        ref_ct = ref_scenario.state.get_ct()
        try:
            branch_ct = ref_ct["branch"]["branch_id"]
        except KeyError:
            branch_ct = {}
        branch_prev_scaling = pd.Series({
            i: (branch_ct[i] if i in branch_ct else 1)
            for i in congested_indices
        })
        branch_ratings = branch_ratings / branch_prev_scaling
    if method == "MW":
        branch_metric = quantile_cong_abs / branch_ratings
    elif method == "MWmiles":
        branch_lengths = ref_grid.branch.loc[congested_indices].apply(
            lambda x: haversine((x.from_lat, x.from_lon),
                                (x.to_lat, x.to_lon)),
            axis=1)
        # Replace zero-length branches by designated default, don't divide by 0
        branch_lengths = branch_lengths.replace(0, value=zero_length_value)
        branch_metric = quantile_cong_abs / (branch_ratings * branch_lengths)
    elif method == "cost":
        branch_metric = quantile_cong_abs / merged_upgrade_costs
    else:
        # By process of elimination, all that's left is method 'branches'
        branch_metric = quantile_cong_abs

    # Sort by our metric, grab indexes for N largest values (tail), return
    ranked_branches = set(branch_metric.sort_values().tail(upgrade_n).index)
    return ranked_branches