Пример #1
0
def pypower_loop (casefile, rootname):
  """ Public function to start PYPOWER solutions under control of FNCS

  The time step, maximum time, and other data must be set up in a JSON file.
  This function will run the case under FNCS, manage the FNCS message traffic,
  and shutdown FNCS upon completion. Five files are written:

  - *rootname.csv*; intermediate solution results during simulation
  - *rootname_m_dict.json*; metadata for post-processing
  - *bus_rootname_metrics.json*; bus metrics for GridLAB-D connections, upon completion
  - *gen_rootname_metrics.json*; bulk system generator metrics, upon completion
  - *sys_rootname_metrics.json*; bulk system-level metrics, upon completion

  Args:
    casefile (str): the configuring JSON file name, without extension
    rootname (str): the root filename for metrics output, without extension
  """
#  if len(sys.argv) == 3:
#    rootname = sys.argv[1]
#    casefile = sys.argv[2]
#  else:
#    print ('usage: python fncsPYPOWER.py metrics_rootname casedata.json')
#    sys.exit()

  ppc = load_json_case (casefile)
  StartTime = ppc['StartTime']
  tmax = int(ppc['Tmax'])
  period = int(ppc['Period'])
  dt = int(ppc['dt'])
  make_dictionary (ppc, rootname)

  bus_mp = open ("bus_" + rootname + "_metrics.json", "w")
  gen_mp = open ("gen_" + rootname + "_metrics.json", "w")
  sys_mp = open ("sys_" + rootname + "_metrics.json", "w")
  bus_meta = {'LMP_P':{'units':'USD/kwh','index':0},'LMP_Q':{'units':'USD/kvarh','index':1},
    'PD':{'units':'MW','index':2},'QD':{'units':'MVAR','index':3},'Vang':{'units':'deg','index':4},
    'Vmag':{'units':'pu','index':5},'Vmax':{'units':'pu','index':6},'Vmin':{'units':'pu','index':7}}
  gen_meta = {'Pgen':{'units':'MW','index':0},'Qgen':{'units':'MVAR','index':1},'LMP_P':{'units':'USD/kwh','index':2}}
  sys_meta = {'Ploss':{'units':'MW','index':0},'Converged':{'units':'true/false','index':1}}
  bus_metrics = {'Metadata':bus_meta,'StartTime':StartTime}
  gen_metrics = {'Metadata':gen_meta,'StartTime':StartTime}
  sys_metrics = {'Metadata':sys_meta,'StartTime':StartTime}

  gencost = ppc['gencost']
  fncsBus = ppc['FNCS']
  gen = ppc['gen']
  ppopt_market = pp.ppoption(VERBOSE=0, OUT_ALL=0, PF_DC=ppc['opf_dc'])
  ppopt_regular = pp.ppoption(VERBOSE=0, OUT_ALL=0, PF_DC=ppc['pf_dc'])
  loads = np.loadtxt(ppc['CSVFile'], delimiter=',')

  for row in ppc['UnitsOut']:
    print ('unit  ', row[0], 'off from', row[1], 'to', row[2], flush=True)
  for row in ppc['BranchesOut']:
    print ('branch', row[0], 'out from', row[1], 'to', row[2], flush=True)

  nloads = loads.shape[0]
  ts = 0
  tnext_opf = -dt

  # initializing for metrics collection
  tnext_metrics = 0
  loss_accum = 0
  conv_accum = True
  n_accum = 0
  bus_accum = {}
  gen_accum = {}
  for i in range (fncsBus.shape[0]):
    busnum = int(fncsBus[i,0])
    bus_accum[str(busnum)] = [0,0,0,0,0,0,0,99999.0]
  for i in range (gen.shape[0]):
    gen_accum[str(i+1)] = [0,0,0]

  op = open (rootname + '.csv', 'w')
  print ('t[s],Converged,Pload,P7 (csv),Unresp (opf),P7 (rpf),Resp (opf),GLD Pub,BID?,P7 Min,V7,LMP_P7,LMP_Q7,Pgen1,Pgen2,Pgen3,Pgen4,Pdisp,Deg,c2,c1', file=op, flush=True)
  fncs.initialize()

  # transactive load components
  csv_load = 0     # from the file
  unresp = 0       # unresponsive load estimate from the auction agent
  resp = 0         # will be the responsive load as dispatched by OPF
  resp_deg = 0     # RESPONSIVE_DEG from FNCS
  resp_c1 = 0      # RESPONSIVE_C1 from FNCS
  resp_c2 = 0      # RESPONSIVE_C2 from FNCS
  resp_max = 0     # RESPONSIVE_MAX_MW from FNCS
  feeder_load = 0  # amplified feeder MW

  while ts <= tmax:
    # start by getting the latest inputs from GridLAB-D and the auction
    events = fncs.get_events()
    new_bid = False
    load_scale = float (fncsBus[0][2])
    for topic in events:
      value = fncs.get_value(topic)
      if topic == 'UNRESPONSIVE_MW':
        unresp = load_scale * float(value)
        fncsBus[0][3] = unresp # to poke unresponsive estimate into the bus load slot
        new_bid = True
      elif topic == 'RESPONSIVE_MAX_MW':
        resp_max = load_scale * float(value)
        new_bid = True
      elif topic == 'RESPONSIVE_C2':
        resp_c2 = float(value) / load_scale
        new_bid = True
      elif topic == 'RESPONSIVE_C1':
        resp_c1 = float(value)
        new_bid = True
      elif topic == 'RESPONSIVE_DEG':
        resp_deg = int(value)
        new_bid = True
      else:
        gld_load = parse_mva (value) # actual value, may not match unresp + resp load
        feeder_load = float(gld_load[0]) * load_scale
    if new_bid == True:
      dummy = 2
