예제 #1
0
def append_batch_results(m, tag=None):
    if tag is not None:
        t = "_"+str(tag)
    else:
        t = ""
    # append results to the batch results file
    demand_components = [c for c in ('lz_demand_mw', 'DemandResponse') if hasattr(m, c)]
    util.append_table(m, 
        output_file=os.path.join(output_dir, "summary{t}.txt".format(t=t)), 
        values=lambda m: (
            m.demand_response_max_share if hasattr(m, 'demand_response_max_share') else 0.0,
            m.Minimize_System_Cost,
            # next expression calculates NPV of total cost / NPV of kWh generated
            m.Minimize_System_Cost
                / sum(
                    m.bring_timepoint_costs_to_base_year[t] * 1000.0 *
                    sum(getattr(m, c)[lz, t] for c in demand_components for lz in m.LOAD_ZONES)
                    for t in m.TIMEPOINTS 
                )
        ) + tuple(
            # next expression calculates NPV of total cost / NPV of kWh generated in each period
            m.SystemCostPerPeriod[p]
                / sum(
                    m.bring_timepoint_costs_to_base_year[t] * 1000.0 *
                    sum(getattr(m, c)[lz, t] for c in demand_components for lz in m.LOAD_ZONES)
                    for t in m.PERIOD_TPS[p]
                )
            for p in m.PERIODS
        )
    )
def write_batch_results(m):
    # append results to the batch results file, creating it if needed
    output_file = os.path.join(m.options.outputs_dir, "demand_response_summary.tsv")

    # create a file to hold batch results if it doesn't already exist
    # note: we retain this file across scenarios so it can summarize all results,
    # but this means it needs to be manually cleared before launching a new 
    # batch of scenarios (e.g., when running get_scenario_data or clearing the
    # scenario_queue directory)
    if not os.path.isfile(output_file):
        util.create_table(output_file=output_file, headings=summary_headers(m))
    
    util.append_table(m, output_file=output_file, values=lambda m: summary_values(m))
예제 #3
0
def write_batch_results(m):
    # append results to the batch results file, creating it if needed
    output_file = os.path.join(m.options.outputs_dir,
                               "demand_response_summary.tsv")

    # create a file to hold batch results if it doesn't already exist
    # note: we retain this file across scenarios so it can summarize all results,
    # but this means it needs to be manually cleared before launching a new
    # batch of scenarios (e.g., when running get_scenario_data or clearing the
    # scenario_queue directory)
    if not os.path.isfile(output_file):
        util.create_table(output_file=output_file, headings=summary_headers(m))

    util.append_table(m,
                      output_file=output_file,
                      values=lambda m: summary_values(m))
예제 #4
0
def post_iterate(m):
    print "\n\n======================================================="
    print "Solved model"
    print "======================================================="
    print "Total cost: ${v:,.0f}".format(v=value(m.SystemCost))

    # TODO:
    # maybe calculate prices for the next round here and attach them to the
    # model, so they can be reported as final prices (currently we don't
    # report the final prices, only the prices prior to the final model run)

    SystemCost = value(m.SystemCost)  # calculate once to save time
    print "prev_SystemCost={}, SystemCost={}, ratio={}".format(
        m.prev_SystemCost, SystemCost,
        None if m.prev_SystemCost is None else SystemCost / m.prev_SystemCost)

    tag = m.options.scenario_name
    outputs_dir = m.options.outputs_dir

    # report information on most recent bid
    if m.iteration_number == 0:
        util.create_table(
            output_file=os.path.join(outputs_dir, "bid_{t}.tsv".format(t=tag)),
            headings=("bid_num", "load_zone", "timeseries", "timepoint",
                      "marginal_cost", "price", "bid_load", "wtp",
                      "base_price", "base_load"))
    b = m.DR_BID_LIST.last()  # current bid
    util.append_table(m,
                      m.LOAD_ZONES,
                      m.TIMEPOINTS,
                      output_file=os.path.join(outputs_dir,
                                               "bid_{t}.tsv".format(t=tag)),
                      values=lambda m, z, tp: (
                          b,
                          z,
                          m.tp_ts[tp],
                          m.tp_timestamp[tp],
                          m.prev_marginal_cost[z, tp],
                          m.dr_price[b, z, tp],
                          m.dr_bid[b, z, tp],
                          m.dr_bid_benefit[b, z, m.tp_ts[tp]],
                          m.base_data_dict[z, tp][1],
                          m.base_data_dict[z, tp][0],
                      ))

    # store the current bid weights for future reference
    if m.iteration_number == 0:
        util.create_table(output_file=os.path.join(
            outputs_dir, "bid_weights_{t}.tsv".format(t=tag)),
                          headings=("iteration", "load_zone", "timeseries",
                                    "bid_num", "weight"))
    util.append_table(m,
                      m.LOAD_ZONES,
                      m.TIMESERIES,
                      m.DR_BID_LIST,
                      output_file=os.path.join(
                          outputs_dir, "bid_weights_{t}.tsv".format(t=tag)),
                      values=lambda m, z, ts, b:
                      (len(m.DR_BID_LIST), z, ts, b, m.DRBidWeight[b, z, ts]))

    # report the dual costs
    write_dual_costs(m)

    # if m.iteration_number % 5 == 0:
    #     # save time by only writing results every 5 iterations
    # write_results(m)

    write_results(m)
    write_batch_results(m)
