Skip to content

Code3:errors

ardok-m edited this page Jun 28, 2017 · 5 revisions

Code 3: Errors.

based on https://lesgourg.github.io/class-tour/Tokyo2014/lecture5_index_and_error.pdf

CLASS is written to display really explicative errors but in a way that the programmer only has to worry about the human message each case. The other information (where the error happened, what functions call the one that complained, in what lines they are, etc. ) is automatically handled by the code itself.

See, for example, the next error, consequence of running ./class with a ini-file with the only line omega_b = 0.07:

Error in thermodynamics_init 
=>thermodynamics_init(L:292) :error in thermodynamics_helium_from_bbn(ppr,pba,pth);
=>thermodynamics_helium_from_bbn(L:1080) :condition (omega_b > omegab[num_omegab-1]) is true; You have asked for an unrealistic high value omega_b = 7.000000e-02. The corrresponding value of the primordial helium fraction cannot be found in the interpolation table. If you really want this value, you should fix YHe to a given value rather than to BBN

In order to have such automatic handling, the code follows 5 rules (extracted with few modifications from previous reference):

  • Rule 1: All functions are of type int, and return either _SUCCESS_ or _FAILURE_ (defined internally in include/common.h: #define _SUCCESS_ 0, #define _FAILURE_ 1 )
int function ( input , & output ) {
    ...
    if ( something goes wrong )
    ...
    return _FAILURE_ ;
  • Rule 2: All functions are called with the macro class_call(..., ..., ....) (all macros class_xxx(...) are defined in include/common.h):

    include/common.h:111:

    /* macro for calling function and returning error if it failed */
#define class_call(function, error_message_from_function, error_message_output)                                  \
  class_call_except(function, error_message_from_function,error_message_output,)

Which, in the end, following all macro calls, is a shortcut of:

if ( function == _FAILURE_ ) {
    ErrorMsg Transmit_Error_Message;
    sprintf (Transmit_Error_Message," %s(L:%d) : error in %s ;\
        n = >%s",__func__,__LINE__,# function,
        error_message_from_function);
    sprintf(error_message_output, "%s", Transmit_Error_Message
        );
    return _FAILURE_;
}
  • Rule 3: Each of the 9 main structures xx has a field called error_message. Any function in the module xxx.c is called xxx_something() and writes its error message in xx.error_message (if pxx is a pointer to xx, in pxx->error_message).

    This way, if we are in perturb_init() and we call perturb_indices() we write:

class_call(perturb_indices(..., ppt),
     ppt->error_message,
     ppt->error_message);

But if we were in perturb_init() and we called background_at_tau(), we would write:

class_call(background_at_tau(..., pba),
     pba->error_message,
     ppt->error_message);
  • Rule 4: Whenever an error could occur, we first write a test with the macro class_test(..., ...):

    include/common.h:198

#define class_test(condition, error_message_output, args...) {                                                   \
  if (condition) {                                                                                               \
    class_test_message(error_message_output,#condition, args);                                                   \
    return _FAILURE_;                                                                                            \
  }                                                                                                              \
}

So, it would be used as:

class_test(condition, error_message, "Some text") ;

or

class_test(condition, error_message, "Some text and numbers %d %e", n, x);

A real example is the following (source/background.c:913):

  /* index_bi_tau must be the last index, because tau is part of this vector for the purpose of being stored, */
  /* but it is not a quantity to be integrated (since integration is over tau itself) */
  class_test(pba->index_bi_tau != index_bi-1,
             pba->error_message,
             "background integration requires index_bi_tau to be the last of all index_bi's");

In case the condition is true, the macro will output the written error message, the condition saying it is true, the function the test is in, the line number, etc.

  • Rule 5: Always allocate memory with the macros class_alloc(), class_calloc(), class_realloc().

    As it can be seen from their definitions, below, they handle the possible allocation errors.

/* macro for allocating memory and returning error if it failed */
#define class_alloc(pointer, size, error_message_output)  {                                                      \
  pointer=malloc(size);                                                                                          \
  if (pointer == NULL) {                                                                                         \
    int size_int;                                                                                                \
    size_int = size;                                                                                             \
    class_alloc_message(error_message_output,#pointer, size_int);                                                \
    return _FAILURE_;                                                                                            \
  }                                                                                                              \
}
/* macro for allocating memory, initializing it with zeros/ and returning error if it failed */
#define class_calloc(pointer, init,size, error_message_output)  {                                                \
  pointer=calloc(init,size);                                                                                     \
  if (pointer == NULL) {                                                                                         \
    int size_int;                                                                                                \
    size_int = size;                                                                                             \
    class_alloc_message(error_message_output,#pointer, size_int);                                                \
    return _FAILURE_;                                                                                            \
  }                                                                                                              \
}
/* macro for re-allocating memory, returning error if it failed */
#define class_realloc(pointer, newname, size, error_message_output)  {                                          \
    pointer=realloc(newname,size);                                                                               \
  if (pointer == NULL) {                                                                                         \
    int size_int;                                                                                                \
    size_int = size;                                                                                             \
    class_alloc_message(error_message_output,#pointer, size_int);                                                \
    return _FAILURE_;                                                                                            \
  }                                                                                                              \
}

Note: in main/class.c there is no "higher level" so the 10 initialization functions are called directly, without macros, like:

int main(int argc, char **argv) {

    [...]

  if (background_init(&pr,&ba) == _FAILURE_) {
    printf("\n\nError running background_init \n=>%s\n",ba.error_message);
    return _FAILURE_;
  }

    [...]