本程序使用C++编写,GUI界面使用Qt库,线性代数运算使用Eigen库,实现了二维桁架结构、钢架结构的通用有限元程序,能够计算静载荷下的位移,以及固有频率和固有振型。
理论基础
桁架结构
二维桁架结构的的刚度矩阵为
质量矩阵为
其中,$\rho$为线密度。坐标变换矩阵为
那么在总体坐标系下的质量矩阵,载荷向量,位移向量为 \begin{equation} \bm{\tilde m}=\bm{T}^T\bm{mT}=\bm{m},\quad \bm{\tilde f}=\bm{T}^T\bm{f},\quad \bm{\tilde v}=\bm{T}^T\bm{v} \end{equation}
弯曲单元
采用三次梁单元,建立梁单元的刚度矩阵为
单元质量矩阵为
对于二维平面上的梁,有坐标转换矩阵为
那么在总体坐标系下的刚度矩阵为 \begin{equation} \bm{\tilde k}=\bm{T}^T\bm{kT} \end{equation} 同理有 \begin{equation} \bm{\tilde m}=\bm{T}^T\bm{mT},\quad \bm{\tilde f}=\bm{T}^T\bm{f},\quad \bm{\tilde v}=\bm{T}^T\bm{v} \end{equation}
约束的处理
如果无约束的有限元方程是下面的形式:
那么应用约束条件后,有限元方程应该是这样的形式:
有限元部分代码
class trussElement;
class node
{
friend class trussElement;
friend class trussStructure;
public:
node(double x1, double y1)
:m_x(x1), m_y(y1) {}
private:
double m_x, m_y;
};
struct Constraint
{
bool UX;
bool UY;
int node;
Constraint(bool X, bool Y, int n){
UX = X; UY = Y;
node = n;
}
};
std::vector<node> globalNodeList;
// Here I should use singleton later
class trussElement
{
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
public:
trussElement(std::vector<node>& nodeList,
int node1, int node2, double EA, double rho);
int getNumNode1() {return mNumNode1;}
int getNumNode2() {return mNumNode2;}
//private:
int mNumNode1;
int mNumNode2;
double m_theta;
double m_x1, m_x2, m_y1, m_y2;
double m_EA, m_rho;
double m_l;
Eigen::Matrix<double, 4, 4> m_K;
Eigen::Matrix<double, 4, 4> m_mass;
};
trussElement::trussElement(std::vector<node>& nodeList,
int node1, int node2, double EA, double rho) :
m_x1(nodeList[node1].m_x), m_y1(nodeList[node1].m_y),
m_x2(nodeList[node2].m_x), m_y2(nodeList[node2].m_y),
mNumNode1(node1), mNumNode2(node2),
m_EA(EA), m_rho(rho)
{
m_theta = std::atan2(m_y2 - m_y1, m_x2 - m_x1);
m_l = sqrt((m_y2 - m_y1) * (m_y2 - m_y1)
+ (m_x2 - m_x1) * (m_x2 - m_x1));
double c = cos(m_theta);
double s = sin(m_theta);
m_K << c * c, s * c, -c * c, -s * c,
s * c, s * s, -s * c, -s * s,
-c * c, -s * c, c * c, s * c,
-s * c, -s * s, s * c, s * s;
m_K *= m_EA / m_l;
Eigen::Matrix<double, 4, 4> mass0, trans;
mass0 << 2., .0, 1., .0,
.0, 2., .0, 1.,
1., .0, 2., .0,
.0, 1., .0, 2.;
mass0 *= m_rho * m_l / 6.0;
trans << c, s, .0, .0,
-s, c, .0, .0,
.0, .0, c, s,
.0, .0, -s, c;
m_mass = trans.transpose() * mass0 * trans;
}
class trussStructure
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
void addTrussElement(const trussElement& elem);
void addConstraint(Constraint con);
void removeTrussElement(int i);
void recalcAllMatrices();
void addLoad(int numNode, double Fx, double Fy);
//private:
void recalcLoad();
void recalcStiffness();
void recalcMass();
std::vector<trussElement> trussArray;
std::vector<Constraint> constrainsArray;
std::map<int, double> xLoadOnNode;
std::map<int, double> yLoadOnNode;
std::vector<int> indicesToConstraint;
// assemble matrices are no constraints applied,
// while global matrices are applied.
Eigen::MatrixXd m_assembleStiffness;
Eigen::MatrixXd m_assembleMass;
Eigen::MatrixXd m_globalStiffness;
Eigen::MatrixXd m_globalMass;
Eigen::MatrixXd m_assembleLoad;
Eigen::MatrixXd m_globalLoad;
Eigen::MatrixXd m_displacement;
};
void trussStructure::addTrussElement(const trussElement &elem)
{
trussArray.push_back(elem);
}
void trussStructure::addConstraint(Constraint con)
{
constrainsArray.push_back(con);
if (con.UX){
indicesToConstraint.push_back(2 * con.node + 0);
}
if (con.UY){
indicesToConstraint.push_back(2 * con.node + 1);
}
}
void trussStructure::addLoad(int numNode, double Fx, double Fy)
{
xLoadOnNode[numNode] += Fx;
yLoadOnNode[numNode] += Fy;
}
void trussStructure::recalcAllMatrices()
{
std::sort(indicesToConstraint.begin(), indicesToConstraint.end());
recalcLoad();
recalcMass();
recalcStiffness();
}
void trussStructure::recalcStiffness()
{
int n = globalNodeList.size();
m_assembleStiffness = Eigen::MatrixXd::Zero(2 * n, 2 * n);
for(auto elem : trussArray){
Eigen::MatrixXd mapMatrix = Eigen::MatrixXd::Zero(4, 2 * n);
int a1 = elem.getNumNode1();
int a2 = elem.getNumNode2();
mapMatrix(0, 2 * a1) = 1.0;
mapMatrix(1, 2 * a1 + 1) = 1.0;
mapMatrix(2, 2 * a2) = 1.0;
mapMatrix(3, 2 * a2 + 1) = 1.0;
m_assembleStiffness += mapMatrix.transpose() *
elem.m_K * mapMatrix;
}
m_globalStiffness = m_assembleStiffness;
int j = 0;
for(int index : indicesToConstraint){
for(int row = 0; row < 2 * n; ++row){
for(int col = 0; col < 2 * n; ++col){
if (row == index || col == index){
m_globalStiffness(row, col) =
(row == col) ? 1.0 : 0.0;
}
}
}
//removeRow(m_globalStiffness,index - j);
//removeColumn(m_globalStiffness, index - j);
j++;
}
}
void trussStructure::recalcMass()
{
int n = globalNodeList.size();
m_assembleMass = Eigen::MatrixXd::Zero(2 * n, 2 * n);
for(auto elem : trussArray){
Eigen::MatrixXd mapMatrix = Eigen::MatrixXd::Zero(4, 2 * n);
int a1 = elem.getNumNode1();
int a2 = elem.getNumNode2();
mapMatrix(0, 2 * a1) = 1.0;
mapMatrix(1, 2 * a1 + 1) = 1.0;
mapMatrix(2, 2 * a2) = 1.0;
mapMatrix(3, 2 * a2 + 1) = 1.0;
m_assembleMass += mapMatrix.transpose() *
elem.m_mass * mapMatrix;
std::cout << mapMatrix.transpose() *
elem.m_mass * mapMatrix << "\n\n";
}
m_globalMass = m_assembleMass;
int j = 0;
for(int index : indicesToConstraint){
for(int row = 0; row < 2 * n; ++row){
for(int col = 0; col < 2 * n; ++col){
if (row == index || col == index){
m_globalMass(row, col) =
(row == col) ? 1.0 : 0.0;
}
}
}
//removeRow(m_globalMass, index - j);
//removeColumn(m_globalMass, index - j);
j++;
}
}
void trussStructure::recalcLoad()
{
int n = globalNodeList.size();
m_assembleLoad = Eigen::MatrixXd::Zero(2 * n, 1);
for(int i = 0; i < n; ++i){
m_assembleLoad(2 * i) += xLoadOnNode[i];
m_assembleLoad(2 * i + 1) += yLoadOnNode[i];
}
m_globalLoad = m_assembleLoad;
int j = 0;
for(int index : indicesToConstraint){
for(int row = 0; row < 2 * n; ++row){
if (row == index){
m_globalLoad(row, 0) = 0.0;
}
}
//removeRow(m_globalLoad, index - j);
j++;
}
}
这里留出比较方便的接口,例如建立一个结构的代码如下:
double l = .01;
double r = .005;
double a = 3.141592693975358 * r * r;
double e = 7e3 * 1e7;
double EA = e * a;
auto rho = 2778.62;
trussStructure structure1;
globalNodeList.push_back(node(0, 0));
globalNodeList.push_back(node(.5 * l, .5 * sqrt(3.0) * l));
globalNodeList.push_back(node(l, 0));
structure1.addTrussElement(trussElement(globalNodeList,
0, 1, EA, rho));
structure1.addTrussElement(trussElement(globalNodeList,
0, 2, EA, rho));
structure1.addTrussElement(trussElement(globalNodeList,
1, 2, EA, rho));
structure1.addLoad(1, .0, 1e-3);
structure1.addConstraint(Constraint(1, 1, 0));
structure1.addConstraint(Constraint(0, 1, 2));
structure1.recalcAllMatrices();
就可以方便地建立一个桁架结构。不过还是需要进一步改善接口,增强其可扩展性。