Реакции с диффузией очень широко используются в компьютерной графике и других областях. Однако, найти по ним информацию (особенно на русском языке) достаточно сложно. Все источники либо сосредотачиваются на химических аспектах (а они весьма замысловаты), либо освещают вопрос очень поверхностно.
Здесь я хочу привести работающий, законченный кусок кода, которым можно поиграться. Кроме того, реализация алгоритмов реакций с диффузией позволяет показать всё мощь numpy.
Мы будем рассматривать очень простую модель (самую простую). В ней есть несколько упрощений, делающие её не очень физичной, но сильно упрощающих все выкладки.
Итак.
Математически это можно записать так:
A' = (Da · ∆A - A·B² + F(1 - A)) · dt
B' = (Db · ∆B + A·B² + (K + F) · B) · dt
Эти выражения описывают, как изменятся A и B через время dt:
#!/usr/bin/python3
# ffmpeg -i foo%04d.png -c:v libx264 -r 30 -pix_fmt yuv420p out.mp4
# или со звуком:
# ffmpeg -ar 48000 -ac 2 -f s16le -i /dev/zero -i foo%04d.png -shortest -c:a aac -c:v libx264 -r 30 -pix_fmt yuv420p -strict experimental out.mp4
import scipy.ndimage as nd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
def main():
size = 300
field_a = np.ones([size, size], dtype=float)
field_b = np.zeros([size, size], dtype=float)
t = size / 2 - 10
field_b[t:size-t,t:size-t] = 1
D_a = 0.2
D_b = 0.1
F = 0.045 # feed
K = 0.062 # kill
dt = 1
for step in range(300):
print("step=%d" % step)
for _ in range(50):
t = field_a * field_b * field_b
field_a += ( D_a * nd.filters.laplace(field_a) - t + F * (1.0 - field_a) ) * dt
field_b += ( D_b * nd.filters.laplace(field_b) + t - (K + F) * field_b ) * dt
plt.figure(figsize=(16, 9))
plt.axis('equal')
plt.contourf(field_b)
plt.colorbar()
plt.title("step=%d" % step)
plt.savefig('foo%04d.png' % step, dpi=40)
plt.clf()
if __name__ == '__main__':
main()
Этот код создаёт набор картинок из которых можно легко собрать видео (см. комментарий в коде и результат ниже).
Обратите внимание, что чтобы реализовать тот алгоритм нам понадобилось бы четыре вложенных цикла (два — для обхода матриц с плотностями, два — для вычисления лапласиана). Numpy позволило не писать ни одного из них.
И конечно, numpy позволяет в десятки (а то и сотни) раз поднять производительность. Здесь мы создаём 300 кадров с шагом в 50 итераций между кадрами. Это 15000 итераций и они выполняются за считанные секунды!
Оттолкнувшись от этого кода, вы можете самостоятельно поиграться с разными значениями параметров. Коэффициенты диффузии не сильно влияют на картину. Они влияют на размер элементов узора. А вот K и F влияют очень сильно. Попробуйте чуть-чуть их поменять и вы увидите, как изменится картина.