Применение метода конечных разностей. Решение с помощью Math.js и построение графика с помощью Plotly.js

Двумерное уравнение Лапласа и метод центрированных разностей

Вот двумерное уравнение Лапласа

Идея состоит в том, чтобы выполнить метод центрированной разности как в направлениях x, так и в направлении y, и сложить их вместе.

Рассмотрим точку сетки, обозначенную i, j в координатах x и y, метод центрированной разности дает результат в направлении x (при условии, что Δx = Δy),

метод центрированной разности дает в направлении Y дает

Складывая два уравнения рядом друг с другом, мы будем иметь

Это уравнение говорит о том, что в точке сетки i и j решение определяется соседними значениями сверху, слева, справа и снизу точки сетки.

Граничное условие

Так же, как и в одномерном случае, мы устанавливаем граничные условия. Представьте себе стены на севере, востоке, юге и западе.

Я считаю, когда i = 0, как Запад, j = 0 как Юг, i = N как Восток, j = N как Север. Таким образом, левый нижний угол — это координата (0, 0).

Построить матрицу

Например, 4 x 4 точки сетки. Всего точек сетки 16. Внутренние 4 из 16 точек можно решить с помощью уравнения. Все точки внешней сетки определяются граничными условиями. Следовательно, нужно решить 4 уравнения.

Эту систему линейных уравнений можно преобразовать в следующую

Тогда его можно записать в матричной форме

или просто скажем

Точно так же, как мы делали это в одномерном случае, мы можем решить матричное уравнение с помощью решателя.

Кстати, я изначально использовал итерационный метод, такой как метод Гаусса-Зейделя, и он тоже был хорош. Изменение формы раствора не требуется. Здесь я использую решатель, используемый в одномерном случае, так как мне кажется, что он более согласован.

Получив решение u, нам нужно изменить форму u для графика, поскольку u задается в векторном формате в матричном уравнении. Также обратите внимание, что это u не включает границы. Поэтому я называю это inner_u в разделе кодирования. Включая границу, я называю это u. Просто для уточнения.

Кодирование

Основной поток:

  1. Установите размер сетки и граничное условие
  2. Построить матрицу A и вектор b
  3. Найди решение
  4. Изменить форму
  5. Сюжет

1. Установите размер сетки и граничное условие

  • Квадратная сетка, ширина сетки 4
  • Северное граничное значение равно 2, восток 2, юг 1 и запад 1.
const GRID_WIDTH = 4
const DIRICHLET = {
  NORTH: 2,
  EAST: 2,
  SOUTH: 1,
  WEST: 1
}

2. Построить матрицу A и вектор b

Я вызываю функцию create2DLaplaceMatrices, и она принимает указанные выше условия.

let { A, b } = create2DLaplaceMatrices(GRID_WIDTH, DIRICHLET);

Чтобы заставить это работать, я считаю следующее.

  • Номер решения определяется как (Ширина сетки -2) x (Ширина сетки -2) из-за граничного условия
  • Имя (Ширина сетки — 2) как «Inner_Grid_Width»
  • Вектор решения u или inner_u в порядке от северо-западного угла к юго-восточному.
  • Установите индекс решения, чтобы отразить вышеуказанный порядок (индексы 2d для индекса 1d)
  • В точке (i, j) элемент Matrix равен -4, соседи (сверху, справа, снизу, слева) равны 1.
  • Если сосед находится на границе, установите граничное условие
  • Обратите внимание, что соседи указаны с точки зрения индекса решения, поскольку решение находится в векторной форме. Следовательно, верх и низ — это либо плюс, либо минус внутренней ширины сетки.

Так должно выглядеть:

