RailGun requires the following constraints in C library to be loaded.
This is an example of a struct:
typedef struct linearode_{
int num_d, num_s;
double dt;
double **a;
double **x;
} LinearODE;
Here, variable named as num_d has special meaning in RailGun. This is the size of array along index d.
You can have temporary variables in your C code, but if you want to get or set C variable from python, you must add that variable to the struct.
If you want to use some functions from python, the function must take a pointer of the struct as its first argument, like this:
int NameOfStruct_name_of_function(NameOfStruct *self, ...)
If this function returns non-zero value, RailGun raises an error.
Here is an example:
int LinearODE_run(LinearODE *self, int s_end)
{
int s, d1, d2;
for (s = 1; s < s_end; ++s){
for (d1 = 0; d1 < self->num_d; ++d1){
self->x[s][d1] = self->x[s-1][d1];
for (d2 = 0; d2 < self->num_d; ++d2){
self->x[s][d1] += self->dt * self->a[d1][d2] * self->x[s-1][d2];
}
}
}
return 0;
}
Note
You don’t need to check if s_end above is in the range of [1, self->num_s]. We will see how you can leave it to RailGun.
All you need to import from RailGun is these two:
from railgun import SimObject, relpath
To load your c function above, all you need to do is define a class which inherits railgun.SimObject:
class LinearODE(SimObject):
_clibname_ = 'liblode.so'
_clibdir_ = relpath('.', __file__)
_cmembers_ = [
'num_d',
'num_s = 10000'
'double dt = 0.001',
'double a[d][d]',
'double x[s][d]',
]
_cfuncs_ = ["x run(int s_end=num_s)"]
This is the definitions of your C functions which take the form ret func_name(arg, ...) where
If you have several C functions of same type such as:
int NameOfStruct_func_method1(NameOfStruct *self, int a, int b)
int NameOfStruct_func_method2(NameOfStruct *self, int a, int b)
int NameOfStruct_func_method3(NameOfStruct *self, int a, int b)
You can load all these functions like this:
'func_{meth | method1, method2, method3}(int a, int b)'
Generated python function will be like this:
NameOfStruct.func(a, b, meth='method1')
as you see, you can specify “method” by option of the python function. The func_{key|c1,c2}-notation is called “choice set”.
You can use name of the index as a type of argument like this:
"run(s end)"
so that end is always in the range [0, num_s).
If end is an “upper bound” of the index, you want it to be in the range (0, num_s]. You can specify this with < like this:
"run(s< end)"
An instance of the simulation class can be created like any other Python classes. Note that num_* arguments are required:
lode = LinearODE(num_d=2)
If you specified the default values for num_*, you can make an instance without passing its value.
Once you create an instance, you can change C variables in various ways:
lode.a = [[0, 1], [-1, 0]]
lode.x[0] = [1, 0]
lode.setv(a_0_0=-0.5)
Note that lode.setv(a_0_0=-0.5) and lode.a[0,0] = -0.5 are the same.
Calling function is easy. Number of arguments are the number of arguments of C function puls number of the “choice set”. The first arguments are used for C function and then the last arguments are used for “choice set”. You can also use keyword-style arguments:
lode.run()
lode.run(10)
lode.run(s_end=10)