From 990583f2c4e2aa298de9f77e28fb5613ffc9404b Mon Sep 17 00:00:00 2001 From: Krishna Vedala <7001608+kvedala@users.noreply.github.com> Date: Wed, 10 Jun 2020 13:25:41 -0400 Subject: [PATCH] added semi-implicit euler method --- numerical_methods/ode_semi_implicit_euler.c | 182 ++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 numerical_methods/ode_semi_implicit_euler.c diff --git a/numerical_methods/ode_semi_implicit_euler.c b/numerical_methods/ode_semi_implicit_euler.c new file mode 100644 index 00000000..3375259b --- /dev/null +++ b/numerical_methods/ode_semi_implicit_euler.c @@ -0,0 +1,182 @@ +/** + * \file + * \authors [Krishna Vedala](https://github.com/kvedala) + * \brief Solve a multivariable first order [ordinary differential equation + * (ODEs)](https://en.wikipedia.org/wiki/Ordinary_differential_equation) using + * [semi implicit Euler + * method](https://en.wikipedia.org/wiki/Semi-implicit_Euler_method) + * + * \description + * The ODE being solved is: + * \f{eqnarray*}{ + * \dot{u} &=& v\\ + * \dot{v} &=& -\omega^2 u\\ + * \omega &=& 1\\ + * [x_0, u_0, v_0] &=& [0,1,0]\qquad\ldots\text{(initial values)} + * \f} + * The exact solution for the above problem is: + * \f{eqnarray*}{ + * u(x) &=& \cos(x)\\ + * v(x) &=& -\sin(x)\\ + * \f} + * The computation results are stored to a text file `semi_implicit_euler.csv` + * and the exact soltuion results in `exact.csv` for comparison. Implementation solution + * \see ode_forward_euler.c, ode_midpoint_euler.c + */ + +#include +#include +#include +#include + +#define order 2 /**< number of dependent variables in ::problem */ + +/** + * @brief Problem statement for a system with first-order differential + * equations. Updates the system differential variables. + * \note This function can be updated to and ode of any order. + * + * @param[in] x independent variable(s) + * @param[in,out] y dependent variable(s) + * @param[in,out] dy first-derivative of dependent variable(s) + */ +void problem(const double *x, double *y, double *dy) +{ + const double omega = 1.F; // some const for the problem + dy[0] = y[1]; // x dot + dy[1] = -omega * omega * y[0]; // y dot +} + +/** + * @brief Exact solution of the problem. Used for solution comparison. + * + * @param[in] x independent variable + * @param[in,out] y dependent variable + */ +void exact_solution(const double *x, double *y) +{ + y[0] = cos(x[0]); + y[1] = -sin(x[0]); +} + +/** + * @brief Compute next step approximation using the midpoint-Euler + * method. + * @f[y_{n+1} = y_n + dx\, f\left(x_n+\frac{1}{2}dx, + * y_n + \frac{1}{2}dx\,f\left(x_n,y_n\right)\right)@f] + * @param[in] dx step size + * @param[in,out] x take @f$x_n@f$ and compute @f$x_{n+1}@f$ + * @param[in,out] y take @f$y_n@f$ and compute @f$y_{n+1}@f$ + * @param[in,out] dy compute @f$y_n+\frac{1}{2}dx\,f\left(x_n,y_n\right)@f$ + */ +void semi_implicit_euler_step(double dx, double *x, double *y, double *dy) +{ + problem(x, y, dy); + double tmp_x = (*x) + 0.5 * dx; + double tmp_y[order]; + int o; + for (o = 0; o < order; o++) + tmp_y[o] = y[o] + 0.5 * dx * dy[o]; + + problem(&tmp_x, tmp_y, dy); + + for (o = 0; o < order; o++) + y[o] += dx * dy[o]; +} + +/** + * @brief Compute approximation using the midpoint-Euler + * method in the given limits. + * @param[in] dx step size + * @param[in] x0 initial value of independent variable + * @param[in] x_max final value of independent variable + * @param[in,out] y take \f$y_n\f$ and compute \f$y_{n+1}\f$ + * @param[in] save_to_file flag to save results to a CSV file (1) or not (0) + * @returns time taken for computation in seconds + */ +double semi_implicit_euler(double dx, double x0, double x_max, double *y, + char save_to_file) +{ + double dy[order]; + + FILE *fp = NULL; + if (save_to_file) + { + fp = fopen("semi_implicit_euler.csv", "w+"); + if (fp == NULL) + { + perror("Error! "); + return -1; + } + } + + /* start integration */ + clock_t t1 = clock(); + double x = x0; + do // iterate for each step of independent variable + { + if (save_to_file && fp) + fprintf(fp, "%.4g,%.4g,%.4g\n", x, y[0], y[1]); // write to file + semi_implicit_euler_step(dx, &x, y, dy); // perform integration + x += dx; // update step + } while (x <= x_max); // till upper limit of independent variable + /* end of integration */ + clock_t t2 = clock(); + + if (save_to_file && fp) + fclose(fp); + + return (double)(t2 - t1) / CLOCKS_PER_SEC; +} + +/** + Main Function +*/ +int main(int argc, char *argv[]) +{ + double X0 = 0.f; /* initial value of x0 */ + double X_MAX = 10.F; /* upper limit of integration */ + double Y0[] = {1.f, 0.f}; /* initial value Y = y(x = x_0) */ + double step_size; + + if (argc == 1) + { + printf("\nEnter the step size: "); + scanf("%lg", &step_size); + } + else + // use commandline argument as independent variable step size + step_size = atof(argv[1]); + + // get approximate solution + double total_time = semi_implicit_euler(step_size, X0, X_MAX, Y0, 1); + printf("\tTime = %.6g ms\n", total_time); + + /* compute exact solution for comparion */ + FILE *fp = fopen("exact.csv", "w+"); + if (fp == NULL) + { + perror("Error! "); + return -1; + } + double x = X0; + double *y = &(Y0[0]); + printf("Finding exact solution\n"); + clock_t t1 = clock(); + + do + { + fprintf(fp, "%.4g,%.4g,%.4g\n", x, y[0], y[1]); // write to file + exact_solution(&x, y); + x += step_size; + } while (x <= X_MAX); + + clock_t t2 = clock(); + total_time = (t2 - t1) / CLOCKS_PER_SEC; + printf("\tTime = %.6g ms\n", total_time); + fclose(fp); + + return 0; +}