От уравнений Максвелла к анимации: реализация FDTD на PascalABC.NET

в 10:38, , рубрики: Delphi, анимация, Волна, конечно-разностные методы, ландавшиц, Паскаль, теория поля, уравнения максвелла, электромагнетизм, электромагнитные волны

В этой статье мы будем моделировать распространение электромагнитных волн. Для этого будем численно решать систему дифференциальных уравнений в частных производных Максвелла.

При этом подойдём к этой задаче более профессионально, чем в предыдущих материалах: я учёл справедливую критику и замечания читателей по поводу выбора и обоснования численного метода, оценил корректность и реалистичность схемы и результатов моделирования, привёл необходимые условия для воспроизводимости.

В системе СИ уравнения Максвелла записываются следующим образом:

begin{align}     nabla times mathbf{E} &=-frac{partial mathbf{B}}{partial t}, \     nabla times mathbf{H} &=mathbf{J} + frac{partial mathbf{D}}{partial t}, \     nabla cdot mathbf{D} &=rho, \     nabla cdot mathbf{B} &=0. end{align}

Первое уравнение- это Закон индукции Фарадея, читается как: Изменение магнитной индукции порождает вихревое электрическое поле .

Второе уравнение- это Теорема о циркуляции магнитного поля , читается как: Электрический ток и изменение электрической индукции порождают вихревое магнитное поле.

Третье уравнение системы-это так называемый Закон Гаусса- Электрический заряд является источником электрической индукции.

Четвёртое уравнение- Закон Гаусса для магнитного поля (Магнитные заряды не обнаружены).

Введённые обозначения:

Систему уравнений необходимо дополнять материальными уравнениями. Для самого простого случая (изотропная и однородная среда) они выглядят так:

mathbf{D}=varepsilon mathbf{E}, quad mathbf{B}=mu mathbf{H}.

Где коэффициенты

varepsilon=varepsilon_0 varepsilon_r, quad mu=mu_0 mu_r.

(это диэлектрическая и магнитная проницаемости)- постоянны.

Частным случаем этих уравнений является электростатика(когда электрическое поле не меняется со временем)

Электростатика

Электростатика

, а также магнитостатика( когда магнитное поле не меняется со временем), например в постоянных магнитах:

магнитное поле постоянного цилиндрического магнита. Неточная модель.

магнитное поле постоянного цилиндрического магнита. Неточная модель.

Однако мы будем моделировать электромагнитные явления в динамике (сигнал Гаусса).

Будем использовать FDTD-метод- Метод конечных разностей во временно́й области.

