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 print_event(self,text_string,print_inline): """Print information about ride through events.""" if text_string != '': if print_inline: #Print in notebook window logging.info(text_string) else: #Print in console window utility_functions.print_to_terminal(text_string) else: pass
def update_Zload1(self, t): """Update load impedance at PCC-LV side.""" if self.standAlone: #Update load at PCC LV side only in stand alone mode Zload1_actual_new = self.events.load_events(t) Zload1_new = Zload1_actual_new / BaseValues.Zbase if abs(self.Zload1 - Zload1_new) > 0.0: self.Zload1 = Zload1_new utility_functions.print_to_terminal( "Load at PCC LV side changed from {:.3f} VA to {:.3f} VA at {:.3f}" .format(self.S_load1, self.S_load1_calc(), t))
def update_Ppv(self, t): """Update PV module power output based on solar events and DC link voltage.""" Sinsol_new, Tactual_new = self.events.solar_events(t) if abs(self.Sinsol - Sinsol_new ) or abs(self.Tactual - Tactual_new ) > 0.0: #Update Iph only if solar insolation changes self.Sinsol = Sinsol_new self.Tactual = Tactual_new utility_functions.print_to_terminal( "{}:PV module current output changed from {:.3f} A to {:.3f} A at {:.3f} s" .format(self.name, self.Iph, self.Iph_calc(), t)) self.Iph = self.Iph_calc() self.Ppv = self.Ppv_calc(self.Vdc_actual)
def Volt_VAR_logic(self, t): """ Volt-VAR.""" #Select RMS voltage source Vrms_measured = self.Vrms self.Qlimit = self.Qlimit_calc() deadband_V = 0.01 if t > self.Volt_VAR_logic_t: #Update volt-var logic only if solver time is greater that current time self.Volt_VAR_logic_t = t if self.VOLT_VAR_ACTIVE: if self.Volt_VAR_inject_range(Vrms_measured, deadband_V): Qref = self.Q_Volt_VAR_inject(Vrms_measured) elif self.Volt_VAR_absorb_range(Vrms_measured, deadband_V): Qref = self.Q_Volt_VAR_absorb(Vrms_measured) else: utility_functions.print_to_terminal( "Volt-VAR control with reference voltage {:.3f} is deactivated at {:.3f} s for {:.3f} V" .format(self.Vrms_ref * self.Vbase, t, Vrms_measured * self.Vbase)) Qref = 0.0 self.VOLT_VAR_ACTIVE = False else: if self.Volt_VAR_inject_range(Vrms_measured, deadband_V): Qref = self.Q_Volt_VAR_inject(Vrms_measured) utility_functions.print_to_terminal( "Volt-VAR control (injection) with reference voltage {:.3f} is activated at {:.3f} s for {:.3f} V" .format(self.Vrms_ref * self.Vbase, t, Vrms_measured * self.Vbase)) self.VOLT_VAR_ACTIVE = True elif self.Volt_VAR_absorb_range(Vrms_measured, deadband_V): Qref = self.Q_Volt_VAR_absorb(Vrms_measured) utility_functions.print_to_terminal( "Volt-VAR control (absorbption) with reference voltage {:.3f} is activated at {:.3f} s for {:.3f} V" .format(self.Vrms_ref * self.Vbase, t, Vrms_measured * self.Vbase)) self.VOLT_VAR_ACTIVE = True else: #Don't supply/absorb reactive power outside operating range Qref = 0.0 else: self.Volt_VAR_logic_t = self.Volt_VAR_logic_t Qref = self.Q_ref return Qref
def Volt_VAR_logic(self,t): """Function for volt_var control.""" assert not (self.VOLT_VAR_ENABLE and self.VOLT_WATT_ENABLE), "Volt-VAR and Volt-Watt cannot be active at the same time" #Volt-VAR logic V1_Volt_VAR = self.Vrms_ref - 0.04*self.Vrms_ref #From IEEE 1547-2018 Catergy B (Table 8 - page 39) V2_Volt_VAR = self.Vrms_ref - 0.01*self.Vrms_ref #From IEEE 1547-2018 Catergy B (Table 8 - page 39) V3_Volt_VAR = self.Vrms_ref + 0.01*self.Vrms_ref #From IEEE 1547-2018 Catergy B (Table 8 - page 39) V4_Volt_VAR = self.Vrms_ref + 0.04*self.Vrms_ref #From IEEE 1547-2018 Catergy B (Table 8 - page 39) #Select RMS voltage source _Vrms_measured = self.Vrms _del_V = 0.02 if self.VOLT_VAR_ENABLE and (_Vrms_measured < V2_Volt_VAR or _Vrms_measured > V3_Volt_VAR) and t>self.t_stable: if self.VOLT_VAR_FLAG: if (_Vrms_measured > V1_Volt_VAR - _del_V and _Vrms_measured < V4_Volt_VAR + _del_V): Qref = self.Qsetpoint_calc(t) else: utility_functions.print_to_terminal("Volt-VAR control with reference voltage {:.3f} is deactivated at {:.3f} s for {:.3f} V".format(self.Vrms_ref*self.Vbase,t,_Vrms_measured*self.Vbase)) Qref = 0.0 self.VOLT_VAR_FLAG = False else: if ( _Vrms_measured >= V1_Volt_VAR and _Vrms_measured < V4_Volt_VAR): utility_functions.print_to_terminal("Volt-VAR control with reference voltage {:.3f} is activated at {:.3f} s for {:.3f} V".format(self.Vrms_ref*self.Vbase,t,_Vrms_measured*self.Vbase)) self.VOLT_VAR_FLAG = True Qref = self.Qsetpoint_calc(t) else: utility_functions.print_to_terminal("Volt-VAR control not activated at {:.3f} s since {:.3f} V is outside range!".format(t,_Vrms_measured*self.Vbase)) Qref = 0.0 else: Qref = 0.0 return Qref
def FRT(self,t): """Frequency ride through and trip logic. """ t_reconnect_limit = 3.0 #Time lag before reconnecting #IEEE 1547-2018 standards F_LF1 = 57.0 #From IEEE 1557-2018 Category III F_LF2 = 58.8 #From IEEE 1557-2018 Category III t_LF1_limit = 0.16 #Time limit for LF1 zone From IEEE 1557-2018 Category III (Table 19 and figure H10) t_LF2_limit = 299.0 #Time limit for LF2 zone From IEEE 1557-2018 Category III (Table 19 and figure H10) #Dummy variables to preserve logic F_LF3 = 0.0 t_LF3_limit = 0.0 del_f =0.02 assert (F_LF1 < F_LF2) and (t_LF1_limit < t_LF2_limit), "Frequency level 2 should be greater than frequency level 1" #Use grid frequency as estimated by PLL fgrid = self.we/(2.0*math.pi) if self.LFRT_ENABLE and t > self.t_stable and not self.LFRT_TRIP: if self.LFRT_DEBUG: text_string = '{time_stamp:.4f} -- fgrid:{f1:.3f} Hz, t_LF1start:{t1:.3f} s, t_LF2start:{t2:.3f} s, t_LF3start:{t3:.3f} s'\ .format(time_stamp=t,f1 = fgrid,t1=self.t_LF1start,t2=self.t_LF2start,t3=self.t_LF3start) utility_functions.print_to_terminal(text_string) if self.t_LF1start == 0.0 or self.t_LF2start == 0.0 or self.t_LF3start == 0.0: if self.t_LF1start == 0.0 and fgrid < F_LF1: self.t_LF1start = t utility_functions.print_LFRT_events(t,fgrid,self.t_LF1start,event_name='LF1_start') if self.t_LF2start == 0.0 and fgrid < F_LF2: self.t_LF2start = t utility_functions.print_LFRT_events(t,fgrid,self.t_LF2start,event_name='LF2_start') if self.t_LF3start == 0.0 and fgrid < F_LF3: self.t_LF3start = t utility_functions.print_LFRT_events(t,fgrid,self.t_LF3start,event_name='LF3_start') if self.t_LF1start > 0.0 or self.t_LF2start > 0.0 or self.t_LF3start > 0.0: if fgrid >= F_LF1+del_f or fgrid >= F_LF2+del_f or fgrid >= F_LF3+del_f: if fgrid >= F_LF1+del_f and self.t_LF1start > 0.0: utility_functions.print_LFRT_events(t,fgrid,self.t_LF1start,event_name='LF1_reset') self.t_LF1start = 0.0 if fgrid >= F_LF2+del_f and self.t_LF2start > 0.0: utility_functions.print_LFRT_events(t,fgrid,self.t_LF2start,event_name='LF2_reset') self.t_LF2start = 0.0 if fgrid >= F_LF3+del_f and self.t_LF3start > 0.0: utility_functions.print_LFRT_events(t,fgrid,self.t_LF3start,event_name='LF3_reset') self.t_LF3start = 0.0 if t-self.t_LF1start >= t_LF1_limit and self.t_LF1start > 0.0: #Trip inverter if any limit breached utility_functions.print_LFRT_events(t,fgrid,self.t_LF1start,event_name='inverter_trip_LF1') #six.print_(t-self.t_LF1start) self.LFRT_trip_signals() elif t-self.t_LF2start >= t_LF2_limit and self.t_LF2start > 0.0: utility_functions.print_LFRT_events(t,fgrid,self.t_LF2start,event_name='inverter_trip_LF2') self.LFRT_trip_signals() elif t-self.t_LF3start >= t_LF3_limit and self.t_LF3start > 0.0: utility_functions.print_LFRT_events(t,fgrid,self.t_LF3start,event_name='inverter_trip_LF3') self.LFRT_trip_signals() if fgrid < F_LF1 and self.t_LF1start > 0.0: utility_functions.print_LFRT_events(t,fgrid,self.t_LF1start,event_name='LF1_zone') #,verbose = False if fgrid < F_LF2 and self.t_LF2start > 0.0: utility_functions.print_LFRT_events(t,fgrid,self.t_LF2start,event_name='LF2_zone') if fgrid < F_LF3 and self.t_LF3start > 0.0: utility_functions.print_LFRT_events(t,fgrid,self.t_LF3start,event_name='LF3_zone') #six.print_(self.t_LF2start) elif self.LFRT_ENABLE and t > self.t_stable and self.LFRT_TRIP: if self.t_LF_reconnect > 0.0: if fgrid < F_LF3: utility_functions.print_LFRT_events(t,fgrid,self.t_LF_reconnect,event_name='reconnect_reset') self.t_LF_reconnect = 0.0 elif fgrid >= F_LF3 and t-self.t_LF_reconnect >= t_reconnect_limit: utility_functions.print_LFRT_events(t,fgrid,self.t_LF_reconnect,event_name='inverter_reconnection') self.LFRT_TRIP = False #Reset trip flag self.LFRT_RECONNECT = True #Set reconnect flag self.t_LF_reconnect = 0.0 #Reset timer elif fgrid >= F_LF3 and t-self.t_LF_reconnect < t_reconnect_limit: utility_functions.print_LFRT_events(t,fgrid,self.t_LF_reconnect,event_name='reconnect_zone') elif self.t_LF_reconnect == 0.0: if fgrid >= F_LF3: self.t_LF_reconnect = t utility_functions.print_LFRT_events(t,fgrid,self.t_LF_reconnect,event_name='reconnect_start') else: utility_functions.print_LFRT_events(t,fgrid,event_name='inverter_tripped') else: self.t_LF1start = 0.0 self.t_LF2start = 0.0 self.t_LF3start = 0.0 self.LFRT_TRIP = False
def debug_simulation(self,t): """ Print to terminal for debugging.""" utility_functions.print_to_terminal('t:{:.4f}'.format(t)) if self.DEBUG_VOLTAGES: utility_functions.print_to_terminal('Vdc_ref:{:.3f},Vdc:{:.3f},Vat:{:.3f},Va:{:.3f},Vag:{:.3f},Vagrid:{:.3f},Vagrid_setpoint:{:.3f}'. format(self.PV_model.Vdc_ref,self.PV_model.Vdc,self.PV_model.Vtrms,self.PV_model.Vrms,self.grid_model.Vgrms,abs(self.grid_model.Vagrid_no_conversion)/math.sqrt(2),abs(self.grid_model.Vagrid)/math.sqrt(2))) if self.DEBUG_CURRENTS: utility_functions.print_to_terminal('ia_ref:{:.3f},ia:{:.3f},iload1:{:.3f}'. format(self.PV_model.ia_ref,self.PV_model.ia,self.PV_model.iaload1)) if self.DEBUG_POWER: utility_functions.print_to_terminal('Sinsol:{:.3f},Q_ref:{:.3f},Ppv:{:.3f},S:{:.3f},S_PCC:{:.3f},S_load1:{:.3f},S_G:{:.3f}'.format(self.PV_model.Sinsol,self.PV_model.Q_ref,self.PV_model.Ppv,self.PV_model.S,self.PV_model.S_PCC,self.PV_model.S_load1,self.PV_model.S_G)) if self.DEBUG_CONTROLLERS: utility_functions.print_to_terminal('xDC:{:.3f},xQ:{:.3f},ua:{:.3f},xa:{:.3f},ma:{:.3f}'. format(self.PV_model.xdc,self.PV_model.xQ,self.PV_model.ua,self.PV_model.xa,self.PV_model.ma)) if self.DEBUG_PLL: utility_functions.print_to_terminal("we:{:.3f}, wte:{:.3f} rad, vd: {:.3f} V, vq {:.3f} V".format(self.PV_model.we,self.PV_model.wte,self.PV_model.vd,self.PV_model.vq))