有限元刚度矩阵的一维变带宽存储用C++实现(一)
“一维变带宽”存储需要一个存储刚度矩阵中带内元素的一维数组pGK[L](L为带内元素总数),和一个用于存储刚度矩阵中各行主对角线上元素在数组pGK[L]中的序号(称作对角元地址) 的一维辅助数组pDiag[n](n为刚度矩阵阶数)。由于刚度矩阵中元素关于主对角线对称,故pGK[L]中只存储刚度矩阵中上三角或者下三角部分的带内元素。由于刚度矩阵中每行上非0元素仅出现在和该行所代表的自由度直接联系的那些自由度的列上,故刚度矩阵每行的最大半带宽为:
下三角存储的行最大半带宽=当前行行号-直接联系的自由度的最小列号+1;
上三角存储的行最大半带宽=直接联系的自由度的最大列号-当前行行号+1;
本篇文章仅讨论辅助数组pDiag[n]的实现,数组pGK[L]的实现将在下一篇文章中讨论,下面讲C++算法:
首先计算一维变带宽存储的辅助数组pDiag[n],因此要想办法对节点自由度编号,之后按编好号的节点自由度组装总刚矩阵,令节点自由度编号规则为:先对未知自由度编号,再对已知自由度编号,最后把从属节点的自由度编号设置为主节点自由度编号(所谓从属节点,就是一个节点被多个单元共用的情况,就是共享节点,多个单元用的是同一个节点,自然也就用的是相同编号的自由度)。最后得到的总刚矩阵为:
也就是:下图中O代表未知位移,Θ代表已知位移,“空”代表元素为0
可以看出,指定一个当前自由度,则当前自由度的编号即为总刚矩阵中当前行的行号,当前列的列号。
得到辅助数组pDiag[n]的要点为:
(1)创建单元定位矩阵pElemDOF[nTotalElem][m](nTotalElem为单元总数;m为每个单元所拥有的自由度数,对于桁架单元有两个节点,每个节点有2个自由度,故m=4),其中每行存储了每个单元所拥有的自由度的编号;
(2)计算下三角存储时刚度矩阵各行最大半带宽时,先取出刚度矩阵中第i行(就是第i号自由度)的编号(即,二维数组pElemDOF[i][]的第i行),之后到数组PElemDOF的第i行中寻找与该自由度相关的最小自由度编号(即,数组PElemDOF的第i行中存储的那几列上的元素的最小值),最后计算行最大半带宽。
程序流程图如下:
附代码如下:
void BandAndDiagCalcu(int nTotalElem,int nTotalDOF,Element *pElem,int ** pElemDOF,int *pDiag)
//用求出的单元定位向量矩阵求“一维压缩存储算法(一维变带宽存储)”中,刚度矩阵中各行的半带宽及对角元地址;
//最终pDiag中存储的是“对角元地址”
//求出的单元定位向量矩阵存储在pElemDOF指向的整型二维数组中//nTotalDOF为总的自由度数;pDiag为指向整型数组的指针,该数组长度为nTotalDOF,用于存储的是“对角元地址”
//nTotalElem为总的单元数量;pElem为指向Element型的结构体数组(单元数组)的首地址
{
int iMinDOF; //节点位移(自由度)编号中的最小编号
int iBuf;
int iDOFIndex; //当前单元的当前列上的自由度编号
int i,j;
for(i=0;i<nTotalDOF;i++) //刚度矩阵中主对角线元素地址置1
pDiag[i]=1;
for(i=0;i<nTotalElem;i++) //遍历所有单元,pElemDOF[i]与(pElem+i)对应同一个单元
{
//选取当前单元的俩节点的自由度编号中的最小值放入iMinDOF
iMinDOF=pElemDOF[i][0]; //取出单元定位向量矩阵中第i行(即,第i单元)的第0号自由度
if((pElem+i)->iType==TRUSS) ////当前单元类型为桁架单元
{
for(j=0;j<4;j++) //一个桁架单元的一行在pElemDOF数组中有4列自由度
{
if(pElemDOF[i][j]<iMinDOF)
iMinDOF=pElemDOF[i][j]; //从当前桁架单元的4个节点位移编号中选出最小号
}
}
else
{
for(j=0;j<6;j++) //一个钢架单元的一行在pElemDOF数组中有6列自由度
{
if(pElemDOF[i][j]<iMinDOF)
iMinDOF=pElemDOF[i][j]; //从当前钢架单元的6个节点位移编号中选出最小号
}
}
//计算总刚矩阵中的行半带宽
if((pElem+i)->iType==TRUSS) //当前单元类型为桁架单元
{
for(j=0;j<4;j++)
{
iDOFIndex=pElemDOF[i][j]; //取出当前单元的当前列上的自由度编号
//之所以如此计算半带宽,和前面函数实现的“节点自由度编号”有关
iBuf=iDOFIndex-iMinDOF+1; //计算刚度矩阵中当前行的半带宽,
if(iBuf>pDiag[iDOFIndex]&&iBuf<=iDOFIndex+1) //判断当前节点自由度对应的半带宽。
pDiag[iDOFIndex]=iBuf;
else if(iBuf>iDOFIndex+1) //这种情况不可能发生!因为iMinDOF>=0!
pDiag[iDOFIndex]=iDOFIndex+1;
}
}
else
{
for(j=0;j<6;j++)
{
iDOFIndex=pElemDOF[i][j]; //取出当前单元的当前列上的自由度编号
//之所以如此计算半带宽,和前面函数实现的“节点自由度编号”有关
iBuf=iDOFIndex-iMinDOF+1; //计算刚度矩阵中当前行的半带宽,
if(iBuf>pDiag[iDOFIndex]&&iBuf<=iDOFIndex+1) //判断当前节点自由度对应的半带宽。
pDiag[iDOFIndex]=iBuf;
else if(iBuf>iDOFIndex+1) //这种情况不可能发生!因为iMinDOF>=0!
pDiag[iDOFIndex]=iDOFIndex+1;
}
}
}
pDiag[0]=0; //将pDiag中存储的刚度矩阵第0行的主对角元地址设为0
for(i=1;i<nTotalDOF;i++) //从pDiag数组中的第二个节点自由度编号开始循环
//pDiag[i]存储的是刚度矩阵中当前行的半带宽,pDiag[i-1]存储的是刚度矩阵中上一行的半带宽
pDiag[i]=pDiag[i]+pDiag[i-1]; //得到辅助数组
}