Для моделирования необходимо использовать конечно-разностные дискретные формулы. Они являются классическими в научной литературе (https://elibrary.ru/download/elibrary_20799764_92247445.pdf и https://elibrary.ru/vvfyhz):

Для компоненты Ez электрического поля:

E_z^{n+1}(i,j,k)=E_z^n(i,j,k) + frac{Delta t}{varepsilon} left[     frac{H_y^{n+1/2}(i+1,j,k) - H_y^{n+1/2}(i,j,k)}{Delta x}     - frac{H_x^{n+1/2}(i,j+1,k) - H_x^{n+1/2}(i,j,k)}{Delta y} right].

Для компоненты Hx магнитного поля:

H_x^{n+1/2}(i,j,k)=H_x^{n-1/2}(i,j,k) - frac{Delta t}{mu} left[     frac{E_z^n(i,j,k+1) - E_z^n(i,j,k)}{Delta z}     - frac{E_y^n(i,j,k+1) - E_y^n(i,j,k)}{Delta y} right].

Источником электромагнитных возмущений будет классический Гауссов импульс:

S(t)=exp!left(-frac{(t - t_0)^2}{sigma_t^2}right).

или в дискретной форме:

S_n=exp!left(-frac{(n,Delta t - t_0)^2}{sigma_t^2}right).

Оговорим, что источник в коде введён как плотность тока, эквивалентный гауссову сигналу.

Параметры Гауссового импульса: J0=10^6 А/м², t0​=30 шагов и σt​=10 шагов, действующий в одной ячейке в центре области (Возбуждение подаётся в ячейку Ex с индексами (50, 49, 49), а не ровно в центр — это следствие сдвига компонентов на сетке Йи.).

Условие устойчивости- критерий Куранта:

Delta t le frac{1}{c sqrt{left(frac{1}{Delta x^2} + frac{1}{Delta y^2} + frac{1}{Delta z^2}right)}},

Шаг по времени выбирался из критерия Куранта с запасом 5%, чему соответствует число Куранта ≈0,55.

Дополнительно, для контроля корректности работы схемы, на каждом N-м шаге вычислялась интегральная энергия электромагнитного поля:

begin{equation}     V(t)=sum_{i,j,k} left( |mathbf{E}_{i,j,k}(t)|^2 + |mathbf{H}_{i,j,k}(t)|^2 right), end{equation}

Отсутствие экспоненциального роста V(t) подтверждает, что численная ошибка не накапливается и схема остаётся устойчивой на всём интервале моделирования.

Также подсчитывается max |div E| с помощью функции MaxDivergenceE. Вне области источника дискретная дивергенция E должна быть близка к машинному нулю; вблизи источника она ненулевая.

Граничные условия- 3 грани PEC, 3 грани PMC: E_tang = 0 на границах

, то есть тангенциальные компоненты на трёх «нижних» гранях x=0, y=0, z=0.

Верхние грани (x=Nx-1 и т.д.) не содержат тангенциальных E-компонент в массивах, поэтому там реализуется «магнитная» стенка (H=0).

Эти условия не диссипируют энергию при идеальных условиях.

Моделирование проводилось в кубической области 100×100×100 мм с шагом сетки Δx=Δy=Δz=1 мм. Выполнялось Nt​=300 шагов, что соответствует ≈550 пс; за это время фронт волны достигает стенок области и отражается от них. Визуализировался срез Ez​ в плоскости z=L/2 через каждые 5 шагов.

Процедура обезразмеривания в данном случае необязательна — она необходима там, где числа «выпадают» из комфортного диапазона машинной арифметики — либо переполняются, либо тонут под единицей.

Данную вычислительную схему реализуем на языке семейства Pascal в среде PascalABC.NET , (старый-добрый), поскольку на Python или C++ у меня возникли неустранимые проблемы с индексацией. (приведённый код- код после отладки и исправлений).

program FDTD3D_Animated_PEC_Energy;

uses
  GraphABC, System;

type
  Real = double;

const
  Nx = 100;
  Ny = 100;
  Nz = 100;

  dx = 1.0e-3;
  dy = 1.0e-3;
  dz = 1.0e-3;
  c  = 2.998e8;
  eps0 = 8.854e-12;
  mu0  = 4.0 * Pi * 1.0e-7;

  steps = 300;
  snap_every = 5;

  t0 = 30.0;
  sigma_t = 10.0;

  J0 = 1.0e6;

  // ---------- Раскладка окна ----------
  WinWidth  = 1240;
  WinHeight = 660;
  AnimX     = 10;
  AnimY     = 40;
  AnimSize  = 600;
  PlotX     = 630;
  PlotY     = 40;
  PlotW     = 590;
  PlotH     = 600;

var
  dt: Real;
  Ex, Ey, Ez, Hx, Hy, Hz: array of Real;
  eps_r, mu_r: array of Real;
  src_i, src_j, src_k, slice_z: integer;
  W, H: integer;
  E_hist: array of Real;   // история полной энергии

// ---------- Индексаторы Yee-сетки ----------
function idxEx(i, j, k: integer): integer;
begin Result := (i * (Ny - 1) + j) * (Nz - 1) + k; end;
function idxEy(i, j, k: integer): integer;
begin Result := (i * Ny + j) * (Nz - 1) + k; end;
function idxEz(i, j, k: integer): integer;
begin Result := (i * (Ny - 1) + j) * Nz + k; end;
function idxHx(i, j, k: integer): integer;
begin Result := (i * Ny + j) * Nz + k; end;
function idxHy(i, j, k: integer): integer;
begin Result := (i * (Ny - 1) + j) * Nz + k; end;
function idxHz(i, j, k: integer): integer;
begin Result := (i * Ny + j) * (Nz - 1) + k; end;
function idxEps(i, j, k: integer): integer;
begin Result := (i * Ny + j) * Nz + k; end;

// ---------- Источник ----------
function source(n: integer): Real;
begin
  Result := Exp(-Sqr((n - t0) / sigma_t));
end;

// ---------- PEC: E_tang = 0 на нижних гранях ----------
procedure ApplyPEC;
var i, j, k: integer;
begin
  for j := 0 to Ny - 1 do
    for k := 0 to Nz - 2 do
      Ey[idxEy(0, j, k)] := 0;
  for j := 0 to Ny - 2 do
    for k := 0 to Nz - 1 do
      Ez[idxEz(0, j, k)] := 0;

  for i := 0 to Nx - 1 do
    for k := 0 to Nz - 2 do
      Ex[idxEx(i, 0, k)] := 0;
  for i := 0 to Nx - 2 do
    for k := 0 to Nz - 1 do
      Ez[idxEz(i, 0, k)] := 0;

  for i := 0 to Nx - 1 do
    for j := 0 to Ny - 2 do
      Ex[idxEx(i, j, 0)] := 0;
  for i := 0 to Nx - 2 do
    for j := 0 to Ny - 1 do
      Ey[idxEy(i, j, 0)] := 0;
end;

// ---------- Полная энергия ----------
function TotalEnergy: Real;
var i, j, k: integer; sum: Real;
begin
  sum := 0;
  for i := 0 to Nx - 1 do
    for j := 0 to Ny - 2 do
      for k := 0 to Nz - 2 do
        sum += eps0 * Sqr(Ex[idxEx(i,j,k)]);
  for i := 0 to Nx - 2 do
    for j := 0 to Ny - 1 do
      for k := 0 to Nz - 2 do
        sum += eps0 * Sqr(Ey[idxEy(i,j,k)]);
  for i := 0 to Nx - 2 do
    for j := 0 to Ny - 2 do
      for k := 0 to Nz - 1 do
        sum += eps0 * Sqr(Ez[idxEz(i,j,k)]);
  for i := 0 to Nx - 2 do
    for j := 0 to Ny - 1 do
      for k := 0 to Nz - 1 do
        sum += mu0 * Sqr(Hx[idxHx(i,j,k)]);
  for i := 0 to Nx - 1 do
    for j := 0 to Ny - 2 do
      for k := 0 to Nz - 1 do
        sum += mu0 * Sqr(Hy[idxHy(i,j,k)]);
  for i := 0 to Nx - 1 do
    for j := 0 to Ny - 1 do
      for k := 0 to Nz - 2 do
        sum += mu0 * Sqr(Hz[idxHz(i,j,k)]);
  Result := 0.5 * sum * dx * dy * dz;
end;

// ---------- max |div E| ----------
function MaxDivergenceE: Real;
var i, j, k: integer; d, maxd: Real;
begin
  maxd := 0;
  for i := 1 to Nx - 2 do
    for j := 1 to Ny - 2 do
      for k := 1 to Nz - 2 do
      begin
        d := (Ex[idxEx(i+1,j,k)] - Ex[idxEx(i,j,k)]) / dx
           + (Ey[idxEy(i,j+1,k)] - Ey[idxEy(i,j,k)]) / dy
           + (Ez[idxEz(i,j,k+1)] - Ez[idxEz(i,j,k)]) / dz;
        if Abs(d) > maxd then maxd := Abs(d);
      end;
  Result := maxd;
end;

// ---------- Срез Ez (рисует только свою панель) ----------
procedure DrawSlice(frameArr: array of Real; x0, y0, sz: integer);
var
  i, j, cell: integer;
  val, norm: Real;
  col: Color;
begin
  norm := 0.0;
  for i := 0 to W * H - 1 do
    if Abs(frameArr[i]) > norm then norm := Abs(frameArr[i]);
  if norm = 0 then norm := 1.0;

  cell := sz div W;
  if cell < 1 then cell := 1;

  for i := 0 to W - 1 do
    for j := 0 to H - 1 do
    begin
      val := frameArr[i * H + j] / norm;
      if val < 0 then
        col := RGB(0, 0, 255 + Round(255 * val))
      else
        col := RGB(255, 255 - Round(255 * val), 0);
      Brush.Color := col;
      FillRect(x0 + i * cell, y0 + j * cell,
               x0 + (i + 1) * cell, y0 + (j + 1) * cell);
    end;

  Font.Color := clBlack;
  Font.Size := 11;
  TextOut(x0 + 5, y0 - 22, 'Срез Ez, z = ' + IntToStr(slice_z));
end;

// ---------- График энергии W(t) ----------
procedure DrawEnergyPlot(E_hist: array of Real; nCur, x0, y0, w, h: integer);
var
  i, px, py: integer;
  Emax, frac: Real;
  marginL, marginB, marginT, marginR, plotW, plotH: integer;
begin
  marginL := 60;
  marginB := 40;
  marginT := 25;
  marginR := 20;
  plotW := w - marginL - marginR;
  plotH := h - marginT - marginB;

  Emax := 1e-30;
  for i := 0 to nCur do
    if E_hist[i] > Emax then Emax := E_hist[i];
  if Emax <= 0 then Emax := 1;

  // Оси
  Pen.Width := 1;
  Pen.Color := clBlack;
  Line(x0 + marginL, y0 + marginT, x0 + marginL, y0 + marginT + plotH);
  Line(x0 + marginL, y0 + marginT + plotH,
       x0 + marginL + plotW, y0 + marginT + plotH);

  Font.Color := clBlack;
  Font.Size := 9;

  // Y-метки: 0%, 25%, 50%, 75%, 100%
  for i := 0 to 4 do
  begin
    frac := i / 4;
    py := y0 + marginT + plotH - Round(plotH * frac);
    Line(x0 + marginL - 5, py, x0 + marginL, py);
    TextOut(x0 + 10, py - 7, IntToStr(Round(frac * 100)) + '%');
  end;

  // X-метки
  for i := 0 to 4 do
  begin
    px := x0 + marginL + Round(plotW * i / 4);
    Line(px, y0 + marginT + plotH, px, y0 + marginT + plotH + 5);
    TextOut(px - 12, y0 + marginT + plotH + 8,
            IntToStr(Round((steps - 1) * i / 4)));
  end;

  // Кривая
  Pen.Width := 2;
  Pen.Color := clRed;
  for i := 0 to nCur do
  begin
    px := x0 + marginL + Round(plotW * i / (steps - 1));
    py := y0 + marginT + plotH - Round(plotH * E_hist[i] / Emax);
    if i = 0 then MoveTo(px, py) else LineTo(px, py);
  end;

  // «Бегунок» текущего шага
  if nCur > 0 then
  begin
    Pen.Width := 1;
    Pen.Color := clBlue;
    px := x0 + marginL + Round(plotW * nCur / (steps - 1));
    Line(px, y0 + marginT, px, y0 + marginT + plotH);
  end;

  // Заголовок и абсолютный максимум
  Font.Color := clBlack;
  Font.Size := 11;
  TextOut(x0 + marginL, y0 + 2, 'Полная энергия W(t)');
  Font.Size := 9;
  TextOut(x0 + marginL + 5, y0 + marginT + 5, Format('W_max = {0:E2}', Emax));
  TextOut(x0 + marginL + plotW div 2 - 25, y0 + h - 15, 'номер шага');
end;

// ======================== ОСНОВНАЯ ПРОГРАММА ========================
begin
  SetWindowSize(WinWidth, WinHeight);
  Window.Title := 'FDTD 3D PEC: срез Ez и энергия';

  dt := 0.95 * dx / (Sqrt(3.0) * c);
  WriteLn('CFL: dt = ', dt, '  c*dt/dx = ', c*dt/dx,
          '  (предел 1/sqrt(3) = 0.5774)');

  src_i := Nx div 2;
  src_j := Ny div 2;
  src_k := Nz div 2;
  slice_z := Nz div 2;

  SetLength(Ex, Nx * (Ny - 1) * (Nz - 1));
  SetLength(Ey, (Nx - 1) * Ny * (Nz - 1));
  SetLength(Ez, (Nx - 1) * (Ny - 1) * Nz);
  SetLength(Hx, (Nx - 1) * Ny * Nz);
  SetLength(Hy, Nx * (Ny - 1) * Nz);
  SetLength(Hz, Nx * Ny * (Nz - 1));
  SetLength(eps_r, Nx * Ny * Nz);
  SetLength(mu_r,  Nx * Ny * Nz);
  SetLength(E_hist, steps);

  for var i := 0 to High(eps_r) do
  begin
    eps_r[i] := 1.0;
    mu_r[i]  := 1.0;
  end;

  W := Nx - 1;
  H := Ny - 1;
  var frameArr := new Real[W * H];

  // ================= Временной цикл =================
  for var n := 0 to steps - 1 do
  begin
    // ---------- Обновление H ----------
    for var i := 1 to Nx - 2 do
      for var j := 1 to Ny - 2 do
        for var k := 1 to Nz - 2 do
        begin
          var dEz_dy := (Ez[idxEz(i, j, k)] - Ez[idxEz(i, j - 1, k)]) / dy;
          var dEy_dz := (Ey[idxEy(i, j, k)] - Ey[idxEy(i, j, k - 1)]) / dz;
          var mu := mu0 * mu_r[idxEps(i, j, k)];
          Hx[idxHx(i - 1, j, k)] += -(dEz_dy - dEy_dz) / mu * dt;
        end;

    for var i := 1 to Nx - 2 do
      for var j := 1 to Ny - 2 do
        for var k := 1 to Nz - 2 do
        begin
          var dEx_dz := (Ex[idxEx(i, j, k)] - Ex[idxEx(i, j, k - 1)]) / dz;
          var dEz_dx := (Ez[idxEz(i, j, k)] - Ez[idxEz(i - 1, j, k)]) / dx;
          var mu := mu0 * mu_r[idxEps(i, j, k)];
          Hy[idxHy(i, j - 1, k)] += -(dEx_dz - dEz_dx) / mu * dt;
        end;

    for var i := 1 to Nx - 2 do
      for var j := 1 to Ny - 2 do
        for var k := 1 to Nz - 2 do
        begin
          var dEy_dx := (Ey[idxEy(i, j, k)] - Ey[idxEy(i - 1, j, k)]) / dx;
          var dEx_dy := (Ex[idxEx(i, j, k)] - Ex[idxEx(i, j - 1, k)]) / dy;
          var mu := mu0 * mu_r[idxEps(i, j, k)];
          Hz[idxHz(i, j, k - 1)] += -(dEy_dx - dEx_dy) / mu * dt;
        end;

    // ---------- Обновление E ----------
    for var i := 0 to Nx - 1 do
      for var j := 0 to Ny - 2 do
        for var k := 0 to Nz - 2 do
        begin
          var dHz_dy := (Hz[idxHz(i, j + 1, k)] - Hz[idxHz(i, j, k)]) / dy;
          var dHy_dz := (Hy[idxHy(i, j, k + 1)] - Hy[idxHy(i, j, k)]) / dz;
          var eps := eps0 * eps_r[idxEps(i, j, k)];
          Ex[idxEx(i, j, k)] += (dHz_dy - dHy_dz) / eps * dt;
          if (i = src_i) and (j = src_j - 1) and (k = src_k - 1) then
            Ex[idxEx(i, j, k)] += dt * J0 * source(n) / eps;
        end;

    for var i := 0 to Nx - 2 do
      for var j := 0 to Ny - 1 do
        for var k := 0 to Nz - 2 do
        begin
          var dHx_dz := (Hx[idxHx(i, j, k + 1)] - Hx[idxHx(i, j, k)]) / dz;
          var dHz_dx := (Hz[idxHz(i + 1, j, k)] - Hz[idxHz(i, j, k)]) / dx;
          var eps := eps0 * eps_r[idxEps(i, j, k)];
          Ey[idxEy(i, j, k)] += (dHx_dz - dHz_dx) / eps * dt;
        end;

    for var i := 0 to Nx - 2 do
      for var j := 0 to Ny - 2 do
        for var k := 0 to Nz - 1 do
        begin
          var dHy_dx := (Hy[idxHy(i + 1, j, k)] - Hy[idxHy(i, j, k)]) / dx;
          var dHx_dy := (Hx[idxHx(i, j + 1, k)] - Hx[idxHx(i, j, k)]) / dy;
          var eps := eps0 * eps_r[idxEps(i, j, k)];
          Ez[idxEz(i, j, k)] += (dHy_dx - dHx_dy) / eps * dt;
        end;

    ApplyPEC;

    // ---------- Энергия на каждом шаге ----------
    E_hist[n] := TotalEnergy;

    // ---------- Диагностика и отрисовка ----------
    if (n mod snap_every = 0) or (n = steps - 1) then
    begin
      WriteLn('n=', n:4,
              '  W=', E_hist[n]:0:6,
              '  max|divE|=', MaxDivergenceE:0:6);

      for var i := 0 to W - 1 do
        for var j := 0 to H - 1 do
          frameArr[i * H + j] := Ez[idxEz(i, j, slice_z)];

      LockDrawing;
      ClearWindow(clWhite);
      DrawSlice(frameArr, AnimX, AnimY, AnimSize);
      DrawEnergyPlot(E_hist, n, PlotX, PlotY, PlotW, PlotH);
      Redraw;
      Sleep(10);
    end;
  end;

  WriteLn('Расчёт завершён.');
end.

В данном коде решение реализовано на сетке Йи — это специальная разнесённая (смещённая) сетка для численного решения уравнений Максвелла методом FDTD. Её придумал Кейн Йи (Kane Yee) в 1966 году.

Её суть в следующем: В обычной сетке все компоненты поля (Ex, Ey, Ez, Hx, Hy, Hz) ставили бы в одни и те же узлы. В сетке Йи их разносят по пространству на полшага и ещё сдвигают по времени.

Поля считают «в шахматном порядке» по времени:

  • Сначала считают En на шаге n

  • Потом считают Hn+1/2 на промежуточном шаге

  • Потом снова En+1 и т. д.

Это и есть алгоритм «leapfrog» («перескок»).

Следует оговорить, что "схема Йи немонотонна- на границах раздела диэлектриков фактический порядок точности падает до первого." Это показано в статье 2017 года https://elibrary.ru/item.asp?edn=vvfyhz Домбровской Ж. О. и Боголюбова А. Н.

Схема Йи консервативна в дискретном смысле — это её классическое свойство, отмеченное ещё в пионерской работе Йи и подробно разбираемое в монографиях по FDTD (смотрите, например, Taflove A., Hagness S. C. Computational Electrodynamics: the Finite–Difference Time–Domain Method. Artech House, 2005. ) Косвенно это устанавливается с помощью контроля энергии на каждом шаге, что я захотел продемонстрировать в своей работе.

Результат работы скрипта- довольно красивая анимация распространения ЭМ-волн (срез Ez):

Срез Ez. Гауссов сигнал. Код до отладки.

Срез Ez. Гауссов сигнал. Код до отладки.

Что мы видим (динамика распространения волны во времени)- на изначально жёлтом фоне из центра появляется и медленно нарастает верхний треугольник из сине-жёлтых маленьких квадратиков- это шахматный узор. На верхнем треугольнике мы видим красную целостную зону овальной формы, которая по мере приближения к левому верхнему углу расплывается и растягивается, а затем исчезает и остаётся только в нескольких ячейках к концу. Также наблюдается волна, которая в процессе анимации представлена в виде чередующейся (поочерёдно меняются жёлтый и синий цвета) кривой линии.По мере приближения к левому верхнему углу кривая растягивается, затем уходит за угол, оставляя только линию, стремящуюся к правому верхнему углу. При этом нижнетреугольная область остаётся жёлтой во время всей анимации.

Сравним с описаниями из литературы данную анимацию:

Наблюдение

Что должно быть по литературе

Обоснование с точки зрения физики

Вердикт и источник

Фронт расходится из центра

Гауссов импульс из центра распространяется наружу — классическая картинка (Салливан, гл. 1 и 3)

Главный лепесток гауссова импульса расходится от точечного источника. Корректно

✓ https://lshoshia.science.tsu.ge/fdtd/Dennissullivan-fixed.pdf

Жёлтое и синее чередуются

У гауссова импульса есть положительная и отрицательная полуволны: передний фронт и хвост противоположного знака

В FDTD-литературе такие поля визуализируются двухцветной картой: красный — положительные значения Ez, синий — отрицательные.

✓ https://www.researchgate.net/publication/5313575_Amplified_total_internal_reflection_theory_analysis_and_demonstration_of_existence_via_FDTD

Овал растягивается и расплывается к углу

Численная дисперсия: высокочастотные компоненты отстают, фронт из сферы становится анизотропным

Фазовая скорость в Yee-сетке анизотропна; при S ≈ 0,55 фронт приобретает ромбовидную форму, максимум скорости — вдоль телесной диагонали.

~ , https://www.academia.edu/112873112/Reduction_of_numerical_dispersion_in_FDTD_method_through_artificial_anisotropy

Растяжение и уход «за угол»

Передний край пакета движется быстрее номинала вблизи Найквиста; вдоль диагонали пакет приходит раньше всего

Скорость вдоль диагонали максимальна (S ≈ 0,55), пакет достигает угла раньше всего; далее наблюдается мода, бегущая вдоль стенки — авторская интерпретация, требует отдельной проверки

~ https://www.researchgate.net/publication/3307223_FDTD_dispersion_revisited_faster-than-light_propagation

Красный овал возле центра — ближняя зона

Дискретный диполь на сетке Йи анизотропен, ближнее поле локализовано в нескольких ячейках

Источник — диполь по x, срез Ez проходит в 1,5 ячейки от него; дальняя Ez на таком срезе подавлена (∝ n·r̂), доминирует ближнее поле. Аналогия: анизотропия точечного диполя на Йи показана в статье 2025 г. (там — резонансные диполи, эффект Пёрселла)

✓https://arxiv.org/html/2504.07444

Нижняя половина жёлтая всё время

До прихода отражений картина обязана быть зеркально симметричной относительно источника

Расхождение с физикой и литературой. Подтверждено репликацией схемы: нулевой треугольник ограничен антидиагональю i + j = 99 через ячейку источника — структурный дефект индексации, а не свойство метода

✗ —

К концу остаётся несколько красных ячеек

Остаточное поле не должно доминировать в кадре

Подтверждено расчётом: обрезанный гауссиан депонирует паразитный заряд (сумма источника по времени ≈ 17,7 ≠ 0), который держит статическое поле; по-кадровая нормировка растягивает его на всю палитру. Плюс артефакт выбора среза/компоненты

~ — расчёт по коду

После отладки кода я добавил график энергии во времени:

Отладка кода+ график энергии.

Отладка кода+ график энергии.

Выводы:

Реализация FDTD‑модели в PascalABC.NET показала, что даже сравнительно сложные физические задачи можно решать и демонстрировать средствами "устаревшего" языка программирования. Использование модуля GraphABC позволило отказаться от внешней визуализации и свести весь пайплайн к одному файлу: от постановки задачи до наглядного результата.

Особое внимание при реализации было уделено корректности индексации на сетке Йи и соблюдению условия устойчивости CFL.

Код получился компактным и прозрачным: его можно использовать как шаблон для дальнейших экспериментов — например, добавить неоднородности среды (для неоднородной среды требуется доработка: усреднение ε и μ на Yee-сетке, иначе схема перестаёт быть консервативной), PML‑слои или другие типы источников.

Автор не имеет специального физико‑математического образования и не является профессиональным физиком!

Если такое дадут на ЕГЭ это будет крах всей системы, я реально занервничал!

Литература:

1.https://ru.wikipedia.org/wiki/Уравнения_Максвелла

2.https://djvu.online/file/NWX9BlUqYyVBq (Ландау и Лифшиц. 2 том "Теория поля").

3.https://en.wikipedia.org/wiki/Pulse_(signal_processing)

4.https://jenyay.net/uploads/Student/Student/13. FDTD-2D.pdf?ysclid=mt9ymb0u2132710910

5.https://cyberleninka.ru/article/n/metodika-analiza-elektromagnitnoy-sovmectimosti-pechatnyh-moduley-s-pomoschyu-metoda-konechnyh-raznostey-vo-vremennoy-oblasti/viewer

6.https://komisc.editorum.ru/ru/nauka/article/86245/view?ysclid=mt9ynlsb4666712194

7.https://makarovpa.ru/files/CTMM-2025.pdf

8.https://elibrary.ru/download/elibrary_20799764_92247445.pdf

9.https://elibrary.ru/vvfyhz

10.Taflove A., Hagness S. C. Computational Electrodynamics: the Finite–Difference Time–Domain Method. Artech House, 2005

Автор: Maximka200

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js