def initialize_states(self,ia0,xa0,ua0,xDC0,xQ0,xPLL0,wte0): """Initialize inverter states. Extended description of function. Args: ia0 (float): Initial current xa0 (float): Initial controller state ua0 (float): Initial controller state """ self.Vdc = self.Vdc_ref #DC link voltage self.Ppv = self.Ppv_calc(self.Vdc_actual) #PV module power output #Initialize all states with steady state values at current operating point if self.STEADY_STATE_INITIALIZATION: #ia0,xa0,ua0,ib0,xb0,ub0,ic0,xc0,uc0,Vdc0,xDC0,xQ0,xPLL0,wte0 self.steady_state_calc() else: #Phase a self.ia = ia0 self.xa = xa0 self.ua = ua0 #DC link voltage and reactive power controller self.xDC = xDC0 self.xQ = xQ0 #PLL self.xPLL = xPLL0 self.wte = wte0 if type(self).__name__ == 'SolarPV_DER_ThreePhase': ib0 = utility_functions.Ub_calc(ia0) xb0 = utility_functions.Ub_calc(xa0) ub0 = utility_functions.Ub_calc(ua0) ic0 = utility_functions.Uc_calc(ia0) xc0 = utility_functions.Uc_calc(xa0) uc0 = utility_functions.Uc_calc(ua0) #Phase b self.ib = ib0 self.xb = xb0 #Shift by -120 degrees self.ub = ub0 #Phase c self.ic = ic0 self.xc = xc0 #Shift by +120 degrees self.uc = uc0
def __init__(self, events, unbalance_ratio_b=1.0, unbalance_ratio_c=1.0, Z2_actual=1.61 + 1j * 5.54): """Creates an instance of `GridSimulation`. Args: events: An instance of `SimulationEvents`. unbalance_ratio_b,unbalance_ratio_c: Scalar specifying difference in Phase B and Phase C voltage magnitude compared to phase A. Z2_actual: Complex scalar specifying the impedance of the feeder connecting the DER with the voltage source. """ #Increment count Grid.grid_count = Grid.grid_count + 1 #Events object self.events = events #Object name self.name = 'grid_' + str(Grid.grid_count) #Voltage unbalance self.unbalance_ratio_b = unbalance_ratio_b self.unbalance_ratio_c = unbalance_ratio_c #Grid impedance self.Z2_actual = Z2_actual self.R2_actual = self.Z2_actual.real self.L2_actual = self.Z2_actual.imag / (2 * math.pi * 60.0) #Converting to per unit self.R2 = self.R2_actual / self.Zbase #Transmission line resistance self.L2 = self.L2_actual / self.Lbase #Transmission line resistance self.Z2 = self.Z2_actual / self.Zbase #Transmission line impedance self.transmission_name = 'transmission_' + str(Grid.grid_count) #Grid voltage/frequency events self.Vagrid, self.wgrid = events.grid_events( t=0.0) #Grid voltage and frequency set-point self.Vagrid = self.Vagrid * (self.Vgridrated / self.Vbase) self.Vbgrid = utility_functions.Ub_calc(self.Vagrid * self.unbalance_ratio_b) self.Vcgrid = utility_functions.Uc_calc(self.Vagrid * self.unbalance_ratio_c) #Actual Grid voltage self.vag = self.Vagrid self.vbg = utility_functions.Ub_calc(self.vag * self.unbalance_ratio_b) self.vcg = utility_functions.Uc_calc(self.vag * self.unbalance_ratio_c) self.Vgrms = self.Vgrms_calc()
def steady_state_model(self, t): """Grid voltage change.""" Vagrid_new, wgrid_new = self.events.grid_events(t) Vagrid_new = Vagrid_new * (self.Vgridrated / self.Vbase) if abs(self.Vagrid - Vagrid_new) > 0.0 and t >= self._t_voltage_previous: utility_functions.print_to_terminal( "{}:Grid voltage changed from {:.3f} V to {:.3f} V at {:.3f} s" .format(self.name, self.Vagrid, Vagrid_new, t)) self.Vagrid = Vagrid_new self.Vbgrid = utility_functions.Ub_calc(self.Vagrid * self.unbalance_ratio_b) self.Vcgrid = utility_functions.Uc_calc(self.Vagrid * self.unbalance_ratio_c) self._t_voltage_previous = t if abs(self.wgrid - wgrid_new) > 0.0 and t >= self._t_frequency_previous: utility_functions.print_to_terminal( "{}:Grid frequency changed from {:.3f} Hz to {:.3f} Hz at {:.3f} s" .format(self.name, self.wgrid / (2.0 * math.pi), wgrid_new / (2.0 * math.pi), t)) self.wgrid = wgrid_new self._t_frequency_previous = t self.vag = self.Vagrid self.vbg = self.Vbgrid self.vcg = self.Vcgrid
def test_steady_state_calc(self): """Test PV-DER three phase mode.""" events = SimulationEvents() voltage_list = [(self.Va, self.Vb, self.Vc), (cmath.rect(206.852, math.radians(-36.9906)), cmath.rect(206.128, math.radians(-157.745)), cmath.rect(208.387, math.radians(82.7291))), (169.18 + 118.52j, utility_functions.Ub_calc(169.18 + 118.52j), utility_functions.Uc_calc(169.18 + 118.52j))] for voltages in voltage_list: Va = voltages[0] Vb = voltages[1] Vc = voltages[2] print('Testing voltages:{}'.format(voltages)) PVDER = SolarPV_DER_ThreePhase( events=events, Sinverter_rated=self.power_rating, Vrms_rated=self.Vrms, #175 gridVoltagePhaseA=Va, gridVoltagePhaseB=Vb, gridVoltagePhaseC=Vc, gridFrequency=self.wgrid, standAlone=False, STEADY_STATE_INITIALIZATION=True, verbosity='INFO') self.assertAlmostEqual( PVDER.Ppv, PVDER.S.real, delta=0.001, msg= 'Inverter power output must be equal to PV module power output at steady-state!' ) self.assertAlmostEqual( PVDER.S_PCC.imag, PVDER.Q_ref, delta=0.001, msg= 'Inverter reactive power output must be equal to Q reference!') self.assertAlmostEqual( PVDER.Vdc, PVDER.Vdc_ref, delta=0.001, msg='DC link voltage should be equal to reference!') self.assertAlmostEqual(PVDER.ma + PVDER.mb + PVDER.mc, 0.0 + 1j * 0.0, delta=0.001, msg='Duty cycles should sum to zero!') self.assertLess( abs(PVDER.ma), 1.0, msg='Magnitude of duty cycle should be less than 1!')
def getDERRatedPower(self, powerRating, voltageRating): """Return actual real and reactive power rating of DER for given power and voltage rating. Args: powerRating (float): Rated power of DER in kW. voltageRating (float): Rated voltage of DER in L-G RMS. """ DERFilePath = OpenDSSData.config['myconfig']['DERFilePath'] DERModelType = OpenDSSData.config['myconfig']['DERModelType'] Va = cmath.rect(voltageRating * math.sqrt(2), 0.0) Vb = utility_functions.Ub_calc(Va) Vc = utility_functions.Uc_calc(Va) PVDER_model = DERModel(modelType=DERModelType, events=events, configFile=DERFilePath, powerRating=powerRating * 1e3, VrmsRating=voltageRating, gridVoltagePhaseA=Va, gridVoltagePhaseB=Vb, gridVoltagePhaseC=Vc, gridFrequency=2 * math.pi * 60.0, standAlone=False, steadyStateInitialization=True) self.PV_model = PVDER_model.DER_model return DER_model.S_PCC.real * DER_model.Sbase, DER_model.S_PCC.imag * DER_model.Sbase
def test_steady_state_calc(self): """Test PV-DER three phase mode.""" events = SimulationEvents() voltage_list = [(self.Va, self.Vb, self.Vc), (cmath.rect(206.852, math.radians(-36.9906)), cmath.rect(206.128, math.radians(-157.745)), cmath.rect(208.387, math.radians(82.7291))), (169.18 + 118.52j, utility_functions.Ub_calc(169.18 + 118.52j), utility_functions.Uc_calc(169.18 + 118.52j))] for voltages in voltage_list: Va = voltages[0] Vb = voltages[1] Vc = voltages[2] print('Testing voltages:{}'.format(voltages)) PVDER = SolarPVDERThreePhase(events=events, configFile=config_file, **{ **self.flag_arguments, **self.ratings_arguments, **self.voltage_arguments }) self.assertAlmostEqual( PVDER.Ppv, PVDER.S.real, delta=0.001, msg= 'Inverter power output must be equal to PV module power output at steady-state!' ) self.assertAlmostEqual( PVDER.S_PCC.imag, PVDER.Q_ref, delta=0.001, msg= 'Inverter reactive power output must be equal to Q reference!') self.assertAlmostEqual( PVDER.Vdc, PVDER.Vdc_ref, delta=0.001, msg='DC link voltage should be equal to reference!') self.assertAlmostEqual(PVDER.ma + PVDER.mb + PVDER.mc, 0.0 + 1j * 0.0, delta=0.001, msg='Duty cycles should sum to zero!') self.assertLess( abs(PVDER.ma), 1.0, msg='Magnitude of duty cycle should be less than 1!')
def update_voltages(self): """Update voltages.""" #Update inverter terminal voltage self.vta = self.vta_calc() self.vtb = self.vtb_calc() self.vtc = self.vtc_calc() #Update PCC LV side voltage self.va = self.va_calc() self.vb = utility_functions.Ub_calc(self.va) self.vc = utility_functions.Uc_calc(self.va)
def update_inverter_states(self,ia,xa,ua,Vdc,xDC,xQ,xPLL,wte): """Update inverter states""" self.ia = ia self.xa = xa self.ua = ua self.ib = utility_functions.Ub_calc(self.ia) self.xb = utility_functions.Ub_calc(self.xa) self.ub = utility_functions.Ub_calc(self.ua) self.ic = utility_functions.Uc_calc(self.ia) self.xc = utility_functions.Uc_calc(self.xa) self.uc = utility_functions.Uc_calc(self.ua) self.Vdc = Vdc self.xDC = xDC self.xQ = xQ self.xPLL = xPLL self.wte = wte
def update_grid_measurements(self,gridVoltagePhaseA, gridVoltagePhaseB, gridVoltagePhaseC): """Update grid voltage and frequency in non-standalone model. Args: gridVoltagePhaseA (complex): Value of gridVoltagePhaseA gridVoltagePhaseB (complex): Value of gridVoltagePhaseB gridVoltagePhaseC (complex): Value of gridVoltagePhaseC """ self.PV_model.gridVoltagePhaseA = gridVoltagePhaseA if type(self.PV_model).__name__ == 'SolarPVDERThreePhase': self.PV_model.gridVoltagePhaseB = gridVoltagePhaseB self.PV_model.gridVoltagePhaseC = gridVoltagePhaseC elif type(self.PV_model).__name__ == 'SolarPVDERThreePhaseBalanced': self.PV_model.gridVoltagePhaseB = utility_functions.Ub_calc(gridVoltagePhaseA) self.PV_model.gridVoltagePhaseC = utility_functions.Uc_calc(gridVoltagePhaseA)
def time_series_vgrid(self): """Time series grid frequency.""" self.vag_t = [] self.vbg_t = [] self.vcg_t = [] for i, t in enumerate( self.t ): #Loop through grid events and calculate wgrid at each time step Vagrid_new, _ = self.simulation_events.grid_events(t) #Conversion of grid voltage setpoint self.grid_model.vag = Vagrid_new * (self.grid_model.Vgridrated / self.Vbase) self.grid_model.vbg = utility_functions.Ub_calc( self.grid_model.vag * self.grid_model.unbalance_ratio_b) self.grid_model.vcg = utility_functions.Uc_calc( self.grid_model.vag * self.grid_model.unbalance_ratio_c) self.vag_t.append(self.grid_model.vag) self.vbg_t.append(self.grid_model.vbg) self.vcg_t.append(self.grid_model.vcg) self.vag_t = np.asarray(self.vag_t) self.vbg_t = np.asarray(self.vbg_t) self.vcg_t = np.asarray(self.vcg_t) self.vagR_t = self.vag_t.real self.vagI_t = self.vag_t.imag self.vbgR_t = self.vbg_t.real self.vbgI_t = self.vbg_t.imag self.vcgR_t = self.vcg_t.real self.vcgI_t = self.vcg_t.imag if not self.LOOP_MODE: self.simulation_events.reset_event_counters( ) #reset event counters
def collect_states(self,solution): """Collect states from ode solution.""" #Phase a states self.iaR_t = solution[:,0] self.iaI_t = solution[:,1] self.xaR_t = solution[:,2] self.xaI_t = solution[:,3] self.uaR_t = solution[:,4] self.uaI_t = solution[:,5] if type(self.PV_model).__name__ == 'SolarPVDERSinglePhase': #DC link voltage variables self.Vdc_t = solution[:,6] self.xDC_t = solution[:,7] self.xQ_t = solution[:,8] #PLL variables self.xPLL_t = solution[:,9] #Frequency integration to get angle self.wte_t = solution[:,10] elif type(self.PV_model).__name__ == 'SolarPVDERThreePhase': #Phase b states self.ibR_t = solution[:,6] self.ibI_t = solution[:,7] self.xbR_t = solution[:,8] self.xbI_t = solution[:,9] self.ubR_t = solution[:,10] self.ubI_t = solution[:,11] #Phase c states self.icR_t = solution[:,12] self.icI_t = solution[:,13] self.xcR_t = solution[:,14] self.xcI_t = solution[:,15] self.ucR_t = solution[:,16] self.ucI_t = solution[:,17] #DC link voltage variables self.Vdc_t = solution[:,18] self.xDC_t = solution[:,19] self.xQ_t = solution[:,20] #PLL variables self.xPLL_t = solution[:,21] #Frequency integration to get angle self.wte_t = solution[:,22] elif type(self.PV_model).__name__ == 'SolarPVDER_SinglePhaseConstantVdc': self.Vdc_t = np.array([self.PV_model.Vdc]*len(self.iaR_t)) self.xP_t = solution[:,6] #Active power control variable self.xQ_t = solution[:,7] #Reactive power control variable self.xPLL_t = solution[:,8] #PLL variables self.wte_t = solution[:,9] #Frequency integration to get angle elif type(self.PV_model).__name__ == 'SolarPVDERThreePhaseConstantVdc': #Phase b states self.ibR_t = solution[:,6] self.ibI_t = solution[:,7] self.xbR_t = solution[:,8] self.xbI_t = solution[:,9] self.ubR_t = solution[:,10] self.ubI_t = solution[:,11] #Phase c states self.icR_t = solution[:,12] self.icI_t = solution[:,13] self.xcR_t = solution[:,14] self.xcI_t = solution[:,15] self.ucR_t = solution[:,16] self.ucI_t = solution[:,17] self.Vdc_t = np.array([self.PV_model.Vdc]*len(self.iaR_t)) self.xP_t = solution[:,18] #Active power control variable self.xQ_t = solution[:,19] #Reactive power control variable self.xPLL_t = solution[:,20] #PLL variables self.wte_t = solution[:,21] #Frequency integration to get angle elif type(self.PV_model).__name__ == 'SolarPVDERThreePhaseBalanced': ia_t = self.iaR_t+self.iaI_t*1j xa_t = self.xaR_t+self.xaI_t*1j ua_t = self.uaR_t+self.uaI_t*1j ib_t = utility_functions.Ub_calc(ia_t) xb_t = utility_functions.Ub_calc(xa_t) ub_t = utility_functions.Ub_calc(ua_t) ic_t = utility_functions.Uc_calc(ia_t) xc_t = utility_functions.Uc_calc(xa_t) uc_t = utility_functions.Uc_calc(ua_t) self.ibR_t = ib_t.real self.ibI_t = ib_t.imag self.xbR_t = xb_t.real self.xbI_t = xb_t.imag self.ubR_t = ub_t.real self.ubI_t = ub_t.imag self.icR_t = ic_t.real self.icI_t = ic_t.imag self.xcR_t = xc_t.real self.xcI_t = xc_t.imag self.ucR_t = uc_t.real self.ucI_t = uc_t.imag #DC link voltage variables self.Vdc_t = solution[:,6] self.xDC_t = solution[:,7] self.xQ_t = solution[:,8] #PLL variables self.xPLL_t = solution[:,9] #Frequency integration to get angle self.wte_t = solution[:,10]
def initialize_y0_t(self): """Initialize y0_t.""" self.iaR_t = np.array([self.PV_model.y0[0]]) self.iaI_t = np.array([self.PV_model.y0[1]]) self.xaR_t = np.array([self.PV_model.y0[2]]) self.xaI_t = np.array([self.PV_model.y0[3]]) self.uaR_t = np.array([self.PV_model.y0[4]]) self.uaI_t = np.array([self.PV_model.y0[5]]) if type(self.PV_model).__name__ == 'SolarPVDERSinglePhase': self.Vdc_t = np.array([self.PV_model.y0[6]]) #DC link voltage variable self.xDC_t = np.array([self.PV_model.y0[7]]) #DC link voltage control variable self.xQ_t = np.array([self.PV_model.y0[8]]) #Reactive power control variable self.xPLL_t = np.array([self.PV_model.y0[9]]) #PLL variables self.wte_t = np.array([self.PV_model.y0[10]]) #Frequency integration to get angle elif type(self.PV_model).__name__ == 'SolarPVDERThreePhase': self.ibR_t = np.array([self.PV_model.y0[6]]) self.ibI_t = np.array([self.PV_model.y0[7]]) self.xbR_t = np.array([self.PV_model.y0[8]]) self.xbI_t = np.array([self.PV_model.y0[9]]) self.ubR_t = np.array([self.PV_model.y0[10]]) self.ubI_t = np.array([self.PV_model.y0[11]]) self.icR_t = np.array([self.PV_model.y0[12]]) self.icI_t = np.array([self.PV_model.y0[13]]) self.xcR_t = np.array([self.PV_model.y0[14]]) self.xcI_t = np.array([self.PV_model.y0[15]]) self.ucR_t = np.array([self.PV_model.y0[16]]) self.ucI_t = np.array([self.PV_model.y0[17]]) self.Vdc_t = np.array([self.PV_model.y0[18]]) self.xDC_t = np.array([self.PV_model.y0[19]]) self.xQ_t = np.array([self.PV_model.y0[20]]) self.xPLL_t = np.array([self.PV_model.y0[21]]) self.wte_t = np.array([self.PV_model.y0[22]]) elif type(self.PV_model).__name__ == 'SolarPVDERThreePhaseConstantVdc': self.ibR_t = np.array([self.PV_model.y0[6]]) self.ibI_t = np.array([self.PV_model.y0[7]]) self.xbR_t = np.array([self.PV_model.y0[8]]) self.xbI_t = np.array([self.PV_model.y0[9]]) self.ubR_t = np.array([self.PV_model.y0[10]]) self.ubI_t = np.array([self.PV_model.y0[11]]) self.icR_t = np.array([self.PV_model.y0[12]]) self.icI_t = np.array([self.PV_model.y0[13]]) self.xcR_t = np.array([self.PV_model.y0[14]]) self.xcI_t = np.array([self.PV_model.y0[15]]) self.ucR_t = np.array([self.PV_model.y0[16]]) self.ucI_t = np.array([self.PV_model.y0[17]]) self.Vdc_t = np.array([self.PV_model.Vdc]) #Voltage is constant self.xP_t = np.array([self.PV_model.y0[18]]) #Active power control variable self.xQ_t = np.array([self.PV_model.y0[19]]) #Reactive power control variable self.xPLL_t = np.array([self.PV_model.y0[20]]) #PLL variables self.wte_t = np.array([self.PV_model.y0[21]]) #Frequency integration to get angle elif type(self.PV_model).__name__ == 'SolarPVDERThreePhaseBalanced': ia_t = self.iaR_t+self.iaI_t*1j xa_t = self.xaR_t+self.xaI_t*1j ua_t = self.uaR_t+self.uaI_t*1j ib_t = utility_functions.Ub_calc(ia_t) xb_t = utility_functions.Ub_calc(xa_t) ub_t = utility_functions.Ub_calc(ua_t) ic_t = utility_functions.Uc_calc(ia_t) xc_t = utility_functions.Uc_calc(xa_t) uc_t = utility_functions.Uc_calc(ua_t) self.ibR_t = ib_t.real self.ibI_t = ib_t.imag self.xbR_t = xb_t.real self.xbI_t = xb_t.imag self.ubR_t = ub_t.real self.ubI_t = ub_t.imag self.icR_t = ic_t.real self.icI_t = ic_t.imag self.xcR_t = xc_t.real self.xcI_t = xc_t.imag self.ucR_t = uc_t.real self.ucI_t = uc_t.imag self.Vdc_t = np.array([self.PV_model.y0[6]]) #DC link voltage variable self.xDC_t = np.array([self.PV_model.y0[7]]) #DC link voltage control variable self.xQ_t = np.array([self.PV_model.y0[8]]) #Reactive power control variable self.xPLL_t = np.array([self.PV_model.y0[9]]) #PLL variables self.wte_t = np.array([self.PV_model.y0[10]]) #Frequency integration to get angle elif type(self.PV_model).__name__ == 'SolarPVDERSinglePhaseConstantVdc': self.Vdc_t = np.array([self.PV_model.Vdc]) #Voltage is constant self.xP_t = np.array([self.PV_model.y0[6]]) #Active power control variable self.xQ_t = np.array([self.PV_model.y0[7]]) #Reactive power control variable self.xPLL_t = np.array([self.PV_model.y0[8]]) #PLL variables self.wte_t = np.array([self.PV_model.y0[9]]) #Frequency integration to get angle
def initialize_grid_measurements(self, DER_arguments): """Initialize inverter states. Args: gridVoltagePhaseA (complex): Value of gridVoltagePhaseA gridVoltagePhaseB (complex): Value of gridVoltagePhaseB gridVoltagePhaseC (complex): Value of gridVoltagePhaseC gridFrequency (float): Value of gridFrequency """ if not self.standAlone: if 'gridFrequency' in DER_arguments: self.gridFrequency = DER_arguments['gridFrequency'] else: raise ValueError( 'Grid voltage source Frequency need to be supplied if model is not stand alone!' ) if templates.DER_design_template[ self.DER_model_type]['basic_specs'][ 'n_phases'] >= 1: #Check if model has one phase if 'gridVoltagePhaseA' in DER_arguments: self.gridVoltagePhaseA = DER_arguments[ 'gridVoltagePhaseA'] / self.Vbase else: raise ValueError( 'Grid voltage source phase A need to be supplied if model is not stand alone!' ) if templates.DER_design_template[ self.DER_model_type]['basic_specs'][ 'n_phases'] >= 2: #Check if model has 2 phases if templates.DER_design_template[ self.DER_model_type]['basic_specs'][ 'unbalanced']: #Check if model is unbalanced if 'gridVoltagePhaseB' in DER_arguments: self.gridVoltagePhaseB = DER_arguments[ 'gridVoltagePhaseB'] / self.Vbase else: raise ValueError( 'Grid voltage source phase B need to be supplied if model is not stand alone!' ) else: self.gridVoltagePhaseB = utility_functions.Ub_calc( self.gridVoltagePhaseA) if templates.DER_design_template[ self.DER_model_type]['basic_specs'][ 'n_phases'] >= 3: #Check if model has 3 phases if templates.DER_design_template[ self.DER_model_type]['basic_specs'][ 'unbalanced']: #Check if model is unbalanced if 'gridVoltagePhaseC' in DER_arguments: self.gridVoltagePhaseC = DER_arguments[ 'gridVoltagePhaseC'] / self.Vbase else: raise ValueError( 'Grid voltage source phase C need to be supplied if model is not stand alone!' ) else: self.gridVoltagePhaseC = utility_functions.Uc_calc( self.gridVoltagePhaseA) if templates.DER_design_template[ self.DER_model_type]['basic_specs'][ 'n_phases'] >= 4: #Check if model has 3 phases raise ValueError('Model has more than 3 phases!')
def initialize_states(self, DER_arguments): """Initialize inverter states. Args: ia0 (float): Initial current xa0 (float): Initial controller state ua0 (float): Initial controller state """ if 'ia' in DER_arguments: ia0 = DER_arguments['ia'] else: ia0 = self.DER_config['initial_states'][ 'iaR'] + 1j * self.DER_config['initial_states']['iaI'] if 'xa' in DER_arguments: xa0 = DER_arguments['xa'] else: xa0 = self.DER_config['initial_states'][ 'xaR'] + 1j * self.DER_config['initial_states']['xaI'] if 'ua' in DER_arguments: ua0 = DER_arguments['ua'] else: ua0 = self.DER_config['initial_states'][ 'uaR'] + 1j * self.DER_config['initial_states']['uaI'] if 'xDC' in templates.DER_design_template[ self.DER_model_type]['initial_states']: if 'xDC' in DER_arguments: xDC0 = DER_arguments['xDC'] else: xDC0 = self.DER_config['initial_states']['xDC'] if 'xP' in templates.DER_design_template[ self.DER_model_type]['initial_states']: if 'xP' in DER_arguments: xP0 = DER_arguments['xP'] else: xP0 = self.DER_config['initial_states']['xP'] if 'xQ' in templates.DER_design_template[ self.DER_model_type]['initial_states']: if 'xQ' in DER_arguments: xQ0 = DER_arguments['xQ'] else: xQ0 = self.DER_config['initial_states']['xQ'] xPLL0 = self.DER_config['initial_states']['xPLL'] wte0 = self.DER_config['initial_states']['wte'] self.Vdc = self.Vdc_ref #DC link voltage self.Ppv = self.Ppv_calc(self.Vdc_actual) #PV module power output #Initialize all states with steady state values at current operating point if self.steady_state_initialization: self.steady_state_calc() else: #Phase a self.ia = ia0 self.xa = xa0 self.ua = ua0 if 'xDC' in templates.DER_design_template[ self.DER_model_type]['initial_states']: self.xDC = xDC0 #DC link voltage controller if 'xP' in templates.DER_design_template[ self.DER_model_type]['initial_states']: self.xP = xP0 #Active power controller if 'xQ' in templates.DER_design_template[ self.DER_model_type]['initial_states']: self.xQ = xQ0 #Reactive power controller self.xPLL = xPLL0 #PLL self.wte = wte0 if self.DER_model_type in templates.three_phase_models: ib0 = utility_functions.Ub_calc(ia0) xb0 = utility_functions.Ub_calc(xa0) ub0 = utility_functions.Ub_calc(ua0) ic0 = utility_functions.Uc_calc(ia0) xc0 = utility_functions.Uc_calc(xa0) uc0 = utility_functions.Uc_calc(ua0) #Phase b self.ib = ib0 self.xb = xb0 #Shift by -120 degrees self.ub = ub0 #Phase c self.ic = ic0 self.xc = xc0 #Shift by +120 degrees self.uc = uc0
def power_error_calc(self, x): """Function for power.""" maR = x[0] maI = x[1] iaR = x[2] iaI = x[3] ma = maR + 1j * maI ia = self.ia = iaR + 1j * iaI vta = ma * (self.Vdc / 2) va = self.va_calc() St = (vta * ia.conjugate()) / 2 S_PCC = (va * ia.conjugate()) / 2 #Sloss_filter = ((abs(ia)/math.sqrt(2))**2)*self.Zf if self.DER_model_type in templates.three_phase_models: if not self.allow_unbalanced_m: mb = utility_functions.Ub_calc(ma) mc = utility_functions.Uc_calc(ma) ib = self.ib = utility_functions.Ub_calc(ia) ic = self.ic = utility_functions.Uc_calc(ia) elif self.allow_unbalanced_m: mbR = x[4] mbI = x[5] ibR = x[6] ibI = x[7] mcR = x[8] mcI = x[9] icR = x[10] icI = x[11] mb = mbR + 1j * mbI mc = mcR + 1j * mcI ib = self.ib = ibR + 1j * ibI ic = self.ic = icR + 1j * icI vtb = mb * (self.Vdc / 2) vtc = mc * (self.Vdc / 2) vb = self.vb_calc() vc = self.vc_calc() St = St + (vtb * ib.conjugate() + vtc * ic.conjugate()) / 2 S_PCC = S_PCC + (vb * ib.conjugate() + vc * ic.conjugate()) / 2 #Sloss_filter = Sloss_filter + ((abs(ib)/math.sqrt(2))**2 + (abs(ic)/math.sqrt(2))**2)*self.Zf P_error = (St.real - self.Ppv)**2 Q_error = (S_PCC.imag - self.Q_ref)**2 #Qloss_filter_expected = self.n_phases*((self.Ppv/(self.n_phases*self.Vrms_ref))**2)*self.Xf #Ploss_filter_expected = self.n_phases*((self.Ppv/(self.n_phases*self.Vrms_ref))**2)*self.Rf #P_PCC_error = ((S_PCC.real + Sloss_filter.real) - self.Ppv)**2 #S_error = (abs(St -(S_PCC+Sloss_filter)))**2 #Q_error_filter_expected = (St.imag - Qloss_filter_expected)**2 if self.DER_model_type in templates.three_phase_models: del_1 = utility_functions.relative_phase_calc(ma, mb) del_2 = utility_functions.relative_phase_calc(ma, mc) del_3 = utility_functions.relative_phase_calc(mb, mc) if del_1 > math.pi: del_1 = abs(del_1 - 2 * math.pi) if del_2 > math.pi: del_2 = abs(del_2 - 2 * math.pi) if del_3 > math.pi: del_3 = abs(del_3 - 2 * math.pi) if self.allow_unbalanced_m: m_error = (abs((va + vb + vc) - (vta + vtb + vtc)))**2 else: m_error = (del_1 - PHASE_DIFFERENCE_120)**2 + (del_2 - PHASE_DIFFERENCE_120)**2 + (del_3 - PHASE_DIFFERENCE_120)**2 +\ (abs(ma) - abs(mb))**2 + (abs(ma) - abs(mc))**2 + (abs(mb) - abs(mc))**2 m_error = m_error i_error = (abs(ia + ib + ic) + abs(vta - va - ia * self.Zf) + abs(vtb - vb - ib * self.Zf) + abs(vtc - vc - ic * self.Zf))**2 else: m_error = 0.0 i_error = (abs(vta - va - ia * self.Zf))**2 return P_error + Q_error + m_error + i_error #+S_error + P_PCC_error
def steady_state_calc(self): """Find duty cycle and inverter current that minimize steady state error and return steady state values.""" self.logger.debug('Solving for steady state at current operating point.') x0 = np.array([self.steadystate_values[self.parameter_ID]['maR0'], self.steadystate_values[self.parameter_ID]['maI0'], self.steadystate_values[self.parameter_ID]['iaR0'], self.steadystate_values[self.parameter_ID]['iaI0']]) disp = bool(self.verbosity == 'DEBUG') result = minimize(self.power_error_calc, x0, method='nelder-mead',options={'xtol': 1e-8, 'disp': disp, 'maxiter':10000}) if not result.success: raise ValueError('Steady state solution did not converge! Change operating point or disable steady state flag and try again.') ma0 = result.x[0] + 1j*result.x[1] self.ia = result.x[2] + 1j*result.x[3] self.ua = 0.0+0.0j self.xa = ma0 self.vta = self.vta_calc() self.va = self.va_calc() self.xDC = self.ia.real self.xQ = self.ia.imag self.xPLL = 0.0 self.wte = 2*math.pi if type(self).__name__ == 'SolarPV_DER_ThreePhase': mb0 = utility_functions.Ub_calc(ma0) mc0 = utility_functions.Uc_calc(ma0) #mb0 = result.x[4] + 1j*result.x[5] #mc0 = result.x[8] + 1j*result.x[9] self.xb = mb0 self.xc = mc0 self.ib = utility_functions.Ub_calc(self.ia) self.ic = utility_functions.Uc_calc(self.ia) #self.ib = result.x[6] + 1j*result.x[7] #self.ic = result.x[10] + 1j*result.x[11] self.ub = 0.0+0.0j self.uc = 0.0+0.0j self.vtb = self.vtb_calc() self.vtc = self.vtc_calc() self.vb = self.vb_calc() self.vc = self.vc_calc() self.S = self.S_calc() self.S_PCC = self.S_PCC_calc() self.Vtrms = self.Vtrms_calc() self.Vrms = self.Vrms_calc() self.Irms = self.Irms_calc() self.logger.debug('{}:Steady state values for operating point defined by Ppv:{:.2f} W, Vdc:{:.2f} V, va:{:.2f} V found at:'.format(self.name,self.Ppv*self.Sbase,self.Vdc*self.Vdcbase,self.va*self.Vbase)) if self.verbosity == 'DEBUG': self.show_PV_DER_states(quantity='power') self.show_PV_DER_states(quantity='duty cycle') self.show_PV_DER_states(quantity='voltage') self.show_PV_DER_states(quantity='current')
def power_error_calc(self,x): """Function for power.""" maR = x[0] maI = x[1] iaR = x[2] iaI = x[3] ma = maR + 1j*maI ia = self.ia = iaR + 1j*iaI vta = ma*(self.Vdc/2) va = self.va_calc() St = (vta*ia.conjugate())/2 S_PCC = (va*ia.conjugate())/2 Ploss_filter = ((abs(ia)/math.sqrt(2))**2)*self.Rf Qloss_filter = ((abs(ia)/math.sqrt(2))**2)*self.Xf if type(self).__name__ == 'SolarPV_DER_ThreePhase': mb = utility_functions.Ub_calc(ma) mc = utility_functions.Uc_calc(ma) ib = self.ib = utility_functions.Ub_calc(ia) ic = self.ic = utility_functions.Uc_calc(ia) #mbR = x[4] #mbI = x[5] #ibR = x[6] #ibI = x[7] #mcR = x[8] #mcI = x[9] #icR = x[10] #icI = x[11] #mb = mbR + 1j*mbI #mc = mcR + 1j*mcI #ib = self.ib = ibR + 1j*ibI #ic = self.ic = icR + 1j*icI vtb = mb*(self.Vdc/2) vtc = mc*(self.Vdc/2) vb = self.vb_calc() vc = self.vc_calc() St = St + (vtb*ib.conjugate() + vtc*ic.conjugate())/2 S_PCC = S_PCC + (vb*ib.conjugate() + vc*ic.conjugate())/2 Ploss_filter = Ploss_filter + ((abs(ib)/math.sqrt(2))**2)*self.Rf + ((abs(ic)/math.sqrt(2))**2)*self.Rf Qloss_filter = Qloss_filter + ((abs(ib)/math.sqrt(2))**2)*self.Xf + ((abs(ic)/math.sqrt(2))**2)*self.Xf Qloss_filter_expected = 3*((self.Ppv/(3*self.Vrms_ref))**2)*self.Xf #print('solver:',ma,mb,mc,Qloss_filter_expected) P_PCC_error = ((S_PCC.real + Ploss_filter) - self.Ppv)**2 Q_PCC_error = (S_PCC.imag - self.Q_ref)**2 P_error = (St.real - self.Ppv)**2 Q_error = (St.imag - Qloss_filter - self.Q_ref)**2 Q_error_filter_expected = (St.imag - Qloss_filter_expected)**2 del_1 = utility_functions.relative_phase_calc(ma,mb) del_2 = utility_functions.relative_phase_calc(ma,mc) del_3 = utility_functions.relative_phase_calc(mb,mc) if del_1 > math.pi: del_1 = abs(del_1 - 2*math.pi) if del_2 > math.pi: del_2 = abs(del_2 - 2*math.pi) if del_3> math.pi: del_3 = abs(del_3 - 2*math.pi) #m_error = abs(ma+mb+mc) m_error = (del_1 - PHASE_DIFFERENCE_120)**2 + (del_2 - PHASE_DIFFERENCE_120)**2 + (del_3 - PHASE_DIFFERENCE_120)**2 +\ (abs(ma) - abs(mb))**2 + (abs(ma) - abs(mc))**2 + (abs(mb) - abs(mc))**2 return P_PCC_error + Q_PCC_error + P_error + m_error + Q_error #+ Q_error_filter_expected
def ib_ref_activepower_control(self): """Phase B current reference for constant Vdc""" return utility_functions.Ub_calc(self.ia_ref)
def ib_ref_calc(self): """Phase B current reference""" return utility_functions.Ub_calc(self.ia_ref)
def steady_state_calc(self): """Find duty cycle and inverter current that minimize steady state error and return steady state values.""" self.logger.debug( 'Solving for steady state at current operating point.') if self.standAlone: x0 = [ self.steadystate_values[self.parameter_ID]['maR0'], self.steadystate_values[self.parameter_ID]['maI0'], self.steadystate_values[self.parameter_ID]['iaR0'], self.steadystate_values[self.parameter_ID]['iaI0'] ] else: va = self.va_calc() x0 = [ va.real, va.imag, self.steadystate_values[self.parameter_ID]['iaR0'], self.steadystate_values[self.parameter_ID]['iaI0'] ] if self.allow_unbalanced_m: self.logger.info( 'Using unbalanced option - steady state duty cycles may be unbalanced.' ) if self.standAlone: x0.extend([ self.steadystate_values[self.parameter_ID]['mbR0'], self.steadystate_values[self.parameter_ID]['mbI0'], self.steadystate_values[self.parameter_ID]['ibR0'], self.steadystate_values[self.parameter_ID]['ibI0'], self.steadystate_values[self.parameter_ID]['mcR0'], self.steadystate_values[self.parameter_ID]['mcI0'], self.steadystate_values[self.parameter_ID]['icR0'], self.steadystate_values[self.parameter_ID]['icI0'] ]) else: vb = self.vb_calc() vc = self.vc_calc() x0.extend([ vb.real, vb.imag, self.steadystate_values[self.parameter_ID]['ibR0'], self.steadystate_values[self.parameter_ID]['ibI0'], vc.real, vc.imag, self.steadystate_values[self.parameter_ID]['icR0'], self.steadystate_values[self.parameter_ID]['icI0'] ]) x0 = np.array(x0) self.solver_spec[self.steadystate_solver].update( {'disp': bool(self.verbosity == 'DEBUG')}) result = minimize(self.power_error_calc, x0, method=self.steadystate_solver, options=self.solver_spec[self.steadystate_solver]) if not result.success: raise ValueError( 'Steady state solution did not converge! Change operating point or disable steady state flag and try again.' ) if 'xDC' in templates.DER_design_template[ self.DER_model_type]['initial_states']: self.xDC = self.ia.real if 'xP' in templates.DER_design_template[ self.DER_model_type]['initial_states']: self.xP = self.ia.real if 'xQ' in templates.DER_design_template[ self.DER_model_type]['initial_states']: self.xQ = self.ia.imag if 'xPLL' in templates.DER_design_template[ self.DER_model_type]['initial_states']: self.xPLL = 0.0 if 'wte' in templates.DER_design_template[ self.DER_model_type]['initial_states']: self.wte = 2 * math.pi if self.DER_model_type in templates.single_phase_models or self.DER_model_type in templates.three_phase_models: ma0 = result.x[0] + 1j * result.x[1] self.ia = result.x[2] + 1j * result.x[3] self.xa = ma0 self.ua = 0.0 + 0.0j self.vta = self.vta_calc() self.va = self.va_calc() if self.DER_model_type in templates.three_phase_models: if not self.allow_unbalanced_m: mb0 = utility_functions.Ub_calc(ma0) mc0 = utility_functions.Uc_calc(ma0) self.ib = utility_functions.Ub_calc(self.ia) self.ic = utility_functions.Uc_calc(self.ia) else: mb0 = result.x[4] + 1j * result.x[5] mc0 = result.x[8] + 1j * result.x[9] self.ib = result.x[6] + 1j * result.x[7] self.ic = result.x[10] + 1j * result.x[11] self.xb = mb0 self.xc = mc0 self.ub = 0.0 + 0.0j self.uc = 0.0 + 0.0j self.vtb = self.vtb_calc() self.vtc = self.vtc_calc() self.vb = self.vb_calc() self.vc = self.vc_calc() self.S = self.S_calc() self.S_PCC = self.S_PCC_calc() self.Vtrms = self.Vtrms_calc() self.Vrms = self.Vrms_calc() self.Irms = self.Irms_calc() self.logger.debug( '{}:Steady state values for operating point defined by Ppv:{:.2f} W, Vdc:{:.2f} V, va:{:.2f} V found at:' .format(self.name, self.Ppv * self.Sbase, self.Vdc * self.Vdcbase, self.va * self.Vbase)) if self.verbosity == 'DEBUG': self.show_PV_DER_states(quantity='power') self.show_PV_DER_states(quantity='duty cycle') self.show_PV_DER_states(quantity='voltage') self.show_PV_DER_states(quantity='current')