简单摆的C代码没有给出预期的结果

时间:2017-02-22 08:35:57

标签: c numerical-methods differential-equations runge-kutta

我正在尝试使用RK4积分器为C中的FitzHugh NAgumo神经元网络编写代码。因为那不起作用,我决定尝试一些简单的,一个简单的钟摆系统。我正在使用单独的函数 - 一个用于返回差分数组,一个用于积分器,还有一些函数用于向数组的每个元素添加和乘以数字。

以下是返回所有“0”值的代码:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

int N = 2;

float h = 0.001;

struct t_y_couple{
    float t;
    float* y;
};


float* array_add(int len_array_in,float array_in[2], float array_add[2]);
struct t_y_couple integrator_rk4(float dt,float t, float* p1);

float* oscnetwork_opt(float t, float *y);
float* array_mul(int len_array_in,float array_in[2], float num);


int main(void){
    /* initializations*/
    struct t_y_couple t_y;


    int i,iter;

//  time span for which to run simulation
    int tspan = 20;


//  total number of time iterations = tspan*step_size   
    int tot_time = (int) ceil(tspan/h);

//  Time array
    float T[tot_time];

//  pointer definitions
    float *p, *q;


//  vector to hold values for each differential variable for all time iterations
    float Y[tot_time][2];
//  2*N+sum_adj = total number of coupled differential equations to solve   

//  initial conditions vector for time = 0

    Y[0][0] = 0;
    Y[0][1] = 0.1;
//  set the time array
    T[0] = 0;

//  This loop calls the RK4 code
    for (i=0;i<tot_time-1;i++){
        p = &Y[i][0];   // current time
        q = &Y[i+1][0]; // next time step
//      printf("\n\n");
//      for (j=0;j<2*N+sum_adj;j++)
//          printf("value passed in from main function: %f ,%d ",*(p+j),j);
//      printf("\n");

//      call the RK4 integrator with current time value, and current 
//      values of voltage           
        t_y = integrator_rk4(h,T[i],p);  

//      Return the time output of integrator into the next iteration of time
        T[i+1] = t_y.t; 

//      copy the output of the integrator into the next iteration of voltage        
        q = memcpy(q, t_y.y, (2) * sizeof(float));
//      q = memcpy(q, p, (2*N+sum_adj) * sizeof(float) );

        printf("%f ",T[i+1]);
        for (iter = 0;iter<N;iter++)
            printf("%f ",*(p+iter));
        printf("\n");
    }


    return 0;
}

struct t_y_couple integrator_rk4(float dt,float t, float y[2])
{   
//  initialize all the pointers
    float *y1,*y2,*y3, *yout;
    float tout,dt_half;
    float *k1,*k2,*k3,*k4;
//  initialize iterator
    int i;

//  printf("\n");
    struct t_y_couple ty1;
    tout = t+dt;
    dt_half = 0.5*dt;
    float addition[2];

//  return the differential array into k1
    k1 = oscnetwork_opt(t,y);
//  multiply the array k1 by dt_half    
    k1 = array_mul(2,k1,dt_half);
//  add k1 to each element of the array y passed in
    y1 = array_add(2,y,k1);

//  for (i=0;i<2*N+sum_adj;i++)
//      printf("k1: %f y: %f y1: %f\n",*(k1+i), *(y+i), *(y1+i));

//  do the same thing 3 times
    k2 = oscnetwork_opt(t+dt_half,y1);
    k2 = array_mul(2,k2,dt_half);
    y2 = array_add(2,y,k2); 
//  for (i=0;i<2*N+sum_adj;i++)
//      printf("k2: %f y: %f y2: %f\n",*(k2+i), *(y+i), *(y2+i));

    k3 = oscnetwork_opt(t+dt_half,y2);
    k3 = array_mul(2,k3,dt);
    y3 = array_add(2,y,k3);
//  for (i=0;i<2*N+sum_adj;i++)
//      printf("k3: %f y: %f y3: %f\n",*(k3+i), *(y+i), *(y3+i));

