GENESIS: Documentation

Related Documentation:

Create Heccer Test

Here we describe how to create a test for the Heccer package. Since Heccer and it’s associated simulation objects are all compiled into a library, programs are written in C and compiled against this library to test each use case. In this example, we create suitable tests for the PulseGen{}Simulation Object (simobj_PulseGen{}) described in Create Solver Object.

Source Files

The source files for the Heccer tests are found in the tests/code directory within the Heccer Component.

Adding an object to the test framework

Since we are testing a new simulation object, the test framework must be made ‘aware’ of it. A line must be added to each of the files:

   tests/code/main.c  
   tests/code/main.h

In tests/code/main.c we add:

   struct simobj_PulseGen *ppg = NULL;

Then in tests/code/main.h we add:

   extern struct simobj_PulseGen *ppg;

The main driver for the test framework will now be aware of the PulseGen{} simulation object. Next a source file is created that demonstrates a specific use case.

Use Case

Ideally, output should be tested against a use case that can be verified to work correctly. Since the PulseGen{} object is a port of the same object used by GENESIS 2, we can obtain output from a GENESIS 2 script to compare with the output of our simulation object.

To test PulseGen{} we want a very basic script that will test minimal functionality in each of its trigger modes. Here is an example that creates three PulseGen{} objects, one for each trigger mode, and sends outputs to a file:

   create pulsegen /pulse0  
   setfield ˆ level1 50.0 width1 3.0 delay1 5.0 level2 -20.0 width2 5.0 delay2 8.0 baselevel 10.0 trig_mode 0  
 
   create pulsegen /pulse1  
   setfield ˆ level1 50.0 width1 3.0 delay1 5.0 level2 -20.0 width2 5.0 delay2 8.0 baselevel 10.0 trig_mode 1  
 
   create pulsegen /pulse2  
   setfield ˆ level1 50.0 width1 3.0 delay1 5.0 level2 -20.0 width2 5.0 delay2 8.0 baselevel 10.0 trig_mode 2  
 
   create asc_file /pulse0_out  
   setfield /pulse0_out filename "pulse-freerun.txt" flush 1 leave_open 1 append 1 float_format \%0.9g  
   addmsg /pulse0 /pulse0_out SAVE output  
   call /pulse0_out OUT_OPEN  
 
   create asc_file /pulse1_out  
   setfield /pulse1_out filename "pulse-exttrig.txt" flush 1 leave_open 1 append 1 float_format \%0.9g  
   addmsg /pulse1 /pulse1_out SAVE output  
   call /pulse1_out OUT_OPEN  
 
   create asc_file /pulse2_out  
   setfield /pulse2_out filename "pulse-extgate.txt" flush 1 leave_open 1 append 1 float_format \%0.9g  
   addmsg /pulse2 /pulse2_out SAVE output  
   call /pulse2_out OUT_OPEN  
 
   reset  
   setclock 0 0.5  
   step 200

From data output files we can see what the behavior of the object is supposed to be like.


PIC

Figure 1: Free run mode.



PIC

Figure 2: External Trigger constant, never triggered.



PIC

Figure 3: External Gate constant, never triggered.


When we run our corresponding PulseGen{} tests we match the value from the solved variable (pdVms0) for each time step and see that it matches the output we obtain from the GENESIS 2 script. We therefore know this use case works and can set the expected output for the test specification to the output of our program. This serves as a good base case for testing. If these tests should happen to fail then we know some necessary functionality has been compromised during the development of new use cases.

Creating a test program

A test program for Heccer consists of a set of defines, structures, and a main function. Simply changing sections of an existing program is enough to get the test running. The code in each test has inline comments to help understand the functionality, but we’ll go through it to be thorough.

At the top of the file you will see defines for setting some simulation and output parameters. Here are some available options:

So here are the settings for our test:

   #define HECCER_TEST_REPORTING_GRANULARITY 1  
   #define HECCER_TEST_STEPS 200  
   #define HECCER_TEST_TESTED_THINGS ( HECCER_DUMP_VM_COMPARTMENT_MATRIX | HECCER_DUMP_VM_MECHANISM_OPERATIONS )  
   #define HECCER_TEST_TIME_STEP (0.5)

The test will run for 200 steps with a time step of 0.5 seconds. It will produce output, complete with Heccer internal data, for every single step of the simulation.

Next, some declarations for data structures that the test will operate on are shown.

