Mitch Richling: Brusselator
Author: | Mitch Richling |
Updated: | 2025-02-27 |
Table of Contents
1. Introduction
The Brusselator is a reaction-diffusion model for an autocatalytic chemical reaction in space and time. This system was proposed by Ilya Romanovich Prigogine and Rene Lefever from Universite libre de Bruxelles – hence the name. The model is given by the following equations:
The functions
In the code below, we use the following values for the parameters:
We use a simple finite differences method to solve the equations with a step size of
The code uses four images to store the state of the system at two steps. At each time step it uses two of the images to update the other two. This doubles the RAM required, but simplifies the code and eliminates the need for swapping data. At the end of the run the last updated state images are combined into a 24-bit RGB image which is written to disk.
2. Code
#include "ramCanvas.hpp" typedef mjr::ramCanvas3c8b rcT; int main(void) { std::random_device rd; std::minstd_rand0 rEng(rd()); std::uniform_real_distribution<double> uniform_dist_double(1.0e-5, 1.0); int width = 7680/32; int height = 4320/32; std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now(); rcT theRamCanvas(width, height); std::vector<mjr::ramCanvas1c64F> imgu1{mjr::ramCanvas1c64F(width, height), mjr::ramCanvas1c64F(width, height)}; std::vector<mjr::ramCanvas1c64F> imgu2{mjr::ramCanvas1c64F(width, height), mjr::ramCanvas1c64F(width, height)}; mjr::ramCanvas1c64F::pointIntVecType st { {0,-1}, {0,1}, {-1,0}, {1,0}}; int N = 3000; double h = 0.3; double t = 0.007; double a = 9.0; double b = 9.8; double d = 3.0; for(rcT::coordIntType y=0;y<theRamCanvas.getNumPixY();y++) for(rcT::coordIntType x=0;x<theRamCanvas.getNumPixX();x++) { imgu1[0].drawPoint(x, y, uniform_dist_double(rEng)); imgu2[0].drawPoint(x, y, uniform_dist_double(rEng)); } int i_in, i_ou; double maxv = 0; for(int step=0; step<N; step++) { i_in = (step%2); i_ou = ((step+1)%2); if (0==(step%100)) std::cout << "STEP: " << step << std::endl; # pragma omp parallel for schedule(static,1) for(rcT::coordIntType y=0;y<theRamCanvas.getNumPixY();y++) { for(rcT::coordIntType x=0;x<theRamCanvas.getNumPixX();x++) { double u1_sum = 0; double u2_sum = 0; for(mjr::ramCanvas1c64F::pointIntType const &p: st) { u1_sum += imgu1[i_in].getPxColorChanWrap<0>(x+p.x, y+p.y); u2_sum += imgu2[i_in].getPxColorChanWrap<0>(x+p.x, y+p.y); } double u1_c = imgu1[i_in].getPxColor(x, y).getC0(); double u2_c = imgu2[i_in].getPxColor(x, y).getC0(); double du1_c = t*(1.0-(b+1)*u1_c+a*u1_c*u1_c*u2_c+(u1_sum-4*u1_c)/(h*h)); double du2_c = t*(b*u1_c-a*u1_c*u1_c*u2_c+d*(u2_sum-4*u2_c)/(h*h)); double u1 = u1_c + du1_c; double u2 = u2_c + du2_c; maxv = std::max(maxv, std::max(u1, u2)); imgu1[i_ou].drawPoint(x, y, u1); imgu2[i_ou].drawPoint(x, y, u2); } } if (maxv > 1e10) { std::cout << "Solution Failure at step " << step << std::endl; return 1; } else if (maxv < 0.0001) { std::cout << "Zero Solution at step " << step << std::endl; return 1; } } imgu1[i_ou].autoHistStrech(); imgu2[i_ou].autoHistStrech(); for(int y=0;y<theRamCanvas.getNumPixY();y++) for(int x=0;x<theRamCanvas.getNumPixX();x++) { rcT::colorChanType r = static_cast<rcT::colorChanType>(255*imgu1[i_ou].getPxColorNC(x, y).getC0()); rcT::colorChanType b = static_cast<rcT::colorChanType>(255*imgu2[i_ou].getPxColorNC(x, y).getC0()); theRamCanvas.drawPoint(x, y, mjr::color3c8b(r, 0, b)); } theRamCanvas.writeTIFFfile("brusselator.tiff"); std::chrono::duration<double> runTime = std::chrono::system_clock::now() - startTime; std::cout << "Total Runtime " << runTime.count() << " sec" << std::endl; return 0; }
Source Link: brusselator.cpp