#include "interaction.h" #include "math.h" //#define WARN_NAN_OCCUR //#define WARN_CORRECTED_R_0 #define raise2(x) (x)*(x) static float interaction_force_function ( float r , float * coefficients) { float result = 0; int i; for(i = 0; i < 7; i++) { result += coefficients[i] * powf(r, i); } result *= coefficients[7] * expf(coefficients[8] * (r - coefficients[9])); result += coefficients[10] * expf(coefficients[11] * (r - coefficients[12])); result += coefficients[13] * expf(coefficients[14] * raise2(r - coefficients[15])); result += coefficients[16] * expf(coefficients[17] * raise2(r - coefficients[18])); return result; } static void interaction_ufunc_force ( char ** args , npy_intp * dimensions , npy_intp * steps , void * data) { char * in = args[0] , * out = args[1]; npy_intp n = dimensions[0]; npy_intp in_step = steps[0] , out_step = steps[1]; npy_intp i; float * coefficients = (float *) data; for(i = 0; i < n; i++) { *(float *)out = interaction_force_function(*(float *)in, coefficients); out += out_step; in += in_step; } } static void interaction_ufunc_float2D ( char ** args , npy_intp * dimensions , npy_intp * steps , void * data) { //NPY_BEGIN_THREADS_DEF; npy_intp i; npy_intp j; npy_intp n = dimensions[0]; char * x_old = args[0] , * y_old = args[1] , * p_x_old = args[2] , * p_y_old = args[3] , * p_x_new = args[4] , * p_y_new = args[5]; npy_intp x_old_steps = steps[0] , y_old_steps = steps[1] , p_x_old_steps = steps[2] , p_y_old_steps = steps[3] , p_x_new_steps = steps[4] , p_y_new_steps = steps[5]; float * coefficients = (float *) data; float dt = coefficients[19]; // Compute the new momenta: // Stuff we will need: float r; // Distance between interacting particles. float delta_x_e; // x-component of unit direction vector. float delty_y_e; // y-component of unit direction vector. float delta_p_x; float delta_p_y; // The current x_old[i], y_old[i] coordinates. float this_x_i; float this_y_i; // The current x_old[j], y_old[j] coordinates. float this_x_j; float this_y_j; //NPY_BEGIN_THREADS; for(i = 0; i < n; i++) { this_x_i = *(float *)(x_old + i*x_old_steps); this_y_i = *(float *)(y_old + i*y_old_steps); // copy current momenta *(float *)(p_x_new + i*p_x_new_steps) = *(float *)(p_x_old + i*p_x_old_steps); *(float *)(p_y_new + i*p_y_new_steps) = *(float *)(p_y_old + i*p_y_old_steps); // compute and add the momentum offset for(j = 0; j < i; j++) { this_x_j = *(float *)(x_old + j*x_old_steps); this_y_j = *(float *)(y_old + j*y_old_steps); // Compute distance and direction between particles i,j. r = sqrtf(raise2(this_x_i - this_x_j) + raise2(this_y_i - this_y_j)); // r = 0, we cannot compute the direction. // In this case choose the direction randomly. if(r > 0) { delta_x_e = (this_x_i - this_x_j) / r; delty_y_e = (this_y_i - this_y_j) / r; } else { long int rand = random(); float random_angle = (((float)rand) / ((float)RAND_MAX)) * 2 * M_PI; #ifdef WARN_CORRECTED_R_0 printf("Warning: corrected r = 0 with random angle pi*%f\n", random_angle / M_PI); #endif delta_x_e = cosf(random_angle); delty_y_e = sinf(random_angle); } // Update the momenta. delta_p_x = delta_x_e * interaction_force_function(r, coefficients); delta_p_y = delty_y_e * interaction_force_function(r, coefficients); #ifdef WARN_NAN_OCCUR if(isnan(delta_p_x)) { printf("Warning: delta_p_x is NaN, after r = %f\n", r); } if(isnan(delta_p_y)) { printf("Warning: delta_p_y is NaN, after r = %f\n", r); } #endif //printf("%d, %d: %f, %f\n", i, j, delta_p_x, delta_p_y); *(float *)(p_x_new + i*p_x_new_steps) += dt*delta_p_x; *(float *)(p_y_new + i*p_y_new_steps) += dt*delta_p_y; *(float *)(p_x_new + j*p_x_new_steps) -= dt*delta_p_x; *(float *)(p_y_new + j*p_y_new_steps) -= dt*delta_p_y; } } //NPY_END_THREADS; } static char interaction_types[] = { NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT, NPY_FLOAT}; static char force_types[] = { NPY_FLOAT, NPY_FLOAT}; static PyUFuncGenericFunction force_funcs[1] = { interaction_ufunc_force}; static PyUFuncGenericFunction interaction_funcs[1] = { interaction_ufunc_float2D}; typedef struct { PyObject_HEAD float coefficients[20]; PyObject * ufunc; void *data[1]; } interaction_UFuncWrapper; static int interaction_UFuncWrapper_init ( interaction_UFuncWrapper * self , PyObject * args , PyObject * kwds) { // 0: ufunc_force // 1: interaction2D char type; PyObject * coefficients; int i; PyObject * this_coefficient; char *kwords[] = { "type_", "coefficients", NULL}; if(!PyArg_ParseTupleAndKeywords(args, kwds, "bO", kwords, &type, &coefficients)) { return -1; } if(!PySequence_Check(coefficients)) { return -1; } if(PySequence_Size(coefficients) != 20) { PyErr_SetString(PyExc_ValueError, "coefficients must have length 20"); return -1; } // copy the coefficients. for(i = 0; i < 20; i++) { this_coefficient = PySequence_GetItem(coefficients, i); if(!this_coefficient) { return -1; } // XXX: PyFloat_AsDouble might call python code, // so make sure that nothing bad can happen. Py_INCREF(this_coefficient); self->coefficients[i] = PyFloat_AsDouble(this_coefficient); Py_DECREF(this_coefficient); if(PyErr_Occurred()) { return -1; } } self->data[0] = (void *)self->coefficients; switch(type) { case 0: { self->ufunc = PyUFunc_FromFuncAndData( force_funcs // func , self->data // data , force_types //types , 1 // ntypes , 1 // nin , 1 // nout , PyUFunc_None // identity , "force_function" // name , "computes the scalar force between two particles with given coefficients" // doc , 0); // unused break; } case 1: { self->ufunc = PyUFunc_FromFuncAndData( interaction_funcs , self->data , interaction_types , 1 , 4 , 2 , PyUFunc_None , "interaction2D" , "Update the momenta according to the given coefficients and positions" , 0); break; } default: { PyErr_SetString(PyExc_ValueError, "unknown ufunc type, must be 0 or 1"); return -1; } } Py_INCREF(self->ufunc); return 0; } static PyObject * interaction_UFuncWrapper_call (interaction_UFuncWrapper * self , PyObject * args , PyObject * kwargs) { return PyObject_Call(self->ufunc, args, kwargs); } static PyMemberDef interaction_UFuncWrapper_members[] = { {"ufunc", T_OBJECT_EX, offsetof(interaction_UFuncWrapper, ufunc), 0, "ufunc"}, {NULL} }; static PyTypeObject interaction_UFuncWrapperType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "brown.interaction.UFuncWrapper", .tp_doc = "A wrapper that wraps the ufuncs for interaction and force, storing the coefficients", .tp_basicsize = sizeof(interaction_UFuncWrapper), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_new = PyType_GenericNew, .tp_init = (initproc) interaction_UFuncWrapper_init, .tp_call = interaction_UFuncWrapper_call, .tp_members = interaction_UFuncWrapper_members }; static PyMethodDef InteractionMethods[] = { {NULL, NULL, 0, NULL} }; static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT , "brown.interaction" , NULL , -1 , InteractionMethods , NULL , NULL , NULL , NULL }; PyMODINIT_FUNC PyInit_interaction(void) { PyObject * module; if(PyType_Ready(&interaction_UFuncWrapperType) < 0) { return NULL; } module = PyModule_Create(&moduledef); if(!module) { return NULL; } import_array(); import_ufunc(); Py_INCREF(&interaction_UFuncWrapperType); PyModule_AddObject(module, "UFuncWrapper", (PyObject *) &interaction_UFuncWrapperType); return module; }