-
Notifications
You must be signed in to change notification settings - Fork 30
Code3: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 ininclude/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 macrosclass_xxx(...)
are defined ininclude/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 callederror_message
. Any function in the modulexxx.c
is calledxxx_something()
and writes its error message inxx.error_message
(ifpxx
is a pointer toxx
, inpxx->error_message
).This way, if we are in
perturb_init()
and we callperturb_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_;
}
[...]
Home
Installation
Basic usage
classy: the python wrapper
Introducing new models
The code:
- Code 1: Philosophy and structure
- Code 2: Indexing
- Code 3: Errors
- Code 4: input.c
- Code 5: background.c
- Code 6: thermodynamics.c
- Code 7: perturbations.c
- Code 8: primordial.c
- Code 10: output.c
- Other modules
- classy: classy.pyx and classy.pxd
Examples: