コード例 #1
0
ファイル: reporting.py プロジェクト: xiangao1/Prescient
def report_load_generation_mismatch_for_deterministic_ruc(ruc_instance):
    time_periods = ruc_instance.data['system']['time_keys']
    buses = ruc_instance.data['elements']['bus']

    for i, t in enumerate(time_periods):
        mismatch_reported = False
        sum_mismatch = round_small_values(
            sum(bdict['p_balance_violation']['values'][i]
                for bdict in buses.values()))
        if sum_mismatch != 0.0:
            posLoadGenerateMismatch = round_small_values(
                sum(
                    max(bdict['p_balance_violation']['values'][i], 0.)
                    for bdict in buses.values()))
            negLoadGenerateMismatch = round_small_values(
                sum(
                    min(bdict['p_balance_violation']['values'][i], 0.)
                    for bdict in buses.values()))
            if negLoadGenerateMismatch != 0.0:
                print(
                    "Projected over-generation reported at t=%s -   total=%12.2f"
                    % (t, negLoadGenerateMismatch))
            if posLoadGenerateMismatch != 0.0:
                print(
                    "Projected load shedding reported at t=%s -     total=%12.2f"
                    % (t, posLoadGenerateMismatch))

        if 'reserve_shortfall' in ruc_instance.data['system']:
            reserve_shortfall_value = round_small_values(
                ruc_instance.data['system']['reserve_shortfall']['values'][i])
            if reserve_shortfall_value != 0.0:
                print(
                    "Projected reserve shortfall reported at t=%s - total=%12.2f"
                    % (t, reserve_shortfall_value))
コード例 #2
0
    def get_load_mismatches(self,
                            sced: OperationsModel) -> Tuple[float, float]:
        """ Get under-generation (load shedding) and over-generation.

        Returns
        -------
        load_shedding: float
            The total load shedding across all buses
        over_generation: float
            The total over-generation across all buses
        """
        load_generation_mismatch_value = round_small_values(
            sum(self.get_load_mismatch(sced, b) for b in self.get_buses(sced)))
        if load_generation_mismatch_value != 0.0:
            load_shedding_value = round_small_values(
                sum(
                    self.get_positive_load_mismatch(sced, b)
                    for b in self.get_buses(sced)))
            over_generation_value = round_small_values(
                sum(
                    self.get_negative_load_mismatch(sced, b)
                    for b in self.get_buses(sced)))
        else:
            load_shedding_value = 0.0
            over_generation_value = 0.0

        return load_shedding_value, over_generation_value
コード例 #3
0
 def get_total_renewables_curtailment(self, sced: OperationsModel) -> float:
     """ get how much renewable power could have been used but wasn't """
     total_curtailment = round_small_values(
         sum(
             self.get_renewables_curtailment(sced, g)
             for g in self.get_nondispatchable_generators(sced)))
     return total_curtailment
コード例 #4
0
    def _get_reserve_property(sced: OperationsModel,
                              reserve_id: ReserveIdentifier,
                              suffix: str) -> float:
        ''' Get the value of a particular reserve property.

            Reserve property name must follow a standard convention,
            f'{reserve_id.reserve_name}{suffix}'.
        '''
        data = ScedDataExtractor._get_reserve_parent(sced, reserve_id)
        attr = f'{reserve_id.reserve_name}{suffix}'
        if attr in data:
            if isinstance(data[attr], dict):
                return round_small_values(data[attr]['values'][0])
            else:
                return round_small_values(data[attr])
        else:
            return 0.
コード例 #5
0
 def get_reserve_shortfall(self, sced: OperationsModel) -> float:
     if 'reserve_shortfall' in sced.data['system']:
         return round_small_values(
             sced.data['system']['reserve_shortfall']['values'][0])
     else:
         return 0.
