Network Models

Network models are constructed from a network executive and a collection of other network and atomic models. The network executive provides the system structure specification. The component models, collectively, provide the network behavior. Building on the previous 2 bit adder, a 4 bit adder can be constructed as shown below. In this example, the staticDigraph class provides a pre-built network model framework. It is only necessary to plug in the components and specify the component coupling.

class adder_4bit: public staticDigraph
{
   public:
      static const port_t input_bits[4];
      static const port_t output_bits[4];
      static const port_t carry_in;
      static const port_t carry_out;

      adder_4bit():
      staticDigraph()
      {
          // Add the two bit adders to the four bit adder model
          adder_2bit* adder1 = new adder_2bit();
          adder_2bit* adder2 = new adder_2bit();
          add(adder1);
          add(adder2);
          // Input bit vector
          couple(this,input_bits[0],adder1,adder_2bit::a);
          couple(this,input_bits[1],adder1,adder_2bit::b);
          couple(this,input_bits[2],adder2,adder_2bit::a);
          couple(this,input_bits[3],adder2,adder_2bit::b);
          couple(this,carry_in,adder1,adder_2bit::carry_in);
          // Output bit vector
          couple(adder1,adder_2bit::a,this,output_bits[0]);
          couple(adder1,adder_2bit::b,this,output_bits[1]);
          couple(adder2,adder_2bit::a,this,output_bits[2]);
          couple(adder2,adder_2bit::b,this,output_bits[3]);
          couple(adder2,adder_2bit::carry_out,this,carry_out);
          // Internal carry
          couple(adder1,adder_2bit::carry_out,adder2,adder_2bit::carry_in);
      }
      ~staticDigraph(){}
};

The four bit adder above was assembled from a collection of atomic 2 bit adders. We could also build an 8-bit adder from two 4-bit adders by reusing the four bit adder model above. The code is nearly identical, as shown below

class adder_8bit: public staticDigraph
{
   public:
      static const port_t input_bits[8];
      static const port_t output_bits[8];
      static const port_t carry_in;
      static const port_t carry_out;
      adder_4bit():
      staticDigraph()
      {
          // Add the four bit adders
          adder_4bit* adder1 = new adder_4bit();
          adder_4bit* adder2 = new adder_4bit();
          add(adder1);
          add(adder2);
          // Input bit vector
          for (int i = 0; i < 4; i++) couple(this,input_bits[i],adder1,adder1->input_bits[i]);
          couple(this,carry_in,adder1,adder1->carry_in);
          for (int i = 4; i < 8; i++) couple(this,input_bits[i],adder2,adder2->input_bits[i-4]);
          // Output bit vector
          for (int i = 0; i < 4; i++) couple(this,output_bits[i],adder1,adder1->output_bits[i]);
          for (int i = 4; i < 8; i++) couple(this,output_bits[i],adder2,adder2->output_bits[i-4]);
          // Internal carry
          couple(adder1,adder1->carry_out,adder2,adder2->carry_in);
      }
      ~staticDigraph(){}
};

A 16 bit or 32 bit adder could be similarly constructed.

For many models, a digraph is the best way to represent component coupling and the staticDigraph class is provided for that purpose. However, adevs allows the modeler to build their own network models. In general, network models can have a static structure like the above adder or have a dynamic structure in which couplings and components are added and removed over time. The structure and structural dynamics of a network model are specified by a network executive. The network executive is a special type of atomic model whose state space is the structure specification of its parent network model. The network executive can change the structure of the system by scheduling internal events or by responding to external events. Of course, the structure need not change at all, in which case the network executive sets up the model structure when it is initialized and then remains passive throughout the course of the simulation.

Consider a simple model of a particle moving through a one dimensional space. The time that a particle remains in a block of space is fixed. The direction (left or right for our simple example) is selected randomly. A cell might be modeled as follows.


class cell: public atomic
{
     public:
          static const port_t in;
          static const port_t out;

          cell(int location, int particles):
          atomic(),location(location),particles(particles){}
          void init()
          {
               if (particles) hold(1.0);
               else passivate();
          }
          void delta_int() 
          {
               particles = 0;
               passivate();
          }
          void delta_ext(stime_t e, const adevs_bag<PortValue>& x)
          {
               particles += x.getSize();
               if (particles) hold(1.0);
               else passivate();
          }
          void delta_conf(const adevs_bag<PortValue>& x)
          {
               delta_int();
               delta_ext(0.0,x);
          }
          void output_func(adevs_bag<PortValue>& y)
          {
               for (int i = 0; i < particles; i++) output(out,1,y);
          }
          void gc_output(adevs_bag<PortValue>& g){}
          int getLocation() const { return location; }
     private:
          int location;
};

The cell space is a one dimensional strip that contains a finite number of particles. A netExec derived model, we'll call it space, is built to represent this structure. The implementation is shown below.


class space: public netExec
{
     public:
          cell(int length, const int* particles):
          netExec(),length(length)
          {
               cells = new cell*[length];
               for (int i = 0; i < length; i++)
               {
                    cells[i] = new cell(i,particles[i]);
               }
          } 
          void init() { passivate(); }
          void delta_int(){} 
          void delta_ext(stime_t e, const adevs_bag<PortValue>& x){}
          void delta_conf(const adevs_bag<PortValue>& x){}
          void getComponents(adevs_set<devs*>& c)
          {
               for (int i = 0; i < length; i++)
                    c.add(cells[i]);
          }
          void route(const PortValue& pv, devs* model, adevs_bag<EventReceiver>& r)
          {
               cell* c = (cell*)model;
               int direction = rand()%2;
               int next = c->getLocation();
               if (direction == 1) next++;
               else next--;
               if (next < 0) next = length - 1;
               else if (next >= length) next = 0;
               r.add(EventReceiver(cells[next],cell::in));
          }
          void output_func(adevs_bag<PortValue>& y){}
          void gc_output(adevs_bag<PortValue>& g){}
          void gc_models(adevs_bag<devs*>& g){}
          ~space()
          {
               for (int i = 0; i < length; i++) delete cells[i];
               delete [] cells;
          }
     private:
          cell* cells;
          int length;
};

The space model has two methods inherited from the netExec class, in addition to the standard methods from the atomic class. These are the route(const PortValue&,devs*,adevs_bag<EventReceiver>&) and getComponents(adevs_set<devs*>&) methods. The route method is called by the simulator whenever a component model belonging to the network executive produces an output. The PortValue is the (port,value) pair produced by the model and the devs* is a pointer to the model itself. The adevs_bag<EventReceiver> should be filled with the (model,port) pairs that are to receive the output event. The route method should not change the state (i.e. structure specification) of the network executive, nor should it alter the state of the output producing or input receiving models.

The getComponents method is called by the simulator whenever it needs the list of component models housed by the network executive. The adevs_set<devs*> should be filled with pointers to the component models, excepting the network executive and its parent. This method will be called whenever the model structure may have changed. That is, whenever the internal, external, or confluent transition function of the network executive have been activated.