Code and Stuff
Social and stuff!
  • MOOSE

The MOOSE API (Part 2)

3/21/2014

0 Comments

 
In the last post, we took a look at a very basic example class. This was my first assignment. my second assignment involved extending the example class so that it actually would do something useful (relatively anyway!)

This new example class is going to take two inputs, sum their values, and send the output over to another object. Apart from the header and source file, I will also explain the python script file that makes all of this happen.

Example.h
First, let's get a sense of the class by taking a look at the header file:
class Example {
    private:
        double x_;
        double y_;
        double output_;

    public:
        Example();
        
        double getX() const;
        void setX( double x );
        double getY() const;
        void setY( double y );

        void process( const Eref& e, ProcPtr p );
        void reinit( const Eref& e, ProcPtr p );

        void handleX(double arg);
        void handleY(double arg);

        static const Cinfo* initCinfo();
}; 
Yes, it has gotten considerably bigger since before. However, a large part of this has already been explained before. There are 3 data members now - x_, y_ and output_. x_ and y_ store the values coming in from the external objects (in this case, the object is called a PulseGen). output_ stores the sum of x_ and y_.

The first function is the constructor of the class. This is standard C, nothing new here. The next four functions are used to get and set variables x_ and y_. We saw this last time. Then comes declerations for process and reinit. These are new functions. They play a very important role in the framework. 
  • Process: Every time-step during execution, the MOOSE framework calls the process function once. Thus, this function is responsible for advancing the state of the object at each time-step. In the case of the example, this is where the summation of x_ and y_ will happen.
  • Reinit: This is used to reinitialize the object. All it's variables are reset to the boundary conditions. Note that every object with a process function must necessarily also have a reinit function.
The next two functions - handleX and handleY are the callback functions that will be used when the object recieves messages from the PulseGen objects. We will see it's working in a bit.
Finally,  the initCinfo function, which as we saw before, is essential to convert this class into a MOOSE class.
Example.cpp
Let us take a look at the initCinfo function now:
const Cinfo* Example::initCinfo(){
    
    //Value Field Definitions
    static ValueFinfo< Example, double > x(
        "x",
        "An example field of an example class",
        &Example::setX,
        &Example::getX
    );

    static ValueFinfo< Example, double > y(
        "y",
        "Another example field of an example class",
        &Example::setY,
        &Example::getY
    );

    //Destination Field Definitions
    static DestFinfo handleX( "handleX",
            "Saves arg value to x_",
            new OpFunc1< Example, double >( &Example::handleX )
    ); 
    static DestFinfo handleY( "handleY",
            "Saves arg value to y_",
            new OpFunc1< Example, double >( &Example::handleY )
    ); 

    static DestFinfo process( "process",
        "Handles process call",
        new ProcOpFunc< Example >( &Example::process ) 
    );
    static DestFinfo reinit( "reinit",
        "Handles reinit call",
        new ProcOpFunc< Example >( &Example::reinit ) 
    );
        
    
    static ReadOnlyLookupElementValueFinfo< Example, string, vector< Id > > fieldNeighbours( 
         "fieldNeighbors",
         "Ids of Elements connected this Element on specified field.", 
         &Example::getNeighbours 

    );

    //////////////////////////////////////////////////////////////
    // SharedFinfo Definitions
    //////////////////////////////////////////////////////////////
    static Finfo* procShared[] = {
        &process, &reinit
    };
    static SharedFinfo proc( "proc",
        "Shared message for process and reinit",
        procShared, sizeof( procShared ) / sizeof( const Finfo* )
    );


    static Finfo *exampleFinfos[] = 
    { 
        &x,         //Value
        &y,         //Value
        &handleX,   //DestFinfo
        &handleY,   //DestFinfo
        output(),   // SrcFinfo
        &proc,      //SharedFinfo
    };

    static Cinfo exampleCinfo(
        "Example",              // The name of the class in python
        Neutral::initCinfo(),   // TODO
        exampleFinfos,          // The array of Finfos created above
        sizeof( exampleFinfos ) / sizeof ( Finfo* ),                      // The number of Finfos
        new Dinfo< Example >()  // The class Example itself (FIXME ?)
    );
    return &exampleCinfo;
}
Again, much bigger than the old function. But it is not as cryptic as it may seem. Let's dive in!
The first two definitions must be somewhat familiar. They are valueFinfo definitions which we saw earlier. 

