Волны на струне: конечные разности
Уравнение волны на сетке: как колебание бежит по струне.
Волновое уравнение
u_tt = c²·u_xxописывает распространение возмущения со скоростью c; на компьютере его решают методом конечных разностей по сетке.
От струны к сетке
Натянутая струна (гитарная, скрипичная) подчиняется волновому уравнению: ускорение каждой точки пропорционально кривизне струны в этой точке. Чтобы решить его численно, разбиваем струну на узлы сетки и заменяем вторые производные конечными разностями: по времени u_tt ≈ (u_новое - 2u + u_старое)/dt², по пространству u_xx ≈ (u_справа - 2u + u_слева)/dx².
Приравняв их, получаем явную формулу: новое положение каждого узла выражается через его текущее, прошлое и положения соседей. Это рабочая лошадка — метод конечных разностей, применимый к любому уравнению в частных производных.
Симуляция: щипок разбегается в две стороны
Зададим начальный «щипок» (узкий пик в центре струны с закреплёнными концами) и посмотрим, как он эволюционирует. Высоту струны покажем символами разной плотности:
N = 21
c = 1.0
dx = 1.0/(N-1)
dt = 0.5*dx/c
r2 = (c*dt/dx)**2
u = [0.0]*N
for i in range(N):
x = i/(N-1)
u[i] = max(0.0, 1 - abs(4*x-2))
u_prev = u[:]
def show(u, label):
chars = " .:-=+*#"
line = "".join(chars[int(min(7, max(0, round(abs(v)*7))))] for v in u)
print(f"{label:12s}|{line}|")
show(u, "t=0")
for step in range(1, 21):
un = [0.0]*N
for i in range(1, N-1):
un[i] = 2*u[i]-u_prev[i]+r2*(u[i+1]-2*u[i]+u[i-1])
u_prev, u = u, un
if step in (5,10,15,20):
show(u, f"t={step}")
print("Пик расщепился на две бегущие волны.")Вывод:
t=0 | .-=*#*=-. | t=5 | .:--=---=--:. | t=10 | .::---:.. ..:---::. | t=15 | :--:.. ..:--: | t=20 | | Пик расщепился на две бегущие волны.
Начальный пик в центре расщепился на две одинаковые волны, разбегающиеся в противоположные стороны к закреплённым концам. К t=20 они уже достигли краёв. Если продолжить, волны отразятся от концов и побегут обратно, создавая интерференцию. Так гитарная струна, оттянутая в одной точке, рождает бегущие и отражённые волны, складывающиеся в стоячую — ноту.
Условие устойчивости (CFL)
Здесь нельзя брать произвольный шаг времени. Условие Куранта-Фридрихса-Леви (CFL): c·dt/dx ≤ 1. Физический смысл прост: за один шаг времени волна не должна пробегать больше одной ячейки сетки. Нарушишь — и симуляция взрывается за несколько шагов, числа уходят в бесконечность. В коде мы взяли dt = 0.5·dx/c — с запасом внутри устойчивой зоны. Это не точность, а устойчивость: превышение CFL рушит счёт мгновенно.
Как работает под капотом
Волновое уравнение связывает время и пространство через одну скорость c. Конечно-разностная схема локальна: каждый узел «общается» только с непосредственными соседями за один шаг. Информация распространяется по сетке ровно на одну ячейку за шаг — отсюда и условие CFL. Этот же метод (вторая разность по пространству плюс шаг по времени) лежит в основе симуляций сейсмических волн, акустики, электромагнитных полей (метод FDTD) и многого другого. Освоив струну, вы освоили универсальный инструмент.
Частые ошибки
- Нарушить условие CFL. Слишком большой
dtмгновенно взрывает симуляцию — это не баг, а неустойчивость схемы. - Забыть граничные условия. Закреплённые концы (
u=0) или свободные ведут себя по-разному; края надо обрабатывать явно. - Перезаписать массив на месте. Новое значение узла зависит от текущих соседей; нужен отдельный массив для следующего слоя.
Итоги
- Волновое уравнение решают методом конечных разностей по сетке.
- Новое положение узла — через текущее, прошлое и соседей.
- Щипок расщепляется на две волны; отражения дают стоячую волну.
- Условие CFL
c·dt/dx ≤ 1обязательно для устойчивости.