def add_bids(m, bids, tag):
    """ 
    accept a list of bids written as tuples like
    (lz, ts, prices, demand, wtp)
    where lz is the load zone, ts is the timeseries, 
    demand is a list of demand levels for the timepoints during that series, 
    and wtp is the private benefit from consuming the amount of power in that bid.
    Then add that set of bids to the model
    """
    # create a bid ID and add it to the list of bids
    if len(m.DR_BID_LIST) == 0:
        b = 1
    else:
        b = max(m.DR_BID_LIST) + 1

    # sometimes the demand side reports a strangely high willingness to pay for a particular bundle.
    # then, when prices change, it chooses other bundles, but reports a lower willingness to pay
    # for them than for the earlier bundle. This suggests that wtp for the earlier bundle is 
    # overstated. So here we go back and reduce wtp for some earlier bundles based on the fact that
    # they were not selected at the current prices (so they must actually have a lower wtp than the
    # current bundle). This has the effect of gradually forgetting older bids that aren't re-offered
    # as the model converges toward final prices.
    # for (lz, ts, prices, demand, wtp) in bids:
    #     cs_new = wtp - sum_product(prices, demand)
    #     for bid in m.DR_BID_LIST:
    #         cs_old = value(m.dr_bid_benefit[bid, lz, ts]) \
    #                     - sum_product(prices, [value(m.dr_bid[bid, lz, tp]) for tp in m.TS_TPS[ts]])
    #         if cs_old > cs_new:
    #             # the old bid is reported to have higher consumer surplus at the current prices
    #             # than the new bid.
    #             # this shouldn't happen, but it does.
    #             # reduce implied consumer surplus for the old bid so it is no more than the cs for the new bid
    #             # at the current prices.
    #             if 'drop_bad_bids' in tag:
    #                 print "dropping bid {b} from model because wtp is too high.".format(b=(bid, lz, ts))
    #                 m.dr_bid_benefit[bid, lz, ts] -= (cs_old - cs_new + 1e7)
    #             if 'adj_bad_bids' in tag:
    #                 print "reducing wtp for bid {b} by ${adj}".format(b=(bid, lz, ts), adj=cs_old-cs_new)
    #                 m.dr_bid_benefit[bid, lz, ts] -= (cs_old - cs_new)


    m.DR_BID_LIST.add(b)
    # m.DR_BIDS_LZ_TP.reconstruct()
    # m.DR_BIDS_LZ_TS.reconstruct()
    # add the bids for each load zone and timepoint to the dr_bid list
    for (lz, ts, prices, demand, wtp) in bids:
        # record the private benefit
        m.dr_bid_benefit[b, lz, ts] = wtp
        # record the level of demand for each timepoint
        timepoints = m.TS_TPS[ts]
        # print "ts: "+str(ts)
        # print "demand: " + str(demand)
        # print "timepoints: " + str([t for t in timepoints])
        for i, d in enumerate(demand):
            # print "i+1: "+str(i+1)
            # print "d: "+str(d)
            # print "timepoints[i+1]: "+str(timepoints[i+1])
            # note: demand is a python list or array, which uses 0-based indexing, but
            # timepoints is a pyomo set, which uses 1-based indexing, so we have to shift the index by 1.
            m.dr_bid[b, lz, timepoints[i+1]] = d

    print "len(m.DR_BID_LIST): {l}".format(l=len(m.DR_BID_LIST))
    print "m.DR_BID_LIST: {b}".format(b=[x for x in m.DR_BID_LIST])

    # store bid information for later reference
    # this has to be done after the model is updated and
    # before DRBidWeight is reconstructed (which destroys the duals)
    if b == 1:
        util.create_table(
            output_file=os.path.join("outputs", "bid_{t}.txt".format(t=tag)), 
            headings=("bid_num", "load_zone", "timepoint_label", "marginal_cost", "bid", "wtp")
        )
    util.append_table(m, m.LOAD_ZONES, m.TIMEPOINTS,
        output_file=os.path.join("outputs", "bid_{t}.txt".format(t=tag)), 
        values=lambda m, lz, tp: (
            b,
            lz,
            m.tp_timestamp[tp],
            m.dual[m.Energy_Balance[lz, tp]]/m.bring_timepoint_costs_to_base_year[tp],
            m.dr_bid[max(m.DR_BID_LIST), lz, tp],
            m.dr_bid_benefit[b, lz, m.tp_ts[tp]]
        )
    )

    # reconstruct the components that depend on m.DR_BID_LIST, m.dr_bid_benefit and m.dr_bid
    m.DRBidWeight.reconstruct()
    m.DR_Convex_Bid_Weight.reconstruct()
    m.FlexibleDemand.reconstruct()
    m.DR_Welfare_Cost.reconstruct()
    # it seems like we have to reconstruct the higher-level components that depend on these 
    # ones (even though these are Expressions), because otherwise they refer to objects that
    # used to be returned by the Expression but aren't any more (e.g., versions of DRBidWeight 
    # that no longer exist in the model).
    # (i.e., Energy_Balance refers to the items returned by FlexibleDemand instead of referring 
    # to FlexibleDemand itself)
    m.Energy_Balance.reconstruct()
    m.SystemCostPerPeriod.reconstruct()
    m.Minimize_System_Cost.reconstruct()    # may not be needed, since it seems to store the rule