The next four definitions define DestFinfos. handleX and handleY, as mentioned before, handle the callbacks when external objects pass messages into the example class. Take a look at their definition. The first two fields are the name and DocString of the DestFinfo. The third parameter is the function that must be called upon activation. You can see that Example::handleX and Example::handleY are passed in to handleX and handleY respectively. The reason for the fairly complicated syntax is that MOOSE needs to know the type of function that is going to be defined. Here, it is OpFunc1, which means it is a generic function and takes in one parameter.
The next two functions are proc and reinit. Notice that they are ProcOpFuncs which mean they are going to be executed at every clock cycle. Apart from this difference, they are declared very similarly to handleX and handleY.

There is also a declaration of a SrcFinfo in this class which doesn't show up in the initCinfo definition. It is instead declared outside the function as shown:
static SrcFinfo1< double > *output() {
    static SrcFinfo1< double > output( 
            "output", 
            "Sends out the computed value"
            );
    return &output;
}
I am not yet fully sure of the use of SharedInfos. I will be updating this space as and when that gets clear.

After that is the exampleFinfos list that we saw earlier. It has many more Finfos, which are those that are initialized above. Note that the SrcFinfo output() is also present in this list.

The declaration of exampleCinfo follows. It looks largely the same a before except that the number of Finfos in the list will have to change. I have used a simple formula to calculate the number of Finfos based on the size of the list, and utilizing the fact that all Finfo pointers have the same length.

Let us now take a look at each of the function definitions
Example::Example()
: 
output_( 0.0 ),
x_( 0.0 ), y_( 0.0 )
{
;
}
The first function is the constructor of the class. It simply initialises its 3 variables to 0.
void Example::process( const Eref& e, ProcPtr p )
{
    output_ = x_ + y_;
    printf("%f\n", output_);
    output()->send( e, output_ );
}

void Example::reinit( const Eref& e, ProcPtr p )
{
    
}
The next two functions are standard MOOSE functions. Remember that these are of type ProcOpFunc (as declared in initCinfo).
Process is called at each tick of the clock. Thus, how often it runs depends upon the timeout of the clock that it has been connected to. This is where the code for changing state of the object will go. In the case of this example, the process function calculates the sum of x_ and y_ and stores it in _output. It then prints this value and also sends the value out through the send function. Since output() is a SrcFinfo, the value sent out will go to the DestFinfo of any objects connected to the output() of this object via messages.
Reinit is called when the model needs to reinitialize itself. In this case, we do not need to do anything upon reinitialization, so reinit is blank.

//TODO: explain Eref and ProcPtr  


Finally, we take a look at handleX and handleY
void Example::handleX(double arg )
{
    x_ = arg;
}

void Example::handleY(double arg )
{
    y_ = arg;
}
As you can see, they are very simple functions, they simply store the values incoming through the messages into the variables x_ and y_ so that they can be summed and returned when the process function is called.

And that is about it for this example class! This one definitely had a lot more components than the previous class, but it can actually do all the basic functions of MOOSE classes - recieve data, process it and send it out to other objects.
0 Comments

The MOOSE API (Part 1)

3/21/2014

0 Comments

 
Over the past few weeks, I have been working with the MOOSE framework, creating sample classes to understand the way they work.
Here, I will detail some of my example classes, to give some idea of what all are required to be done.

All of my work on these example classes are derivatives of the example provided as part of the MOOSE tutorial docs.

The first one was a class that has one data member 'x'. It doesn't do anything with this member though - pretty much as much of a dummy class as you can get! Nevertheless, it shows the way a normal C class can be elevated to the status of a MOOSE class with initCinfo.

Here is the header file:
class Example {

   private:
       int x_;

   public:

       int getX() const;
       void setX( int x );

       static const Cinfo *initCinfo();

}; 
It looks fairly similar to a C class with a few important additions. Firstly, the private variable x is always accompanied by get and set functions (getX() and setX()). Secondly, there is a function called initCinfo(), which provides the required functionality in order for MOOSE to recognize Example as a class.

This is the code for initCinfo, which forms the bulk of example.cpp:
const Cinfo* Example::initCinfo(){
   
   static ValueFinfo< Example, int > x(
       "x",
       "An example field of an example class",
       &Example::setX,
       &Example::getX
   );
   static Finfo *exampleFinfos[] = { &x };
   static Cinfo exampleCinfo(
       "Example",              // The name of the class in python
       Neutral::initCinfo(),   
       exampleFinfos,          // The array of Finfos created above
       1,                      // The number of Finfos
       new Dinfo< Example >()  // The class Example itself
   );
   
   return &exampleCinfo;
}
Before we go into the above code, we need to understand a little more about Moose Classes. Variables and functions in Moose Classes are stored in special fields called Finfos. They are of 3 main types - ValueFinfo, SrcFinfo and DestFinfo, although many others exist.
  • ValueFinfos store a single value. Moose creates a large number of helper functions implicitly for each ValueFinfo. These functions help with getting and setting values.
  • SrcFinfos are used as a connection point for the object to communicate with other objects. As you may have guessed, SrcFinfos can be used to send out data to other objects via messages.
  • DstFinfos are the points of reception of messages. They take a function of the class as parameter and work as a callback. Thus, when a DestFinfo receives a message, it simply calls the associated function and provides it access to the contents of the message.

