Simulation Architecture

Introduction
The Simulation Loop
Model Connections
Messaging
Variables

Introduction

DSim is a hierarchical, object-oriented simulation framework. What this means that in practical terms is that a simulation is composed of instantiations of a set of reusable models, and that these objects can then be arranged in a hierarchical tree. For example, we might have an object called Satellite that represents the physical body of a spacecraft. One of its child objects would be the object for its thruster. We might also have another top-level object representing the Earth; this separate top-level object would be part of the simulation but would be at the same 'level' as the Satellite- neither would be hierarchically above the other.

Objects are instantiations of models. A model is a class in C++ that derives from the appropriately named dsim_model class in the DSim framework. Models are generally organized into libraries. DSim knows how to load these external libraries and create objects from the models contained within. The model describes all of the attributes that objects instantiated from the model have: position, velocity, temperature, etc. Each model can have an entirely different set of variables associated with it. Each model also provides the code that updates the variables as the simulation progresses, both by providing derivatives of integrated variables (DSim then does the actual integration) and directly updating non-integrated variables that still need to change each timestep. Each object instantiated from a particular model thus has the same attributes and dynamics, but the particular values will be different in each case.

The setup and relationships of the objects in a simulation are specified in a simulation setup file (see Setup Files), also known as a ds2 file because of the extension used. Some simulation parameters, such as timestep, and the initial values for the objects' variables and states can be specified as well. These files are plain-text XML files. On Mac OS X, these setup files can be built using DSim Manager.

The Simulation Loop

As the physical simulation, DSim is strongly deterministic and does not allow for the more complicated programming structures available within ControlDeck. Models implement certain methods to update variables at specific times. The entire simulation operates on a single timestep, and the simulation engine will step through a specific sequence of function calls within a loop. You can investigate this API for more information on what the functions do, but the structure of the loop is shown below.



It is important to note that each object within the simulation is considered to be independent unless a dependency is created between two objects. When "foo" is marked as dependent upon "bar", DSim will ensure that, at each step of the simulation, methods are called on "bar" before they are called on "foo". This allows you to ensure that objects update in the proper order. If there are no dependencies between two objects, you cannot rely on the relative order of function calls between those objects.

Model Connections

Outlets enable variable values from one model to be sent to another. Each outlet must be associated with a source component which provides the variable. The path to the parameter consists of the component hierarchy with components separated by a pipe (|) and the parameter separated by a colon. For instance, ParentObject|ChildObject:param1. Outlets are useful for connecting data between any objects with other than a simple parent-child relationship.

Outlets are configured in the setup file. See datamanagement and create_outlet().

Messaging

Most inter-object communication within DSim happens by way of setting and reading variables. However, there are times when this is not the most appropriate form of communication. When something better is needed, messages can be used. Targets allow message destinations to be configurable via the setup file. See the Model Messaging module.

Messages

Messages allow an object to send a value, with a message name, to another object. The best example of this from the base DSim is the application of forces and torques to the built-in dsim_rigid_body model. Objects which wish to apply a force to a dsim_rigid_body object send it an apply_force message, along with the force that they wish to apply. The dsim_rigid_body object records and aggregates all of these forces, and then applies them the next time its rhs() function is called to compute state derivatives. Any number of other objects can in this way apply forces to a single object. The message passing, and the recipient's handling of it, is immediate and synchronous: the recipient will have handled the message before the send_message call returns.

Messages may also receive return values. The message handler that is called when a message is received has the option of returning a value back to the sender. For instance, if object A applies a force to object B, object B might choose to return a force that it, in turn, applies to object A.

Targets

Targets allow connections between objects to be specified within the simulation setup file, rather than hard-coded into the model. For example, consider a simple mass-spring simulation where we have two models: one representing the mass, and one representing the spring. In developing the model for the spring, we'd like it to be reusable; at the same time we know we need to send a message to the mass at each timestep to apply the appropriate force. How can we accomplish this?

The correct method is via a target. The spring model creates the target during initialization. The user who sets up the simulation, then, sees that the spring has a target and connects it to the mass object. At runtime, then, the spring looks up the position variable at whatever object the target points to, and simply sends its apply_force message to the same: the code doesn't care where the variable is located or where message is going; it just knows that its going to some other object that has a position variable and can respond to an apply_force message. Basically, you should never hard-code paths or object names into your model code: instead, use targets.

While the example above has a target pointing to a single destination, it's possible for a target to be multidirectional, in which case the target actually points to a set of objects rather than a single one. The general principle is the same, but the code would be written to iterate over the set of objects pointed to by the target rather than simply getting a single value from the target. This might be used to help model a broadcast communication system, for instance.

Networks

Networks allow you to connect multiple modules together so simulate power, thermal, electrical and other kinds of networks.

Connections are directed. In your power object that's managing the network, it iterates through the connections, looks up the object that is the destination of the connection, and asks that destination object for its "power" variable. Given that, you need to make sure that your dsim setup file has, for each of those connections, the power object as the *source* and the other object, the one in the network, as the destination.

Variables

All DSim objects interact with DSim variables in order to implement functionality and affect simulation step. These variables are distinct from the general member variables of the C++ programming language; instead, they are instances of the dsim_variable objects. They represent inputs, outputs, and states of the objects being simulated.

It's important to note that DSim only makes a single distinction when it comes to variables: whether or not they are integrated. Integrated variables must be treated specially by the framework. However, there are a number of other possible variable types: inputs, outputs, parameters, etc. These other variable types are there simply to make the model more self-documenting, especially when using DSim Manager to construct a simulation.

Models create their own variables, and can get and set their values freely. Models may also request the variables that are created by other models; once these "remote" variables have been requested, the model can interact with them the same way that it can interact with "local" (self-created) variables. It is up to the model writer to avoid improperly setting the value of a remote object's variable.

Models must be aware of whether or not the variables with which they are interacting are co-integrated or not. A model, having requested a variable from another object, may either enable or disable co-integrated mode for the variable. While DSim is performing the integration loop within a timestamp, an integrated variables value may be changing over the course of the integration according to the integrator being used by that variable's creating model. When an object is looking at a variable in co-integrated mode, that object will always see the most recent value of the variable, and will see these changes over the course of integration. However, when an object is looking at a variable that is not in co-integrated mode, it will always see the value of the variable immediately prior to the beginning of the integration loop; it will not see updates over the course of integration.

Objects using different integrators should always be careful to avoid co-integrated mode on variables from the remote object, since updates during the course of integration may not have matching times or meaning.

 All Classes Files Functions Typedefs Enumerations Enumerator