A cellspace model is a collection of DEVS models that are arranged as a 1, 2, or 3 dimensional array of cells. Each cell in the cellspace can influence, and be influenced by, its neighboring cells. Adevs supports two types of neighborhoods, Moore (26 point) and Von Neumann (6 point) types. In one dimension, these neighborhoods are the same. The two dimensional neighborhoods are shown in figure 1. The center cell is shown in green and its neighbors are shown in blue. The left configuration is the Von Neumann (6 point) neighborhood. The right configuration is the Moore (26 point) neighborhood. The three dimensional neighbors are the natural extension of the two dimensional case.
Figure
1. The two dimensional 6 point (left) and 26 point (right)
neighborhoods.
Every component model in a cellspace must inherent from the
cell_interface class. This base class provides the input and output
ports that must be supported by the component cells. It also provides
several helper functions for computing the cell's location in a
cellspace. The cell_interface definition is shown below.
class cell_interface { public: /** Input grid, where this cell is at 1,1,1. To find the input from the cell displaced from this cell by +1 or -1, add the displacement value to 1 to get the correct input port index. For example, the cell at -1,-1,0 provides input on port 0,0,1. */ static const port_t in [3][3][3]; /// Output port (will be sent to neighboring cells) static const port_t out; /// Directed output ports. Indexing is as per input ports static const port_t dout [3][3][3]; /// Lookup table for finding offsets from port number static const int offset_table[28][3]; /** Compute a key value for a location. Arguments should be such that 0 <= x < w and 0 <= y < h. */ static unsigned long get_key(int x, int y, int z, int w, int h) { return (unsigned long)(x+y*w+z*w*h); } /// Get the coordinates from a key static void get_coord(int& x, int& y, int& z, int w, int h, int key) { z = (int)ldiv(key,w*h).quot; unsigned long p = key-z*w*h; y = (int)ldiv(p,w).quot; x = p - y*w; } };
The cell_interface defines 27 input ports and 28 output ports. Any output placed on the “out” port is delivered to all of the cells adjacent to the output producing cell. The “in” and “dout” ports are directed input and output ports, respectively. The “in” ports correspond to each of the sides and corners of the cell. Similarly, events appearing on the “dout” port will appear as inputs only for those cells that are adjacent to the appropriate side or corner of the output producing cell. The directed ports are selected via the three array indices. The first index gives the x-direction, the second index gives the y-direction, and the third index gives the z-direction. Each index can take a value of 0, 1, or 2. These indexes correspond to a displacement of -1, 0, and +1 respectively. The corresponding target (source) cell for a directed output (input) can be determined by taking the (x,y,z) coordinate of the producing (receiving) cell and adding the displacements to that coordinate.
For example, suppose the cell at location (x,y,z) receives an input on port in[0][1][1]. This input port corresponds to a displacement of (-1,0,0), and so the source cell coordinate is (x-1,y,z). Similarly, if the cell at (x,y,z) produces an output on port dout[0][0][0], the receiving cell will be at location (x-1,y-1,z-1) and it will receive the event on port in[2][2][2] (i.e., (x-1+1,y-1+1,z-1+1) = (x,y,z)).
There are two special uses of the directed input and directed output ports. An input received on port in[1][1][1] indicates an event that originated from outside of the cellspace. The effect of sending output events to dout[1][1][1] is undefined. Figure 2 illustrates the use of the directed input and output ports inhereted from the cell_interface class. The figure shows the x-y plane. The x-z and y-z plane can be similarly described.
Figure
1. Directed input and output port indices for the x-y plane of a
cellSpace model.
To illustrate the construction and use of cellSpace models, we can
employ a very simple model of a forest fire. The model divides the
forest into a two-dimensional grid of cells. Each cell is a DEVS
model with three state variables. These are the cell phase, the
amount of fuel in the cell, and the amount of heat in the cell. The
cell phase can be burning, burnt, or unburnt. The amount of heat is
equal to the number of neighboring cell's whose phase is burning. The
fuel describes how long the cell will burn after it catches fire.
Each cell is implemented as a fireCell object. The header and source
file for the fireCell is shown below. Notice that the fireCell is
derived from both the atomic and cell_interface classes. In all other
respects, it is just like any other atomic model. Here is the header
file.
#include <adevs.h> #include <iostream> /** The phase of the cell. */ typedef enum { BURNING, BURNT, UNBURNT } CellPhase; /** A cell in the simple forest fire model. The cell catches fire when at least two of its neighbors are burning. The cell continues to burn until it is out of fuel. */ class fireCell: public atomic, public cell_interface { public: // Constructor fireCell(); // State initialization function void init(); // Internal transition function void delta_int(); // External transition function void delta_ext(stime_t e, const adevs_bag<PortValue>& x); // Confluent transition function void delta_conf(const adevs_bag<PortValue>& x); // Output function void output_func(adevs_bag<PortValue>& y); // Garbage collection void gc_output(adevs_bag<PortValue>& g); // Reports the state of the cell to the provided output stream void report(std::ostream& stream) const; // Destructor ~fireCell(); // Set the initial data for the cell void set_init_conditions(double fuel, bool burning); protected: // The phase of the cell CellPhase phase; // Amount of fuel available in the cell double fuel; // Number of burning neighbor cells double heat; };
And here is the source file. Notice that the output_func() method sends the fireCell phase to all of the cell's neighbors via the cell_interface::out port. The external transition function computes the cell's heat value by iterating through the inputs provided by all adjacent cell's. An astute reader will notice that the type of the output value has type double, and not object*. This is because the adevs_config.h file used to compile this example set the ADEVS_IO_TYPE to double, rather than the default object* (see the configuration and installation section of this manual for details).
#include "fireCell.h" using namespace std; fireCell::fireCell(): atomic(), cell_interface() { } void fireCell::set_init_conditions(double f, bool burning) { heat = 0.0; fuel = f; if (burning) { phase = BURNING; } else { phase = UNBURNT; } } void fireCell::init() { if (phase == BURNING) { hold(1.0); } else { passivate(); } } void fireCell::delta_int() { fuel = fuel - elapsed(); // If the phase is burnt, then the cell is burned up. if (phase == BURNT) { passivate(); } // Otherwise, hold for as long as the cell has fuel and // then produce an output indicating that the cell is burnt up. else { hold(max(fuel,0.0)); phase = BURNT; } } void fireCell::delta_ext(stime_t e, const adevs_bag<PortValue>& input) { for (int i = 0; i < input.getSize(); i++) { // For each neighbor that is burning (or cooling), alter the heat level heat = heat + input.get(i).value; } // If the cell is on fire if (heat >= 2.0 && phase == UNBURNT && fuel > 0.0) { // Set the phase to burning and tell the neighbors that it is lit up phase = BURNING; hold(1.0); } } void fireCell::delta_conf(const adevs_bag<PortValue>& x) { delta_int(); delta_ext(0,x); } void fireCell::output_func(adevs_bag<PortValue>& y) { // Place the cell phase on the output port out. // The phase will be delivered to all neighboring cells. if (phase == BURNING) { output(out,1.0,y); } else if (phase == BURNT) { output(out,-1.0,y); } } void fireCell::gc_output(adevs_bag<PortValue>& g) { } void fireCell::report(ostream& stream) const { if (phase == BURNING || (phase == BURNT && fuel > 0.0)) stream << "2 "; else if (phase == BURNT) stream << "1 "; else if (phase == UNBURNT) stream << "0 "; } fireCell::~fireCell() { }
The complete forest fire model is constructed from the component fireCell models in two steps. The first step is to construct an instance of a cellSpace() network model. The cellSpace constructor takes for arguments. The first argument describes the type of neighborhood that the cellSpace implements. This argument can be either cellSpace::TWENTY_SIX_POINT or cellSpace::SIX_POINT. The first value creates a cellSpace model with a Moore style neighborhood, the latter creates a Von Neumann style neighborhood. The second argument is the extent of the cellSpace x-dimension. This can be any value greater than or equal to 1. The second and third arguments are optional. They describe the y-, and z-extent of the cellSpace. Their default values are 1. The following code snippet demonstrates the construction of the cellSpace() network model for the forest fire model. Here, we assume a configuration object that holds all of the solution parameters (i.e., extent of the forest in the x- and y- directions, fuel distributions, and which cells are burning at time zero).
cellSpace* space = new cellSpace(cellSpace::TWENTY_SIX_POINT,config->get_width(),config->get_height());
The cellSpace has a grid of pointers to devs models. Initially, each pointer has a NULL value. To populate the cell space, we need to create individual cells and assign them to specific grid locations. This is done using the cellSpace add() method. The add() method has four arguments. The first is a pointer to the model that we want to add. The next three arguments specify the x-, y-, and z- coordinate that the model should occupy. The y- and z- arguments are optional and take default values of zero. Note that the grid points are addressed just like C arrays (i.e., the indices start from zero and end at the maximum extent minus one). The following code snippet shows how the cellSpace is populated with fireCell models.
for (int x = 0; x < config->get_width(); x++) { for (int y = 0; y < config->get_height(); y++) { fireCell* fire_cell = new fireCell(); fire_cell->set_init_conditions(config->get_fuel(x,y),config->get_fire(x,y)); space->add(fire_cell,x,y); } }
The cellSpace model can be simulated just like any other DEVS model. Once the simulation is complete, we can use the cellSpace's getModel() method to report on the final state of each cell. The getModel() method takes three arguments which are the x, y, and z coordinate of the model to be retrieved. The last two arguments are optional, and they have a default value of zero. The method returns a pointer to a devs object that must be cast as needed. The following snippet demonstrates simulating the forest fire model and reporting its final state.
// Create and execute a simulator for the forest fire model devssim sim(space); sim.run(config->get_tend()); // Save the results to a file ofstream log_file("soln"); for (int y = 0; y < space->getHeight(); y++) { for (int x = 0; x < space->getWidth(); x++) { const fireCell* fire_cell=dynamic_cast<const fireCell*>(space->getModel(x,y)); fire_cell->report(log_file); } log_file << endl; } log_file.close(); // Delete the cellSpace and all of its component models delete space;
The result of a small (20 x 20) cell forest fire model is shown below. Here is the initial fuel configuration,
2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 1 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 3 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 3 3 4 6 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 1 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 8 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 5 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 3 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 6 3 4 5 3 4 4 4 4 4 4 4 4 4 2 3 4 1 2 5 1 3 4 5 3 4 4 4 4 4 4 4 4 4
and here is the initial burning region.
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Burnt areas are indicated with the number 2, burning areas are indicated with the number 1, and unburnt areas are shown using the number 0. The result of burning the fire for 5 units of time is shown next. The fire can be seen to spread in all directions at roughly the same rate in each direction. Also, areas with more fuel burn for a longer time than areas with less fuel.
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 2 1 1 2 1 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 2 1 1 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
In order to demonstrate the use of the directed ports, we can add wind to the forest fire model. To do this, we'll add the wind direction to the set of configuration parameters. The wind can blow in any of the eight cardinal directions. A burning forest fire cell will generate two points of heat in the wind direction and in each direction to the immediate left and right of the wind direction. It will generate one point of heat in all other directions. The new model can be built from the fireCell model via inheretance by overriding the output function and creating a new setWindDirection() function. Here is the header file for the new model.
#include <adevs.h> #include <iostream> #include "fireCell.h" typedef enum { N, S, E, W, NW, NE, SW, SE, NONE } Direction; /** A forest fire cell that accounts for wind direction. */ class fireCellWind: public fireCell { public: // Constructor fireCellWind(); // Output function void output_func(adevs_bag<PortValue>& y); // Destructor ~fireCellWind(); // Set the wind direction. The wind blows in the same direction everywhere. static void setWindDirection(Direction wind); protected: static Direction wind; };
Here is the source file for the fireCellWind model. The directed output ports are used to send the appropriate heat value in each direction. Notice that the definitions of NORTH, SOUTH, EAST, WEST, NORTHEAST, SOUTHEAST, NORTHWEST, and SOUTHWEST correspond to the directed output ports illustrated in Figure 2. A switch statement is used to assign heat values to output ports as required by the wind direction. Note that, in the absence of any wind, a single unit of heat is sent to all of the neighboring cells via the “cell_interface::out” port.
#include "fireCellWind.h" Direction fireCellWind::wind = NONE; #define WEST dout[0][1][1] #define EAST dout[2][1][1] #define NORTH dout[1][0][1] #define SOUTH dout[1][2][1] #define NORTHWEST dout[0][0][1] #define NORTHEAST dout[2][0][1] #define SOUTHWEST dout[0][2][1] #define SOUTHEAST dout[2][2][1] fireCellWind::fireCellWind(): fireCell() { } void fireCellWind::output_func(adevs_bag<PortValue>& y) { double sign = 0.0; if (phase == BURNT) { sign = -1.0; } else if (phase == BURNING) { sign = 1.0; } else { return; } switch(wind) { case N: output(NORTH,sign*2.0,y); output(NORTHWEST,sign*2.0,y); output(NORTHEAST,sign*2.0,y); output(EAST,sign*1.0,y); output(WEST,sign*1.0,y); output(SOUTHEAST,sign*1.0,y); output(SOUTHWEST,sign*1.0,y); output(SOUTH,sign*1.0,y); break; case S: output(NORTH,sign*1.0,y); output(NORTHWEST,sign*1.0,y); output(NORTHEAST,sign*1.0,y); output(EAST,sign*1.0,y); output(WEST,sign*1.0,y); output(SOUTHEAST,sign*2.0,y); output(SOUTHWEST,sign*2.0,y); output(SOUTH,sign*2.0,y); break; case E: output(NORTH,sign*1.0,y); output(NORTHWEST,sign*1.0,y); output(NORTHEAST,sign*2.0,y); output(EAST,sign*2.0,y); output(WEST,sign*1.0,y); output(SOUTHEAST,sign*2.0,y); output(SOUTHWEST,sign*1.0,y); output(SOUTH,sign*1.0,y); break; case W: output(NORTH,sign*1.0,y); output(NORTHWEST,sign*2.0,y); output(NORTHEAST,sign*1.0,y); output(EAST,sign*1.0,y); output(WEST,sign*2.0,y); output(SOUTHEAST,sign*1.0,y); output(SOUTHWEST,sign*2.0,y); output(SOUTH,sign*1.0,y); break; case NW: output(NORTH,sign*2.0,y); output(NORTHWEST,sign*2.0,y); output(NORTHEAST,sign*1.0,y); output(EAST,sign*1.0,y); output(WEST,sign*2.0,y); output(SOUTHEAST,sign*1.0,y); output(SOUTHWEST,sign*1.0,y); output(SOUTH,sign*1.0,y); break; case NE: output(NORTH,sign*2.0,y); output(NORTHWEST,sign*1.0,y); output(NORTHEAST,sign*2.0,y); output(EAST,sign*2.0,y); output(WEST,sign*1.0,y); output(SOUTHEAST,sign*1.0,y); output(SOUTHWEST,sign*1.0,y); output(SOUTH,sign*1.0,y); break; case SW: output(NORTH,sign*1.0,y); output(NORTHWEST,sign*1.0,y); output(NORTHEAST,sign*1.0,y); output(EAST,sign*1.0,y); output(WEST,sign*2.0,y); output(SOUTHEAST,sign*1.0,y); output(SOUTHWEST,sign*2.0,y); output(SOUTH,sign*2.0,y); break; case SE: output(NORTH,sign*1.0,y); output(NORTHWEST,sign*1.0,y); output(NORTHEAST,sign*1.0,y); output(EAST,sign*2.0,y); output(WEST,sign*1.0,y); output(SOUTHEAST,sign*2.0,y); output(SOUTHWEST,sign*1.0,y); output(SOUTH,sign*2.0,y); break; case NONE: output(out,sign,y); break; } } fireCellWind::~fireCellWind() { } void fireCellWind::setWindDirection(Direction wind) { fireCellWind::wind = wind; }
Shown below is a fire with the same fuel and initial burning cells used in the previous example, but with the wind blowing east. Notice that the fire spreads more rapidly to the east with the aide of the wind.
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 2 1 1 2 1 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 2 1 1 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
There is one more aspect of the cellSpace model that is of interest: how do we connect other models (e.g., block diagram models) to the cellSpace model? Here, we will only describe the input and output ports that belong to a cellSpace network model. The input and output ports that belong to each individual cell have already been discussed. A cellSpace network model has a unique input and output port for each individual cell within the space. The input port that can be used to send events to the cell at position (x,y,z) can be obtained by calling the getPort() method of the cellSpace model. The getPort() method takes three arguments. These are the x-, y-, and z- coordinates of the cell that we want to address. The method returns the corresponding input port as a port_t object.
For example, suppose we wanted to reduce the heat at a particular cell via an event that originates from outside of the cellSpace (e.g., a model of a fire fighting strategy). The following code snippet shows how to obtain the external input port associated with the cell at position (x,y). The third argument (i.e., the z-coordinate) has a default value of zero. An event received by the cellSpace model on this port will be delivered to the in[1][1][1] input port of the cell at location (x,y).
// Get the external input port associated with position x,y. port_t input_port = space->getPort(x,y);
When a cell produces output on any of its ports, the cellSpace model will produce a corresponding external output. There is a unique external output port for each cell in the cellSpace. Given the output port, the address of the cell associated with this output port can be obtained using the cellSpace's getCoordinates() method. This method takes the output port as its only argument. The method produces a cellSpace::coord_t struct as output. This struct has three fields which are the x, y, and z coordinate of the cell associated with the output port. The following code snippet shows how the model responsible for producing an output can be obtained by using the getCoordinates() and getModel() methods of the cellSpace.
cellSpace::coord_t coord = space->getCoordinates(output_port); const devs* model = space->getModel(coord.x,coord.y,coord.z);