def update_demand(m, tag):
    """
    This should be called after solving the model, in order to calculate new bids
    to include in future runs. The first time through, it also uses the fixed demand
    and marginal costs to calibrate the demand system, and then replaces the fixed
    demand with the flexible demand system.
    """
    global baseData
    first_run = (baseData is None)

    if first_run:
        # first time through, calibrate the demand system and add it to the model
        # baseData consists of a list of tuples showing (load_zone, timeseries, baseLoad (list) and basePrice)
        # note: the constructor below assumes list comprehensions will preserve the order of the underlying list
        # (which is guaranteed according to http://stackoverflow.com/questions/1286167/is-the-order-of-results-coming-from-a-list-comprehension-guaranteed)
        
        # calculate the average-cost price for the current study period
        # TODO: find basePrice for each period that corresponds to the assumptions 
        # used in the base-case load forecast
        # TODO: add in something for the fixed costs, to make marginal cost commensurate with the basePrice
        # for now, we just use a flat price roughly equal to backstop generation.
        #baseCosts = [m.dual[m.EnergyBalance[lz, tp]] for lz in m.LOAD_ZONES for tp in m.TIMEPOINTS]
        basePrice = 110  # average-cost price ($/MWh)
        baseData = [(
            lz, 
            ts, 
            [m.lz_demand_mw[lz, tp] for tp in m.TS_TPS[ts]],
            basePrice
        ) for lz in m.LOAD_ZONES for ts in m.TIMESERIES]

        util.create_table(
            output_file=os.path.join("outputs", "bid_weights_{t}.txt".format(t=tag)), 
            headings=("iteration", "load_zone", "timeseries", "bid_num", "weight")
        )

    else:
        # print "m.DRBidWeight (first day):"
        # print [(b, lz, ts, value(m.DRBidWeight[b, lz, ts])) 
        #     for b in m.DR_BID_LIST
        #     for lz in m.LOAD_ZONES
        #     for ts in m.TIMESERIES]
        print "m.DRBidWeight:"
        pprint([(lz, ts, [(b, value(m.DRBidWeight[b, lz, ts])) for b in m.DR_BID_LIST])
            for lz in m.LOAD_ZONES
            for ts in m.TIMESERIES])
        #print "DR_Convex_Bid_Weight:"
        #m.DR_Convex_Bid_Weight.pprint()

        # store the current bid weights for future reference
        # This should be done before adding the new bid.
        util.append_table(m, m.LOAD_ZONES, m.TIMESERIES, m.DR_BID_LIST, 
            output_file=os.path.join("outputs", "bid_weights_{t}.txt".format(t=tag)), 
            values=lambda m, lz, ts, b: (len(m.DR_BID_LIST), lz, ts, b, m.DRBidWeight[b, lz, ts])
        )


    # get new demand bids at the current marginal costs
    bids = []
    for i, (lz, ts, baseLoad, basePrice) in enumerate(baseData):
        # TODO: add in something for the fixed costs
        prices = [m.dual[m.Energy_Balance[lz, tp]]/m.bring_timepoint_costs_to_base_year[tp] for tp in m.TS_TPS[ts]]
        # set a floor on prices to avoid division-by-zero in the CES functions
        prices = [max(0.0001, p) for p in prices]
        # if i < 2:
        #     print "prices (day {i}): {p}".format(i=i, p=prices)
        #     print "weights: {w}".format(w=[m.bring_timepoint_costs_to_base_year[tp] for tp in m.TS_TPS[ts]])
        if '_ce_' in tag:
            (demand, wtp) = ce.bid(prices, baseLoad, basePrice)
        else:
            demand = ces.double_ces(prices, Theta, baseLoad, basePrice)
            wtp = ces.wtp(prices, Theta, baseLoad, basePrice)
        bids.append((lz, ts, prices, demand, wtp))

        # if i < 2:
        #     import pdb; pdb.set_trace()

    print "adding bids to model; first day="
    pprint(bids[0])
    # add the new bids to the model
    add_bids(m, bids, tag)
    print "m.dr_bid_benefit (first day):"
    pprint([(b, lz, ts, value(m.dr_bid_benefit[b, lz, ts])) 
        for b in m.DR_BID_LIST
        for lz in m.LOAD_ZONES
        for ts in [m.TIMESERIES.first()]])
    
    # print "m.dr_bid (first day):"
    # print [(b, lz, ts, value(m.dr_bid[b, lz, ts]))
    #     for b in m.DR_BID_LIST
    #     for lz in m.LOAD_ZONES 
    #     for ts in m.TS_TPS[m.TIMESERIES.first()]]
    
    if first_run:
        # add FlexibleDemand to the energy balance constraint and remove lz_demand_mw_as_consumption
        # note: it is easiest to do this after retrieving the bids because this
        # destroys the dual values which are needed for calculating the bids
        m.LZ_Energy_Balance_components.remove('lz_demand_mw_as_consumption')
        m.LZ_Energy_Balance_components.append('FlexibleDemand')
        m.Energy_Balance.reconstruct()
