Tutorial 1b - An alternative ============================ The state dictionary always contains current values for all variables. This is why, in Tutorial 1, we could pass it from the Assembly to each component. Here, we'll see a different way to achieve the same result. We'll make the components store the state first in their own stage calls, to provide it to the Assembly on request. :: class Component1: def set_state(self, state, t, log): self._state = state def get_pos(self): return self._state[COMP1_POS] def set_comp2_force(self, force): self._comp2_force = force def step(self, state, t, log): """Called by the solver at each time step Calculate acceleration based on the net component2_value. """ acceleration = self._comp2_force * 1.0 derivatives = { "position1": state[COMP1_VEL], "velocity1": acceleration, } return derivatives class Component2: def set_state(self, state, t, log): self._state = state def get_force(self): return 1.0 * self._state[COMP2_VALUE] def set_comp1_pos(self, pos): self._comp1_pos = pos def calculate(self, state, t): """Some arbitrary calculations based on current time t and the position at that time calculated in Component1. This returns a derivative for variable 'c' """ dc = 1.0 * np.cos(2 * t) * self._comp1_pos derivatives = {COMP2_VALUE: dc} return derivatives def step(self, state, t, log): """Called by the solver at each time step""" return self.calculate(state, t) Now, we'll modify Assembly as follows. :: class Assembly: """Handle inter-dependencies.""" def __init__(self, comp1, comp2): self.comp1 = comp1 self.comp2 = comp2 def precalcs(self, state, t, log): """Inject dependencies for later calculations in 'step' methods.""" comp1 = self.comp1 comp2 = self.comp2 comp1_pos = comp1.get_pos() comp2_force = comp2.get_force() if log: # Log whatever we want here into a dictionary. log[COMP2_FORCE] = comp2_force comp1.set_comp2_force(comp2_force) comp2.set_comp1_pos(comp1_pos) Finally, we'll add stage calls to the components before the call to `precals`: :: def get_system(): component1 = Component1() component2 = Component2() assembly = Assembly(component1, component2) system = npsolve.System() system.add_component(component1, "comp1", "step") system.add_component(component2, "comp2", "step") system.add_component(assembly, "assembly", None) system.set_stage_calls([ ('comp1', 'set_state'), ('comp2', 'set_state'), ("assembly", "precalcs"), ]) return system This method is arguably more flexible as it allows for logging when setting the state.