为什么我的 guess_nonlinear() 的输入都是 1?
Why are the inputs to my guess_nonlinear() all 1s?
我的完整问题的 N2 图如下。
问题耦合部分的 N2 图如下。
我有一个 DirectSolver 处理 LLTForces 和 ImplicitLiftingLine 之间的耦合,还有一个 LNBGS 求解器处理 LiftingLineGroup 和 TestCL 之间的耦合。
问题的要点在这里:https://gist.github.com/eufren/31c0e569ed703b2aea3e2ef5360610f7
我已经在 ImplicitLiftingLine 上实现了 guess_nonlinear(),它应该使用 LLTGeometry 的各种输出,根据控制方程的线性形式对涡旋强度给出一个很好的初始猜测。
def guess_nonlinear(self, inputs, outputs, resids):
freestream_unit_vector = inputs['freestream_unit_vector']
freestream_velocity = inputs['freestream_velocity']
n = inputs['normal_vectors']
A = inputs['surface_areas']
l = inputs['bound_vortices']
ic_tot = inputs['influence_coefficients_total']
v_inf = freestream_velocity
v_inf_vec = v_inf*freestream_unit_vector
lin_numerator = np.pi * v_inf * A * np.sum(n * v_inf_vec, axis=1)
lin_denominator = (np.linalg.norm(np.cross(v_inf_vec, l), axis=1) - np.pi * v_inf * A * np.sum(np.sum(n * ic_tot, axis=2), axis=1))
lin_vtx_str = lin_numerator / lin_denominator
outputs['vortex_strengths'] = lin_vtx_str
然而,当问题是第一次 运行 时,任何未明确设置为 p.set_val() 的输入均为 1。这会导致 guess_nonlinear() 给出错误的输出,因此系统无法收敛:
据我所知,LLT 组的执行顺序是正确的,几何组件应该在隐式组件之前执行。我很困惑为什么当代码为 运行 时这似乎并没有真正发生,而是这些输入采用了它们的默认值。
我需要更改什么才能使其正常工作?此外,我发现在优化期间很难让 LNBGS 收敛(因此添加 guess_nonlinear())——只有 DirectSolver 一路顺利通过优化,但对于大量 LLT 节点来说它非常慢)。如何改进线性和非线性求解器的选择,提高迭代求解器的可靠性?
注意:感谢您提供可测试的示例。它使找出问题的答案变得更加简单。您的问题有点微妙,如果没有 运行nable code
,我将无法给出好的答案
你的第一个问题:“为什么所有的输入都是 1”
“简短”回答
您已将非线性求解器置于模型层次结构的较高位置,然后包含一个计算输入值的关键前体组件。通过将求解器向下移动到模型的较低级别,我能够确保前体组件 (LTTGeometry
) 运行 在到达隐式的 guess_nonlinear
之前具有有效输出组件。
这是您所拥有的(注意隐式求解器包含 LTTGeometry
,即使数据循环不需要该组件:
我将非线性求解器和线性求解器都移到了 LTTCycle
组中,然后允许 LTTGeometry
组件在进入非线性求解器和 guess_nonlinear
步骤之前执行:
我的修复只是部分正确,因为 TestCL
组件中有一个辅助循环也需要解算器,但没有解算器。但是,那个循环仍然不涉及LTTGeometry
组。因此,完全正确的解决方法是首先对顶部 运行 几何体建模,然后将 LTTCycle
和 TestCL
组放在一起,这样您就可以 运行 求解器只对它们进行求解。在你的测试问题上,这比我想做的要多一些,但你可以从上面调整后的 N2 中看到总体思路。
长答案
OpenMDAO 中的 guess_nonlinear
序列执行 NOT 运行 显式组件或组的计算方法。它遵循执行层次结构,并调用它找到的任何 guess_nonlinear
。因此,这意味着您模型中的任何显式组件都将 NOT 执行,它们的输出不会使用计算值更新,并且这些计算值不会传递到下游的输入组件。
当模型层次结构很深时,事情会变得有点棘手。 guess_nonlinear
方法被称为非线性求解过程的第一步。如果您在顶层有一个 NonLinearRunOnce 求解器,它将沿着计算链向下调用每个 child 上的 compute
或 solve_nonlinear
并执行数据 t运行sfer每一个之后。如果其中一个 children 恰好是一个具有非线性求解器的组,那么该求解器将在其 children 上调用 guess_nonlinear
(g运行dchildren 作为第一步,使用 NonLinearRunOnce 求解器)。因此,由该组的兄弟姐妹计算的任何输出都是有效的,但是 g运行dchild 级别的输出中的 none 已经计算出来了。
您可能想知道为什么不让 guess_nonlinear
方法为任何显式组件调用计算?这里很难权衡取舍。如果您假设所有显式组件对于 运行 来说都非常便宜,那么它 可能 对 运行 计算方法有意义 --- 也可能没有。很大程度上取决于循环数据结构。如果组中的某些早期组件需要后期组件的猜测,那么 运行 计算它根本不会帮助你。或许更重要的是,并非所有显式组件对 运行 来说都是便宜的。您可能有一个非常昂贵的计算,并且在猜测过程中调用计算的成本太高了。
这里的妥协是,如果您需要某种顶级猜测过程,您可以 implement guess_nonlinear
at the group level。这样做不太常见,但它可以让您完全控制发生的事情。您可以按任何顺序调用任何您需要调用的内容。
因此,绝对要记住的关键是,当调用 guess_nonlinear
时,您唯一可用的数据是在执行包含求解器之前计算的任何数据。这意味着在到达包含求解器的模型范围之前计算的任何东西(不是 guess_method
本身的组件范围)。
你的第二个问题:“当节点数量变大时,我怎样才能加快速度?”
这个根本不可能给出一个通用的答案。我注意到您已经指定了稀疏偏导数。这是一个很好的开始,但如果它仍然不够快,那么这意味着您已经达到了 DirectSolver 的极限。你注意到这个求解器是唯一一个可以让你顺利完成优化的求解器,我将把它理解为 ScipyKryloventer link description here and PetscKrylov are not converging the linear system well for you --- at least not by themselves. Thats not surprising, as krylov solvers almost always require some kind of preconditioner... and this is why I can't offer a generic answer. Setting up efficient linear solvers for larger-scale compute is a tricky subject. If you look into the literature, you'll find some good suggestions. You can also study open source implementations like VSPAero 的一些技巧。
实际上,您已经达到了简单线性求解器所能提供的极限。从现在开始,OpenMDAO 可以通过简化一些预处理来提供一些帮助,但您将不得不自己承受数学方面的痛苦。
我的完整问题的 N2 图如下。
问题的要点在这里:https://gist.github.com/eufren/31c0e569ed703b2aea3e2ef5360610f7
我已经在 ImplicitLiftingLine 上实现了 guess_nonlinear(),它应该使用 LLTGeometry 的各种输出,根据控制方程的线性形式对涡旋强度给出一个很好的初始猜测。
def guess_nonlinear(self, inputs, outputs, resids):
freestream_unit_vector = inputs['freestream_unit_vector']
freestream_velocity = inputs['freestream_velocity']
n = inputs['normal_vectors']
A = inputs['surface_areas']
l = inputs['bound_vortices']
ic_tot = inputs['influence_coefficients_total']
v_inf = freestream_velocity
v_inf_vec = v_inf*freestream_unit_vector
lin_numerator = np.pi * v_inf * A * np.sum(n * v_inf_vec, axis=1)
lin_denominator = (np.linalg.norm(np.cross(v_inf_vec, l), axis=1) - np.pi * v_inf * A * np.sum(np.sum(n * ic_tot, axis=2), axis=1))
lin_vtx_str = lin_numerator / lin_denominator
outputs['vortex_strengths'] = lin_vtx_str
然而,当问题是第一次 运行 时,任何未明确设置为 p.set_val() 的输入均为 1。这会导致 guess_nonlinear() 给出错误的输出,因此系统无法收敛:
据我所知,LLT 组的执行顺序是正确的,几何组件应该在隐式组件之前执行。我很困惑为什么当代码为 运行 时这似乎并没有真正发生,而是这些输入采用了它们的默认值。
我需要更改什么才能使其正常工作?此外,我发现在优化期间很难让 LNBGS 收敛(因此添加 guess_nonlinear())——只有 DirectSolver 一路顺利通过优化,但对于大量 LLT 节点来说它非常慢)。如何改进线性和非线性求解器的选择,提高迭代求解器的可靠性?
注意:感谢您提供可测试的示例。它使找出问题的答案变得更加简单。您的问题有点微妙,如果没有 运行nable code
,我将无法给出好的答案你的第一个问题:“为什么所有的输入都是 1”
“简短”回答
您已将非线性求解器置于模型层次结构的较高位置,然后包含一个计算输入值的关键前体组件。通过将求解器向下移动到模型的较低级别,我能够确保前体组件 (LTTGeometry
) 运行 在到达隐式的 guess_nonlinear
之前具有有效输出组件。
这是您所拥有的(注意隐式求解器包含 LTTGeometry
,即使数据循环不需要该组件:
我将非线性求解器和线性求解器都移到了 LTTCycle
组中,然后允许 LTTGeometry
组件在进入非线性求解器和 guess_nonlinear
步骤之前执行:
我的修复只是部分正确,因为 TestCL
组件中有一个辅助循环也需要解算器,但没有解算器。但是,那个循环仍然不涉及LTTGeometry
组。因此,完全正确的解决方法是首先对顶部 运行 几何体建模,然后将 LTTCycle
和 TestCL
组放在一起,这样您就可以 运行 求解器只对它们进行求解。在你的测试问题上,这比我想做的要多一些,但你可以从上面调整后的 N2 中看到总体思路。
长答案
OpenMDAO 中的 guess_nonlinear
序列执行 NOT 运行 显式组件或组的计算方法。它遵循执行层次结构,并调用它找到的任何 guess_nonlinear
。因此,这意味着您模型中的任何显式组件都将 NOT 执行,它们的输出不会使用计算值更新,并且这些计算值不会传递到下游的输入组件。
当模型层次结构很深时,事情会变得有点棘手。 guess_nonlinear
方法被称为非线性求解过程的第一步。如果您在顶层有一个 NonLinearRunOnce 求解器,它将沿着计算链向下调用每个 child 上的 compute
或 solve_nonlinear
并执行数据 t运行sfer每一个之后。如果其中一个 children 恰好是一个具有非线性求解器的组,那么该求解器将在其 children 上调用 guess_nonlinear
(g运行dchildren 作为第一步,使用 NonLinearRunOnce 求解器)。因此,由该组的兄弟姐妹计算的任何输出都是有效的,但是 g运行dchild 级别的输出中的 none 已经计算出来了。
您可能想知道为什么不让 guess_nonlinear
方法为任何显式组件调用计算?这里很难权衡取舍。如果您假设所有显式组件对于 运行 来说都非常便宜,那么它 可能 对 运行 计算方法有意义 --- 也可能没有。很大程度上取决于循环数据结构。如果组中的某些早期组件需要后期组件的猜测,那么 运行 计算它根本不会帮助你。或许更重要的是,并非所有显式组件对 运行 来说都是便宜的。您可能有一个非常昂贵的计算,并且在猜测过程中调用计算的成本太高了。
这里的妥协是,如果您需要某种顶级猜测过程,您可以 implement guess_nonlinear
at the group level。这样做不太常见,但它可以让您完全控制发生的事情。您可以按任何顺序调用任何您需要调用的内容。
因此,绝对要记住的关键是,当调用 guess_nonlinear
时,您唯一可用的数据是在执行包含求解器之前计算的任何数据。这意味着在到达包含求解器的模型范围之前计算的任何东西(不是 guess_method
本身的组件范围)。
你的第二个问题:“当节点数量变大时,我怎样才能加快速度?”
这个根本不可能给出一个通用的答案。我注意到您已经指定了稀疏偏导数。这是一个很好的开始,但如果它仍然不够快,那么这意味着您已经达到了 DirectSolver 的极限。你注意到这个求解器是唯一一个可以让你顺利完成优化的求解器,我将把它理解为 ScipyKryloventer link description here and PetscKrylov are not converging the linear system well for you --- at least not by themselves. Thats not surprising, as krylov solvers almost always require some kind of preconditioner... and this is why I can't offer a generic answer. Setting up efficient linear solvers for larger-scale compute is a tricky subject. If you look into the literature, you'll find some good suggestions. You can also study open source implementations like VSPAero 的一些技巧。
实际上,您已经达到了简单线性求解器所能提供的极限。从现在开始,OpenMDAO 可以通过简化一些预处理来提供一些帮助,但您将不得不自己承受数学方面的痛苦。