def post_iterate(m):
    print "\n\n======================================================="
    print "Solved model"
    print "======================================================="
    print "Total cost: ${v:,.0f}".format(v=value(m.SystemCost))


    # TODO: 
    # maybe calculate prices for the next round here and attach them to the
    # model, so they can be reported as final prices (currently we don't
    # report the final prices, only the prices prior to the final model run)

    SystemCost = value(m.SystemCost)    # calculate once to save time
    print "prev_SystemCost={}, SystemCost={}, ratio={}".format(
        m.prev_SystemCost, SystemCost, 
        None if m.prev_SystemCost is None else SystemCost/m.prev_SystemCost
    )

    tag = m.options.scenario_name
    outputs_dir = m.options.outputs_dir

    # report information on most recent bid
    if m.iteration_number == 0:
        util.create_table(
            output_file=os.path.join(outputs_dir, "bid_{t}.tsv".format(t=tag)), 
            headings=(
                "bid_num", "load_zone", "timeseries", "timepoint", "marginal_cost", "price", 
                "bid_load", "wtp", "base_price", "base_load"
            )
        )
    b = m.DR_BID_LIST.last()    # current bid
    util.append_table(m, m.LOAD_ZONES, m.TIMEPOINTS,
        output_file=os.path.join(outputs_dir, "bid_{t}.tsv".format(t=tag)), 
        values=lambda m, z, tp: (
            b,
            z,
            m.tp_ts[tp],
            m.tp_timestamp[tp],
            m.prev_marginal_cost[z, tp],
            m.dr_price[b, z, tp],
            m.dr_bid[b, z, tp],
            m.dr_bid_benefit[b, z, m.tp_ts[tp]],
            m.base_data_dict[z, tp][1],
            m.base_data_dict[z, tp][0],
        )
    )

    # store the current bid weights for future reference
    if m.iteration_number == 0:
        util.create_table(
            output_file=os.path.join(outputs_dir, "bid_weights_{t}.tsv".format(t=tag)), 
            headings=("iteration", "load_zone", "timeseries", "bid_num", "weight")
        )
    util.append_table(m, m.LOAD_ZONES, m.TIMESERIES, m.DR_BID_LIST, 
        output_file=os.path.join(outputs_dir, "bid_weights_{t}.tsv".format(t=tag)), 
        values=lambda m, z, ts, b: (len(m.DR_BID_LIST), z, ts, b, m.DRBidWeight[b, z, ts])
    )
    
    # report the dual costs
    write_dual_costs(m)

    # if m.iteration_number % 5 == 0:
    #     # save time by only writing results every 5 iterations
    # write_results(m)
    
    write_results(m)
    write_batch_results(m)