#      print('**Bid', ts, unresp, resp_max, resp_deg, resp_c2, resp_c1)

    # update the case for bids, outages and CSV loads
    idx = int ((ts + dt) / period) % nloads
    bus = ppc['bus']
    gen = ppc['gen']
    branch = ppc['branch']
    gencost = ppc['gencost']
    csv_load = loads[idx,0]
    bus[4,2] = loads[idx,1]
    bus[8,2] = loads[idx,2]
    # process the generator and branch outages
    for row in ppc['UnitsOut']:
      if ts >= row[1] and ts <= row[2]:
        gen[row[0],7] = 0
      else:
        gen[row[0],7] = 1
    for row in ppc['BranchesOut']:
      if ts >= row[1] and ts <= row[2]:
        branch[row[0],10] = 0
      else:
        branch[row[0],10] = 1

    if resp_deg == 2:
      gencost[4][3] = 3
      gencost[4][4] = -resp_c2
      gencost[4][5] = resp_c1
    elif resp_deg == 1:
      gencost[4][3] = 2
      gencost[4][4] = resp_c1
      gencost[4][5] = 0.0
    else:
      gencost[4][3] = 1
      gencost[4][4] = 999.0
      gencost[4][5] = 0.0
    gencost[4][6] = 0.0

    if ts >= tnext_opf:  # expecting to solve opf one dt before the market clearing period ends, so GridLAB-D has time to use it
      # for OPF, the FNCS bus load is CSV + Unresponsive estimate, with Responsive separately dispatchable
      bus = ppc['bus']
      gen = ppc['gen']
      bus[6,2] = csv_load
      for row in ppc['FNCS']:
        unresp = float(row[3])
        newidx = int(row[0]) - 1
        if unresp >= feeder_load:
          bus[newidx,2] += unresp
        else:
          bus[newidx,2] += feeder_load
      gen[4][9] = -resp_max
      res = pp.runopf(ppc, ppopt_market)
      if res['success'] == False:
        conv_accum = False
      opf_bus = deepcopy (res['bus'])
      opf_gen = deepcopy (res['gen'])
      lmp = opf_bus[6,13]
      resp = -1.0 * opf_gen[4,1]
      fncs.publish('LMP_B7', 0.001 * lmp) # publishing $/kwh
