Двумерный газ
В этом листке-проекты мы создадим и визуализируем модель двумерного газа. Также мы поставим над этим газом некоторые эксперименты: посмотрим на броуновское движение, диффузию и т.п.
В этом листке-проекты мы создадим и визуализируем модель двумерного газа. Также мы поставим над этим газом некоторые эксперименты: посмотрим на броуновское движение, диффузию и т.п.
Делать визуальную модель мы будем при помощи библиотеки 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 __ in range(N)] circs.sort(key=lambda circ: -circ['r']) # Сортируем, чтобы большие не заслоняли маленьких # Немного pygame-магии pygame.init() screen = pygame.display.set_mode(SCREEN_SIZE) fps = pygame.time.Clock() # Обновляем коодинаты всех шариков def update(): for circ in circs: circ['xy'][0] += uniform(-5, 5) / circ['r'] circ['xy'][1] += uniform(-5, 5) / circ['r'] # Чистим экран и отрисовываем каждый шарик def render(): 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 раз в секунду # Главный цикл: пока на нажали крестик обновляем и отрисовываем while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() update() render()
Модифицируйте программу так, чтобы все круги двигались прямолинейно со случайными скоростями, зафиксированными на момент старта.
Сделайте так, чтобы шарики из предыдущей задачи отскакивали от стенок:
Будем рассматривать только одновременное столкновение ровно двух шариков. Чтобы честно всё посчитать, потребуются закон сохранения импульса, закон сохранения энергии и закон сохранения момента импульса.
Пусть масса первого шарика — $m_1$, радиус — $r_1$, его скорость до столкновения — $\vec{v_1} = (v_{1x}, v_{1y})$, скорость после столкновения — $\vec{w_1} = (w_{1x}, w_{1y})$. А ещё обозначим через $\vec{c_1}$ вектор от точки контакта шаров до центра первого шара. Для второго шарика всё то же самое, только с индексом 2. Тогда закон сохранения момента требует, чтобы $$ m_1 \vec{v_1} + m_2 \vec{v_2} = m_1 \vec{w_1} + m_2 \vec{w_2}.$$ Закон сохранения энергии требует, чтобы $$ \frac{m_1 |\vec{v_1}|^2}{2} + \frac{m_2 |\vec{v_2}|^2}{2} = \frac{m_1 |\vec{w_1}|^2}{2} + \frac{m_2 |\vec{w_2}|^2}{2}. $$ Закон сохранения момента импульса более хитрый. Он требует, чтобы $$ [\vec{c_1}, m_1\vec{v_1}] + [\vec{c_2}, m_2\vec{v_2}] = [\vec{c_1}, m_1\vec{w_1}] + [\vec{c_2}, m_2\vec{w_2}]. $$ Всё вместе это даёт систему из 4 уравнений на 4 неизвестных — $x$- и $y$-компоненты новых скоростей шариков.
К счастью, если массы и радиусы шаров равны, то решать эту систему не потребуется. Перейдём в систему отсчёта первого шарика. В ней он покоится, а второй шарик натыкается на него со скоростью $\vec{v_2}-\vec{v_1}$. После столкновения вся часть этой скорости, направленная вдоль линии центров, достанется первому шарику, а ортогональная ей — второму. То есть после столкновения первый шар полетит вдоль линии центров, а второй — в направлении касательной.
Чтобы доказать это, пригодится система отсчёта общего центра масс, двигающая со скоростью $\dfrac{m_1\vec{v_1}+m_2\vec{v_2}}{m_1+m_2}=\dfrac{\vec{v_1}+\vec{v_2}}{2}$. Центр масс в ней неподвижен. Дальше попробуйте разобраться сами.
Реализуйте столкновение одинаковых шаров. Алгорим, работающий за время $O(n^2)$, где $n$ — число шаров, подойдёт.
Реализуйте столкновение шаров разного радиуса, но одинаковой массы. Алгорим, работающий за время $O(n^2)$, где $n$ — число шаров, подойдёт.
Рассчитайте столкновение шаров произвольного радиуса и произвольной массы. Можно либо решить систему уравнений (сложно), либо предварительно дополнительно пошаманить с системами отсчёта (станет проще), либо ещё подумать и сразу написать ответ без решения системы.
На вход даются:
Вычислите скорости первого и второго шара после их упругого столкновения.
Реализуйте столкновение шаров произвольного радиуса и произвольной массы.
Сделайте модель броуновского движения.
Добавьте возможность расположить в поле произвольное количество прямолинейных перегородок. От них шарики-молекулы тоже должны отскакивать. Границы «сосуда» теперь можно сделать из этих перегородок.
Сделайте «сосуд», состоящий из двух одинаковых камер. В перегородке между камерами должно быть маленькое отверстие. В левую камеру поместите «горячие» красные молекулы. В правую камеру поместите очень-очень холодные синие молекулы. Проведите несколько экспериментов: с разной разницей «температур» и разным размером отверстия.
В этом эксперименте мы будем изучать сопло ракетного двигателя. Сам двигатель в этой задаче — это просто квадрат с маленьким отверстием, смотрящим направо. Внуть квадрата нужно поместить много-много очень-очень горячих молекул. В этом эксперименте можно отключить взаимодействия молекул друг с другом, оставив только взаимодействия со стенками. После того, как половина молекул вылетет из «двигателя», симуляцию нужно остановить и посчитать среднюю проекцию вылетевших молекул на ось $Ox$ (так как только эта часть момента нужна ракете). Попробуйте приделать к отверстию «сопло», для начала просто рупор. И сравнить результат. Попробуйте объяснить, почему это происходит.
Попробуйте «изобразить» сопло Лаваля и провести эксперимент из предыдущей задачи для него. Скорее всего, ничего интересного не получится, но вдруг? Это было бы очень круто :)
Вернёмся к обычному «ящику» с молекулами. Отрисовывайте на каждом кадре распределение молекул по модулю скоростей. Да-да, график придётся рисовать «руками» по пикселям. Изучите типичные скорости в вашем газе. Разбейте диапазон от 0 до максимальной скорости на, скажем, 100 частей. Посчитайте для каждой, сколько молекул имеют модуль скорости из этой части. И нарисуйте соответствующую гистрограмму. Изучите её вид при разной «температуре» газа.
Добавьте в высокий-высокий ящик гравитацию. Разбейте правую стенку сосуда, скажем, на 100 частей. Для каждой посчитайте «давление». И нарисуйте гистрограмму.