Описание проблемы
При написании симулятора частиц Монте-Карло (движение буриунов и фотонное излучение) в python/numpy. Мне нужно сохранить выходной файл моделирования ( → 10 ГБ) в файл и обработать данные на втором шаге. Совместимость с Windows и Linux важна.
Число частиц (n_particles
) составляет 10-100. Количество шагов времени (time_size
) составляет ~ 10 ^ 9.
Моделирование имеет 3 шага (код ниже для версии "все-в-ОЗУ" ):
-
Имитировать (и сохранить) массив значений
emission
(содержит много элементов почти-0):- shape (
n_particles
xtime_size
), float32, размер 80 ГБ
- shape (
-
Вычислить массив
counts
(случайные значения из процесса Пуассона с ранее вычисленными скоростями):-
shape (
n_particles
xtime_size
), uint8, размер 20 ГБcounts = np.random.poisson(lam=emission).astype(np.uint8)
-
-
Найти временные метки (или индекс) счетчиков. Графы почти всегда равны 0, поэтому массивы timestamp будут помещаться в ОЗУ.
# Loop across the particles timestamps = [np.nonzero(c) for c in counts]
Я делаю шаг 1 один раз, затем повторяю шаг 2-3 много (~ 100) раз. В будущем мне может понадобиться предварительно обработать emission
(применить cumsum
или другие функции) до вычисления counts
.
Вопрос
У меня есть работающая в памяти реализация, и я пытаюсь понять, что лучше всего подходит для реализации устаревшей версии, которая может масштабировать (намного) более длительные симуляции.
Что бы я хотел:
Мне нужно сохранить массивы в файл, и я хотел бы использовать один файл для моделирования. Мне также нужен "простой" способ хранения и вызова словаря параметра моделирования (скаляры).
В идеале мне нужен массив numpy с поддержкой файлов, который я могу предварительно распределить и заполнить куски. Затем я хотел бы, чтобы методы массива numpy (max
, cumsum
,...) работали прозрачно, требуя только ключевое слово chunksize
, чтобы указать, сколько массива загружается на каждой итерации.
Еще лучше, мне нужен Numexpr, который работает не между кешем и оперативной памятью, а между ОЗУ и жестким диском.
Каковы практические варианты
В качестве первого варианта Я начал экспериментировать с pyTables, но я не доволен его сложностью и абстракциями (так отличается от numpy). Более того, мое текущее решение (см. Ниже) является UGLY и не очень эффективным.
Итак, мои варианты, для которых я ищу ответ,
-
реализовать массив numpy с требуемой функциональностью (как?)
-
использовать pytable более умным способом (разные структуры данных/методы)
-
используйте другую библиотеку: h5py, blaze, pandas... (я пока не пробовал ни одного из них).
Предварительное решение (pyTables)
Я сохраняю параметры моделирования в группе '/parameters'
: каждый параметр преобразуется в скалярный массив numpy. Подробное решение, но оно работает.
Я сохраняю emission
как расширяемый массив (EArray
), потому что я генерирую данные в кусках, и мне нужно добавить каждый новый кусок (хотя я знаю конечный размер). Сохранение counts
более проблематично. Если сохранить его как массива pytable, сложно выполнить такие запросы, как "counts >= 2". Поэтому я сохранил counts как несколько таблиц (по одной на каждую частицу) [UGLY], и я запрашиваю с .get_where_list('counts >= 2')
. Я не уверен, что это экономически эффективно, и
генерируя все эти таблицы вместо использования одного массива, значительно улучшает файл HDF5. Более того, как ни странно, создание этих таблиц требует создания настраиваемого типа dtype (даже для стандартных типов numpy):
dt = np.dtype([('counts', 'u1')])
for ip in xrange(n_particles):
name = "particle_%d" % ip
data_file.create_table(
group, name, description=dt, chunkshape=chunksize,
expectedrows=time_size,
title='Binned timetrace of emitted ph (bin = t_step)'
' - particle_%d' % particle)
Каждая таблица счетчиков с частицами имеет другое имя (name = "particle_%d" % ip
) и что мне нужно поместить их в список python для легкой итерации.
EDIT. Результатом этого вопроса является симулятор броуновского движения, называемый PyBroMo.