Search code examples
javascriptcanvasdifferential-equations

Weird pattern of Heat Equation Simulation on 2D Canvas


I'm playing with a Heat Equation on 2D Canvas with additional heat source in a form of a donut. The result I got some "acidic" pattern around this donut.

const width = 200; // width of the grid
const height = 200; // height of the grid
const dt = 0.25; // time step
const dx = 1; // space step in the x-direction
const dy = 1; // space step in the y-direction
const alpha = 0.25; // thermal diffusivity


const Q = [];
// Heat of the heat source
const Q0 = 80;

const r1 = 8;
const r2 = 12;

for (let i = 0; i < width - 1; i++) {
    Q[i] = [];
    for (let j = 0; j < height - 1; j++) {
        // Calculate the distance from the center of the region
        const r = Math.sqrt((i - width / 2) ** 2 + (j - height / 2) ** 2);

        Q[i][j] = (r1 < r && r < r2) ? Q0 : 0;
    }
}

let grid = []; // array to store the grid

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

// Initialize the grid with random temperatures
for (let i = 0; i < width; i++) {
    grid[i] = [];
    for (let j = 0; j < height; j++) {
        grid[i][j] = 50
    }
}

function updateGrid() {
// Update the temperature of each cell based on the heat equation
    for (let i = 1; i < width - 1; i++) {
            for (let j = 1; j < height - 1; j++) {
                const d2Tdx2 = (grid[i + 1][j] - 2 * grid[i][j] + grid[i - 1][j]) / (dx ** 2);
                const d2Tdy2 = (grid[i][j + 1] - 2 * grid[i][j] + grid[i][j - 1]) / (dy ** 2);

                grid[i][j] = grid[i][j] + alpha * dt * (d2Tdx2 + d2Tdy2) + (Q[i][j] * dt);
            }
    }
}

// This function is called repeatedly to update the grid and render it
function main() {
    updateGrid();
    renderGrid();
    requestAnimationFrame(main);
}

// This function render the grid
function renderGrid() {

    // Clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Iterate over the grid and draw each cell
    for (let i = 0; i < width; i++) {
        for (let j = 0; j < height; j++) {
            let hue = ((100 - grid[i][j]) / 100) * 240;
            //ctx.fillStyle = `rgb(${temp}, ${temp}, ${temp})`;
            ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
            ctx.fillRect(i * dx, j * dy, dx, dy);
        }
    }
}

// Start the simulation
main();

Tried different approaches, like playing with hsl colors, initial parameters but something definitely missing there.

What I aslo noticed is that during debuging, some values seems to be blowing up and I think that's the root of the problem but can't find the source of it, I have tried to found more information about this behaviour of differential equiations and why this can happen but couldn't apply it to the source code.


Solution

  • during calculation values in grid can go over 100, thus making formula ((100 - grid[i][j]) / 100) * 240; produce negative values

    the simples way to fix is to limit values:

    grid[i][j] = Math.min(100, grid[i][j] + alpha * dt * (d2Tdx2 + d2Tdy2) + (Q[i][j] * dt));
    
           
    

    const width = 200; // width of the grid
    const height = 200; // height of the grid
    const dt = 0.25; // time step
    const dx = 1; // space step in the x-direction
    const dy = 1; // space step in the y-direction
    const alpha = 0.25; // thermal diffusivity
    
    
    const Q = [];
    // Heat of the heat source
    const Q0 = 80;
    
    const r1 = 8;
    const r2 = 12;
    
    for (let i = 0; i < width - 1; i++) {
        Q[i] = [];
        for (let j = 0; j < height - 1; j++) {
            // Calculate the distance from the center of the region
            const r = Math.sqrt((i - width / 2) ** 2 + (j - height / 2) ** 2);
    
            Q[i][j] = (r1 < r && r < r2) ? Q0 : 0;
        }
    }
    
    let grid = []; // array to store the grid
    
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    
    // Initialize the grid with random temperatures
    for (let i = 0; i < width; i++) {
        grid[i] = [];
        for (let j = 0; j < height; j++) {
            grid[i][j] = 50
        }
    }
    
    function updateGrid() {
    // Update the temperature of each cell based on the heat equation
            for (let i = 1; i < width - 1; i++) {
        for (let j = 1; j < height - 1; j++) {
                    const d2Tdx2 = (grid[i + 1][j] - 2 * grid[i][j] + grid[i - 1][j]) / (dx ** 2);
                    const d2Tdy2 = (grid[i][j + 1] - 2 * grid[i][j] + grid[i][j - 1]) / (dy ** 2);
    
                    grid[i][j] = Math.min(100, grid[i][j] + alpha * dt * (d2Tdx2 + d2Tdy2) + (Q[i][j] * dt));
                }
        }
    }
    
    // This function is called repeatedly to update the grid and render it
    function main() {
        updateGrid();
        renderGrid();
        requestAnimationFrame(main);
    }
    
    // This function render the grid
    function renderGrid() {
    
        // Clear the canvas
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    
        // Iterate over the grid and draw each cell
            for (let i = 0; i < width; i++) {
        for (let j = 0; j < height; j++) {
                let hue = (100 - grid[i][j]) / 100 * 240;
                //ctx.fillStyle = `rgb(${temp}, ${temp}, ${temp})`;
                ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
                ctx.fillRect(i * dx, j * dy, dx, dy);
            }
        }
    }
    
    // Start the simulation
    main();
    <canvas width="500" height="500" id="canvas"></canvas>