Я использовал NonLinearBlockGS
как nonlinear_solver
для моей MDO-системы, состоящей из ExplicitComponents
и это работает, как ожидалось. Сначала я использовал это с помощью простых математических функций (отсюда время выполнения << 1 с), но теперь я также внедряю систему с несколькими явными компонентами с временем работы около одной минуты или более. Это, когда я заметил, что NonLinearBlockGS
фактически необходимо запустить инструменты в связанной системе два раза за итерацию. Эти прогоны исходят из self._iter_execute()
и self._run_apply()
в _run_iterator()
решателя (класс Solver
в файле solver.py
).
Мой главный вопрос: нужны ли два прогона на итерацию, и если да, то почему?
Кажется, что первый запуск компонента (self.iter_execute()
) использует начальное предположение для переменных обратной связи, которые необходимо self.iter_execute()
а затем последовательно запускает компоненты при обновлении любых данных, связанных с форвардом. Это тот шаг, который я ожидал бы от Гаусса-Зейделя. Но затем второй запуск компонента (self._run_apply()
) снова запускает компоненты с обновленными переменными обратной связи, которые возникли в результате первого запуска, сохраняя при этом форварды как они были в этом первом запуске. Если я не ошибаюсь, эта информация тогда (только) используется для оценки сходимости этой итерации (self._iter_get_norm()
).
Вместо того, чтобы выполнить этот второй запуск внутри итерации, не было бы более эффективным прямое продолжение следующей итерации? В этой итерации мы можем использовать новые значения переменных обратной связи и делать еще один self._iter_execute()
с обновлением данных, связанных с форвардом, и затем оценивать конвергенцию на основе разницы между результатами между этими двумя итерациями. Конечно, это означает, что для оценки конвергенции нам нужны как минимум две итерации, но она сохраняет один компонент за одну итерацию. (Это фактически существующая реализация, которая у меня есть для конвергенции этих компонентов в MATLAB и работает, как ожидалось, следовательно, она находит тот же конвергентный дизайн, но с половиной объема выполнения компонентов).
Итак, еще один способ поставить это: зачем нам нужно self._run_apply()
на каждой итерации при сближении Гаусса-Сейделя? (И можно ли это отключить?)
На ваш вопрос есть несколько разных аспектов. Во-первых, я расскажу о деталях solve_nonlinear
vs apply_nonlinear
. С помощью лежащих в основе математических алгоритмов OpenMDAO, основанных на структуре MAUD, solve_nonlinear
вычисляет только значения выходных значений (не устанавливает остаточные значения). apply_nonlinear
вычисляет только остатки (и не устанавливает выходы). Для подклассов ExplicitComponent
пользователь только реализует метод compute
, а базовый класс реализует как solve_nonlinear
и apply_nonlinear
с использованием compute
.
Как вы описали его, в OpenMDAO V2.4 текущая реализация NonlinearBlockGaussSeidel для каждой итерации выполняет один рекурсивный solve_nonlinear
вызов в своей группе, а затем вызывает apply_nonlinear
для проверки остатка и поиска конвергенции.
Тем не менее, вы также правы, что мы могли бы сделать это более эффективно. Модификация, которую вы предложили алгоритму, будет работать, и мы поместим ее в конвейер разработки для V2.6 (со времени этого сообщения мы как раз собираемся выпустить V2.5, и не будет время, чтобы добавить это в этот выпуск)