Belousov-Zhabotinsky reaction#

We would like to solve this system

\[\begin{split}\begin{cases} \dot{y}_1 = \alpha - (\beta+1)y_1 + y_1^2 y_2 \\ \dot{y}_2 = \beta y_1 - y_1^2 y_2 \end{cases}\end{split}\]

with \(\alpha = 1\) and \(\beta = 3\).

%system mkdir -p belousov_zhabotinsky_demo
[]
%%writefile belousov_zhabotinsky_demo/main.cpp

#include <iostream>

#include "ponio/solver.hpp"
#include "ponio/observer.hpp"
#include "ponio/problem.hpp"
#include "ponio/runge_kutta.hpp"
#include "ponio/eigen_linear_algebra.hpp"

#include <eigen3/Eigen/Dense>

template <typename T>
struct exact_2x2_solver_t
{
    static constexpr std::size_t size = 2;
    using matrix_type = Eigen::Matrix<T, size, size>;
    using vector_type = Eigen::Vector<T, size>;

    //// incomplet linear_algebra type
    //static matrix_type
    //identity ( vector_type const& )
    //{
    //  return matrix_type::Identity();
    //}

    static vector_type solver (matrix_type const& dfx, vector_type const& fx) {
        T det = dfx(0,0)*dfx(1,1) - dfx(1,0)*dfx(0,1);
        vector_type r;
        r(0) = 1./det*( dfx(1,1)*fx(0) - dfx(0,1)*fx(1) );
        r(1) = 1./det*( dfx(0,0)*fx(1) - dfx(1,0)*fx(0) );
        return r;
    }
};

using exact_2x2_solver = exact_2x2_solver_t<double>;

template <typename T>
struct newton_t
{
    static constexpr std::size_t size = 2;
    using matrix_type = Eigen::Matrix<T, size, size>;
    using vector_type = Eigen::Vector<T, size>;

    T tol;
    std::size_t max_iter;

    newton_t( std::size_t _max_iter , T _tol )
    : tol(_tol), max_iter(_max_iter)
    {}

    static matrix_type
    identity ( vector_type const& )
    {
      return matrix_type::Identity();
    }

    vector_type
    _solver (matrix_type const& dfx, vector_type const& fx) {
        T det = dfx(0,0)*dfx(1,1) - dfx(1,0)*dfx(0,1);
        vector_type r;
        r(0) = 1./det*( dfx(1,1)*fx(0) - dfx(0,1)*fx(1) );
        r(1) = 1./det*( dfx(0,0)*fx(1) - dfx(1,0)*fx(0) );
        return r;
    }

    T
    _norm( vector_type const& u ) {
        return std::sqrt( u(0)*u(0) + u(1)*u(1) );
    }

    template <typename func_t, typename jacobian_t>
    vector_type
    newton ( func_t && f, jacobian_t && df, vector_type const& x0 )
    {
        vector_type xk = x0;
        auto fxk = f(xk);
        T res = _norm(fxk);
        std::size_t count_iter = 0;

        while( count_iter < max_iter && res > tol ) {
            auto increment = _solver(df(xk), -f(xk));

            xk = xk + increment;
            res = _norm(f(xk));

            count_iter += 1;
        }

        return xk;
    }
};

int
main()
{
  using namespace ponio::observer;
  using state_t  = Eigen::Matrix<double, 2, 1>;
  using matrix_t = Eigen::Matrix<double, 2, 2>;

  double a=1., b=3.;

  auto f = [=](double t, state_t const& u, state_t& du)
  {
      auto & y1 = u[0];
      auto & y2 = u[1];

      du[0] = a - (b+1.)*y1 + y1*y1*y2;
      du[1] = b*y1 - y1*y1*y2;
  };
  auto df = [=](double t, state_t const& u) -> matrix_t
  {
      auto & y1 = u[0];
      auto & y2 = u[1];

      matrix_t r;
      r <<  2.*y1*y2-(b+1.) ,  y1*y1 ,
           -2.*y1*y2+b      , -y1*y1 ;
      return r;
  };

  state_t u0 = {1.5, 3.};
  double dt = 0.1;
  double tf = 20.0;

  auto pb = ponio::make_implicit_problem(f, df);

  ponio::solve(pb, ponio::runge_kutta::rk_44()   , u0, {0.,tf}, dt, "belousov_zhabotinsky_demo/bz_rk44.dat"_fobs);
  ponio::solve(pb, ponio::runge_kutta::backward_euler(), u0, {0.,tf}, dt, "belousov_zhabotinsky_demo/bz_be.dat"_fobs);
  ponio::solve(pb, ponio::runge_kutta::implicit_midpoint<newton_t<double>>(0.,0, 50, 1e-10), u0, {0.,tf}, dt, "belousov_zhabotinsky_demo/bz_mp.dat"_fobs);
  ponio::solve(pb, ponio::runge_kutta::dirk_qin_zhang<exact_2x2_solver>(), u0, {0.,tf}, dt, "belousov_zhabotinsky_demo/bz_dirk_qz.dat"_fobs);

  return 0;
}
Writing belousov_zhabotinsky_demo/main.cpp
%system $CXX -std=c++20 -I ../include -I $CONDA_PREFIX/include belousov_zhabotinsky_demo/main.cpp -o belousov_zhabotinsky_demo/main
[]
%system ./belousov_zhabotinsky_demo/main
[]
import numpy as np
import matplotlib.pyplot as plt
methods = {
    "be": "Backward Euler",
    "dirk_qz": "DIRK Qui Zhang",
    "mp": "Middle point",
    "rk44": "RK(4,4)"
}
colors      = ["#badc58", "#ff7979", "#f6e58d", "#7ed6df"]
dark_colors = ["#6ab04c", "#eb4d4b", "#f9ca24", "#22a6b3"]

for i,(tag, scheme) in enumerate(methods.items()):
    data = np.loadtxt(f"belousov_zhabotinsky_demo/bz_{tag}.dat")
    t  = data[:,0]
    y1 = data[:,1]
    y2 = data[:,2]

    line = "--" if tag == "rk44" else "-"

    plt.plot(t, y1, line, color=colors[i]     , label=f"$y_1$ {scheme}")
    plt.plot(t, y2, line, color=dark_colors[i], label=f"$y_2$ {scheme}")

plt.legend()
plt.xlabel("$t$"); plt.ylabel("$y_1$, $y_2$")
plt.title("Numerical solution of BZ equation")
plt.show()
../../_images/belousov_zhabotinsky_6_0.png
for i,(tag, scheme) in enumerate(methods.items()):
    data = np.loadtxt(f"belousov_zhabotinsky_demo/bz_{tag}.dat")
    y1 = data[:,1]
    y2 = data[:,2]

    line = "--" if scheme == "rk44" else "-"
    plt.plot(y1, y2, line, color=colors[i], label=scheme)

plt.legend()
plt.xlabel("$y_1$"); plt.ylabel("$y_2$")
plt.title("Numerical solution of BZ equation in phase plan")
plt.show()
../../_images/belousov_zhabotinsky_7_0.png