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)))
'{:.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]:
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()
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)))
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