写这个博客是为电力系统系统课设的潮流计算程序提供一种新的思路。
在做这个课设之前,我在网上查阅了很多潮流计算的程序,几乎全部是用 matlab完成的。这些程序基本都是自带系统各项参数的矩阵,没有输入,即使有输入也极为繁琐,相比这种输入方式,我更希望我的程序能直接读取算例中给的信息。
这里介绍一种用C++调用 matlab实现潮流计算的方法,可以很轻松的实现算例的读取和存储。
如果没学过C++的话,现在自学也是来得及的,大概明白类和引用是怎么一回事就可以了,在这个课设里不会用到多态和继承。
如果对指针和内存管理都不了解,那还是用 matlab比较合适,现学的话,哪里出错几天几夜都想不明白的。
本来是想着从 excel输入的,但有点太麻烦了,就改用 txt文件输入,输出是输出到 excel文件中,和老师要求类似。
C++调用MATLAB函数
这篇文章里说的比较详细,可以先用 matlab做一个简单函数的m文件,看看能否在VS中编译通过。但还是需要注意几个点,一是要在x64模式下编译,同时要禁用Oxc0000005 Access violation,这个异常是在32位模式下使用的。(ctrl+alt+e 调出异常设置)
二是要在项目属性中的c++目录中添加一些路径,这个上面给的链接里已经说得很清楚了。
三是要在系统环境变量中添加路径,这个添加以后重启才能生效。
四是要在项目属性中的链接器-输入-附加依赖项里添加一些库文件,这些文件在我上面给的链接里有。
由于 matlab中的所有元素都是以矩阵形式存储的,所以在传参时,所有的参数都要转换为 mwArray,关于这个类的使用,在上面链接里已经写得很清楚了。
如果你使用的是 mxArray的话,需要注意这个矩阵类是按列存储的,所以传参过后要转置一下才行。
环境调好后,我们就可以开始写潮流计算的函数,由于C++对矩阵的处理能力一般,矩阵的运算要尽量在 matlab中完成。
我们可以写三个m文件,分别是PQ分解法,牛拉法极坐标,牛拉法直角坐标,最后将三个函数合并生成一个库文件。需要注意的是,这三个m文件之间不能相互调用,这样好像会出错,我也懒得试。这里是我的PQ分解法的代码
% 函数名:PQPFC_UD
% 功能:PQ分解法计算潮流
% 输入参数:G,B,PQU0,EF0,TYPE,err,max_inter
% G/B:结点导纳矩阵的实数与虚数部分,(n,n)矩阵
% PQ0:功率的给定值,为(2*n,1)矩阵,参数分布为[n*P,n*Q]
% UD0:电压的给定值,为(2*n,1)矩阵,参数分布为[n*D,n*U]
% TYPE:结点类型矩阵,VD为0,PQ为1,PV为2,为(n,1)矩阵
% err:误差大小
% max_inter:最大迭代次数
% 输出参数:PQ,UD,interation,time
% PQ:输出功率(2n,1)矩阵,参数分布为[n*P,n*Q]
% UD:输出电压(2n,1)矩阵,参数分布为[n*D,n*U]
% interation:迭代次数
% time:迭代用时
function [PQ,UD,inter,time] = PQPFC_UD(G,B,PQ0,UD0,TYPE,err,max_inter)
n = length(TYPE); % 取结点数
D = UD0(1:n,:); % 取D,U,P,Q初值
U = UD0(n+1:2*n,:);
P0 = PQ0(1:n,:);
Q0 = PQ0(n+1:2*n,:);
VDpos = find(TYPE == 0); % 取VD,PQ,PV结点位置
PQpos = find(TYPE == 1);
PVpos = find(TYPE == 2);
B1 = B; % 截取导纳矩阵
B2 = B;
B1(VDpos,:) = [];
B2([VDpos;PVpos],:) = [];
B1(:,VDpos) = [];
B2(:,[VDpos;PVpos]) = [];
B1 = inv(B1); % 提前进行求逆
B2 = inv(B2);
inter = 0; % 迭代次数置零
tic % 开始迭代
while inter < max_inter
delta_P = delta_Pcal(G,B,U,D,P0,n,VDpos); % 生成不平衡向量△P
[Up,Uq,delta] = setUD(U,D,VDpos,PVpos); % 去除UD中多余结点项
if max(abs(delta_P))>err % 若△P不满足收敛条件
delta_P = delta_P./Up; % 解修正方程
result = -B1*delta_P;
result = result./Up;
delta = delta+result;
D = Dreset(delta,VDpos,D); % 重设D
elseif max(abs(delta_Qcal(G,B,U,D,Q0,n,VDpos,PVpos)))<err
break; % 若△P△Q同时满足收敛条件则退出
end
delta_Q = delta_Qcal(G,B,U,D,Q0,n,VDpos,PVpos); % 生成不平衡向量△Q
if max(abs(delta_Q))>err % 若△Q不满足收敛条件
delta_Q = delta_Q./Uq; % 解修正方程
result = -B2*delta_Q;
Uq = Uq+result;
U = Ureset(Uq,PQpos,U); % 重设U
elseif max(abs(delta_Pcal(G,B,U,D,P0,n,VDpos)))<err
break; % 若△P△Q同时满足收敛条件则退出
end
inter = inter +1;
end
PQ = PQcal(G,B,U,D,n); % 计算各结点功率
UD = [D;U];
time = toc;
end
function delta_P = delta_Pcal(G,B,U,D,P0,n,VDpos)
delta_P = zeros(n,1);
for i = 1:n
delta_P(i) = P0(i);
for j =1:n
delta_P(i)= delta_P(i)-U(i)*U(j)*(G(i,j)*cos(D(i)-D(j))+B(i,j)*sin(D(i)-D(j)));
end
end
delta_P(VDpos) = [];
end
function delta_Q = delta_Qcal(G,B,U,D,Q0,n,VDpos,PVpos)
delta_Q = zeros(n,1);
for i = 1:n
delta_Q(i) = Q0(i);
for j =1:n
delta_Q(i)= delta_Q(i)-U(i)*U(j)*(G(i,j)*sin(D(i)-D(j))-B(i,j)*cos(D(i)-D(j)));
end
end
delta_Q([VDpos;PVpos]) = [];
end
function [Up,Uq,delta] = setUD(U,D,VDpos,PVpos)
Up = U;
Uq = U;
delta = D;
Up(VDpos,:) = [];
Uq([VDpos;PVpos]) = [];
delta(VDpos,:) = [];
end
function D = Dreset(delta,VDpos,D0)
D = [delta(1:VDpos-1);D0(VDpos); delta(VDpos:end)];
end
function U = Ureset(Uq,PQpos,U0)
for i = 1:length(PQpos)
U0(PQpos(i)) = Uq(i);
end
U = U0;
end
function PQ = PQcal(G,B,U,D,n)
PQ = zeros(2*n,1);
for i = 1:n
PQ(i) = 0;
PQ(i+n) = 0;
for j = 1:n
PQ(i) = PQ(i)+U(i)*U(j)*(G(i,j)*cos(D(i)-D(j))+B(i,j)*sin(D(i)-D(j)));
PQ(i+n) = PQ(i+n) +U(i)*U(j)*(G(i,j)*sin(D(i)-D(j))-B(i,j)*cos(D(i)-D(j)));
end
end
end
电力系统本质上是一个无向图的结构,适合用邻接表的方式去存储。忽略一些不必要的参数,我们可以只关注结点的电压相角及幅值,有功与无功,对地导纳,结点类型,以及支路的导纳。当然,各个结点的名称或序号也是我们需要关注的。
之所以把对地导纳存在结点中,是因为在最后生成结点导纳矩阵时,自导纳需要减去对地导纳,而对地导纳对互导纳没有影响。
我们可以将电力系统的无向图结构封装成一个类,类中存有节点数,支路数,各类型节点数等信息,同时还存有一个邻接表,以及其对应的增加结点,增加支路函数,还有返回各个结点类型信息的函数。由于怕出错,内存释放啥的我基本没整,不过这程序也占不了多少内存。最后写出一个生成结点导纳矩阵的函数,需要注意的是,这个节点导纳矩阵最后需要生成两个一维数组,因为 mwArray赋值时只接收一维数组。为了图方便,还可以使用C++的复数类。
读取算例可以使用 stringstream类
这里的m_in 是 ifstream类,用于读取文件,m_ss是stringstream类
string buf;
while (getline(m_in, buf)) //获取一行
{
m_ss.clear(); //清空m_ss中的内容
m_ss.str("");
m_ss << buf; //将当前行传入m_ss
m_ss >> buf; //传出当前行的第一个单词
if (buf == "THLOAD") //通过该单词判断该点类型,并进入相应的读取函数
{
readLoad();
}
else if (buf == "THSHUNT")
{
readLoad();
}
else if (buf == "GENER")
{
readGener();
}
else if (buf == "GENERCV")
{
readGenerCv();
}
else if (buf == "THSLACK")
{
readSLACK();
}
}
这是读取结点部分的代码,stringstream类可以读取一行的字符串,并将其一个单词一个单词地输出,这与我们算例的编排很契合。需要注意的是,57结点的算例中部分支路连接的是联络结点,算例中不单独声明联络结点。
到这里基本上课设的任务就完成了,剩下图形化界面的内容是用mfc做的,可以在b站上搜索mfc教程来学。
这篇博客只是为大家提供一种思路,没有详细的说怎么写,如果有什么问题,可以加我微信 qiao123aaasina
我上传了这个潮流计算器,正在审核,标题是电力系统课设——潮流计算器