#     print ('  OPF', ts, csv_load, '{:.3f}'.format(unresp), '{:.3f}'.format(resp),
#            '{:.3f}'.format(feeder_load), '{:.3f}'.format(opf_bus[6,2]),
#            '{:.3f}'.format(opf_gen[0,1]), '{:.3f}'.format(opf_gen[1,1]), '{:.3f}'.format(opf_gen[2,1]),
#            '{:.3f}'.format(opf_gen[3,1]), '{:.3f}'.format(opf_gen[4,1]), '{:.3f}'.format(lmp))
      # if unit 2 (the normal swing bus) is dispatched at max, change the swing bus to 9
      if opf_gen[1,1] >= 191.0:
        ppc['bus'][1,1] = 2
        ppc['bus'][8,1] = 3
        print ('  SWING Bus 9')
      else:
        ppc['bus'][1,1] = 3
        ppc['bus'][8,1] = 1
        print ('  SWING Bus 2')
      tnext_opf += period
    
    # always update the electrical quantities with a regular power flow
    bus = ppc['bus']
    gen = ppc['gen']
    bus[6,13] = lmp
    gen[0,1] = opf_gen[0, 1]
    gen[1,1] = opf_gen[1, 1]
    gen[2,1] = opf_gen[2, 1]
    gen[3,1] = opf_gen[3, 1]
    # during regular power flow, we use the actual CSV + feeder load, ignore dispatchable load and use actual
    bus[6,2] = csv_load + feeder_load
    gen[4,1] = 0 # opf_gen[4, 1]
    gen[4,9] = 0
    rpf = pp.runpf(ppc, ppopt_regular)
    if rpf[0]['success'] == False:
      conv_accum = False
    bus = rpf[0]['bus']
    gen = rpf[0]['gen']
    
    Pload = bus[:,2].sum()
    Pgen = gen[:,1].sum()
    Ploss = Pgen - Pload

    # update the metrics
    n_accum += 1
    loss_accum += Ploss
    for i in range (fncsBus.shape[0]):
      busnum = int(fncsBus[i,0])
      busidx = busnum - 1
      row = bus[busidx].tolist()
      # LMP_P, LMP_Q, PD, QD, Vang, Vmag, Vmax, Vmin: row[11] and row[12] are Vmax and Vmin constraints
      PD = row[2] + resp # the ERCOT version shows how to track scaled_resp separately for each FNCS bus
      Vpu = row[7]
      bus_accum[str(busnum)][0] += row[13]*0.001
      bus_accum[str(busnum)][1] += row[14]*0.001
      bus_accum[str(busnum)][2] += PD
      bus_accum[str(busnum)][3] += row[3]
      bus_accum[str(busnum)][4] += row[8]
      bus_accum[str(busnum)][5] += Vpu
      if Vpu > bus_accum[str(busnum)][6]:
        bus_accum[str(busnum)][6] = Vpu
      if Vpu < bus_accum[str(busnum)][7]:
        bus_accum[str(busnum)][7] = Vpu
    for i in range (gen.shape[0]):
      row = gen[i].tolist()
      busidx = int(row[0] - 1)
      # Pgen, Qgen, LMP_P  (includes the responsive load as dispatched by OPF)
      gen_accum[str(i+1)][0] += row[1]
      gen_accum[str(i+1)][1] += row[2]
      gen_accum[str(i+1)][2] += float(opf_bus[busidx,13])*0.001

    # write the metrics
    if ts >= tnext_metrics:
      sys_metrics[str(ts)] = {rootname:[loss_accum / n_accum,conv_accum]}

      bus_metrics[str(ts)] = {}
      for i in range (fncsBus.shape[0]):
        busnum = int(fncsBus[i,0])
        busidx = busnum - 1
        row = bus[busidx].tolist()
        met = bus_accum[str(busnum)]
        bus_metrics[str(ts)][str(busnum)] = [met[0]/n_accum, met[1]/n_accum, met[2]/n_accum, met[3]/n_accum,
                                             met[4]/n_accum, met[5]/n_accum, met[6], met[7]]
        bus_accum[str(busnum)] = [0,0,0,0,0,0,0,99999.0]

      gen_metrics[str(ts)] = {}
      for i in range (gen.shape[0]):
        met = gen_accum[str(i+1)]
        gen_metrics[str(ts)][str(i+1)] = [met[0]/n_accum, met[1]/n_accum, met[2]/n_accum]
        gen_accum[str(i+1)] = [0,0,0]

      tnext_metrics += period
      n_accum = 0
      loss_accum = 0
      conv_accum = True

    volts = 1000.0 * bus[6,7] * bus[6,9] / sqrt(3.0)  # VLN for GridLAB-D
    fncs.publish('three_phase_voltage_B7', volts)

    # CSV file output
    print (ts, res['success'], 
           '{:.3f}'.format(Pload),          # Pload
           '{:.3f}'.format(csv_load),       # P7 (csv)
           '{:.3f}'.format(unresp),         # GLD Unresp
           '{:.3f}'.format(bus[6,2]),       # P7 (rpf)
           '{:.3f}'.format(resp),           # Resp (opf)
           '{:.3f}'.format(feeder_load),    # GLD Pub
           new_bid, 
           '{:.3f}'.format(gen[4,9]),       # P7 Min
           '{:.3f}'.format(bus[6,7]),       # V7
           '{:.3f}'.format(bus[6,13]),      # LMP_P7
           '{:.3f}'.format(bus[6,14]),      # LMP_Q7
           '{:.2f}'.format(gen[0,1]),       # Pgen1
           '{:.2f}'.format(gen[1,1]),       # Pgen2 
           '{:.2f}'.format(gen[2,1]),       # Pgen3
           '{:.2f}'.format(gen[3,1]),       # Pgen4
           '{:.2f}'.format(res['gen'][4, 1]), # Pdisp
           '{:.4f}'.format(resp_deg),       # degree
           '{:.8f}'.format(ppc['gencost'][4, 4]),  # c2
           '{:.8f}'.format(ppc['gencost'][4, 5]),  # c1 
           sep=',', file=op, flush=True)

    # request the next time step, if necessary
    if ts >= tmax:
      print ('breaking out at',ts,flush=True)
      break
    ts = fncs.time_request(min(ts + dt, tmax))

  # ===================================
  print ('writing metrics', flush=True)
  print (json.dumps(sys_metrics), file=sys_mp, flush=True)
  print (json.dumps(bus_metrics), file=bus_mp, flush=True)
  print (json.dumps(gen_metrics), file=gen_mp, flush=True)
  print ('closing files', flush=True)
  bus_mp.close()
  gen_mp.close()
  sys_mp.close()
  op.close()
  print ('finalizing FNCS', flush=True)
  fncs.finalize()

  if sys.platform != 'win32':
    usage = resource.getrusage(resource.RUSAGE_SELF)
    RESOURCES = [
      ('ru_utime', 'User time'),
      ('ru_stime', 'System time'),
      ('ru_maxrss', 'Max. Resident Set Size'),
      ('ru_ixrss', 'Shared Memory Size'),
      ('ru_idrss', 'Unshared Memory Size'),
      ('ru_isrss', 'Stack Size'),
      ('ru_inblock', 'Block inputs'),
      ('ru_oublock', 'Block outputs')]
    print('Resource usage:')
    for name, desc in RESOURCES:
      print('  {:<25} ({:<10}) = {}'.format(desc, name, getattr(usage, name)))