    k4 = oscnetwork_opt(tout,y3);
    k4 = array_mul(2,k4,dt);
    yout = array_add(2,y,k4);
//  for (i=0;i<2*N+sum_adj;i++)
//      printf("k4: %f y: %f y4: %f\n",*(k4+i), *(y+i), *(yout+i));     

//  Make the final additions with k1,k2,k3 and k4 according to the RK4 code
    for (i=0;i<2;i++){
        addition[i] = ((*(k1+i)) + (*(k2+i))*2 + (*(k3+i))*2 + (*(k4+i))) *dt/6;
//      printf("final result : addition %f  ", addition[i]);
    }
//  printf("\n");
//  add this to the original array
    yout = array_add(2,y,addition);

//  for (i=0;i<2*N+sum_adj;i++)
//      printf("yout: %f  ",*(yout+i));
//  printf("\n");
//  return a struct with the current time and the updated voltage array
    ty1.t = tout;
    ty1.y = yout;

    return ty1;
}



// function to add two arrays together, element by element
float* array_add(int len_array_in,float array_in[2], float array_sum[2]){
    int i;
    static float *array_out_add= NULL;
    if (array_out_add != 0) {
        array_out_add = (float*) realloc(array_out_add, sizeof(float) * (2));
    } else {
        array_out_add = (float*) malloc(sizeof(float) * (2));
    }

    for (i=0;i<len_array_in;i++){
        array_out_add[i] = array_in[i]+array_sum[i];
//      printf("before adding: %f, add amount: %f , after adding: %f, iteration: %d\n ", array_in[i], array_sum[i], array_out[i],i);
    }
    return array_out_add;
//  return 0;
}


// function to multiply each element of the array by some number
float* array_mul(int len_array_in,float array_in[2], float num){
    int i;
    static float *array_out_mul= NULL;
    if (array_out_mul != 0) {
        array_out_mul = (float*) realloc(array_out_mul, sizeof(float) * (2));
    } else {
        array_out_mul = (float*) malloc(sizeof(float) * (2));
    }
    for (i=0;i<len_array_in;i++){
        array_out_mul[i] =array_in[i]*num;
    }
    return array_out_mul;
//  return 0;
}


// function to return the vector with coupled differential variables for each time iteration
float* oscnetwork_opt(float t, float *y){

//  define and allocate memory for the differential vector
    static float* dydt = NULL;
    if (dydt != 0) {
        dydt = (float*) realloc(dydt, sizeof(float) * (2));
    } else {
        dydt = (float*) malloc(sizeof(float) * (2));
    }


    dydt[0] = y[1];
    dydt[1] = -(0.1*0.1)*sin(y[0]);
    return dydt;
}

这是一个代码,它确实在我的笔记本电脑上返回omega变量的正弦波,但不在我的PC上(64位,运行Ubuntu 14.04的笔记本电脑和运行Debian 8的PC)。

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

int N = 2;

float h = 0.0001;

struct t_y_couple{
    float t;
    float* y;
};


float* array_add(int len_array_in,float array_in[2], float array_add[2]);
struct t_y_couple integrator_rk4(float dt,float t, float* p1);

float* oscnetwork_opt(float t, float *y);
float* array_mul(int len_array_in,float array_in[2], float num);


int main(void){
    /* initializations*/
    struct t_y_couple t_y;


    int i,iter,j;

//  time span for which to run simulation
    int tspan = 20;


//  total number of time iterations = tspan*step_size   
    int tot_time = (int) ceil(tspan/h);

//  Time array
    float T[tot_time];

//  pointer definitions
    float *p, *q;


//  vector to hold values for each differential variable for all time iterations
    float Y[tot_time][2];
//  N = total number of coupled differential equations to solve     

//  initial conditions vector for time = 0

    Y[0][0] = 3.14;
    Y[0][1] = 0;
//  set the time array
    T[0] = 0;

//  This loop calls the RK4 code
    for (i=0;i<tot_time-1;i++){
        p = &Y[i][0];   // current time
//      q = &Y[i+1][0]; // next time step
//      printf("\n\n");
//      for (j=0;j<N;j++)
//          printf("value passed in from main function: %f ,%d ",*(p+j),j);
//      printf("\n");

//      call the RK4 integrator with current time value, and current 
//      values of voltage           
        t_y = integrator_rk4(h,T[i],p);  

//      Return the time output of integrator into the next iteration of time
        T[i+1] = t_y.t; 

//      copy the output of the integrator into the next iteration of voltage        
//      q = memcpy(q, t_y.y, (2) * sizeof(float));
//      q = memcpy(q, p, (N) * sizeof(float) );

        printf("%f ",T[i+1]);
        for (iter = 0;iter<N;iter++){
            Y[i+1][iter] = t_y.y[iter];
            printf("%f ",Y[i+1][iter]);
        }
        printf("\n");
    }


    return 0;
}