We now declare a Compartment{} struct. As the V m variable (membrane potential) is addressed to produce output, the PulseGen{} object needs the Compartment{} declaration even although it does not perform any compartment specific operations:

   struct Compartment compSoma =  
   {  
      //m administrative overhead  
 
      {  
         //m type of structure  
 
         MATH_TYPE_Compartment,  
      },  
 
      //m index of parent compartment, -1 for none  
      -1,  
 
      //m descriptive values, alphabetical order  
      /* double dCm; */  
      4.57537e-11, // unscaled 0.0164,  
 
      /* double dEm; */  
      -0.08,  
 
      /* double dInitVm; */  
      -0.068,  
 
      /* double dInject; */  
      0,  
 
      /* double dRa; */  
      360502, // unscaled 2.5,  
 
      /* double dRm; */  
      3.58441e+08, // unscaled 1  
};

These declarations set this Compartment{} to be a Math type with no parent, Cm to 4.57537e-11, E m to -0.08, initial membrane potential to -0.068, inject to 0, Ra to 360502, and Rm to 3.58441e+08, respectively.

Then an intermediary is declared. Here, the default is used as the test will only use one compartment with one intermediary:

   int piC2m[] =  
   {  
      0,  
      -1,  
   };  
 
   struct Intermediary inter =  
   {  
      //m compartment array  
 
      1,  
 
      &compSoma,  
 
      //m all other mathematical components  
 
      NULL,  
 
      //m compartment 2 first mechanism number  
 
      piC2m,  
};

Next, the main function that actually uses the functions in the simulation object is given. Here a PulseGen{} object is created and its parameters set. This is followed by two #define s, one to connect the PulseGen{} object to a compartment variable, the other to perform a simulation update time step.

Allocating a new PulseGen{} is done via:

   ppg = PulseGenNew("pulsegen freerun");

The parameters of PulseGen{} are then set with the PulseGenSetFields() function:

   double dLevel1 = 50.0;  
   double dWidth1 = 3.0;  
   double dDelay1 = 5.0;  
 
   double dLevel2 = -20.0;  
   double dWidth2 = 5.0;  
   double dDelay2 = 8.0;  
 
   double dBaseLevel = 10.0;  
 
   int iTriggerMode = FREE_RUN;  
 
   PulseGenSetFields(ppg, dLevel1, dWidth2, dDelay1, dLevel2, dWidth2, dDelay2, dBaseLevel, iTriggerMode);

Next, for the define HECCER_TEST_INITIATE we fetch a compartment variable from Heccer and attach it to the PulseGen{} output:

   #define HECCER_TEST_INITIATE \  
      double *pdVm = HeccerAddressCompartmentVariable(pheccer, 0, "Vm");  \  
      PulseGenAddVariable(ppg, pdVm)

Finally, for HECCER_TEST_SCHEDULE we add the PulseGen() step function so that it is performed on every step of the simulation:

   #define HECCER_TEST_SCHEDULE PulseGenSingleStep(ppg, (dSimulationTime))

Adding source files to the build

The tests/code directory has a Makefile.am which automake uses to generate a Makefile for building each test program. We only need to add some information to a couple of places and automake will handle the rest.

In the target check_PROGRAMS we need to add the resulting executable for our test program. For the case pulsegen-freerun.c we add:

   pulsegen-freerun \

Next we add rules near the bottom of the file to build and link our program. It is preferable to keep this in alphabetical order with the rest of the test programs build rules:

   pulsegen_freerun_DEPENDENCIES = ../../libheccer.a main.c  
   pulsegen_freerun_LDADD = -L../.. -lheccer -lm

This must be done for each test we want to add.

Note: As defined in the first target, the resulting executable is called pulsegen-freerun. However, to declare the accompanying build rule you must use an underscore, e.g. pulsegen_freerun.

Adding a Test Specification

The final step is to declare a test specification which tests the new test program against output that is known to be correct.

Create the file tests/specifications/pulsegen.t and input the following to test the pulsegen-freerun test program. This assumes that the expected output is in the tests/specifications/strings directory with the filename pulsegen-freerun.txt.

#!/usr/bin/perl -w  
#  
 
use strict;  
 
my $test  
   = {  
      command_definitions => [  
         {  
            arguments => [  
            ],  
            command => ’tests/code/pulsegen-freerun’,  
            command_tests => [  
               {  
                  description => "Can a single pulsegen object output amplitude in free run mode ?",  
                  read => (join ’’, ‘cat $::config->{core_directory}/tests/specifications/strings/pulsegen-freerun.txt‘),  
               },  
            ],  
            description => "pulsegen functionality, can we output a current in free run mode?",  
         },  
      ],  
      description => "pulsegen simulation object",  
      name => ’pulsegen.t’,  
   };  
   return $test;

For more information on declaring test specifications see Neurospaces Tester.