def run(self, P_req=[0], Q_req=[0], initSoC=50, dt=1): P_req = P_req / self.num_of_devices Q_req = Q_req / self.num_of_devices # error checking if initSoC < self.min_soc or initSoC > self.max_soc: print('ERROR: initSoC out of range') return [[], []] elif P_req == []: print('ERROR: P_req vector must not be empty') return [[], []] else: self.t = self.t + dt response = FleetResponse() # pre-define output vectors SoC SoC = numpy.zeros(2) SoC[0] = initSoC # initialize SoC p_ach = 0 q_ach = 0 # Max ramp rate and aparent powerlimit checking if (P_req - self.P_injected) > self.max_ramp_up: p_ach = self.max_ramp_up elif (P_req - self.P_injected) < self.max_ramp_down: p_ach = self.max_ramp_down else: p_ach = P_req if (Q_req - self.Q_injected) > self.max_ramp_up: q_ach = self.max_ramp_up elif (Q_req - self.Q_injected) < self.max_ramp_down: q_ach = self.max_ramp_down else: q_ach = Q_req if p_ach < self.max_power_discharge: p_ach = self.max_power_discharge if p_ach > self.max_power_charge: p_ach = self.max_power_charge S_req = float(numpy.sqrt(p_ach**2 + q_ach**2)) if S_req > self.max_apparent_power: q_ach = float( numpy.sqrt(numpy.abs(self.max_apparent_power**2 - p_ach**2)) * numpy.sign(q_ach)) S_req = float(numpy.sqrt(p_ach**2 + q_ach**2)) if p_ach != 0.0: if float(numpy.abs(S_req / p_ach)) < self.min_pf: q_ach = float( numpy.sqrt( numpy.abs((p_ach / self.min_pf)**2 - p_ach**2)) * numpy.sign(q_ach)) # run function for ERM model type if self.model_type == 'ERM': response.P_injected_max = self.max_power_discharge response.Q_injected_max = float( numpy.sqrt(numpy.abs(self.max_apparent_power**2 - p_ach**2))) # Calculate SoC and Power Achieved Ppos = min(self.max_power_charge, max(p_ach, 0)) Pneg = max(self.max_power_discharge, min(p_ach, 0)) SoC[1] = SoC[0] + float(100) * dt * ( Pneg + (Ppos * self.energy_efficiency) + self.self_discharge_power) / self.energy_capacity if SoC[1] > self.max_soc: Ppos = (self.energy_capacity * (self.max_soc - SoC[0]) / (float(100) * dt) - self.self_discharge_power) / self.energy_efficiency SoC[1] = self.max_soc if SoC[0] == self.max_soc: Ppos = 0 if SoC[1] < self.min_soc: Pneg = self.energy_capacity * (self.min_soc - SoC[0]) / ( float(100) * dt) - self.self_discharge_power SoC[1] = self.min_soc if SoC[0] == self.min_soc: Pneg = 0 p_ach = (Ppos + Pneg) * self.num_of_devices q_ach = q_ach * self.num_of_devices response.P_injected = p_ach response.Q_injected = q_ach response.P_dot = p_ach - self.P_injected response.Q_dot = q_ach - self.Q_injected response.P_service = 0 response.Q_service = 0 response.P_service_max = 0 response.Q_service_max = 0 response.Loss_standby = self.self_discharge_power response.Eff_throughput = float( p_ach > 0) * self.energy_efficiency + float(p_ach > 0) self.soc = SoC[1] return response # run function for ERM model type elif self.model_type == 'CRM': # convert AC power p_ach to DC power pdc self.pdc = self.coeff_2 * (p_ach**2) + self.coeff_1 * ( p_ach) + self.coeff_0 # convert DC power pdc to DC current """ self.ibat = self.pdc *1000 / self.vbat self.pdc = self.ibat * ((self.v1 + self.v2 + self.voc + self.ibat*self.r0) *self.n_cells) *1000 0 = -self.pdc + (self.v1 + self.v2 + self.voc)*self.n_cells) *1000*self.ibat + self.r0 *self.n_cells*1000* (self.ibat**2) """ b = ((self.v1 + self.v2 + self.voc) * self.n_cells) a = self.r0 * self.n_cells c = -self.pdc * 1000 self.ibat = (-b + numpy.sqrt(b**2 - 4 * a * c)) / (2 * a) self.vbat = (self.v1 + self.v2 + self.voc + self.ibat * self.r0) * self.n_cells # calculate dynamic voltages self.v1 = self.v1 + dt * ((1 / (self.r1 * self.c1)) * self.v1 + (1 / (self.c1)) * self.ibat) self.v2 = self.v2 + dt * ((1 / (self.r2 * self.c2)) * self.v1 + (1 / (self.c2)) * self.ibat) response.P_injected_max = self.max_power_discharge response.Q_injected_max = float( numpy.sqrt(numpy.abs(self.max_apparent_power**2 - p_ach**2))) # Calculate SoC and Power Achieved Ipos = min(self.max_current_charge, max(self.ibat, 0)) Ineg = max(self.max_current_discharge, min(self.ibat, 0)) SoC[1] = SoC[0] + float(100) * dt * ( Ineg + (Ipos * self.coulombic_efficiency) + self.self_discharge_current) / self.charge_capacity if SoC[1] > self.max_soc: Ipos = (self.charge_capacity * (self.max_soc - SoC[0]) / (float(100) * dt) - self.self_discharge_current ) / self.coulombic_efficiency SoC[1] = self.max_soc if SoC[0] == self.max_soc: Ipos = 0 self.pdc = Ipos * self.vbat / 1000 if self.coeff_2 != 0: p_ach = (-self.coeff_1 + float( numpy.sqrt(self.coeff_1**2 - 4 * self.coeff_2 * (self.coeff_0 - self.pdc)))) / ( 2 * self.coeff_2) else: p_ach = (self.pdc - self.coeff_0) / self.coeff_1 if SoC[1] < self.min_soc: Ineg = self.charge_capacity * (self.min_soc - SoC[0]) / ( float(100) * dt) - self.self_discharge_current SoC[1] = self.min_soc if SoC[0] == self.min_soc: Ineg = 0 self.pdc = Ineg * self.vbat / 1000 if self.coeff_2 != 0: p_ach = (-self.coeff_1 + float( numpy.sqrt(self.coeff_1**2 - 4 * self.coeff_2 * (self.coeff_0 - self.pdc)))) / ( 2 * self.coeff_2) else: p_ach = (self.pdc - self.coeff_0) / self.coeff_1 self.ibat = Ipos + Ineg self.soc = SoC[1] self.voc_update() self.vbat = (self.v1 + self.v2 + self.voc + self.ibat * self.r0) * self.n_cells p_ach = p_ach * self.num_of_devices q_ach = q_ach * self.num_of_devices response.P_injected = p_ach response.Q_injected = q_ach response.P_dot = p_ach - self.P_injected response.Q_dot = q_ach - self.Q_injected response.P_service = 0 response.Q_service = 0 response.P_service_max = 0 response.Q_service_max = 0 response.Loss_standby = self.self_discharge_current * ( self.voc) response.E = (self.soc - self.min_soc) * self.charge_capacity * self.voc response.Eff_throughput = (response.E - self.es) / (p_ach * dt) self.es = response.E self.vbat = (self.v1 + self.v2 + self.voc + self.ibat * self.r0) * self.n_cells return response
def run(self, P_req, Q_req, initSOC, t, dt, ts): # ExecuteFleet(self, Steps, Timestep, P_request, Q_request, forecast): # run(self, P_req=[0], Q_req=[0], ts=datetime.utcnow(), del_t=timedelta(hours=1)): # Give the code the capability to respond to None requests if P_req == None: P_req = 0 if Q_req == None: Q_req = 0 P_togrid = 0 P_service = 0 P_base = 0 P_service_max = 0 self.P_request_perWH = P_req / self.numWH # this is only for the first step number = 0 # index for running through all the water heaters NumDevicesToCall = 0 P_service_max0 = 0 # decision making about which water heater to call on for service, check if available at last step, if so then # check for SoC > self.minSOC and Soc < self.maxSOC for n in range(self.numWH): if self.P_request_perWH < 0 and self.IsAvailableAdd[ n] > 0 and self.SOC[n] < self.maxSOC: NumDevicesToCall += 1 elif self.P_request_perWH > 0 and self.IsAvailableShed[ n] > 0 and self.SOC[n] > self.minSOC: NumDevicesToCall += 1 if P_req != None: self.P_request_perWH = P_req / max( NumDevicesToCall, 1 ) # divide the fleet request by the number of devices that can be called upon # create a .csv outputfile with each water heater's metrics # outputfilename = join(self.base_path,"WH_fleet_outputs.csv") # self.outputfile = open(outputfilename,"w") # self.outputfile.write("Timestep,") # create a .csv outputfile with P_service, P_togrid, and P_base # outputfilename = join(self.base_path,"WH_fleet_outputs.csv") ################################# for wh in self.whs: # loop through all water heaters if P_req == None: response = wh.execute( self.TtankInitial[number], self.TtankInitial_b[number], self.TsetInitial[number], self.Tamb[number][0], self.RHamb[number][0], self.Tmains[number][0], self.draw[number][0], 0, self.Type, self.dt, self.draw_fleet_ave[0], self.element_on_last) P_service = 0 if P_req < 0 and self.IsAvailableAdd[number] > 0: response = wh.execute( self.TtankInitial[number], self.TtankInitial_b[number], self.TsetInitial[number], self.Tamb[number][0], self.RHamb[number][0], self.Tmains[number][0], self.draw[number][0], P_req, self.Type, self.dt, self.draw_fleet_ave[0], self.element_on_last) P_req = P_req - response.Eservice P_service += response.Eservice elif P_req > 0 and self.IsAvailableShed[number] > 0: response = wh.execute( self.TtankInitial[number], self.TtankInitial_b[number], self.TsetInitial[number], self.Tamb[number][0], self.RHamb[number][0], self.Tmains[number][0], self.draw[number][0], P_req, self.Type, self.dt, self.draw_fleet_ave[0], self.element_on_last) P_req = P_req + response.Eservice P_service -= response.Eservice #print("P_req = {}, P_service = {}, Eservice = {}".format(P_req,P_service,response.Eservice)) else: response = wh.execute( self.TtankInitial[number], self.TtankInitial_b[number], self.TsetInitial[number], self.Tamb[number][0], self.RHamb[number][0], self.Tmains[number][0], self.draw[number][0], 0, self.Type, self.dt, self.draw_fleet_ave[0], self.element_on_last) # print('P_req = {}'.format(P_req)) # assign returned parameters to associated lists to be recorded self.element_on_last[number] = response.ElementOn self.TtankInitial[number] = response.Ttank self.TtankInitial_b[number] = response.Ttank_b self.SOC[number] = response.SOC self.SOCb[number] = response.SOC_b self.IsAvailableAdd[number] = response.IsAvailableAdd self.IsAvailableShed[number] = response.IsAvailableShed self.AvailableCapacityAdd[number] = response.AvailableCapacityAdd self.AvailableCapacityShed[number] = response.AvailableCapacityShed self.ServiceCallsAccepted[number] = response.ServiceCallsAccepted self.ServiceProvided[number] = response.Eservice ''' P_togrid -= response.Eused P_base -= response.Pbase if P_req <0 or P_req > 0: P_response = (P_togrid - P_base) else: P_response = 0 ''' P_togrid -= response.Eused P_base -= response.Pbase # self.outputfile.write(str(response.Ttank) +"," + str(self.TsetInitial[number]) + "," + str(response.Eused) + "," + str(response.PusedMax) + "," + str(response.Eloss) + "," + str(response.ElementOn) + "," + str(response.Eservice) + "," + str(response.SOC) + "," + str(response.AvailableCapacityAdd) + "," + str(response.AvailableCapacityShed) + "," + str(response.ServiceCallsAccepted) + "," + str(response.IsAvailableAdd) + "," + str(response.IsAvailableShed) + "," + str(self.draw[number][0]) + "," + str(response.Edel) + ",") # resp.sim_step = response.sim_step number += 1 # go to next device if P_req <= 0: P_service_max += response.AvailableCapacityShed # NOTE THIS ASSUMES THE MAX SERVICE IS LOAD SHED else: P_service_max0 += response.AvailableCapacityAdd P_service_max = -1.0 * P_service_max0 # self.outputfile.write("\n") self.step += 1 # To advance the step by step in the disturbance file # Output Fleet Response resp = FleetResponse() resp.P_service = [] resp.P_service_max = [] resp.P_service_min = [] resp.P_togrid = [] resp.P_togrid_max = [] resp.P_togrid_min = [] resp.P_forecast = [] resp.P_base = [] resp.E = [] resp.C = [] resp.ts = ts resp.sim_step = dt # resp.P_dot_up = resp.P_togrid_max / ServiceRequest.Timestep.seconds resp.P_service_max = P_service_max resp.P_service = P_service resp.P_base = P_base resp.P_togrid = P_togrid # if P_service != 0: # print("resp.P_base = {}".format(resp.P_base)) # print("Pbase = {}".format(resp.P_base)) # print("Ptogrid = {}".format(resp.P_togrid)) # Available Energy stored at the end of the most recent timestep # resp.E += response.Estored resp.E = 0 resp.C += response.SOC / (self.numWH) resp.Q_togrid = 'NA' resp.Q_service = 'NA' resp.Q_service_max = 'NA' resp.Q_service_min = 'NA' resp.Q_togrid_min = 'NA' resp.Q_togrid_max = 'NA' resp.Q_dot_up = 'NA' resp.Q_dot_down = 'NA' resp.P_dot_up = 0 resp.P_dot_down = 0 resp.Eff_charge = 1.0 # TODO: change this if we ever use a HPWH to use the HP efficiency resp.Eff_discharge = 1.0 # always equal to 1 for this device resp.P_dot = resp.P_togrid / dt resp.P_service_min = 0 resp.dT_hold_limit = 'NA' resp.T_restore = 'NA' resp.Strike_price = 'NA' resp.SOC_cost = 'NA' # TotalServiceProvidedPerTimeStep[step] = -1.0*self.P_service # per time step for all hvacs -1.0* """ Modify the power and SOC of the different subfeets according to the frequency droop regulation according to IEEE standard """ if self.FW21_Enabled and self.is_autonomous: power_ac = resp.P_service_max p_prev = resp.P_togrid power_fleet = self.frequency_watt(power_ac, p_prev, self.ts, self.location, self.db_UF_subfleet, self.db_OF_subfleet) power_fleet, SOC_step = self.update_soc_due_to_frequency_droop( initSOC, power_fleet, dt) self.time = t + dt self.ts = self.ts + timedelta(seconds=dt) # Restart time if it surpasses 24 hours if self.time > 24 * 3600: self.time = self.time - 24 * 3600 # Impact Metrics # Update the metrics ''' Ttotal = 0 SOCTotal = 0 self.cycle_basee = np.sum(self.cycle_off_base) self.cycle_basee += np.sum(self.cycle_on_base) self.cycle_grid = np.sum(self.cycle_off_grid) self.cycle_grid += np.sum(self.cycle_on_grid) ''' for number in range(self.numWH): if response.Ttank <= self.TsetInitial[ number] - 10: # assume 10F deadband (consistent with wh.py) self.unmet_hours += 1 * self.sim_step / 3600.0 if resp.P_base == 0 and resp.P_togrid == 0: self.ratio_P_togrid_P_base = 1.0 elif resp.P_base == 0 and resp.P_togrid != 0: self.ratio_P_togrid_P_base = 'NA' else: self.ratio_P_togrid_P_base = resp.P_togrid / (resp.P_base) self.energy_impacts += abs(resp.P_service) * (self.sim_step / 3600) return resp
def fc_model(self, ts, sim_step, Preq, forecast=False, start_time=None): """ :param ts: Request created for current time-step: datetime :param sim_step: Request for simulation time-step: timedelta object :param Preq: Request for current real power request :param forecast: Returns fleet response forecast :param start_time: Request for current real power request :return resp: Fleet response object """ if self.P_tank_ideal <= self.min_charge: pass else: # Create base power profile if isinstance(self.p, ndarray): self.__p_base = sum([ self.p[j] * (self.__inc + 1)**(21 - j) for j in range(22) ]) else: self.__p_base = self.p # Ptogrid: Pbase for None or zero requests since FC is a source if Preq is None or float(Preq) == 0.0: Preq = self.__p_base # Ptogrid: Preq+Pbase for +ve or -ve requests else: Preq += self.__p_base # Compute the power generated by the fleet self.__fc_p_calc(Preq) if self.FW21_Enabled and self.is_autonomous: # all in kW Preq, self.f = self.frequency_watt(p_pre=self.__p_tot_ideal, p_avl=self.fc_Pmax_fleet, p_min=self.fc_Pmin_fleet, ts=ts, start_time=start_time) self.__p_tot_ideal = Preq # Update SOC self.__soc_calc() self.__inc += 1 # Response # Power injected is positive resp = FleetResponse() resp.ts = ts resp.sim_step = sim_step resp.C = 0 resp.dT_hold_limit = 0 resp.E = self.soc_ideal resp.Eff_charge = 1.0 resp.Eff_discharge = self.__eta_ds resp.P_dot_down = 0 resp.P_dot_up = 0 resp.P_togrid = self.__p_tot_ideal resp.P_togrid_max = self.fc_Pmax_fleet resp.P_togrid_min = self.fc_Pmin_fleet resp.P_service = resp.P_togrid - self.__p_base resp.P_service_max = self.fc_Pmax_fleet - self.__p_base resp.P_service_min = self.__p_base - self.fc_Pmin_fleet resp.Q_dot_down = 0 resp.Q_dot_up = 0 resp.Q_service = 0 resp.Q_service_max = 0 resp.Q_service_min = 0 resp.Q_togrid = 0 resp.Q_togrid_max = 0 resp.Q_togrid_min = 0 resp.T_restore = 0 resp.P_base = self.__p_base resp.Q_base = 0 resp.Strike_price = 0 resp.SOC_cost = 0 # Impact metrics if not forecast: self.metrics.append([ str(ts), str(self.__Vr), str(self.__Vr_age), str(self.__ne), str(self.__ne_age), str(self.soc_ideal * 1e2), str(self.soc_age * 1e2), str(self.lka_h2), str(self.__eta_ds * 1e2) ]) # Print Soc every 5 secs. if self.__inc % 5000 == 0: print("Soc:%4.2f%%" % (resp.E * 1e2)) return resp
def Run_Fleet(self, ts, sim_step, P_req, Q_req, return_forecast, WP): # this module aggregates the PV information from a large pool of PV/invertre system # Updates prepares report for system operator based on PV fleet # Breaks down the operation command from system operator to subfleet and device level if P_req == None: P_req = [] if Q_req == None: Q_req = [] def Grid_Param(): from grid_info import GridInfo a = GridInfo('Grid_Info_DATA_2.csv') V = a.get_voltage('XX') f = a.get_frequency('XX') return f, V # pull information about the PV subfleet P_rated = self.p_rated P_min = self.p_min S_max = self.s_max Qmax_Plus = self.qmax_plus Qmax_minus = self.qmax_minus P_up = self.p_ramp_down P_down = self.p_ramp_down Q_up = self.q_ramp_down Q_down = self.q_ramp_down # Create Battery equivalent model (BEM) variables for the subfleets and PV fleet # SubFleetA_NP_C='NA' # SubFleetA_NP_P_grid_max=P_rated*self.SubFleet_NumberOfUnits #Minimum real power for services # SubFleetA_NP_Q_grid_max=Qmax_Plus*self.SubFleet_NumberOfUnits#Maximum reactive power for services # SubFleetA_NP_P_grid_min=P_min*self.SubFleet_NumberOfUnits#Minimum reactive power for services # SubFleetA_NP_Q_grid_min=Qmax_minus*self.SubFleet_NumberOfUnits#Ramp rate real power up # SubFleetA_NP_P_service_max=SubFleetA_NP_P_grid_max-SubFleetA_NP_P_grid_min#Ramp rate real power down # SubFleetA_NP_Q_service_max=SubFleetA_NP_Q_grid_max-SubFleetA_NP_Q_grid_min#Ramp rate reactive power up # SubFleetA_NP_P_service_min=0#Ramp rate reactive power down # SubFleetA_NP_Q_service_min=0 SubFleetA_NP_P_up = P_up SubFleetA_NP_Q_up = Q_up SubFleetA_NP_P_down = P_down SubFleetA_NP_Q_down = Q_down # SubFleetA_NP_e_in='NA' SubFleetA_NP_e_out = [] SubFleetA_NP_S_rating = S_max * self.SubFleet_NumberOfUnits Fleet_PV_NP_C = 'NA' # Fleet_PV_NP_P_grid_max=SubFleetA_NP_P_grid_max#Minimum real power for services # Fleet_PV_NP_Q_grid_max=SubFleetA_NP_Q_grid_max#Maximum reactive power for services # Fleet_PV_NP_P_grid_min=SubFleetA_NP_P_grid_min#Minimum reactive power for services # Fleet_PV_NP_Q_grid_min=SubFleetA_NP_Q_grid_min#Ramp rate real power up # Fleet_PV_NP_P_service_max=SubFleetA_NP_P_service_max#Ramp rate real power down # Fleet_PV_NP_Q_service_max=SubFleetA_NP_Q_service_max#Ramp rate reactive power up # Fleet_PV_NP_P_service_min=0#Ramp rate reactive power down # Fleet_PV_NP_Q_service_min=0 Fleet_PV_NP_P_up = SubFleetA_NP_P_up Fleet_PV_NP_Q_up = SubFleetA_NP_Q_up Fleet_PV_NP_P_down = SubFleetA_NP_P_down Fleet_PV_NP_Q_down = SubFleetA_NP_Q_down Fleet_PV_NP_e_in = 'NA' # Fleet_PV_NP_e_out=[] FleetA_NP_S_rating = SubFleetA_NP_S_rating # break down the command for fleet to command for device Command_to_Device = self.Aggregator_Command( P_req, Q_req, self.SubFleet_NumberOfUnits) # Grid parameters required for autonomous mode of operation #[Time_,P_rec,Q_rec,P_req_,Q_req_,P_MPP,G_rec,T_rec]\ [P_grid,Q_grid,P_service,Q_service,E_t0,c,P_output,Q_output,P_grid_max,Q_grid_max,\ P_grid_min,Q_grid_min,P_service_max,Q_service_max,P_service_min,Q_service_min,del_t_hold,\ t_restore,SP,N_req,Effeciency,sim_step]=self.Device_PV(ts,sim_step,Command_to_Device,return_forecast,WP) #print(P_Output) SubFleetA_P_grid = P_grid * self.SubFleet_NumberOfUnits / 1000 SubFleetA_Q_grid = Q_grid * self.SubFleet_NumberOfUnits / 1000 SubFleetA_P_service = P_service * self.SubFleet_NumberOfUnits / 1000 SubFleetA_Q_service = Q_service * self.SubFleet_NumberOfUnits / 1000 SubFleetA_E_t0 = E_t0 SubFleetA_c = c SubFleetA_P_output = [ x * self.SubFleet_NumberOfUnits / 1000 for x in P_output ] SubFleetA_Q_output = [ x * self.SubFleet_NumberOfUnits / 1000 for x in Q_output ] SubFleetA_P_grid_max = [ x * self.SubFleet_NumberOfUnits / 1000 for x in P_grid_max ] SubFleetA_Q_grid_max = [ x * self.SubFleet_NumberOfUnits / 1000 for x in Q_grid_max ] SubFleetA_P_grid_min = [ x * self.SubFleet_NumberOfUnits / 1000 for x in P_grid_min ] SubFleetA_Q_grid_min = [ x * self.SubFleet_NumberOfUnits / 1000 for x in Q_grid_min ] SubFleetA_P_service_max = [ x * self.SubFleet_NumberOfUnits / 1000 for x in P_service_max ] SubFleetA_Q_service_max = [ x * self.SubFleet_NumberOfUnits / 1000 for x in Q_service_max ] SubFleetA_P_service_min = [ x * self.SubFleet_NumberOfUnits / 1000 for x in P_service_min ] SubFleetA_Q_service_min = [ x * self.SubFleet_NumberOfUnits / 1000 for x in Q_service_min ] SubFleetA_del_t_hold = del_t_hold SubFleetA_t_restore = t_restore SubFleetA_SP = SP SubFleetA_N_req = N_req SubFleetA_NP_e_out = Effeciency # Calculate information about Subfleets # Calculate information about Device Fleet: PV # Calculate information about Subfleets Fleet_PV_P_grid = SubFleetA_P_grid Fleet_PV_Q_grid = SubFleetA_Q_grid Fleet_PV_P_service = SubFleetA_P_service Fleet_PV_Q_service = SubFleetA_Q_service Fleet_PV_E_t0 = SubFleetA_E_t0 # Fleet_PV_c=SubFleetA_c # # Fleet_PV_P_output=SubFleetA_P_output # Fleet_PV_Q_output=SubFleetA_Q_output Fleet_PV_P_grid_max = SubFleetA_P_grid_max Fleet_PV_Q_grid_max = SubFleetA_Q_grid_max Fleet_PV_P_grid_min = SubFleetA_P_grid_min Fleet_PV_Q_grid_min = SubFleetA_Q_grid_min Fleet_PV_P_service_max = SubFleetA_P_service_max Fleet_PV_Q_service_max = SubFleetA_Q_service_max Fleet_PV_P_service_min = SubFleetA_P_service_min Fleet_PV_Q_service_min = SubFleetA_Q_service_min Fleet_PV_del_t_hold = SubFleetA_del_t_hold Fleet_PV_t_restore = SubFleetA_t_restore # Fleet_PV_SP=SubFleetA_SP # Fleet_PV_N_req=SubFleetA_N_req response = FleetResponse() response.ts = ts response.C = Fleet_PV_NP_C response.dT_hold_limit = Fleet_PV_del_t_hold response.E = Fleet_PV_E_t0 response.Eff_charge = Fleet_PV_NP_e_in response.Eff_discharge = SubFleetA_NP_e_out response.P_dot_down = Fleet_PV_NP_P_down response.P_dot_up = Fleet_PV_NP_P_up response.P_service = Fleet_PV_P_service response.P_service_max = Fleet_PV_P_service_max response.P_service_min = Fleet_PV_P_service_min response.P_togrid = Fleet_PV_P_grid response.P_togrid_max = Fleet_PV_P_grid_max[0] response.P_togrid_min = Fleet_PV_P_grid_min[0] response.Q_dot_down = Fleet_PV_NP_Q_down response.Q_dot_up = Fleet_PV_NP_Q_up response.Q_service = Fleet_PV_Q_service response.Q_service_max = Fleet_PV_Q_service_max response.Q_service_min = Fleet_PV_Q_service_min response.Q_togrid = Fleet_PV_Q_grid response.Q_togrid_max = Fleet_PV_Q_grid_max[0] response.Q_togrid_min = Fleet_PV_Q_grid_min[0] response.S_Rating = FleetA_NP_S_rating response.T_restore = Fleet_PV_t_restore response.P_base = Fleet_PV_P_grid_max[0] response.sim_step = sim_step response.Strike_price = 0 response.SOC_cost = 'na' self.energy_impacts += abs( response.P_service) * (int(sim_step.total_seconds()) / 3600) return response
def run(self, P_req, Q_req, initSOC, t, dt, ts): #ExecuteFleet(self, Steps, Timestep, P_request, Q_request, forecast): # run(self, P_req=[0], Q_req=[0], ts=datetime.utcnow(), del_t=timedelta(hours=1)): # Give the code the capability to respond to None requests if P_req == None: P_req = 0 if Q_req == None: Q_req = 0 # run through fleet once as a forecast just to get initial conditions number = 0 # run through all the devices servsum = 0 servmax = 0 servmax2 = 0 NumDevicesToCall = 0 p_togrid = 0 p_service = 0 p_base = 0 service_max = 0 service_min = 0 # laststep = step - 1 # decision making about which RF to call on for service, check if available at last step, if so then # check for SoC > self.minSOC and Soc < self.maxSOC P_request_perRF = P_req/max(NumDevicesToCall,1) #divide the fleet request by the number of devices that can be called upon for n in range(self.numRF): if P_request_perRF < 0 and self.IsAvailableAdd[n] > 0: NumDevicesToCall += 1 elif P_request_perRF > 0 and self.IsAvailableShed[n] > 0: NumDevicesToCall += 1 P_request_perRF = P_req/max(NumDevicesToCall,1) #divide the fleet request by the number of devices that can be called upon ################################# for rfdg in self.RFs: #loop through all RFs TsetLast = self.TsetInitial[number] TairLastB = self.TairInitialB[number] TfoodLastB = self.TfoodInitialB[number] TcaseLastB = self.TcaseInitialB[number] TairLast = self.TairInitial[number] TfoodLast = self.TfoodInitial[number] TcaseLast = self.TcaseInitial[number] if P_req<0 and self.IsAvailableAdd[number] > 0 : # For step >=1, can use last step temperature... response = rfdg.FRIDGE(TairLastB, TfoodLastB, TcaseLastB, TairLast, TfoodLast, TcaseLast, TsetLast, self.Tamb[number][self.step], self.R_case[number], self.R_food[number], self.R_infil[number], self.C_case[number], self.C_food[number], self.C_air[number], P_request_perRF, dt, self.elementOnB[number], self.elementOn[number], self.lockonB[number], self.lockoffB[number], self.lockon[number], self.lockoff[number], self.cycle_off_base[number], self.cycle_on_base[number], self.cycle_off_grid[number], self.cycle_on_grid[number]) P_req = P_req - response.Eservice if P_req >= 0: # all the service request has been met P_req = 0 elif P_req>0 and self.IsAvailableShed[number] > 0 : # For step >=1, can use last step temperature... response = rfdg.FRIDGE(TairLastB, TfoodLastB, TcaseLastB, TairLast, TfoodLast, TcaseLast, TsetLast, self.Tamb[number][self.step], self.R_case[number], self.R_food[number], self.R_infil[number], self.C_case[number], self.C_food[number], self.C_air[number], P_request_perRF, dt, self.elementOnB[number], self.elementOn[number], self.lockonB[number], self.lockoffB[number], self.lockon[number], self.lockoff[number], self.cycle_off_base[number], self.cycle_on_base[number], self.cycle_off_grid[number], self.cycle_on_grid[number]) P_req = P_req - response.Eservice if P_req <= 0: # all the service request has been met P_req = 0 # break else: response = rfdg.FRIDGE(TairLastB, TfoodLastB, TcaseLastB, TairLast, TfoodLast, TcaseLast, TsetLast, self.Tamb[number][self.step], self.R_case[number], self.R_food[number], self.R_infil[number], self.C_case[number], self.C_food[number], self.C_air[number], 0, dt, self.elementOnB[number], self.elementOn[number], self.lockonB[number], self.lockoffB[number], self.lockon[number], self.lockoff[number], self.cycle_off_base[number], self.cycle_on_base[number], self.cycle_off_grid[number], self.cycle_on_grid[number]) # assign returned parameters to associated lists to be recorded self.TsetInitial[number] = response.Tset self.TairInitialB[number] = response.TairB self.TfoodInitialB[number] = response.TfoodB self.TcaseInitialB[number] = response.TcaseB self.elementOnB[number] = response.ElementOnB self.TairInitial[number] = response.Tair self.TfoodInitial[number] = response.Tfood self.TcaseInitial[number] = response.Tcase self.elementOn[number] = response.ElementOn self.lockonB[number] = response.lockonB self.lockoffB[number] = response.lockoffB self.lockon[number] = response.lockon self.lockoff[number] = response.lockoff self.cycle_off_base[number] = response.cycle_off_base self.cycle_on_base[number] = response.cycle_on_base self.cycle_off_grid[number] = response.cycle_off_grid self.cycle_on_grid[number] = response.cycle_on_grid self.SOC[number] = response.SOC self.SOCb[number] = response.SOC_b self.IsAvailableAdd[number] = response.IsAvailableAdd self.IsAvailableShed[number] = response.IsAvailableShed self.AvailableCapacityAdd[number] = response.AvailableCapacityAdd self.AvailableCapacityShed[number] = response.AvailableCapacityShed self.ServiceCallsAccepted[number] = response.ServiceCallsAccepted self.ServiceProvided[number] = response.Eservice p_togrid += response.Eused # resp.P_togrid_max += response.PusedMax # resp.P_togrid_min += response.PusedMin p_service += response.Eservice #Eused/10 #Eservice p_base += response.Pbase # FleetResponse.sim_step = response.sim_step number += 1 # go to next device if P_req>=0: service_max += response.AvailableCapacityShed # NOTE THIS ASSUMES THE MAX SERVICE IS LOAD SHED else: service_min += response.AvailableCapacityAdd # NOTE THIS ASSUMES THE MIN SERVICE IS LOAD ADD self.step += 1 # Output Fleet Response resp = FleetResponse() # Positive Prequest means reducing power consumption resp.ts = ts resp.sim_step = timedelta(seconds=dt) resp.P_service = [] resp.P_service_max = [] # positive only? resp.P_service_min = [] resp.P_togrid = [] resp.P_togrid_max = [] resp.P_togrid_min = [] resp.P_forecast = [] resp.P_base = [] resp.E = [] resp.C = [] # resp.P_dot_up = resp.P_togrid_max / ServiceRequest.Timestep.seconds resp.P_service = p_service resp.P_service_max = service_max # positive decrease loads resp.P_service_min = service_min # negative increase loasd resp.P_base = -p_base resp.P_togrid = -p_togrid resp.P_togrid_max = -(p_base-service_max) resp.P_togrid_min = -(p_base-service_min) resp.E = 0 resp.C = 0 # response.SOC/(self.numRF) resp.Q_togrid = 'NA' resp.Q_service = 'NA' resp.Q_service_max = 'NA' resp.Q_service_min = 'NA' resp.Q_togrid_min = 'NA' resp.Q_togrid_max = 'NA' resp.Q_dot_up = 'NA' resp.Q_dot_down = 'NA' resp.P_dot_up = 0 resp.P_dot_down = 0 resp.dT_hold_limit = 'NA' resp.T_restore = 'NA' resp.Strike_price = 'NA' resp.SOC_cost = 'NA' # TotalServiceProvidedPerTimeStep[step] = -1.0*self.P_service # per time step for all hvacs -1.0* """ Modify the power and SOC of the different subfeets according to the frequency droop regulation according to IEEE standard """ if self.FW21_Enabled and self.is_autonomous: power_ac = resp.P_service_max p_prev = resp.P_togrid power_fleet = self.frequency_watt(power_ac, p_prev, self.ts, self.location, self.db_UF_subfleet, self.db_OF_subfleet) power_fleet_step, SOC_step = self.update_soc_due_to_frequency_droop(initSOC, power_fleet, dt) self.time = t + dt self.ts = self.ts + timedelta(seconds = dt) # Restart time if it surpasses 24 hours if self.time > 24*3600: self.time = self.time - 24*3600 # Impact Metrics # Update the metrics Ttotal = 0 SOCTotal = 0 self.cycle_basee = np.sum(self.cycle_off_base) self.cycle_basee += np.sum(self.cycle_on_base) self.cycle_grid = np.sum(self.cycle_off_grid) self.cycle_grid += np.sum(self.cycle_on_grid) for numberr in range(self.numRF-1): if self.TairInitial[numberr]>=self.TsetInitial[numberr] + 3: #assume 2F, self.deadband self.unmet_hours += 1*self.sim_step/3600.0 self.ave_TairB = np.average(self.TairInitialB) self.ave_Tair = np.average(self.TairInitial) self.SOCb_metric = np.average(self.SOCb) self.SOC_metric = np.average(self.SOC) self.unmet_hours = self.unmet_hours/self.numRF self.ratio_P_togrid_P_base = resp.P_togrid/(resp.P_base) self.energy_impacts += abs(resp.P_service)*(self.sim_step/3600) return resp