struct t_y_couple integrator_rk4(float dt,float t, float y[2])
{   
//  initialize all the pointers
    float y1[2],y2[2],y3[2], yout[2];
    float tout,dt_half;
    float k1[2],k2[2],k3[2],k4[2];
//  initialize iterator
    int i;
//  for(i=0;i<N;i++)
//      printf("into integrator %f ",y[i]);
//  printf("\n");
    struct t_y_couple ty1;
    tout = t+dt;
    dt_half = 0.5*dt;
    float addition[2];

//  return the differential array into k1
    k1[0] = y[1];
    k1[1] = -(1)*sin(y[0]);


//  multiply the array k1 by dt_half
    for (i=0;i<N;i++){
        y1[i] = y[i] + k1[i]*dt_half;
//      printf("k1: %f y: %f y1: %f\n",*(k1+i), *(y+i), *(y1+i));       
    }

//  do the same thing 3 times
    k2[0] = y1[1];
    k2[1] = -(1)*sin(y1[0]);


    for (i=0;i<N;i++){
        y2[i] = y[i] + k2[i]*dt_half;
//      printf("k2: %f y: %f y2: %f\n",*(k2+i), *(y+i), *(y2+i));       
    }
    k3[0] = y2[1];
    k3[1] = -(1)*sin(y2[0]);


    for (i=0;i<N;i++){
        y3[i] = y[i] + k3[i]*dt;
//      printf("k3: %f y: %f y3: %f\n",*(k3+i), *(y+i), *(y3+i));
    }


    k4[0] = y3[1];
    k4[1] = -(1)*sin(y3[0]);


//  Make the final additions with k1,k2,k3 and k4 according to the RK4 code

    for (i=0;i<N;i++){

        addition[i] = (( k1[i]*dt) + (k2[i])*2 + (k3[i])*2 + (k4[i]))/6;
        yout[i] = y[i] + addition[i];
//      printf("y[%d]: %f  +  addition[%d]: %f  = yout[%d] :%f  ",i,y[i],i, addition[i], i, yout[i]);

//      printf("final result : addition %f  ", addition[i]);
    }
//  add this to the original array

//  printf("\n");
//  return a struct with the current time and the updated voltage array
    ty1.t = tout;
    ty1.y = yout;

    return ty1;
}

我怀疑问题主要在于我通过函数传递数组的方式 - 因为当我没有单独的函数来运行积分器时它会起作用。希望得到一些帮助。

1 个答案:

答案 0 :(得分:1)

您的代码有未定义的行为。这是因为你在乘法和加法函数中使用静态指针。请允许我将其浓缩成有问题的部分:

k2 = array_mul(2,k2,dt_half);
k3 = array_mul(2,k3,dt); // This either makes k2 point to freed memory, or overwrites the values in the location it points to.

addition[i] = ((*(k1+i)) + (*(k2+i))*2 + (*(k3+i))*2 + (*(k4+i))) *dt/6; // This uses all four pointers as though they must point to distinct memory locations.

首先摆脱那种静电。然后你可以做两件事之一:

  1. 只需在函数内部分配内存并返回指向它的指针。最终将其释放是调用代码的责任,如下所示:

    free(k1);
    free(k2);
    // etc
    
  2. 将指针传递给您的函数以供它们填充,将内存管理完全留给调用代码:

    // function to multiply each element of the array by some number
    void array_mul(int len_array_in,float *array_in, float num, float *array_out){
        int i;
        for (i=0;i<len_array_in;i++){
          array_out[i] =array_in[i]*num;
        }
    }