我正在组装耦合多物理系统的雅可比。雅可比由每个系统的对角线上的块矩阵和耦合的对角线块组成。 我发现最好组装成分离块,然后用投影矩阵对它们求和以获得完整的雅可比。 伪代码(其中J [i]是对角元素,C [ij]是耦合,P是完整矩阵的投影)。
// diagonal blocks
J.setZero();
for(int i=0;i<N;++i){
J+=P[i]J[i]P[i].transpose()
}
// off diagonal elements
for(int i=0;i<N;++i){
for(int j=i+1;j<N;++j){
J+=P[i]C[ij]P[j].transpose()
J+=P[j]C[ji]P[i].transpose()
}
}
这需要很多性能,大约占整个程序的20%,这对于某些组装来说太多了。由于系统是高度非线性的,我必须每次重新计算jacobian。
Valgrind表示资源消耗方法是Eigen::internal::assign_sparse_to_sparse
,并且在此方法中调用Eigen::SparseMatrix<>::InsertBackByOuterInner
。
有没有更有效的方法来组装这样的矩阵?
(我还必须使用P *(J P.transpose())而不是P J * J.transpose()来使程序编译,可能已经出现了问题)
P.S:NDEBUG和优化已开启
编辑:通过将P.transpose存储在一个额外的矩阵中,我获得了更好的性能,但总和仍占该程序的15%
答案 0 :(得分:1)
通过在线工作,您的代码会更快。首先,估算最终矩阵和预留空间中每列的非零数(如果尚未完成):
int nnz_per_col = ...;
J.reserve(VectorXi::Constant(n_cols, nnz_per_col));
如果每列的nnz数量非常不均匀,那么您也可以按列计算:
VectorXi nnz_per_col(n_cols);
for each j
nnz_per_col(j) = ...;
J.reserve(nnz_per_col);
然后手动插入元素:
for each block B[k]
for each elements i,j
J.coeffRef(foo(i),foo(j)) += B[k](i,j)
其中foo
实现适当的索引映射。
对于下一次迭代,不需要保留,但是你需要在保留结构的同时将系数值设置为零:
J.coeffs().setZero();