Пример #2
0
          '{:.3f}'.format(bus[6, 7]),
          '{:.3f}'.format(bus[7, 7]),
          sep=',',
          file=vp,
          flush=True)

    # update the metrics
    n_accum += 1
    loss_accum += Ploss
    for i in range(fncsBus.shape[0]):
        busnum = int(fncsBus[i, 0])
        busidx = busnum - 1
        row = bus[busidx].tolist()
        # publish the bus VLN and LMP [$/kwh] for GridLAB-D
        bus_vln = 1000.0 * row[7] * row[9] / math.sqrt(3.0)
        fncs.publish('three_phase_voltage_Bus' + str(busnum), bus_vln)
        lmp = float(opf_bus[busidx, 13]) * 0.001
        fncs.publish('LMP_Bus' + str(busnum), lmp)  # publishing $/kwh
        # LMP_P, LMP_Q, PD, QD, Vang, Vmag, Vmax, Vmin: row[11] and row[12] are Vmax and Vmin constraints
        PD = row[
            2]  #  + resp # TODO, if more than one FNCS bus, track scaled_resp separately
        Vpu = row[7]
        bus_accum[str(busnum)][0] += row[13] * 0.001
        bus_accum[str(busnum)][1] += row[14] * 0.001
        bus_accum[str(busnum)][2] += PD
        bus_accum[str(busnum)][3] += row[3]
        bus_accum[str(busnum)][4] += row[8]
        bus_accum[str(busnum)][5] += Vpu
        if Vpu > bus_accum[str(busnum)][6]:
            bus_accum[str(busnum)][6] = Vpu
        if Vpu < bus_accum[str(busnum)][7]:
Пример #3
0
def auction_loop (configfile, metrics_root, flag='WithMarket'):
    bWantMarket = True
    if flag == 'NoMarket':
        bWantMarket = False
        print ('Disabled the market', flush=True)
    time_stop = int (48 * 3600) # simulation time in seconds
    StartTime = '2013-07-01 00:00:00 -0800'
    time_fmt = '%Y-%m-%d %H:%M:%S %z'
    dt_now = datetime.strptime (StartTime, time_fmt)

    # ====== load the JSON dictionary; create the corresponding objects =========

    lp = open (configfile).read()
    dict = json.loads(lp)

    market_key = list(dict['markets'].keys())[0]  # TODO: only using the first market
    market_row = dict['markets'][market_key]
    unit = market_row['unit']

    auction_meta = {'clearing_price':{'units':'USD','index':0},'clearing_type':{'units':'[0..5]=[Null,Fail,Price,Exact,Seller,Buyer]','index':1}}
    controller_meta = {'bid_price':{'units':'USD','index':0},'bid_quantity':{'units':unit,'index':1}}
    auction_metrics = {'Metadata':auction_meta,'StartTime':StartTime}
    controller_metrics = {'Metadata':controller_meta,'StartTime':StartTime}

    aucObj = simple_auction.simple_auction (market_row, market_key)

    dt = float(dict['dt'])
    period = aucObj.period

    topicMap = {} # to dispatch incoming FNCS messages; 0..5 for LMP, Feeder load, airtemp, mtr volts, hvac load, hvac state
    topicMap['LMP'] = [aucObj, 0]
    topicMap['refload'] = [aucObj, 1]

    hvacObjs = {}
    hvac_keys = list(dict['controllers'].keys())
    for key in hvac_keys:
      row = dict['controllers'][key]
      hvacObjs[key] = simple_auction.hvac (row, key, aucObj)
      ctl = hvacObjs[key]
      topicMap[key + '#Tair'] = [ctl, 2]
      topicMap[key + '#V1'] = [ctl, 3]
      topicMap[key + '#Load'] = [ctl, 4]
      topicMap[key + '#On'] = [ctl, 5]

    # ==================== Time step looping under FNCS ===========================

    fncs.initialize()
    aucObj.initAuction()
    LMP = aucObj.mean
    refload = 0.0
    bSetDefaults = True

    tnext_bid = period - 2 * dt  #3 * dt  # controllers calculate their final bids
    tnext_agg = period - 2 * dt  # auction calculates and publishes aggregate bid
    tnext_opf = period - 1 * dt  # PYPOWER executes OPF and publishes LMP (no action here)
    tnext_clear = period         # clear the market with LMP
    tnext_adjust = period        # + dt   # controllers adjust setpoints based on their bid and clearing

    time_granted = 0
    time_last = 0
    while (time_granted < time_stop):
        time_granted = fncs.time_request(time_stop)
        time_delta = time_granted - time_last
        time_last = time_granted
        hour_of_day = 24.0 * ((float(time_granted) / 86400.0) % 1.0)
