OpenMDAO隐式组件添加类显式输出的可行性及计算资源与实现疑问
针对你提出的三个具体疑问,结合你给出的Node隐式组件例子,我来逐一解释:
1. 即使solve_nonlinear()已显式计算出功率,组件是否仍会通过apply_nonlinear()来求解功率?
是的,必须通过apply_nonlinear()定义功率的残差,哪怕你在solve_nonlinear()里已经显式算出了P_total的正确值。
OpenMDAO的隐式组件逻辑是:所有输出都需要满足残差为0的约束。对于P_total这个输出,你定义的残差residuals['P_total'] = outputs['P_total'] - total_abs_current * outputs['V'] / 2,本质是在告诉求解器“P_total必须等于total_abs_current * V / 2”。
当你在solve_nonlinear()里直接计算出这个正确值时,残差会直接等于0,求解器不会额外迭代功率相关的部分——但如果不定义这个残差,求解器根本不知道P_total的取值规则,会导致计算错误或者收敛失败。
2. 这种实现方式相比单独使用显式组件的方案,是否会消耗更多计算资源?
不会,反而可能更少。
单独用显式组件的话,你需要在隐式组件里遍历一次电流输入计算残差,然后在显式组件里再遍历一次相同的电流输入计算功率,这就重复了循环遍历的计算量。
而把功率计算放在隐式组件内的话,你可以复用中间计算结果:比如把total_abs_current的计算抽成一个辅助函数,在apply_nonlinear()和solve_nonlinear()里只调用一次,避免重复遍历输入。这样总计算量和单独显式组件方案持平甚至更少,还省去了组件间数据传递的开销。
举个优化后的代码例子:
class Node(om.ImplicitComponent): # ... initialize和setup部分不变 ... def _compute_total_abs_current(self, inputs): total = 0. for i_conn in range(self.options['n_in']): total += np.abs(inputs['I_in:{}'.format(i_conn)]) for i_conn in range(self.options['n_out']): total += np.abs(inputs['I_out:{}'.format(i_conn)]) return total def apply_nonlinear(self, inputs, outputs, residuals): residuals['V'] = 0. total_abs_current = self._compute_total_abs_current(inputs) # 计算V的残差... for i_conn in range(self.options['n_in']): residuals['V'] += inputs['I_in:{}'.format(i_conn)] for i_conn in range(self.options['n_out']): residuals['V'] -= inputs['I_out:{}'.format(i_conn)] residuals['P_total'] = outputs['P_total'] - total_abs_current * outputs['V'] / 2 def solve_nonlinear(self, inputs, outputs): total_abs_current = self._compute_total_abs_current(inputs) outputs['P_total'] = total_abs_current * outputs['V'] / 2
3. 在通过linearize()方法定义偏导数时,应遵循apply_nonlinear()还是solve_nonlinear()的计算逻辑?
必须遵循apply_nonlinear()里的残差逻辑。
linearize()的作用是计算残差对输入、输出的偏导数(雅可比矩阵),而求解器是基于残差的迭代来收敛的,所以偏导数必须对应残差的表达式。
拿P_total的残差来说:
residuals['P_total'] = outputs['P_total'] - (total_abs_current * outputs['V'] / 2)
对应的偏导数应该是:
- 对
outputs['P_total']的偏导是1.0 - 对
outputs['V']的偏导是-total_abs_current / 2 - 对每个
I_in:i的偏导是-np.abs(inputs['I_in:i']) * outputs['V'] / 2 - 对每个
I_out:i的偏导是-np.abs(inputs['I_out:i']) * outputs['V'] / 2
而solve_nonlinear()里的outputs['P_total'] = ...只是给输出提供初始猜测或者显式解,和雅可比矩阵的计算无关。
内容的提问来源于stack exchange,提问作者JvanW

