Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2Provides classes for constructing systems and block hierarchies. 

3""" 

4import warnings 

5from typing import List 

6 

7import numpy as np 

8 

9from modypy.model.states import State 

10 

11 

12class System: 

13 """A system is a composition of states, signals and events.""" 

14 

15 def __init__(self): 

16 self.num_states = 0 

17 self.states: List[State] = list() 

18 

19 self.num_inputs = 0 

20 self.inputs = list() 

21 

22 self.events = list() 

23 

24 self.clocks = set() 

25 

26 @property 

27 def system(self): 

28 """The system itself""" 

29 return self 

30 

31 @property 

32 def initial_condition(self): 

33 """The initial condition vector for the state of this system""" 

34 initial_condition = np.zeros(self.num_states) 

35 for state in self.states: 

36 initial_condition[state.state_slice] = \ 

37 state.initial_condition.ravel() 

38 return initial_condition 

39 

40 @property 

41 def initial_input(self): 

42 """The initial inputs of this system""" 

43 initial_inputs = np.zeros(self.num_inputs) 

44 for signal in self.inputs: 

45 initial_inputs[signal.input_slice] = np.ravel(signal.value) 

46 return initial_inputs 

47 

48 def allocate_state_lines(self, count): 

49 """Allocate a sequence of consecutive state lines. 

50 

51 Args: 

52 count: The number of state lines to allocate 

53 

54 Returns: 

55 The index of the first state line allocated 

56 

57 """ 

58 start_index = self.num_states 

59 self.num_states += count 

60 return start_index 

61 

62 def allocate_input_lines(self, count): 

63 """Allocate a sequence of consecutive input lines. 

64 

65 Args: 

66 count: The number of input lines to allocate 

67 

68 Returns: 

69 The index of the first input line allocated 

70 

71 """ 

72 start_index = self.num_inputs 

73 self.num_inputs += count 

74 return start_index 

75 

76 def register_event(self, event): 

77 """Register an event. 

78 

79 Args: 

80 event: The event to register 

81 

82 Returns: 

83 The index of the event line allocated for the event 

84 

85 """ 

86 event_index = len(self.events) 

87 self.events.append(event) 

88 return event_index 

89 

90 def register_clock(self, clock): 

91 """Register a clock. 

92 

93 Args: 

94 clock: The clock to register. 

95 

96 Returns: 

97 

98 """ 

99 self.clocks.add(clock) 

100 

101 @property 

102 def num_events(self): 

103 """The number of events registered with this system""" 

104 return len(self.events) 

105 

106 def event_values(self, system_state): 

107 """Determine the value of all event functions for the given system 

108 state. 

109 

110 Args: 

111 system_state: The state for which to determine the event values. 

112 

113 Returns: 

114 The vector containing the value of all event functions for the 

115 given system state. 

116 """ 

117 event_vector = np.empty(self.num_events) 

118 for event_instance in self.events: 

119 event_vector[event_instance.event_index] = \ 

120 event_instance(system_state) 

121 return event_vector 

122 

123 def state_derivative(self, system_state): 

124 """Determine the value of all state derivative functions for the given 

125 system state. 

126 

127 Args: 

128 system_state: The state for which to determine the event values. 

129 

130 Returns: 

131 The vector of state derivatives for this system. 

132 """ 

133 state_derivative = np.zeros(self.num_states) 

134 for state_instance in self.states: 

135 if state_instance.derivative_function is not None: 

136 state_derivative[state_instance.state_slice] = \ 

137 np.ravel(state_instance.derivative_function(system_state)) 

138 return state_derivative 

139 

140 

141class Block: 

142 """A block is a re-usable building block for systems.""" 

143 

144 def __init__(self, parent): 

145 self.parent = parent 

146 self.system = self.parent.system 

147 

148 

149class SystemState: 

150 """This class allows to evaluate the individual aspects (signals, state 

151 derivatives, ...) of a system at any given time.""" 

152 

153 def __init__(self, time, system: System, state=None, inputs=None): 

154 self.time = time 

155 self.system = system 

156 

157 if state is None: 

158 state = system.initial_condition.copy() 

159 self.state = state 

160 if inputs is None: 

161 inputs = system.initial_input.copy() 

162 self.inputs = inputs 

163 

164 def get_state_value(self, state: State): 

165 """Determine the value of a given state. 

166 

167 Args: 

168 state: The state 

169 

170 Returns: 

171 The value of the state 

172 """ 

173 return self.state[state.state_slice].reshape(state.shape) 

174 

175 def get_input_value(self, signal): 

176 """Determine the value of a given input signal. 

177 

178 Args: 

179 signal: The input signal 

180 

181 Returns: 

182 The value of the input signal 

183 """ 

184 return self.inputs[signal.input_slice].reshape(signal.shape) 

185 

186 def __getitem__(self, key): 

187 warnings.warn("The dictionary access interface is deprecated", 

188 DeprecationWarning) 

189 if isinstance(key, tuple): 

190 # In case of a tuple, the first entity is the actual object to 

191 # access and the remainder is the index into the object 

192 obj = key[0] 

193 idx = key[1:] 

194 value = obj(self) 

195 if len(idx) > 1: 

196 return value[idx] 

197 return value[idx[0]] 

198 # Otherwise, the item is an object to access, and we simply defer to 

199 # the callable interface 

200 return key(self)