# TODO - getting an overflow error when killing process - investigate whether that happens if simulation runs to completion
#        print (dt_now, time_delta, timedelta (seconds=time_delta))
        dt_now = dt_now + timedelta (seconds=time_delta)
        day_of_week = dt_now.weekday()
        hour_of_day = dt_now.hour
        print ('  ', time_last, time_granted, time_stop, time_delta, hour_of_day, day_of_week, flush=True)

        # update the data from FNCS messages
        events = fncs.get_events()
        for key in events:
            topic = key.decode()
            value = fncs.get_value(key).decode()
            row = topicMap[topic]
            if row[1] == 0:
                LMP = simple_auction.parse_fncs_magnitude (value)
                aucObj.set_lmp (LMP)
            elif row[1] == 1:
                refload = simple_auction.parse_kw (value)
                aucObj.set_refload (refload)
            elif row[1] == 2:
                row[0].set_air_temp (value)
            elif row[1] == 3:
                row[0].set_voltage (value)
            elif row[1] == 4:
                row[0].set_hvac_load (value)
            elif row[1] == 5:
                row[0].set_hvac_state (value)

        # set the time-of-day schedule
        for key, obj in hvacObjs.items():
            if obj.change_basepoint (hour_of_day, day_of_week):
                fncs.publish (obj.name + '/cooling_setpoint', obj.basepoint)
        if bSetDefaults:
            for key, obj in hvacObjs.items():
                fncs.publish (obj.name + '/bill_mode', 'HOURLY')
                fncs.publish (obj.name + '/monthly_fee', 0.0)
                fncs.publish (obj.name + '/thermostat_deadband', obj.deadband)
                fncs.publish (obj.name + '/heating_setpoint', 60.0)
            bSetDefaults = False

        if time_granted >= tnext_bid:
            print ('**', tnext_clear, flush=True)
            aucObj.clear_bids()
            time_key = str (int (tnext_clear))
            controller_metrics [time_key] = {}
            for key, obj in hvacObjs.items():
                bid = obj.formulate_bid () # bid is [price, quantity, on_state]
                if bWantMarket:
                    aucObj.collect_bid (bid)
                controller_metrics[time_key][obj.name] = [bid[0], bid[1]]
            tnext_bid += period

        if time_granted >= tnext_agg:
            aucObj.aggregate_bids()
            fncs.publish ('unresponsive_mw', aucObj.agg_unresp)
            fncs.publish ('responsive_max_mw', aucObj.agg_resp_max)
            fncs.publish ('responsive_c2', aucObj.agg_c2)
            fncs.publish ('responsive_c1', aucObj.agg_c1)
            fncs.publish ('responsive_deg', aucObj.agg_deg)
            tnext_agg += period

        if time_granted >= tnext_clear:
            if bWantMarket:
                aucObj.clear_market()
                fncs.publish ('clear_price', aucObj.clearing_price)
                for key, obj in hvacObjs.items():
                    obj.inform_bid (aucObj.clearing_price)
            time_key = str (int (tnext_clear))
            auction_metrics [time_key] = {aucObj.name:[aucObj.clearing_price, aucObj.clearing_type]}
            tnext_clear += period

        if time_granted >= tnext_adjust:
            if bWantMarket:
                for key, obj in hvacObjs.items():
                    fncs.publish (obj.name + '/price', aucObj.clearing_price)
                    if obj.bid_accepted ():
                        fncs.publish (obj.name + '/cooling_setpoint', obj.setpoint)
            tnext_adjust += period

    # ==================== Finalize the metrics output ===========================

    print ('writing metrics', flush=True)
    auction_op = open ('auction_' + metrics_root + '_metrics.json', 'w')
    controller_op = open ('controller_' + metrics_root + '_metrics.json', 'w')
    print (json.dumps(auction_metrics), file=auction_op)
    print (json.dumps(controller_metrics), file=controller_op)
    auction_op.close()
    controller_op.close()

    print ('finalizing FNCS', flush=True)
    fncs.finalize()
