import matplotlib.lines as mlines
import numpy as np
from relsad.Time import Time, TimeUnit
from relsad.utils import convert_yearly_fail_rate, random_choice
from .Component import Component
from .Controller import Controller, ControllerState
from .ICTNode import ICTNode
[docs]class MainController(Component, Controller):
"""
Common class for main controller
...
Attributes
----------
name : string
Name of the main controller
ict_node : ICTNode
The ICT node connected to the controller
harware_fail_rate_per_year : float
The failure rate per year for hardware failuers of the controller
software_fail_rate_per_year : float
The failure rate per year for software failures of the controller
p_fail_repair_new_signal : float
The probability tuat a new signal cannot be sent
p_fail_reboot : float
The probability that a reboot of the controller will fail to fix the failure
new_signal_time : Time
The time it takes to send a new signal
reboot_time : Time
The time it takes to reboot the controller
remaining_repair_time : Time
The remaining time of the repair
manual_software_repair_time : Time
The time it takes to manually repair a software failure
manual_hardware_repair_time : Time
The time it takes to manually repair a hardware failure
state : enum
The state of the controller
sectioning_time : Time
The sectioning time of the system (can differ based on if the controller is OK or not)
manual_sectioning_time : Time
The sectioning time of a passive system without controller (controller is failed)
distribution_controllers : list
List of controllers from the distribution systems in the power system
microgrid_controllers : list
List of controllers from the microgrids in the power system
history : dict
Dictonary attribute that stores the historic variables
monte_carlo_history : dict
Dictonary attribute that stores the historic variables from the Monte Carlo simulation
Methods
----------
fail_software()
Sets the controller state to software fail
fail_hardware()
Sets the controller state to hardware fail
not_fail()
Sets the controller state to ok (not fail)
draw_fail_status(dt)
Draws the failure status of the controller
draw_hardware_status(prob)
Draws if a hardware failure has occured and sets the fail status of the controller
draw_software_status(prob)
Draws if a software failure has occured and sets the fail status of the controller
repair_software_fail(dt)
Goes through the repair sequence of a software failure, draws the repair mechanism, returns the repair time and updates the state of the controller
update_fail_status(dt)
Updates the failure status of the controller based on the remaining outage time
add_distribution_controller(controller)
Adds distribution controllers from connected distribution systems to a list and
sets the sectioning time for the controller. Assignes the
ICT node to the distribution system controller.
add_microgrid_controller(controller)
Adds microgird controllers from connected microgrids to a list and
sets the sectioning time for the controller. Assignes the
ICT node to the microgrid controller.
run_control_loop(curr_time, dt)
Runs the control loop for the distribution system controllers and the microgird controllers
spread_sectioning_time_to_sub_controllers()
Sets the sectioning time for the controllers in the connected distribution systems and microgrids
update_history(prev_time, curr_time, save_flag)
Updates the history variables
get_history(attribute)
Returns the history variables of an attribute
add_random_instance(random_gen)
Adds global random seed
print_status()
Prints the status
reset_status(save_flag)
Resets and sets the status of the class parameters
initialize_history()
Initializes the history variables
"""
## Random instance
ps_random: np.random.Generator = None
def __init__(
self,
name: str,
ict_node: ICTNode = None,
hardware_fail_rate_per_year: float = 0.2,
software_fail_rate_per_year: float = 12,
p_fail_repair_new_signal: float = 1 - 0.95,
p_fail_repair_reboot: float = 0.9,
new_signal_time: Time = Time(2, TimeUnit.SECOND),
reboot_time: Time = Time(5, TimeUnit.MINUTE),
manual_software_repair_time: Time = Time(0.3, TimeUnit.HOUR),
manual_hardware_repair_time: Time = Time(2.5, TimeUnit.HOUR),
manual_sectioning_time: Time = Time(1, TimeUnit.HOUR),
state: ControllerState = ControllerState.OK,
):
self.name = name
self.ict_node = ict_node
self.hardware_fail_rate_per_year = hardware_fail_rate_per_year
self.software_fail_rate_per_year = software_fail_rate_per_year
self.p_fail_repair_new_signal = p_fail_repair_new_signal
self.p_fail_repair_reboot = p_fail_repair_reboot
self.new_signal_time = new_signal_time
self.reboot_time = reboot_time
self.remaining_repair_time = Time(0)
self.manual_software_repair_time = manual_software_repair_time
self.manual_hardware_repair_time = manual_hardware_repair_time
self.state = state
self.sectioning_time = Time(0)
self.manual_sectioning_time = manual_sectioning_time
self.distribution_controllers = list()
self.microgrid_controllers = list()
## History
self.history = {}
self.monte_carlo_history = {}
self.initialize_history()
def __str__(self):
return self.name
def __repr__(self):
return f"MainController(name={self.name})"
def __eq__(self, other):
if hasattr(other, "name"):
return self.name == other.name and isinstance(
other, MainController
)
else:
return False
def __hash__(self):
return hash(self.name)
[docs] def fail_software(self):
"""
Sets the controller state to software fail
Parameters
----------
None
Returns
----------
None
"""
self.state = ControllerState.SOFTWARE_FAIL
[docs] def fail_hardware(self):
"""
Sets the controller state to hardware fail
Parameters
----------
None
Returns
----------
None
"""
self.state = ControllerState.HARDWARE_FAIL
[docs] def not_fail(self):
"""
Sets the controller state to ok (not fail)
Parameters
----------
None
Returns
----------
None
"""
self.state = ControllerState.OK
[docs] def draw_fail_status(self, dt: Time):
"""
Draws the failure status of the controller
Parameters
----------
dt : Time
The current time step
Returns
----------
None
"""
p_hardware_fail = convert_yearly_fail_rate(
self.hardware_fail_rate_per_year, dt
)
self.draw_hardware_status(p_hardware_fail)
if self.state == ControllerState.OK:
p_software_fail = convert_yearly_fail_rate(
self.software_fail_rate_per_year, dt
)
self.draw_software_status(p_software_fail)
[docs] def draw_hardware_status(self, prob):
"""
Draws if a hardware failure has occured and sets the fail status of the controller
Parameters
----------
prob : float
The probability of a hardware failure
Returns
----------
None
"""
if random_choice(self.ps_random, prob):
self.fail_hardware()
else:
self.not_fail()
[docs] def draw_software_status(self, prob):
"""
Draws if a software failure has occured and sets the fail status of the controller
Parameters
----------
prob : float
The probability of a software failure
Returns
----------
None
"""
if random_choice(self.ps_random, prob):
self.fail_software()
else:
self.not_fail()
[docs] def repair_software_fail(self, dt: Time):
"""
Goes through the repair sequence of a software failure, draws the repair mechanism, returns the repair time and updates the state of the controller
Parameters
----------
dt : Time
The current time step
Returns
----------
repair_time : Time
The repair time of the software failure
"""
repair_time = self.new_signal_time
self.draw_software_status(self.p_fail_repair_new_signal)
if self.state == ControllerState.OK:
return repair_time
elif self.state == ControllerState.SOFTWARE_FAIL:
repair_time += self.reboot_time
self.draw_software_status(self.p_fail_repair_reboot)
if self.state == ControllerState.SOFTWARE_FAIL:
self.remaining_repair_time = self.manual_software_repair_time
self.state = ControllerState.REPAIR
return repair_time
[docs] def update_fail_status(self, dt: Time):
"""
Updates the failure status of the controller based on the remaining outage time
Parameters
----------
dt : Time
The current time step
Returns
----------
None
"""
if self.state == ControllerState.REPAIR:
self.remaining_repair_time -= dt
if self.remaining_repair_time <= Time(0):
self.not_fail()
elif self.state == ControllerState.OK:
self.draw_fail_status(dt)
if self.state == ControllerState.HARDWARE_FAIL:
self.remaining_repair_time = self.manual_hardware_repair_time
self.state = ControllerState.REPAIR
elif self.state == ControllerState.SOFTWARE_FAIL:
self.sectioning_time = self.repair_software_fail(dt)
self.spread_sectioning_time_to_sub_controllers()
[docs] def add_distribution_controller(self, controller):
"""
Adds distribution controllers from connected distribution systems to a list and
sets the sectioning time for the controller. Assignes the
ICT node to the distribution system controller.
Parameters
----------
controller : Controller
Distribution system controller
Returns
----------
None
"""
self.distribution_controllers.append(controller)
controller.manual_sectioning_time = self.manual_sectioning_time
controller.ict_node = self.ict_node
if self.ict_node is not None:
controller.ict_network = self.ict_node.parent_network
[docs] def add_microgrid_controller(self, controller):
"""
Adds microgird controllers from connected microgrids to a list and
sets the sectioning time for the controller. Assignes the
ICT node to the microgrid controller.
Parameters
----------
controller : Controller
Microgird controller
Returns
----------
None
"""
self.microgrid_controllers.append(controller)
controller.manual_sectioning_time = self.manual_sectioning_time
controller.ict_node = self.ict_node
if self.ict_node is not None:
controller.ict_network = self.ict_node.parent_network
[docs] def run_control_loop(self, curr_time: Time, dt: Time):
"""
Runs the control loop for the distribution system controllers and the microgird controllers
Parameters
----------
curr_time : Time
The current time
dt : Time
The current time step
Returns
----------
None
"""
if self.state == ControllerState.OK:
for controller in self.distribution_controllers:
controller.run_control_loop(curr_time, dt)
for controller in self.microgrid_controllers:
controller.run_control_loop(curr_time, dt)
elif self.state == ControllerState.REPAIR:
for controller in self.distribution_controllers:
controller.run_manual_control_loop(curr_time, dt)
for controller in self.microgrid_controllers:
controller.run_manual_control_loop(curr_time, dt)
[docs] def spread_sectioning_time_to_sub_controllers(self):
"""
Sets the sectioning time for the controllers in the connected distribution systems and microgrids
Parameters
----------
None
Returns
----------
None
"""
for controller in self.distribution_controllers:
if controller.power_network.connected_line.circuitbreaker.is_open:
controller.set_sectioning_time(self.sectioning_time)
for controller in self.microgrid_controllers:
if controller.power_network.connected_line.circuitbreaker.is_open:
controller.set_sectioning_time(self.sectioning_time)
[docs] def update_history(
self, prev_time: Time, curr_time: Time, save_flag: bool
):
"""
Updates the history variables
Parameters
----------
prev_time : Time
The previous time
curr_time : Time
Current time
save_flag : bool
Indicates if saving is on or off
Returns
----------
None
"""
if save_flag:
time = curr_time.get_unit_quantity(curr_time.unit)
self.history["sectioning_time"][
time
] = self.sectioning_time.get_unit_quantity(curr_time.unit)
self.history["remaining_repair_time"][
time
] = self.remaining_repair_time.get_unit_quantity(curr_time.unit)
self.history["state"][time] = self.state.value
[docs] def get_history(self, attribute: str):
"""
Returns the history variables of an attribute
Parameters
----------
attribute : str
Controller attribute
Returns
----------
history[attribute] : dict
Returns the history variables of an attribute
"""
return self.history[attribute]
[docs] def add_random_instance(self, random_gen):
"""
Adds global random seed
Parameters
----------
random_gen : int
Random number generator
Returns
----------
None
"""
self.ps_random = random_gen
[docs] def print_status(self):
"""
Prints the status
Parameters
----------
None
Returns
----------
None
"""
pass
[docs] def reset_status(self, save_flag: bool):
"""
Resets and sets the status of the class parameters
Parameters
----------
save_flag : bool
Indicates if saving is on or off
Returns
----------
None
"""
self.state = ControllerState.OK
self.sectioning_time = Time(0)
self.remaining_repair_time = Time(0)
if save_flag:
self.initialize_history()
[docs] def initialize_history(self):
"""
Initializes the history variables
Parameters
----------
None
Returns
----------
None
"""
self.history["sectioning_time"] = {}
self.history["remaining_repair_time"] = {}
self.history["state"] = {}