Let's see how the example class above uses these Finfos. In this case, only one ValueFinfo has been used. No SrcFinfos or DstFinfos. the decleration consists of the name of the variable, a brief summary (the DocString), the set method and the get method.

After that, there is a definition of exampleFinfos, which is a list of all the addresses of every Finfo. This will go into exampleCinfo, defined immediately after.

exampleCinfo makes the class available to the rest of Moose. The first parameter is the name of the class. This is followed by the initializer for the parent class (the default parent class for a new class in Neutral). This is followed by the pointer pointing to the list of Finfos and the number of Finfos present in the list.

The rest of example.cpp is just the definitions for getX and setX:
int Example::getX() const
{
   return x_;
}

void Example::setX(int x)
{
   x_ = x;
}
Also make sure you incude header.h and example.h in the file.

That's everything there is to know about this very basic example class. In the next post, I will go into some more details about the Moose API by explaining another example class which has a few more features.
0 Comments

MOOSE- An Overview

3/21/2014

0 Comments

 
"MOOSE is the Multiscale Object-Oriented Simulation Environment. It is the base and numerical core for large, detailed simulations including Computational Neuroscience and Systems Biology."
This is the definition of MOOSE given by its creators. It basically provides a framework using which biological systems can be easily modelled and simulated, with comparatively less work from the programmer (than starting the programming from scratch).

The project is hosted at sourceforge here: http://moose.sourceforge.net/

A lot of the information on this page (most of it in fact) is derived from the work of James M Bower and David Beeman in The Book of Genesis. Here, I have merely picked the parts of the book that are most relevant to my work and presented it. In order to gain an in depth understanding of this field, I would suggest reading up the original book.
Introduction

Neurons work on the basis of exchanging currents between them through synaptic connections. This current exchange happens by virtue of fluctuating potentials within each neuron, caused by a number of chemical and electrical processes within it. Thus, in order to simulate all the processes taking place inside a collection of neuron and simulate it accurately, its electrical properties must be accurately understood and simulated.

The electrical properties of a neuron are not constant throughout its structure. It varies with space and time. Thus, in order to simulate it, two methods can be used:
  • A complex function can be formulated that defines its electrical properties at every point, and also specifies the way this varies over time.
  • The neuron can be broken up into smaller compartments, each of which will have constant properties, but will interact with each other in order to represent the whole neuron together.
Clearly, the second option is far more feasible and is what is used in MOOSE.
Picture
The electrical properties of the compartments can be approximated to the following electrical circuit:
Picture
The area inside the dotted lines is one compartment. This will be connected to other compartments on one or both sides. Here is a brief description of the different components of the circuit:
  • Ra is the axial resistance. That is, the resistance that will be encountered as current enters the compartment.
  •  Cm is the capacitance offered by the cell membrane. This happens because the membrane itself is an insulator of current, and there is a potential difference between the inside and outside of the neuron.
  • Rm and Em are the membrane resistance and membrane potential respectively, and are caused by the passive ion channels in the neuron.
  • Gk is the conductance caused by the active ion channels in the neuron. This is expected to change dynamically during a simulation. Hence the variable resistor in its depiction. Ek is the associated potential, called Equilibrium Potential
  •  Iinject is the sudden current flow caused by firing of action potentials or provided by inserting of an electrode into the compartment.
All of these components give the general compartment equation:
Picture
This equation depends on Vm’ and Vm’’ which are the potentials of the two neighboring compartments. These compartments have their own equations for their potentials which will depend on their neighbors and so on. Thus, all these coupled equations must be solved in parallel.

Solving for Vm using this equation at every compartment in the neuron, and further for every neuron, will give the required prediction of the state of the model at the next time step. This is the ultimate goal of MOOSE.

This is done by creating a difference equation and solving for this using one of mutliple methods. The default is the Exponential Euler (EE) method. Other methods are the Backward Euler and Crank-Nicholson Methods, which are faster to compute, but require more work in order to set up correctly.
0 Comments

    Vivek Vidyasagaran

    Participant, Google Summer of Code 2014

    Archives

    August 2014
    July 2014
    June 2014
    April 2014
    March 2014

    Categories

    All

    RSS Feed

Powered by Create your own unique website with customizable templates.