Пример #4
0
def precool_loop(nhours, metrics_root):
    time_stop = int(3600 * nhours)

    lp = open(metrics_root + "_agent_dict.json").read()
    dict = json.loads(lp)
    precool_meta = {
        'temperature_deviation_min': {
            'units': 'degF',
            'index': 0
        },
        'temperature_deviation_max': {
            'units': 'degF',
            'index': 1
        },
        'temperature_deviation_avg': {
            'units': 'degF',
            'index': 2
        }
    }
    StartTime = "2013-07-01 00:00:00 PST"
    precool_metrics = {'Metadata': precool_meta, 'StartTime': StartTime}

    dt = dict['dt']
    mean = dict['mean']
    stddev = dict['stddev']
    period = dict['period']
    k = dict['k_slope']

    print('run till', time_stop, 'period', period, 'step', dt, 'mean', mean,
          'stddev', stddev, 'k_slope', k)

    fncs.initialize()

    time_granted = 0
    price = 0.11  # mean

    # time_next = dt
    voltages = {}
    temperatures = {}
    setpoints = {}  # publish a new one only if changed
    lastchange = {}
    precooling_quiet = 4 * 3600
    precooling_off = 25 * 3600  # never turns off
    precooling_status = {}
    lockout_period = 360

    bSetDeadbands = True
    nPrecoolers = 0

    while time_granted < time_stop:
        time_granted = fncs.time_request(time_stop)  # time_next
        hour_of_day = 24.0 * ((float(time_granted) / 86400.0) % 1.0)
        events = fncs.get_events()
        for topic in events:
            value = fncs.get_value(topic)
            if topic == 'price':
                price = float(value)
            else:
                pair = topic.split('#')
                houseName = pair[0]
                if pair[1] == 'V1':
                    voltages[houseName] = parse_fncs_magnitude(value)
                elif pair[1] == 'Tair':
                    temperatures[houseName] = parse_fncs_magnitude(value)

        if bSetDeadbands:
            bSetDeadbands = False
            print('setting thermostat deadbands and heating setpoints at',
                  time_granted)
            # set all of the house deadbands and initial setpoints
            for house, row in dict['houses'].items():
                topic = house + '_thermostat_deadband'
                value = row['deadband']
                fncs.publish(topic, value)
                setpoints[house] = 0.0
                lastchange[house] = -lockout_period
                precooling_status[house] = False
                fncs.publish(house + '_heating_setpoint', 60.0)

        # update all of the house setpoints
        count_temp_dev = 0
        sum_temp_dev = 0.0
        min_temp_dev = 10000.0
        max_temp_dev = 0.0
        for house, row in dict['houses'].items():
            # time-scheduled setpoints
            if hour_of_day >= row['day_start_hour'] and hour_of_day <= row[
                    'day_end_hour']:
                value = row['day_set']
            else:
                value = row['night_set']
            # comfort metrics
            if house in temperatures:
                temp_dev = abs(temperatures[house] - value)
                if temp_dev < min_temp_dev:
                    min_temp_dev = temp_dev
                if temp_dev > max_temp_dev:
                    max_temp_dev = temp_dev
                sum_temp_dev += temp_dev
                count_temp_dev += 1
            # time-of-day price response
            tdelta = (price - mean) * row['deadband'] / k / stddev
            value += tdelta
            # overvoltage checks
            if time_granted >= precooling_quiet and not precooling_status[
                    house]:
                if house in voltages:
                    if voltages[house] > row['vthresh']:
                        precooling_status[house] = True
                        nPrecoolers += 1
            elif time_granted >= precooling_off:
                precooling_status[house] = False
            # overvoltage response
            if precooling_status[house]:
                value += row['toffset']
            if abs(value - setpoints[house]) > 0.1:
                if (time_granted - lastchange[house]) > lockout_period:
                    topic = house + '_cooling_setpoint'
                    fncs.publish(topic, value)
                    setpoints[house] = value
                    lastchange[house] = time_granted
                    print('setting', house, 'to', value, 'at', time_granted,
                          'precooling', precooling_status[house])

        if count_temp_dev < 1:
            count_temp_dev = 1
            min_temp_dev = 0.0
        precool_metrics[str(time_granted)] = [
            min_temp_dev, max_temp_dev, sum_temp_dev / count_temp_dev
        ]

        time_next = time_granted + dt

    print(nPrecoolers, 'houses participated in precooling')
    print('writing metrics', flush=True)
    mp = open("precool_" + metrics_root + "_metrics.json", "w")
    print(json.dumps(precool_metrics), file=mp)
    mp.close()
    print('done', flush=True)

    print('finalizing FNCS', flush=True)
    fncs.finalize()

    if sys.platform != 'win32':
        usage = resource.getrusage(resource.RUSAGE_SELF)
        RESOURCES = [('ru_utime', 'User time'), ('ru_stime', 'System time'),
                     ('ru_maxrss', 'Max. Resident Set Size'),
                     ('ru_ixrss', 'Shared Memory Size'),
                     ('ru_idrss', 'Unshared Memory Size'),
                     ('ru_isrss', 'Stack Size'),
                     ('ru_inblock', 'Block inputs'),
                     ('ru_oublock', 'Block outputs')]
        print('Resource usage:')
        for name, desc in RESOURCES:
            print('  {:<25} ({:<10}) = {}'.format(desc, name,
                                                  getattr(usage, name)))