예제 #8
0
def add_bids(m, bids):
    """ 
    accept a list of bids written as tuples like
    (lz, ts, prices, demand, wtp)
    where lz is the load zone, ts is the timeseries, 
    demand is a list of demand levels for the timepoints during that series, 
    and wtp is the private benefit from consuming the amount of power in that bid.
    Then add that set of bids to the model
    """
    # create a bid ID and add it to the list of bids
    if len(m.DR_BID_LIST) == 0:
        b = 1
    else:
        b = max(m.DR_BID_LIST) + 1

    tag = m.options.scenario_name
    outputs_dir = m.options.outputs_dir

    m.DR_BID_LIST.add(b)
    # m.DR_BIDS_LZ_TP.reconstruct()
    # m.DR_BIDS_LZ_TS.reconstruct()
    # add the bids for each load zone and timepoint to the dr_bid list
    for (lz, ts, prices, demand, wtp) in bids:
        # record the private benefit
        m.dr_bid_benefit[b, lz, ts] = wtp
        # record the level of demand for each timepoint
        timepoints = m.TS_TPS[ts]
        # print "ts: "+str(ts)
        # print "demand: " + str(demand)
        # print "timepoints: " + str([t for t in timepoints])
        for i, d in enumerate(demand):
            # print "i+1: "+str(i+1)
            # print "d: "+str(d)
            # print "timepoints[i+1]: "+str(timepoints[i+1])
            # note: demand is a python list or array, which uses 0-based indexing, but
            # timepoints is a pyomo set, which uses 1-based indexing, so we have to shift the index by 1.
            m.dr_bid[b, lz, timepoints[i + 1]] = d
            m.dr_price[b, lz, timepoints[i + 1]] = prices[i]

    print "len(m.DR_BID_LIST): {l}".format(l=len(m.DR_BID_LIST))
    print "m.DR_BID_LIST: {b}".format(b=[x for x in m.DR_BID_LIST])

    # store bid information for later reference
    # this has to be done after the model is updated and
    # before DRBidWeight is reconstructed (which destroys the duals)
    if b == 1:
        util.create_table(
            output_file=os.path.join(outputs_dir, "bid_{t}.tsv".format(t=tag)),
            headings=("bid_num", "load_zone", "timeseries", "timepoint",
                      "marginal_cost", "price", "bid_load", "wtp",
                      "base_price", "base_load"))
    util.append_table(m,
                      m.LOAD_ZONES,
                      m.TIMEPOINTS,
                      output_file=os.path.join(outputs_dir,
                                               "bid_{t}.tsv".format(t=tag)),
                      values=lambda m, lz, tp: (
                          b,
                          lz,
                          m.tp_ts[tp],
                          m.tp_timestamp[tp],
                          electricity_marginal_cost(m, lz, tp),
                          m.dr_price[max(m.DR_BID_LIST), lz, tp],
                          m.dr_bid[max(m.DR_BID_LIST), lz, tp],
                          m.dr_bid_benefit[b, lz, m.tp_ts[tp]],
                          m.base_data_dict[lz, tp][1],
                          m.base_data_dict[lz, tp][0],
                      ))

    write_results(m)
    write_batch_results(m)

    # reconstruct the components that depend on m.DR_BID_LIST, m.dr_bid_benefit and m.dr_bid
    m.DRBidWeight.reconstruct()
    m.DR_Convex_Bid_Weight.reconstruct()
    m.FlexibleDemand.reconstruct()
    m.DR_Welfare_Cost.reconstruct()
    # it seems like we have to reconstruct the higher-level components that depend on these
    # ones (even though these are Expressions), because otherwise they refer to objects that
    # used to be returned by the Expression but aren't any more (e.g., versions of DRBidWeight
    # that no longer exist in the model).
    # (i.e., Energy_Balance refers to the items returned by FlexibleDemand instead of referring
    # to FlexibleDemand itself)
    reconstruct_energy_balance(m)
    m.SystemCostPerPeriod.reconstruct()
    m.SystemCost.reconstruct()
