Source code for Application.emt_simulation

[docs] class SimulationEMT: """ PowerFactory EMT Simulation Class. author: Ilya Burlakin, Elisabeth Scheiner """ def __init__(self, app): self.app = app self.inc = None self.sim = None self.events = None self.results = None self.inc_settings_are_set = False self.sim_settings_are_set = False
[docs] def init_emt(self, inc: bool = True, sim: bool = True, events: bool = True, results: bool = True) -> None: """ Initialize EMT simulation. TODO: Handling for creating new result files. TODO: Handling if Language is set to German. :param inc: decision if the initial conditions of the emt \ model have to be set :type inc: bool :param sim: decision if the simulation parameters of the \ emt model have to be set :type sim: bool :param events: decision if there are events in the power \ system simulation :type events: bool :param results: decision if the result data structure will \ be stored or not :type results: bool """ if inc: self.inc = self.app.GetFromStudyCase('ComInc') if sim: self.sim = self.app.GetFromStudyCase('ComSim') if events: self.events = self.app.GetFromStudyCase( "Simulationsereignisse/Fehler.IntEvt" ) if results: self.results = self.app.GetFromStudyCase( "Alle Berechnungsarten.ElmRes" )
[docs] def clear_events(self) -> None: """ Clear list of simulation events. """ # check if the data structure of events is not empty if self.events is not None: # iterate over all events for element in self.events.GetContents(): # delete each event element.Delete()
[docs] def clear_results(self) -> None: """ Clear simulation results. """ # check if the result data structure is not empty if self.results is not None: # iterate over each result element for element in self.results.GetContents(): # delete the result element element.Delete()
[docs] def clear(self, events: bool = True, results: bool = True) -> None: """ Clear simulation objects. TODO: define events and results if events, results is None :param events: decision if the power system simulation \ contains events :type events: bool :param results: decision if the power system simulation \ results will be cleared :type results: bool """ if events and self.events is not None: self.clear_events() if results and self.results is not None: self.clear_results()
""" Short-Circuit Event Handling """
[docs] @staticmethod def shc_settings( event: object, event_time: float, fault_location: float, fault_type: int, fault_impedance_r: float, fault_impedance_x: float ) -> None: """ Set Short-Circuit event settings. TODO: Extend fault settings and put parameters in dict, \ check if obj is bus (fault_location) TODO: check type of event object :param event: powerfactory event object to be modified :type event: object :param event_time: time at which the event occurs :type event_time: float :param fault_location: relative position of the fault \ location on a powersystem object (e.g. overheadline) :type fault_location: float :param fault_type: integer representing the fault type \ of the simulated short circuit. Possible entries: \ 0: 3ph, 1: 2phwoG, 2: 1phG, 3: 2phG, 4 clear fault :type fault_type: int :param fault_impedance_r: resistance of the power system \ fault :type fault_impedance_r: float :param fault_impedance_x: reactance of the power system \ fault :type fault_impedance_x: float """ event.time = event_time event.shcLocation = fault_location event.i_shc = fault_type # Impedance is set to zero since the input option for # resistance and reactance is used event.Zfaultlnp = 0 event.R_f = fault_impedance_r event.X_f = fault_impedance_x
[docs] def add_shc_event( self, obj: object, event_time: float = 0.0, fault_location: float = 50.0, fault_type: int = 0, fault_impedance_r: float = 0.0, fault_impedance_x: float = 0.0 ) -> None: """ Add Short-Circuit Event and set fault settings. TODO: Handling if object is a Load. TODO: check type of obj :param obj: powerfactory object to attach the shc event to :type obj: object :param event_time: time at which the event occurs :type event_time: float :param fault_location: relative position of the fault \ location on a powersystem object (e.g. overheadline) :type fault_location: float :param fault_type: integer representing the fault type \ of the simulated short circuit. Possible entries: \ 0: 3ph, 1: 2phwoG, 2: 1phG, 3: 2phG, 4: clear fault :type fault_type: int :param fault_impedance_r: resistance of the power system \ fault :type fault_impedance_r: float :param fault_impedance_x: reactance of the power system \ fault :type fault_impedance_x: float """ # check if the initialization of the emt simulation already has # been done if self.events is None: # if not initialize the emt simulation self.init_emt(inc=False, sim=False, events=True, results=False) # create event event = self.events.CreateObject("EvtShc", obj.loc_name) # set target object event.p_target = obj # set the event settings of the previously created event object self.shc_settings( event=event, event_time=event_time, fault_location=fault_location, fault_type=fault_type, fault_impedance_r=fault_impedance_r, fault_impedance_x=fault_impedance_x )
""" Load Event Handling """ #
[docs] @staticmethod def load_event_settings( event: object, event_time: float, event_type: int, load_dp: float, load_dq: float ) -> None: """ Set Single Load event settings. TODO: Extend fault settings and put parameters in dict, \ add ramp event. TODO: check event type :param event: powerfactory event object to be modified :type event: object :param event_time: time at which the event occurs :type event_time: float :param event_type: integer representing the event type \ of the simulated load change. Possible entries: \ 0: Step, 1: Ramp :type event_type: int :param load_dp: relative change of the active power of the \ by the event effected load :type load_dp: float :param load_dq: relative change of the reactive power of \ the by the event effected load :type load_dq: float """ event.time = event_time event.iopt_type = event_type event.dP = load_dp event.dQ = load_dq
[docs] def add_load_event( self, obj: object, event_time: float = 0.0, event_type: int = 0, load_dp: float = 0.0, load_dq: float = 0.0, ) -> None: """ Add Load Event and set event settings. TODO: Handling if object is not a Load. TODO: check type of obj :param obj: powerfactory object to attach the load event to :type obj: object :param event_time: time at which the event occurs :type event_time: float :param event_time: time at which the event occurs :type event_time: float :param event_type: integer representing the event type \ of the simulated load change. Possible entries: \ 0: Step, 1: Ramp :type event_type: int :param load_dp: relative change of the active power of the \ by the event effected load :type load_dp: float :param load_dq: relative change of the reactive power of \ the by the event effected load :type load_dq: float """ # check if the initialization of the emt simulation already has # been done if self.events is None: # if not initialize the emt simulation self.init_emt(inc=False, sim=False, events=True, results=False) # create event event = self.events.CreateObject("EvtLod", obj.loc_name) # set target object event.p_target = obj # set the event settings of the previously created event object self.load_event_settings( event=event, event_time=event_time, event_type=event_type, load_dp=load_dp, load_dq=load_dq )
""" Results Handling """
[docs] def add_variables(self, objects: list[object], attributes: list[str]) -> None: """ Add Variables to result file :param objects: List of power factory objects to be \ considered in the creation of the result file :type objects: list[object] :param attributes: List of attributes of the entered power \ factory objects to be attached to the result file :type attributes: list[str] """ # check if the initialization of the emt simulation already has # been done if self.results is None: # if not initialize the emt simulation self.init_emt(inc=False, sim=False, events=False, results=True) # check if objects and attributes are lists. if not isinstance(objects, list): objects = [objects] if not isinstance(attributes, list): attributes = [attributes] # add attributes as variables to the results object of the emt # simulation for obj in objects: for attr in attributes: self.results.AddVariable(obj, attr)
""" Initial Condition Settings """
[docs] def inc_settings( self, verify_inc: bool = True, step_size: float = 0.01, start_time: float = -0.1, synch: bool = True ) -> None: """ Set initial condition settings of the emt simulation. TODO: Extend settings. TODO: Add Loadflow :param verify_inc: bool indicating if the inserted \ initial conditions shall be checked or not :type verify_inc: bool :param step_size: float holding the temporal resolution of \ time emt simulation to be run later :type step_size: float :param start_time: start point of the simulation in seconds :type start_time: float :param synch: bool indicating if the simulation results \ shall be recorded synchronized :type synch: bool """ # check if the initialization of the emt simulation already has # been done if self.inc is None: # if not initialize the emt simulation self.init_emt(inc=True, sim=False, events=False, results=False) # define the simulation type possible entries rms & ins self.inc.iopt_sim = 'ins' # check if the emt simulation event data structure already # exists if self.events is None: # if not initialize the emt simulation to create the events # data structure self.init_emt(inc=False, sim=False, events=True, results=False) self.inc.p_event = self.events # check if the emt simulation results data structure already # exists if self.results is None: # if not initialize the emt simulation to create the # results data structure self.init_emt(inc=False, sim=False, events=False, results=True) self.inc.p_resvar = self.results # further options self.inc.iopt_show = verify_inc # Verify initial conditions self.inc.dtgrd = step_size # Integration step size self.inc.tstart = start_time # Start time if synch: self.inc.iopt_sync = synch # Enforced synchronization self.inc.syncperiod = step_size # Sync Period self.inc.ciopt_sample = 2 # Record results after every simulation step else: self.inc.iopt_sync = synch self.inc.ciopt_sample = 0 self.inc_settings_are_set = True
""" Simulation Settings """
[docs] def sim_settings(self, stop_time: float = 5.0) -> None: """ Set Simulation settings. :param stop_time: End time of the power system simulation \ in seconds :type stop_time: float """ # check if the initialization of the emt simulation already has # been done if self.sim is None: # if not initialize the emt simulation to create the sim # data structure self.init_emt(inc=False, sim=True, events=False, results=False) # Set the simulation end time in seconds self.sim.tstop = stop_time self.sim_settings_are_set = True
""" Run Simulation """
[docs] def run(self) -> int: """ Run RMS Simulation. Return 1 if RMS Simulation is successful. TODO: Add Error Handling, if inc/sim are not defined or init-errors. :return: **-** (int) - integer representing the exit \ status of the simulation run """ run_inc = self.inc.Execute() # inc_run=0 if success run_sim = self.sim.Execute() # inc_sim=0 if success return not run_inc and not run_sim
""" Export simulation results """
[docs] def export_to_csv(self, file_name: str) -> bool: """ Export simulation results as csv files. Return True if successful. :param file_name: Name of the csv file created within this \ method :type file_name: str :return: **-** (bool) - bool representing the exit \ status of the result file creation process """ # get the result structure from Study Case export = self.app.GetFromStudyCase("ComRes") export.pResult = self.results # export from # set the export type to csv file export.iopt_exp = 6 # enter the csv file name export.f_name = file_name # use the csv file seperator set on the user's operating system export.iopt_sep = 0 # change the col seperator to semicolon export.col_Sep = ';' # change the decimal seperator to point export.dec_Sep = '.' export.iopt_csel = 0 # export all variables export.iopt_tsel = 0 # 0 deactivate export user def. interval (adv opt) export.resultobj = [None] return not export.Execute()