Пример #5
0
        if bWantMarket:
            resp_max = gld_bus[busnum]['pcrv'] * 0.5
            unresp = gld_bus[busnum]['pcrv'] * 0.5
            c2 = gld_bus[busnum]['c2']
            c1 = gld_bus[busnum]['c1']
            deg = gld_bus[busnum]['deg']
        else:
            resp_max = 0.0
            unresp = gld_bus[busnum]['pcrv']
            c2 = 0
            c1 = 0
            deg = 0
        pubtopic = 'substationBus' + str(
            busnum
        )  # this is what the tso8stub.yaml expects to receive from a substation auction
        fncs.publish(pubtopic + '/unresponsive_mw', unresp / gld_scale)
        fncs.publish(pubtopic + '/responsive_max_mw', resp_max / gld_scale)
        fncs.publish(pubtopic + '/responsive_c2', c2 * gld_scale)
        fncs.publish(pubtopic + '/responsive_c1', c1)
        fncs.publish(pubtopic + '/responsive_deg', deg)

        # the actual load is the unresponsive load, plus a cleared portion of the responsive load
        lmp = 1000.0 * gld_bus[busnum]['lmp']
        p_cleared = 0
        if c1 > lmp and bWantMarket:
            p_cleared = 0.5 * gld_scale * (lmp - c1) / c2
            if p_cleared > resp_max:
                p_cleared = resp_max
        p = unresp + p_cleared
        q = p * qf
        gld_bus[busnum]['p'] = p