예제 #9
0
def update_demand(m):
    """
    This should be called after solving the model, in order to calculate new bids
    to include in future runs. The first time through, it also uses the fixed demand
    and marginal costs to calibrate the demand system, and then replaces the fixed
    demand with the flexible demand system.
    """
    first_run = (m.base_data is None)
    outputs_dir = m.options.outputs_dir
    tag = m.options.scenario_name

    print "attaching new demand bid to model"
    if first_run:
        calibrate_model(m)

        util.create_table(output_file=os.path.join(
            outputs_dir, "bid_weights_{t}.tsv".format(t=tag)),
                          headings=("iteration", "load_zone", "timeseries",
                                    "bid_num", "weight"))
    else:  # not first run
        # print "m.DRBidWeight (first day):"
        # print [(b, lz, ts, value(m.DRBidWeight[b, lz, ts]))
        #     for b in m.DR_BID_LIST
        #     for lz in m.LOAD_ZONES
        #     for ts in m.TIMESERIES]
        print "m.DRBidWeight:"
        pprint([(lz, ts, [(b, value(m.DRBidWeight[b, lz, ts]))
                          for b in m.DR_BID_LIST]) for lz in m.LOAD_ZONES
                for ts in m.TIMESERIES])
        #print "DR_Convex_Bid_Weight:"
        #m.DR_Convex_Bid_Weight.pprint()

        # store the current bid weights for future reference
        # This should be done before adding the new bid.
        util.append_table(
            m,
            m.LOAD_ZONES,
            m.TIMESERIES,
            m.DR_BID_LIST,
            output_file=os.path.join(outputs_dir,
                                     "bid_weights_{t}.tsv".format(t=tag)),
            values=lambda m, lz, ts, b:
            (len(m.DR_BID_LIST), lz, ts, b, m.DRBidWeight[b, lz, ts]))

    # get new bids from the demand system at the current prices
    bids = get_bids(m)

    print "adding bids to model"
    # print "first day (lz, ts, prices, demand, wtp) ="
    # pprint(bids[0])
    # add the new bids to the model
    add_bids(m, bids)
    print "m.dr_bid_benefit (first day):"
    pprint([(b, lz, ts, value(m.dr_bid_benefit[b, lz, ts]))
            for b in m.DR_BID_LIST for lz in m.LOAD_ZONES
            for ts in [m.TIMESERIES.first()]])

    # print "m.dr_bid (first day):"
    # print [(b, lz, ts, value(m.dr_bid[b, lz, ts]))
    #     for b in m.DR_BID_LIST
    #     for lz in m.LOAD_ZONES
    #     for ts in m.TS_TPS[m.TIMESERIES.first()]]

    if first_run:
        # replace lz_demand_mw with FlexibleDemand in the energy balance constraint
        # note: it is easiest to do this after retrieving the bids because this
        # destroys the dual values which are needed for calculating the bids
        # note: the first two lines are simpler than the method I use, but my approach
        # preserves the ordering of the list, which is nice for reporting.
        # m.LZ_Energy_Components_Consume.remove('lz_demand_mw')
        # m.LZ_Energy_Components_Consume.append('FlexibleDemand')
        ecc = m.LZ_Energy_Components_Consume
        ecc[ecc.index('lz_demand_mw')] = 'FlexibleDemand'
        reconstruct_energy_balance(m)
