这在技术上不是 Pyomo 的问题——而是你的配方的问题。
当求解器将 P_branch_ij 和 Q_branch_ij 的值驱动为 0 时,ASL(求解器可执行文件的一部分)会抛出该错误(这可能是由于行不活动或初始值错误而发生的)。为零时, 的导数sqrt(0)
未定义。此数值错误会导致求解器退出而不返回解。
有几种可能的解决方法:
正如您在编辑中指出的那样,您可以尝试不同的初始变量值。当求解器从不同的起点开始时,它可能不会采用驱动P_branch_ij
和Q_branch_ij
归零的路径。这种方法的缺点是您可能必须尝试许多不同的初始值,然后才能获得一个没有求解器通过的初始值sqrt(0)
,并且不能保证找到一个有效的初始值(特别是如果问题的解决方案确实为 0 !)。
绑定变量,使总分支功率 ( P_branch_ij**2 + Q_branch_ij**2
) 永远不会达到零。这有点棘手,因为您不能使用简单的约束来做到这一点:大多数求解器在求解过程中被允许违反约束,致命错误是求解器曾经尝试评估 的导数sqrt(0)
。幸运的是,大多数求解器将始终尊重变量 bounds。因此,为了防止求解器进行评估sqrt'(0)
,您需要定义中间 Pyomo 变量(不仅仅是 Python 表达式)PQ2_branch_ij
,然后将变量下限设置为大于 0 的值。这种方法的缺点是您现在正在解决一个不同的问题,并且如果原始问题的解决方案确实为 0,则可能会迫使自己获得次优解决方案。
model.PQ2_branch_ij = Var(branch_from_to, scenario_set, bounds=(1e-8, None))
def compute_PQ2_branch_ij(m, i, j, k):
P_branch_ij = (m.V_magn[i, k]**2) * np.float(real(Y_matrix[i, j])) - m.V_magn[i, k] * m.V_magn[j, k] * (np.float(real(Y_matrix[i, j])) * cos(m.V_angle[i, k]-m.V_angle[j, k]) + np.float(imag(Y_matrix[i,j])) * sin(m.V_angle[i, k]-m.V_angle[j, k]))
Q_branch_ij = (m.V_magn[i, k]**2) * np.float(imag(Y_matrix[i, j])) + m.V_magn[i, k] * m.V_magn[j, k] * (np.float((real(Y_matrix[i, j]))) * sin(m.V_angle[i, k]-m.V_angle[j, k]) - np.float(imag(Y_matrix[i,j])) * cos(m.V_angle[i, k]-m.V_angle[j, k]))
return m.PQ2_branch_ij[i,j,k] == P_branch_ij**2 + Q_branch_ij**2
def thermal_lim_ineq_con(m, i, j, k):
return sqrt(m.PQ2_branch_ij) <= limits_flows[i, j]
if line_loading_limit:
model.compute_PQ2_branch_ij = Constraint(branch_from_to, scenario_set, rule=compute_PQ2_branch_ij)
model.thermal_lim_ineq_con = Constraint(branch_from_to, scenario_set, rule=thermal_lim_ineq_con)`
sqrt()
通过平方从配方中删除limits_flows[i,j]
。这通常是最好的选择:您的问题在数学上是等价的,但不再包含sqrt()
函数,并且如果分支流变为 0,则不会出现问题。您在尝试此方法时观察到的不可行性可能完全是一个单独的问题。例如,对于非凸问题(如 ACOPF),您可以初始化求解器,使其无法移动到可行位置并“收敛到局部不可行点”(更多解释,请参阅特定求解器的文档;例如IPOPT,Knitro)。
def thermal_lim_ineq_con(model, i, j, k):
if line_loading_limit == True:
P_branch_ij = (model.V_magn[i, k]**2) * np.float(real(Y_matrix[i, j])) - model.V_magn[i, k] * model.V_magn[j, k] * (np.float(real(Y_matrix[i, j])) * cos(model.V_angle[i, k]-model.V_angle[j, k]) + np.float(imag(Y_matrix[i,j])) * sin(model.V_angle[i, k]-model.V_angle[j, k]))
Q_branch_ij = (model.V_magn[i, k]**2) * np.float(imag(Y_matrix[i, j])) + model.V_magn[i, k] * model.V_magn[j, k] * (np.float((real(Y_matrix[i, j]))) * sin(model.V_angle[i, k]-model.V_angle[j, k]) - np.float(imag(Y_matrix[i,j])) * cos(model.V_angle[i, k]-model.V_angle[j, k]))
return P_branch_ij**2 + Q_branch_ij**2 <= limits_flows[i, j]**2
else:
return Constraint.Skip