Как распараллелить расчеты движения небесных тел?
У меня есть код, который вычисляет положения некоторых спутников и планет с помощью Skyfield. Для наглядности я использую Pandas DataFrame как контейнер позиций и соответствующих моментов времени. Я хочу сделать расчет параллельным, но всегда получаю одну и ту же ошибку:TypeError: can't pickle Satrec objects
. Были протестированы различные распараллеливатели, такие как Dask, pandarallel, swifter и Pool.map().
Пример фрагмента кода для распараллеливания:
def get_sun_position(self, row):
t = self.ts.utc(row["Date"]) # from skyfield
pos = self.earth.at(t).observe(self.sun).apparent().position.m # from skyfield, error is here
return pos
def get_sat_position(self, row):
t = self.ts.utc(row["Date"]) # from skyfield
pos = self.sat.at(t).position.m # from skyfield, error is here
return pos
def get_positions(self):
self.df["sat_pos"] = self.df.swifter.apply(self.get_sat_position, axis=1) # all the parallelization goes here
self.df["sun_pos"] = self.df.swifter.apply(self.get_sun_position, axis=1) # and here
# the same implementation but using dask
# self.df["sat_pos"] = dd.from_pandas(self.df, npartitions=4*cpu_count())\
# .map_partitions(lambda df : df.apply(lambda row : self.get_sat_position(row),axis=1))\
# .compute(scheduler='processes')
# self.df["sun_pos"] = dd.from_pandas(self.df, npartitions=4*cpu_count())\
# .map_partitions(lambda df : df.apply(lambda row : self.get_sun_position(row),axis=1))\
# .compute(scheduler='processes')
Для Dask, чтобы избежать Pickle, я попытался установить сериализацию вручную следующим образом serializers=['dask', 'pickle']
но это не помогло.
Насколько я понимаю, Skyfield использует sgp4, который содержит класс Satrec.
Мне было бы интересно, есть ли способ распараллелить это .apply()
. А может вообще не стоит пробовать функции Skyfield для параллельной обработки?
1 ответ
Увы, все механизмы, которые вы используете для параллельного вычисления, делают это путем создания другого процесса и последующей отправки копий всех объектов, участвующих в вычислении, другому процессу - и Satrec
объект написан на C++, а не на Python, чтобы сделать его быстрее, а объекты C++ не имеют собственного способа "сериализовать" себя в байты для передачи другому процессу. (У объектов Python есть встроенная возможность.)
Вы профилировали свой код, чтобы увидеть, какие шаги самые дорогие? Я предполагаю, что большая часть ваших расходов идет на вычисление Солнца, потому что для достижения такой высокой точности Skyfield необходимо вычислить ориентацию Земли с очень высокой точностью, чтобы определить положение Солнца в небе с достаточно высокой точностью даже для радиоастрономов.
Но если вам самому не нужна такая высокая точность, вы можете переключиться на более низкие координаты неба для Солнца. Перед использованиемt
в get_sun_position()
, попробуйте сделать с ним следующее:
t._nutation_angles = iau2000b(t.tt)
Это будет использовать оценку нутации Земли с более низкой точностью (распечатайте значения до и после этого изменения, чтобы увидеть, насколько велика разница, и сравните это с тем, насколько неточности может выдержать ваше приложение), но также, надеюсь, работать быстрее.