예제 #10
0
def add_bids(m, bids):
    """ 
    accept a list of bids written as tuples like
    (lz, ts, prices, demand, wtp)
    where lz is the load zone, ts is the timeseries, 
    demand is a list of demand levels for the timepoints during that series, 
    and wtp is the private benefit from consuming the amount of power in that bid.
    Then add that set of bids to the model
    """
    # create a bid ID and add it to the list of bids
    if len(m.DR_BID_LIST) == 0:
        b = 1
    else:
        b = max(m.DR_BID_LIST) + 1

    tag = m.options.scenario_name
    outputs_dir = m.options.outputs_dir
    
    m.DR_BID_LIST.add(b)
    # m.DR_BIDS_LZ_TP.reconstruct()
    # m.DR_BIDS_LZ_TS.reconstruct()
    # add the bids for each load zone and timepoint to the dr_bid list
    for (lz, ts, prices, demand, wtp) in bids:
        # record the private benefit
        m.dr_bid_benefit[b, lz, ts] = wtp
        # record the level of demand for each timepoint
        timepoints = m.TS_TPS[ts]
        # print "ts: "+str(ts)
        # print "demand: " + str(demand)
        # print "timepoints: " + str([t for t in timepoints])
        for i, d in enumerate(demand):
            # print "i+1: "+str(i+1)
            # print "d: "+str(d)
            # print "timepoints[i+1]: "+str(timepoints[i+1])
            # note: demand is a python list or array, which uses 0-based indexing, but
            # timepoints is a pyomo set, which uses 1-based indexing, so we have to shift the index by 1.
            m.dr_bid[b, lz, timepoints[i+1]] = d
            m.dr_price[b, lz, timepoints[i+1]] = prices[i]

    print "len(m.DR_BID_LIST): {l}".format(l=len(m.DR_BID_LIST))
    print "m.DR_BID_LIST: {b}".format(b=[x for x in m.DR_BID_LIST])

    # store bid information for later reference
    # this has to be done after the model is updated and
    # before DRBidWeight is reconstructed (which destroys the duals)
    if b == 1:
        util.create_table(
            output_file=os.path.join(outputs_dir, "bid_{t}.tsv".format(t=tag)), 
            headings=(
                "bid_num", "load_zone", "timeseries", "timepoint", "marginal_cost", "price", 
                "bid_load", "wtp", "base_price", "base_load"
            )
        )   
    util.append_table(m, m.LOAD_ZONES, m.TIMEPOINTS,
        output_file=os.path.join(outputs_dir, "bid_{t}.tsv".format(t=tag)), 
        values=lambda m, lz, tp: (
            b,
            lz,
            m.tp_ts[tp],
            m.tp_timestamp[tp],
            electricity_marginal_cost(m, lz, tp),
            m.dr_price[max(m.DR_BID_LIST), lz, tp],
            m.dr_bid[max(m.DR_BID_LIST), lz, tp],
            m.dr_bid_benefit[b, lz, m.tp_ts[tp]],
            m.base_data_dict[lz, tp][1],
            m.base_data_dict[lz, tp][0],
        )
    )

    write_results(m)
    write_batch_results(m)

    # reconstruct the components that depend on m.DR_BID_LIST, m.dr_bid_benefit and m.dr_bid
    m.DRBidWeight.reconstruct()
    m.DR_Convex_Bid_Weight.reconstruct()
    m.FlexibleDemand.reconstruct()
    m.DR_Welfare_Cost.reconstruct()
    # it seems like we have to reconstruct the higher-level components that depend on these 
    # ones (even though these are Expressions), because otherwise they refer to objects that
    # used to be returned by the Expression but aren't any more (e.g., versions of DRBidWeight 
    # that no longer exist in the model).
    # (i.e., Energy_Balance refers to the items returned by FlexibleDemand instead of referring 
    # to FlexibleDemand itself)
    reconstruct_energy_balance(m)
    m.SystemCostPerPeriod.reconstruct()
    m.SystemCost.reconstruct()
