I created the class EventList
as an extension of the native list
class in python. The problem I am having is that when I create an instance of the class, those instances are not creating new objects.
If you run the code below (which is complete), you'll observe that the content of the class instance elm1
, gets overwritten by elm2
, and both by elm3
. (see the bottom of the code)
What do I need to change in the class EventList
to correctly extend the class list
such that every instance of EventList
is new and independent?
from numpy.random import exponential, normal
from enum import Enum
from matplotlib import pyplot as plt
class States(int):
Failed = 0
Working = 1
Maintenance = 2
class ElementTypes(Enum):
Generic = 100
Bus = 0
Gen = 1
Branch = 2
Switch = 3
Load = 4
Grid = 5
class EventList(list):
def add(self, event):
self.append(event)
def size(self):
return len(self)
def remove_redundant(self):
# Remove the redundant events
n = self.size()
i = n-2
while i > 0:
if self[i][1] == self[i-1][1]:
del self[i]
i -= 1
def merge_events(self, event_list, remove_redundant=True):
"""
Add another list of events to this list.
It can also remove the redundant events
"""
self += event_list
# print(self)
self.sort()
# print(self)
# set the final event state as the previous event state
n = self.size()
self[n-1] = (self[n-1][0], self[n-2][1])
# print(self)
# remove redundant events
if remove_redundant:
self.remove_redundant()
def merge_series_component_events(self, events_list):
"""
Composes the series events list of a number of components
Args:
events_list: list of EventList objects corresponding to a number of components in series
"""
c_num = len(events_list) # number of components
for k in range(c_num):
for evt in events_list[k]:
if evt[1] == States.Failed:
self.add(evt) # a down-event is always added
else:
all_working = True
i = 0
while i < c_num and all_working:
t = evt[0]
if not events_list[i].state_at(t + 1e-6):
all_working = False
i += 1
# up-events are added only if all the components are up for the examined up-event time
if all_working:
self.add(evt)
self.sort()
self.remove_redundant()
def state_at(self, t):
"""
Returns the state at any given time
"""
n = self.size()
s0 = self[0]
sn = self[n-1]
if t <= s0[0]:
return s0[1]
elif t >= sn[0]:
return sn[1]
else: # look for it
for i in range(1, n):
if t >= self[i-1][0] and t <= self[i][0]:
# t is found
return self[i-1][1]
def plot(self, ylab=''):
n = len(self)
for i in range(n-1):
#print(str(self[i][0]) + ", " + str(self[i+1][0]) + '->' + str(str(self[i][1])))
plt.plot((self[i][0], self[i+1][0]), (self[i][1], self[i][1]), 'k-')
plt.plot((self[i][0], self[i][0]), (0, 1), 'k-')
plt.plot((self[i+1][0], self[i+1][0]), (0, 1), 'k-')
if self[i][1] == States.Failed:
color = 'r'
else:
color = 'g'
plt.axvspan(ymin=0, ymax=1, xmin=self[i][0], xmax=self[i+1][0], facecolor=color, alpha=0.5)
plt.ylabel(ylab)
plt.xlim((0, self[n-1][0]+10))
class Element(object):
"""
This class represents the system element.
In a graph it is given by a node element, regardless of the element's nature
"""
# element name
name = ''
type = None
# flag denoting if the element is active or not. If not active it counts as if it was failed
is_on_line = True
# Current state of the element given by the possible states enumerated by the class States
current_state = States.Working
# Weight of the element, in an electrical grid, this value can be the element impedance
weight = 0
# mean time to failure
MTTF = 0
# mean time to repair
MTTR = 0
# deviation from time to repair, relevant when using normal laws for the repair time
DEVTR = 1
use_normal_recovery_law = False
# list of tuples representing an event
# each tuple if of the form: (time, state)
events = EventList()
# Time based maintenance event list
time_based_maintenance_events = EventList()
def __init__(self, name, element_type, MTTF, MTTR, DEVTR=None, state=States.Working, weight=0, online=True):
"""
Class constructor
Args:
name = name of the element
element_type = type of element given by the class ElementTypes
MTTF = mean time to failure
MTTR = mean time to repair
DEVTR = deviation from time to repair
state = initial state of the element, given by the possible states enumerated by the class States
weight = possible weight of the element
online = flag denoting if the element is active or not.
"""
self.name = name
self.type = element_type
self.MTTF = MTTF
self.MTTR = MTTR
self.DEVTR = DEVTR
self.current_state = state
self.weight = weight
self.is_on_line = online
if DEVTR is not None:
self.use_normal_recovery_law = True
def generate_states(self, simulation_time):
"""
Generate the system events randomly.
Once failed, a component is accounted to be replaced (as good as new)
Args:
simulation_time: simulation time limit (typically the expected life of the system)
"""
events = EventList()
self.events.clear()
# append the initial state event
self.events.append((0, self.current_state * self.is_on_line))
# append the last state event
self.events.append((simulation_time, -1 * self.is_on_line))
mttf = self.MTTF
mttr = self.MTTR
# generate failure-repair events
t = 0
last_state = self.current_state
while t < simulation_time: # simulate failures and recoveries until the time is reached
if (not last_state) == States.Failed: # if the state to set is failed, get the time of the failure
t_evt = t + exponential(mttf)
last_state = States.Failed
else: # the element is failed, calculate its recovery time
if self.use_normal_recovery_law:
t_evt = t + normal(mttr, self.DEVTR)
else:
t_evt = t + exponential(mttr)
last_state = States.Working
if t_evt <= simulation_time:
events.append((t_evt, last_state * self.is_on_line))
t = t_evt
self.events.merge_events(events + self.time_based_maintenance_events)
if __name__ == '__main__':
simulation_time = 172000
name = 'element'
type = ElementTypes.Generic
MTTF = 36402.64
MTTR = 6500.30
plt.subplot(4, 1, 1)
elm1 = Element(name, type, MTTF, MTTR)
elm1.generate_states(simulation_time)
print(elm1.events)
elm1.events.plot()
plt.subplot(4, 1, 2)
elm2 = Element(name, type, MTTF, MTTR)
elm2.generate_states(simulation_time)
print(elm2.events)
elm2.events.plot()
plt.subplot(4, 1, 3)
elm3 = Element(name, type, MTTF, MTTR)
elm3.generate_states(simulation_time)
print(elm3.events)
elm3.events.plot()
print('after')
print(elm1.events)
print(elm2.events)
print(elm3.events)
plt._show()
input()