コード例 #6
0
def solve_deterministic_day_ahead_pricing_problem(solver, ruc_results, options, ptdf_manager):

    ## create a copy because we want to maintain the solution data
    ## in ruc_results
    pricing_type = options.day_ahead_pricing
    print("Computing day-ahead prices using method "+pricing_type.name+".")
    
    pricing_instance = ruc_results.clone()
    if pricing_type == PricingType.LMP:
        for g, g_dict in pricing_instance.elements(element_type='generator', generator_type='thermal'):
            g_dict['fixed_commitment'] = g_dict['commitment']
            if 'reg_provider' in g_dict:
                g_dict['fixed_regulation'] = g_dict['reg_provider']
        ## TODO: add fixings for storage; need hooks in EGRET
    elif pricing_type == PricingType.ELMP:
        ## for ELMP we fix all commitment binaries that were 0 in the RUC solve
        time_periods = pricing_instance.data['system']['time_keys']
        for g, g_dict in pricing_instance.elements(element_type='generator', generator_type='thermal'):
            g_dict['fixed_commitment'] = _get_fixed_if_off(g_dict['commitment'], 
                                                           g_dict.get('fixed_commitment', None))
            if 'reg_provider' in g_dict:
                g_dict['fixed_regulation'] = _get_fixed_if_off(g_dict['reg_provider'],
                                                               g_dict.get('fixed_regulation', None))
        ## TODO: add fixings for storage; need hooks in EGRET
    elif pricing_type == PricingType.ACHP:
        # don't do anything
        pass
    else:
        raise RuntimeError("Unknown pricing type "+pricing_type+".")

    ## change the penalty prices to the caps, if necessary
    reserve_requirement = ('reserve_requirement' in pricing_instance.data['system'])

    system = pricing_instance.data['system']

    # In case of shortfall, the price skyrockets, so we threshold the value.
    for system_key, threshold_value in get_attrs_to_price_option(options):
        if threshold_value is not None and ((system_key not in system) or
                (system[system_key] > threshold_value)):
            system[system_key] = threshold_value

    ptdf_manager.mark_active(pricing_instance)
    pyo_model = create_pricing_model(pricing_instance, relaxed=True,
                                     ptdf_options=ptdf_manager.damarket_ptdf_options,
                                     PTDF_matrix_dict=ptdf_manager.PTDF_matrix_dict)

    pyo_model.dual = Suffix(direction=Suffix.IMPORT)

    try:
        ## TODO: Should there be separate options for this run?
        pricing_results, _, _ = call_solver(solver,
                                            pyo_model,
                                            options,
                                            options.deterministic_ruc_solver_options,
                                            relaxed=True)
    except:
        print("Failed to solve pricing instance - likely because no feasible solution exists!")        
        output_filename = "bad_pricing.json"
        pricing_instance.write(output_filename)
        print("Wrote failed RUC model to file=" + output_filename)
        raise

    ptdf_manager.update_active(pricing_results)

    ## Debugging
    if pricing_results.data['system']['total_cost'] > ruc_results.data['system']['total_cost'] and not \
            math.isclose(pricing_results.data['system']['total_cost'],
                         ruc_results.data['system']['total_cost'],
                         rel_tol=1e-06, abs_tol=1e-06):
        print("The pricing run had a higher objective value than the MIP run. This is indicative of a bug.")
        print(f"pricing run cost: {pricing_results.data['system']['total_cost']}")
        print(f"MIP run cost    : {ruc_results.data['system']['total_cost']}")
        print("Writing LP pricing_problem.json")
        output_filename = 'pricing_instance.json'
        pricing_results.write(output_filename)

        output_filename = 'ruc_results.json'
        ruc_results.write(output_filename)

        raise RuntimeError("Halting due to bug in pricing.")

    day_ahead_prices = {}
    for b, b_dict in pricing_results.elements(element_type='bus'):
        for t,lmp in enumerate(b_dict['lmp']['values']):
            day_ahead_prices[b,t] = lmp

    if reserve_requirement:
        day_ahead_reserve_prices = {}
        for t,price in enumerate(pricing_results.data['system']['reserve_price']['values']):
            # Thresholding the value of the reserve price to the passed in option
            day_ahead_reserve_prices[t] = price

        print("Recalculating RUC reserve procurement")

        ## scale the provided reserves by the amount over we are
        thermal_reserve_cleared_DA = {}

        g_reserve_values = { g : g_dict['rg']['values'] for g, g_dict in ruc_results.elements(element_type='generator', generator_type='thermal') }
        reserve_shortfall = ruc_results.data['system']['reserve_shortfall']['values']
        reserve_requirement = ruc_results.data['system']['reserve_requirement']['values']

        for t in range(0,options.ruc_every_hours):
            reserve_provided_t = sum(reserve_vals[t] for reserve_vals in g_reserve_values.values()) 
            reserve_shortfall_t = reserve_shortfall[t]
            reserve_requirement_t = reserve_requirement[t]

            surplus_reserves_t = reserve_provided_t + reserve_shortfall_t - reserve_requirement_t

            ## if there's a shortfall, grab the full amount from the RUC solve
            ## or if there's no provided reserves this can safely be set to 1.
            if round_small_values(reserve_shortfall_t) > 0 or reserve_provided_t == 0:
                surplus_multiple_t = 1.
            else:
                ## scale the reserves from the RUC down by the same fraction
                ## so that they exactly meed the needed reserves
                surplus_multiple_t = reserve_requirement_t/reserve_provided_t
            for g, reserve_vals in g_reserve_values.items():
                thermal_reserve_cleared_DA[g,t] = reserve_vals[t]*surplus_multiple_t
    else:
        day_ahead_reserve_prices = { t : 0. for t,_ in enumerate(ruc_results.data['system']['time_keys']) }
        thermal_reserve_cleared_DA = { (g,t) : 0. \
                for t,_ in enumerate(ruc_results.data['system']['time_keys']) \
                for g,_ in ruc_results.elements(element_type='generator', generator_type='thermal') }
               
    thermal_gen_cleared_DA = {}
    renewable_gen_cleared_DA = {}
    virtual_gen_cleared_DA = {}

    for g, g_dict in ruc_results.elements(element_type='generator'):
        pg = g_dict['pg']['values']
        if g_dict['generator_type'] == 'thermal':
            store_dict = thermal_gen_cleared_DA
        elif g_dict['generator_type'] == 'renewable':
            store_dict = renewable_gen_cleared_DA
        elif g_dict['generator_type'] == 'virtual':
            store_dict = virtual_gen_cleared_DA
        else:
            raise RuntimeError(f"Unrecognized generator type {g_dict['generator_type']}")
        for t in range(0,options.ruc_every_hours):
            store_dict[g,t] = pg[t]

    return RucMarket(day_ahead_prices=day_ahead_prices,
                    day_ahead_reserve_prices=day_ahead_reserve_prices,
                    thermal_gen_cleared_DA=thermal_gen_cleared_DA,
                    thermal_reserve_cleared_DA=thermal_reserve_cleared_DA,
                    renewable_gen_cleared_DA=renewable_gen_cleared_DA,
                    virtual_gen_cleared_DA=virtual_gen_cleared_DA)
コード例 #7
0
 def has_load_shedding(self, sced: OperationsModel) -> bool:
     return any(round_small_values(self.get_positive_load_mismatch(sced, b)) > 0.0
                for b in self.get_buses(sced))