Параллельный расчет конечных разностей в OpenMDAO выполняет каждую точку в каждом процессе

Я пытаюсь создать проблему в OpenMDAO и хотел бы использовать параллельные вычисления конечных разностей. Однако, когда я вызываю compute_totals(), каждый процесс MPI фактически вычисляет все возмущенные точки.

Я сделал минимальный пример, демонстрирующий проблему. Рассмотрим простой случай модели, которая может быть представлена ​​матричным умножением. Якобиан этой модели — это просто матрица модели. См. код ниже:

import numpy as np
import time

from openmdao.api import ExplicitComponent, Problem, IndepVarComp, Group
from openmdao.utils.mpi import MPI

rank = 0 if not MPI else MPI.COMM_WORLD.rank

class MatMultComp(ExplicitComponent):
    def __init__(self, matrix, **kwargs):
        super().__init__(**kwargs)
        self.matrix = matrix

    def setup(self):
        self.add_input('x', val=np.ones(self.matrix.shape[1])))
        self.add_output('y', val=np.ones(self.matrix.shape[0])))

    def compute(self, inputs, outputs, **kwargs):
        outputs['y'] = self.matrix.dot(inputs['x'])
        print('{} :: x = {}'.format(rank, np.array_str(inputs['x'])))


class Model(Group):
    def setup(self):
        matrix = np.arange(25, dtype=float).reshape(5, 5)
        self.add_subsystem('ivc', IndepVarComp('x', np.ones(matrix.shape[1])), promotes=['*'])
        self.add_subsystem('mat', MatMultComp(matrix), promotes=['*'])
        self.approx_totals(step=0.1)
        self.num_par_fd = matrix.shape[1]


if __name__ == '__main__':
    p = Problem()
    p.model = Model()
    p.setup()
    p.run_model()

    t0 = time.time()
    jac = p.compute_totals(of=['y'], wrt=['x'], return_format='array')
    dt = time.time() - t0

    if rank == 0:
        print('Took {:2.3f} seconds.'.format(dt))
        print('J = ')
        print(np.array_str(jac, precision=0))

Когда я запускаю этот код без MPI, я получаю следующий вывод:

0 :: x = [1. 1. 1. 1. 1.]
0 :: x = [1.1 1.  1.  1.  1. ]
0 :: x = [1.  1.1 1.  1.  1. ]
0 :: x = [1.  1.  1.1 1.  1. ]
0 :: x = [1.  1.  1.  1.1 1. ]
0 :: x = [1.  1.  1.  1.  1.1]
Took 5.008 seconds.
J = 
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]
 [10. 11. 12. 13. 14.]
 [15. 16. 17. 18. 19.]
 [20. 21. 22. 23. 24.]]

Это правильный результат, и он занимает около 5 секунд, как и ожидалось. Теперь, когда я запускаю это с MPI, используя 5 процессов, с командой mpirun -np 5 python matmult.py, я получаю следующий вывод:

0 :: x = [1. 1. 1. 1. 1.]
1 :: x = [1. 1. 1. 1. 1.]
2 :: x = [1. 1. 1. 1. 1.]
3 :: x = [1. 1. 1. 1. 1.]
4 :: x = [1. 1. 1. 1. 1.]
0 :: x = [1.001 1.    1.    1.    1.   ]
1 :: x = [1.001 1.    1.    1.    1.   ]
2 :: x = [1.001 1.    1.    1.    1.   ]
3 :: x = [1.001 1.    1.    1.    1.   ]
4 :: x = [1.001 1.    1.    1.    1.   ]
3 :: x = [1.    1.001 1.    1.    1.   ]
0 :: x = [1.    1.001 1.    1.    1.   ]
1 :: x = [1.    1.001 1.    1.    1.   ]
2 :: x = [1.    1.001 1.    1.    1.   ]
4 :: x = [1.    1.001 1.    1.    1.   ]
2 :: x = [1.    1.    1.001 1.    1.   ]
3 :: x = [1.    1.    1.001 1.    1.   ]
0 :: x = [1.    1.    1.001 1.    1.   ]
1 :: x = [1.    1.    1.001 1.    1.   ]
4 :: x = [1.    1.    1.001 1.    1.   ]
1 :: x = [1.    1.    1.    1.001 1.   ]
2 :: x = [1.    1.    1.    1.001 1.   ]
3 :: x = [1.    1.    1.    1.001 1.   ]
0 :: x = [1.    1.    1.    1.001 1.   ]
4 :: x = [1.    1.    1.    1.001 1.   ]
0 :: x = [1.    1.    1.    1.    1.001]
1 :: x = [1.    1.    1.    1.    1.001]
2 :: x = [1.    1.    1.    1.    1.001]
3 :: x = [1.    1.    1.    1.    1.001]
4 :: x = [1.    1.    1.    1.    1.001]
Took 5.072 seconds.
J = 
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]
 [10. 11. 12. 13. 14.]
 [15. 16. 17. 18. 19.]
 [20. 21. 22. 23. 24.]]

Итоговый результат, конечно, правильный. Однако это противоречит цели использования MPI, потому что каждый из 5 процессов вычисляет все возмущенные точки, а общее выполнение занимает около 5 секунд, как и раньше. Я ожидал следующего результата:

0 :: x = [1. 1. 1. 1. 1.]
1 :: x = [1. 1. 1. 1. 1.]
2 :: x = [1. 1. 1. 1. 1.]
3 :: x = [1. 1. 1. 1. 1.]
4 :: x = [1. 1. 1. 1. 1.]
0 :: x = [1.1 1.  1.  1.  1. ]
1 :: x = [1.  1.1 1.  1.  1. ]
2 :: x = [1.  1.  1.1 1.  1. ]
3 :: x = [1.  1.  1.  1.1 1. ]
4 :: x = [1.  1.  1.  1.  1.1]
Took 1.000 seconds.
J = 
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]
 [10. 11. 12. 13. 14.]
 [15. 16. 17. 18. 19.]
 [20. 21. 22. 23. 24.]]

Обратите внимание, что на самом деле порядок завершения процессов произвольный, а время выполнения будет чуть больше 1 секунды.

Как я могу заставить это работать, как ожидалось? Обратите внимание, что я использую OpenMDAO 2.5.0.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
92
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Здесь есть несколько проблем. Во-первых, num_par_fd обычно следует передавать в качестве аргумента __init__ вашей группе или компоненту. Задавать его в функции setup() компонента или группы слишком поздно, потому что OpenMDAO выполняет все разбиение коммуникатора MPI в функции _setup_procs, что происходит до при вызове setup компонента/группы. Та же проблема с синхронизацией относится и к вызову функции approx_totals. Он должен быть вызван до вызова Проблема setup. Наконец, имя атрибута, который мы используем внутри для указания количества параллельных вычислений FD, на самом деле self._num_par_fd, а не self.num_par_fd. Установка внутреннего атрибута _num_par_fd не рекомендуется, но если необходимо, вам придется установить его до Проблема вызывается setup.

Примечание: это сильно отредактированная версия моего исходного ответа.

Это имеет смысл. Однако я попытался изменить self.num_par_fd = matrix.shape[1] на self._num_par_fd = matrix.shape[1], но получил точно такой же результат. Кроме того, когда я вместо этого изменил код, поэтому я делаю p.model = Model(num_par_fd=5), я получаю RuntimeError: '': num_par_fd = 5 but FD is not active..

D. de Vries 01.02.2019 21:32

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