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.
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>