Coverage for D:\Ralf Gerlich\git\modypy\modypy\model\system.py: 27%

# Coverage for D:\Ralf Gerlich\git\modypy\modypy\model\system.py : 27%

## 83 statements

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"""

2 Provides classes for constructing systems and block hierarchies.

3"""

4 import warnings

5 from typing import List

6

7 import numpy as np

8

9 from modypy.model.states import State

10

11

12 class 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

141 class 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

149 class 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)