def process_request(self, fleet_request):
        """
        Request for timestep ts

        :param fleet_request: an instance of FleetRequest
        :return fleet_response: an instance of FleetResponse
        """
        fleet_response = FleetResponse()
        fleet_response.ts = fleet_request.ts_req
        fleet_response.P_service = fleet_request.P_req

        return fleet_response
    def forecast(self, fleet_requests):
        """
        Request for current time step

        :param requests: list of FleetRequest instances
        :return res: list of FleetResponse instances
        """
        fleet_responses = []
        for fleet_request in fleet_requests:
            fleet_response = FleetResponse()
            fleet_responses.append(fleet_response)

        return fleet_responses
    def process_request(self, fleet_request):
        """
        The expectation that configuration will have at least the following
        items

        :param fleet_request: an instance of FleetRequest

        :return res: an instance of FleetResponse
        """

        res = FleetResponse()
        # Dummy values for testing
        res.P_togrid = 100
        res.P_service = 100
        res.Q_service = 100
        res.Q_togrid = 100

        return res
    def run(self, P_req=[0], Q_req=[0],ts=datetime.utcnow(), del_t=timedelta(hours=1)):
        '''
        This function takes the real and reactive power requested, date, time, and time step and 
        calculates the updated fleet variables as well as a FleetResponse object based on what the 
        simulated fleet is able to provide. It does this by dividing up the requests and sending each 
        device in the fleet its own request. If some of the fleet is unable to supply the requested 
        power, then the remainder is devided umung the remaining devices. 
        :param P_req: requested real power, Q_req: requested reactive power
               ts:datetime opject, del_t: timedelta object
        :return  fleet_response: an instance of FleetResponse 
        '''
        np = numpy.ones(self.num_of_devices,int)
        nq = numpy.ones(self.num_of_devices,int)
        p_none = 0
        q_none = 0
        if P_req==None:
            P_req = 0
            p_req = 0
            p_none = 1
        else:
            p_req = P_req/self.num_of_devices
        if Q_req==None:
            Q_req = 0
            q_req = 0
            q_none = 1
        else:
            q_req = Q_req/self.num_of_devices
        p_tot = 0.0
        q_tot = 0.0
        dt = del_t.total_seconds() / 3600.0 
        self.t = self.t + dt

        p_FW = numpy.zeros(self.num_of_devices,float)
        q_VV = numpy.zeros(self.num_of_devices,float)
        
        response = FleetResponse()
        response.ts = ts
        
        last_P = numpy.zeros(self.num_of_devices,int)
        last_Q = numpy.zeros(self.num_of_devices,int)
        soc_update = copy.copy(self.soc)
        if self.model_type == 'CRM':
            pdc_update = self.pdc
            ibat_update = self.ibat
            v1_update = self.v1
            v2_update = self.v2
            vbat_update = self.vbat

        for i in range(self.num_of_devices):
            last_P[i] = self.P_service[i]
            last_Q[i] = self.Q_service[i]
            self.P_service[i] = 0
            self.Q_service[i] = 0
        TOL = 0.000001  # tolerance 
        one_pass = 1 #guarantees at least one pass through the control loop
        while ((p_tot < P_req-TOL or p_tot > P_req+TOL) or (q_tot < Q_req-TOL or q_tot > Q_req+TOL)) and sum(np)!=0 and sum(nq)!=0 or one_pass == 1: #continue looping through devices until the power needs are met or all devices are at their limits
            one_pass = 0
            # distribute the requested power equally among the devices that are not at their limits
            p_req = (P_req - p_tot)/sum(np)
            q_req = (Q_req - q_tot)/sum(nq) 
            for i in range(self.num_of_devices):
                if self.model_type == 'ERM':
                    soc_update[i] = self.run_soc_update(p_req,q_req,np,nq,last_P,last_Q,i,dt)
                if self.model_type == 'CRM':
                    [soc_update[i],pdc_update[i],ibat_update[i],vbat_update[i],v1_update[i],v2_update[i]] = \
                         self.run_soc_update(p_req,q_req,np,nq,last_P,last_Q,i,dt)
            # at the end of looping through the devices, add up their power to determine if the request has been met
            p_tot = sum(self.P_service)
            q_tot = sum(self.Q_service)
        
        # after all the power needs have been placed and met, then make adjustments based on autonomous operation settings 
        if (self.FW21_Enabled == True or self.VV11_Enabled == True) and self.is_autonomous == True:
            for i in range(self.num_of_devices) :
                p_req = self.P_service[i]
                q_req = self.Q_service[i]
                # determin if VV11 or FW21 change the p_req or q_req
                if self.FW21_Enabled == True:
                    p_mod = self.frequency_watt(p_req,ts,self.location[i])
                if self.VV11_Enabled == True:
                    if q_none == 1:
                        q_mod = self.volt_var(ts,self.location[i])
                    if self.model_type == 'ERM':
                        soc_update[i] = self.run_soc_update(p_req=(p_mod-p_req),q_req=(q_mod-q_req),np=numpy.ones(self.num_of_devices),nq=numpy.ones(self.num_of_devices),last_P=last_P,last_Q=last_Q,i=i,dt=dt)
                    if self.model_type == 'CRM':
                        [soc_update[i],pdc_update[i],ibat_update[i],vbat_update[i],v1_update[i],v2_update[i]] = \
                            self.run_soc_update(p_req=(p_mod-p_req),q_req=(q_mod-q_req),np=numpy.ones(self.num_of_devices),nq=numpy.ones(self.num_of_devices),last_P=last_P,last_Q=last_Q,i=i,dt=dt)
                else :
                    if self.model_type == 'ERM':
                        soc_update[i] = self.run_soc_update(p_req=(p_req),q_req=(q_req),np=numpy.ones(self.num_of_devices),nq=numpy.ones(self.num_of_devices),last_P=last_P,last_Q=last_Q,i=i,dt=dt)
                    if self.model_type == 'CRM':
                        [soc_update[i],pdc_update[i],ibat_update[i],vbat_update[i],v1_update[i],v2_update[i]] = \
                            self.run_soc_update(p_req=p_req,q_req=q_req,np=numpy.ones(self.num_of_devices),nq=numpy.ones(self.num_of_devices),last_P=last_P,last_Q=last_Q,i=i,dt=dt)

        p_tot = sum(self.P_service)
        q_tot = sum(self.Q_service)    
        # update SoC
        self.soc = soc_update
        # update SoH
        if self.model_type == 'ERM':
            self.soh = self.soh - 100*dt*abs(self.P_service)/((1+1/self.energy_efficiency)*self.cycle_life*self.energy_capacity)
        if self.model_type == 'CRM':
            self.v1 = v1_update
            self.v2 = v2_update
            self.voc_update()
            self.ibat = ibat_update
            self.vbat = (self.v1 + self.v2 + self.voc + self.ibat*self.r0) *self.n_cells
            self.soh = self.soh - 100*dt*abs(self.ibat)/((1+1/self.coulombic_efficiency)*self.cycle_life*self.charge_capacity)
        # once the power request has been met, or all devices are at their limits, return the response variables
        response.P_service = p_tot
        response.Q_service = q_tot  
        response.soc = numpy.average(self.soc)
        response.E = numpy.average(self.soc) * self.energy_capacity / 100.0
        return response 
    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
Beispiel #7
0
    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 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
Beispiel #9
0
    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