Как распараллелить расчеты движения небесных тел?

У меня есть код, который вычисляет положения некоторых спутников и планет с помощью 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)

Это будет использовать оценку нутации Земли с более низкой точностью (распечатайте значения до и после этого изменения, чтобы увидеть, насколько велика разница, и сравните это с тем, насколько неточности может выдержать ваше приложение), но также, надеюсь, работать быстрее.

Другие вопросы по тегам