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

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
7import numpy as np
9from modypy.model.states import State
12class System:
13 """A system is a composition of states, signals and events."""
15 def __init__(self):
16 self.num_states = 0
17 self.states: List[State] = list()
19 self.num_inputs = 0
20 self.inputs = list()
22 self.events = list()
24 self.clocks = set()
26 @property
27 def system(self):
28 """The system itself"""
29 return self
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
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
48 def allocate_state_lines(self, count):
49 """Allocate a sequence of consecutive state lines.
51 Args:
52 count: The number of state lines to allocate
54 Returns:
55 The index of the first state line allocated
57 """
58 start_index = self.num_states
59 self.num_states += count
60 return start_index
62 def allocate_input_lines(self, count):
63 """Allocate a sequence of consecutive input lines.
65 Args:
66 count: The number of input lines to allocate
68 Returns:
69 The index of the first input line allocated
71 """
72 start_index = self.num_inputs
73 self.num_inputs += count
74 return start_index
76 def register_event(self, event):
77 """Register an event.
79 Args:
80 event: The event to register
82 Returns:
83 The index of the event line allocated for the event
85 """
86 event_index = len(self.events)
87 self.events.append(event)
88 return event_index
90 def register_clock(self, clock):
91 """Register a clock.
93 Args:
94 clock: The clock to register.
96 Returns:
98 """
99 self.clocks.add(clock)
101 @property
102 def num_events(self):
103 """The number of events registered with this system"""
104 return len(self.events)
106 def event_values(self, system_state):
107 """Determine the value of all event functions for the given system
108 state.
110 Args:
111 system_state: The state for which to determine the event values.
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
123 def state_derivative(self, system_state):
124 """Determine the value of all state derivative functions for the given
125 system state.
127 Args:
128 system_state: The state for which to determine the event values.
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
141class Block:
142 """A block is a re-usable building block for systems."""
144 def __init__(self, parent):
145 self.parent = parent
146 self.system = self.parent.system
149class SystemState:
150 """This class allows to evaluate the individual aspects (signals, state
151 derivatives, ...) of a system at any given time."""
153 def __init__(self, time, system: System, state=None, inputs=None):
154 self.time = time
155 self.system = system
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
164 def get_state_value(self, state: State):
165 """Determine the value of a given state.
167 Args:
168 state: The state
170 Returns:
171 The value of the state
172 """
173 return self.state[state.state_slice].reshape(state.shape)
175 def get_input_value(self, signal):
176 """Determine the value of a given input signal.
178 Args:
179 signal: The input signal
181 Returns:
182 The value of the input signal
183 """
184 return self.inputs[signal.input_slice].reshape(signal.shape)
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)