function create2DLaplaceMatrices(GRID_WIDTH, DIRICHLET) {
  const INNER_GRID_WIDTH = GRID_WIDTH - 2;
  const NUMBER_OF_SOLUTIONS = (INNER_GRID_WIDTH)*(INNER_GRID_WIDTH);
  let solutionIndex = 0;
  let A = [];
  let b = new Array(NUMBER_OF_SOLUTIONS).fill(0);
  for (j = 0; j < INNER_GRID_WIDTH; j++) {
    for (i = 0; i < INNER_GRID_WIDTH; i++) {
      A.push(new Array(NUMBER_OF_SOLUTIONS).fill(0));
      solutionIndex = i + (j) * (INNER_GRID_WIDTH);
      A[solutionIndex][solutionIndex] = -4;
      if (i == 0) {
        b[solutionIndex] -= DIRICHLET.WEST;
      } else {
        A[solutionIndex][solutionIndex - 1] = 1
      }
      if (j == 0) {
        b[solutionIndex] -= DIRICHLET.NORTH;
      } else {
        A[solutionIndex][solutionIndex - (INNER_GRID_WIDTH)] = 1;
      }
      if (i == (INNER_GRID_WIDTH - 1)) {
        b[solutionIndex] -= DIRICHLET.EAST;
      } else {
        A[solutionIndex][solutionIndex + 1] = 1;
      }
      if (j == (INNER_GRID_WIDTH - 1)) {
        b[solutionIndex] -= DIRICHLET.SOUTH;
      } else {
        A[solutionIndex][solutionIndex + (INNER_GRID_WIDTH)] = 1;
      }
    }
  }
  return { A, b };
}

Это даст желаемый результат.

3. Решите это!

Чтобы решить матричное уравнение, используйте тот же решатель, что и в одномерном случае, функцию math.lusolve. Поэтому перед запуском убедитесь, что математическая библиотека загружена. Для решения просто

let inner_u = math.lusolve(A, b);

Просто помните, что решение находится в виде вектора, а значения хранятся в массиве в массиве. Я называю решение inner_u, потому что inner_u не содержит граничных значений.

4. Изменить форму

Прежде чем построить решение, измените форму одномерного массива inner_u на двумерный массив u. Я называю эту функцию «изменить форму».

let u = reshape(inner_u, GRID_WIDTH, DIRICHLET);

Чтобы создать двумерный массив, создайте массив u и сначала вставьте массив Grid Width с северным граничным условием. Затем вставьте решения из inner_u, затем вставьте западное и восточное граничные значения и добавьте южные граничные значения. Наконец, углы брали средние значения двух соседей.

function reshape(inner_u, GRID_WIDTH, DIRICHLET) {
  const INNER_GRID_WIDTH = GRID_WIDTH — 2;
  let u = [];
  u.push(new Array(GRID_WIDTH).fill(DIRICHLET.NORTH));
  while (inner_u.length) u.push((inner_u.splice(0, INNER_GRID_WIDTH)).flat());
  for (j = 1; j < GRID_WIDTH — 1; j++) {
    u[j].splice(0, 0, DIRICHLET.WEST);
    u[j].splice(u[j].length, 0, DIRICHLET.EAST);
  }
  u.push(new Array(GRID_WIDTH).fill(DIRICHLET.SOUTH));
  u[0][0] = (u[0][1] + u[1][0]) / 2;
  u[0][GRID_WIDTH — 1] = (u[0][GRID_WIDTH — 2] + u[1][GRID_WIDTH — 1]) / 2;
  u[GRID_WIDTH — 1][0] = (u[GRID_WIDTH — 2][0] + u[GRID_WIDTH — 1][1]) / 2;
  u[GRID_WIDTH — 1][GRID_WIDTH — 1] = (u[GRID_WIDTH — 2][GRID_WIDTH — 1] + u[GRID_WIDTH — 1][GRID_WIDTH — 2]) / 2;
  return u;
}

Сюжет

Наконец, Plotly.js упрощает построение трехмерных поверхностей. Давайте создадим функцию графика поверхности «plotSurface», взяв решение u.

plotSurface(u);

Убедитесь, что тег div с идентификатором «myDiv» подготовлен в html-файле.

function plotSurface(u) {
  let data = { z: u, type: ‘surface’ };
  let layout = {
    font: { size: 16 }
  };
  let config = { responsive: true };
  Plotly.newPlot(‘myDiv’, [data], layout, config);
}

Если все готово, загрузите подготовленный index.html в одномерном случае, вы должны увидеть

После примечания

Используя решатель, вы должны почувствовать задержку, как только вы увеличите ширину сетки до 20 или около того, в зависимости от вашей машины. Итерационный метод с Gauss Seidel и SOR значительно ускорил процесс. Даже должна быть лучшая библиотека для решения линейных уравнений. Что-то рассмотреть.

Что попробовать:

  • Итерационный метод решения
  • Решите уравнение Пуассона

Справочник