예제 #11
0
def update_demand(m):
    """
    This should be called after solving the model, in order to calculate new bids
    to include in future runs. The first time through, it also uses the fixed demand
    and marginal costs to calibrate the demand system, and then replaces the fixed
    demand with the flexible demand system.
    """
    first_run = (m.base_data is None)
    outputs_dir = m.options.outputs_dir
    tag = m.options.scenario_name

    print "attaching new demand bid to model"
    if first_run:
        calibrate_model(m)

        util.create_table(
            output_file=os.path.join(outputs_dir, "bid_weights_{t}.tsv".format(t=tag)), 
            headings=("iteration", "load_zone", "timeseries", "bid_num", "weight")
        )
    else:   # not first run
        # print "m.DRBidWeight (first day):"
        # print [(b, lz, ts, value(m.DRBidWeight[b, lz, ts])) 
        #     for b in m.DR_BID_LIST
        #     for lz in m.LOAD_ZONES
        #     for ts in m.TIMESERIES]
        print "m.DRBidWeight:"
        pprint([(lz, ts, [(b, value(m.DRBidWeight[b, lz, ts])) for b in m.DR_BID_LIST])
            for lz in m.LOAD_ZONES
            for ts in m.TIMESERIES])
        #print "DR_Convex_Bid_Weight:"
        #m.DR_Convex_Bid_Weight.pprint()

        # store the current bid weights for future reference
        # This should be done before adding the new bid.
        util.append_table(m, m.LOAD_ZONES, m.TIMESERIES, m.DR_BID_LIST, 
            output_file=os.path.join(outputs_dir, "bid_weights_{t}.tsv".format(t=tag)), 
            values=lambda m, lz, ts, b: (len(m.DR_BID_LIST), lz, ts, b, m.DRBidWeight[b, lz, ts])
        )

    # get new bids from the demand system at the current prices
    bids = get_bids(m)
    
    print "adding bids to model"
    # print "first day (lz, ts, prices, demand, wtp) ="
    # pprint(bids[0])
    # add the new bids to the model
    add_bids(m, bids)
    print "m.dr_bid_benefit (first day):"
    pprint([(b, lz, ts, value(m.dr_bid_benefit[b, lz, ts])) 
        for b in m.DR_BID_LIST
        for lz in m.LOAD_ZONES
        for ts in [m.TIMESERIES.first()]])
    
    # print "m.dr_bid (first day):"
    # print [(b, lz, ts, value(m.dr_bid[b, lz, ts]))
    #     for b in m.DR_BID_LIST
    #     for lz in m.LOAD_ZONES 
    #     for ts in m.TS_TPS[m.TIMESERIES.first()]]
    
    if first_run:
        # replace lz_demand_mw with FlexibleDemand in the energy balance constraint
        # note: it is easiest to do this after retrieving the bids because this
        # destroys the dual values which are needed for calculating the bids
        # note: the first two lines are simpler than the method I use, but my approach
        # preserves the ordering of the list, which is nice for reporting.
        # m.LZ_Energy_Components_Consume.remove('lz_demand_mw')
        # m.LZ_Energy_Components_Consume.append('FlexibleDemand')
        ecc = m.LZ_Energy_Components_Consume
        ecc[ecc.index('lz_demand_mw')] = 'FlexibleDemand'
        reconstruct_energy_balance(m)
예제 #12
0
def append_batch_results(m, scenario=None):
    # append results to the batch results file
    util.append_table(m, 
        output_file=os.path.join(output_dir, "summary_all_scenarios.tsv"), 
        values=lambda m: summary_values(m, scenario)
    )