В этом листке-проекты мы создадим и визуализируем модель двумерного газа.
Также мы поставим над этим газом некоторые эксперименты: посмотрим на броуновское движение, диффузию и т.п.
Делать визуальную модель мы будем при помощи библиотеки pygame.
Для того, чтобы её установить, нужно выполнить команду pip install pygame --user в anaconda prompt или терминале.
В Windows с большой вероятностью команда потерпит неудачу из-за отсутсвия компилятора языка C.
Поэтому можно сразу отправиться на страницу собранных для windows пакетов и скачать оттуда подходящий whl-файл (в имени pygame‑1.9.4‑cp36‑cp36m‑win_amd64.whl 36 — это версия питона 3.6, win32/win_amd64 — это битность ОС).
Дальше для установки пакета нужно будет из anaconda prompt или терминала выполнить команду вида
pip install "Y:\path\to\whl\pygame‑1.9.4‑cp36‑cp36m‑win_amd64.whl" --user
и дело в шляпе!
Библиотека pygame достаточно обширная, но нам от неё будет нужно не слишком много.
Почти всё, что нужно, это — рисование элементарных фигур.
Остальное вы будете делать руками.
from random import randint, uniform
import pygame
import sys
SCREEN_SIZE = WIDTH, HEIGHT = (600, 400)
# Готовим 100 шариков в случайных местах случайного радиуса и цвета
N = 100
circs = [
{
'xy': [uniform(0, WIDTH), uniform(0, HEIGHT)],
'r': randint(2, 10),
'color': [randint(0, 255), randint(0, 255), randint(0, 255)]
}
for __ inrange(N)]
circs.sort(key=lambda circ: -circ['r']) # Сортируем, чтобы большие не заслоняли маленьких# Немного pygame-магии
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
fps = pygame.time.Clock()
# Обновляем коодинаты всех шариковdefupdate():
for circ in circs:
circ['xy'][0] += uniform(-5, 5) / circ['r']
circ['xy'][1] += uniform(-5, 5) / circ['r']
# Чистим экран и отрисовываем каждый шарикdefrender():
screen.fill((0, 0, 0)) # Заливаем всё чёрнымfor circ in circs:
pygame.draw.circle(screen, circ['color'], list(map(int, circ['xy'])), circ['r'], 0)
pygame.display.update()
fps.tick(60) # Не обновляем экран чаще, чем 60 раз в секунду# Главный цикл: пока на нажали крестик обновляем и отрисовываемwhileTrue:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
update()
render()
Пусть масса первого шарика — m1, радиус — r1,
его скорость до столкновения — v1=(v1x,v1y),
скорость после столкновения — w1=(w1x,w1y).
А ещё обозначим через c1 вектор от точки контакта шаров до центра первого шара.
Для второго шарика всё то же самое, только с индексом 2.
Тогда закон сохранения момента требует, чтобы
m1v1+m2v2=m1w1+m2w2.
Закон сохранения энергии требует, чтобы
2m1∣v1∣2+2m2∣v2∣2=2m1∣w1∣2+2m2∣w2∣2.
Закон сохранения момента импульса более хитрый. Он требует, чтобы
[c1,m1v1]+[c2,m2v2]=[c1,m1w1]+[c2,m2w2].
Всё вместе это даёт систему из 4 уравнений на 4 неизвестных — x- и y-компоненты новых скоростей шариков.
К счастью, если массы и радиусы шаров равны, то решать эту систему не потребуется.
Перейдём в систему отсчёта первого шарика.
В ней он покоится, а второй шарик натыкается на него со скоростью v2−v1.
После столкновения вся часть этой скорости, направленная вдоль линии центров, достанется первому шарику, а ортогональная ей — второму.
То есть после столкновения первый шар полетит вдоль линии центров, а второй — в направлении касательной.
Чтобы доказать это, пригодится система отсчёта общего центра масс, двигающая со скоростью
m1+m2m1v1+m2v2=2v1+v2.
Центр масс в ней неподвижен.
Дальше попробуйте разобраться сами.
Рассчитайте столкновение шаров произвольного радиуса и произвольной массы.
Можно либо решить систему уравнений (сложно), либо предварительно дополнительно пошаманить с системами отсчёта (станет проще),
либо ещё подумать и сразу написать ответ без решения системы.
На вход даются:
масса первого шара m1,
масса второго шара m2,
пара чисел — скорость первого шара v1,
пара чисел — скорость второго шара v2,
пара чисел — координаты вектора из точки контакта в центр первого шара c1,
пара чисел — координаты вектора из точки контакта в центр второго шара c2.
Вычислите скорости первого и второго шара после их упругого столкновения.
Добавьте возможность расположить в поле произвольное количество прямолинейных перегородок. От них шарики-молекулы тоже должны отскакивать.
Границы «сосуда» теперь можно сделать из этих перегородок.
Сделайте «сосуд», состоящий из двух одинаковых камер.
В перегородке между камерами должно быть маленькое отверстие.
В левую камеру поместите «горячие» красные молекулы.
В правую камеру поместите очень-очень холодные синие молекулы.
Проведите несколько экспериментов: с разной разницей «температур» и разным размером отверстия.
В этом эксперименте мы будем изучать сопло ракетного двигателя.
Сам двигатель в этой задаче — это просто квадрат с маленьким отверстием, смотрящим направо.
Внуть квадрата нужно поместить много-много очень-очень горячих молекул.
В этом эксперименте можно отключить взаимодействия молекул друг с другом, оставив только взаимодействия со стенками.
После того, как половина молекул вылетет из «двигателя», симуляцию нужно остановить и посчитать среднюю проекцию вылетевших молекул на ось Ox
(так как только эта часть момента нужна ракете).
Попробуйте приделать к отверстию «сопло», для начала просто рупор.
И сравнить результат.
Попробуйте объяснить, почему это происходит.
Попробуйте «изобразить» сопло Лаваля и провести эксперимент из предыдущей задачи для него.
Скорее всего, ничего интересного не получится, но вдруг?
Это было бы очень круто :)
Вернёмся к обычному «ящику» с молекулами.
Отрисовывайте на каждом кадре распределение молекул по модулю скоростей.
Да-да, график придётся рисовать «руками» по пикселям.
Изучите типичные скорости в вашем газе.
Разбейте диапазон от 0 до максимальной скорости на, скажем, 100 частей.
Посчитайте для каждой, сколько молекул имеют модуль скорости из этой части.
И нарисуйте соответствующую гистрограмму.
Изучите её вид при разной «температуре» газа.
Добавьте в высокий-высокий ящик гравитацию.
Разбейте правую стенку сосуда, скажем, на 100 частей.
Для каждой посчитайте «давление».
И нарисуйте гистрограмму.