MATLAB中有限元矩阵的高效组装

计算科学 算法 matlab 稀疏矩阵
2021-12-24 08:59:07

问题

查找与给定行匹配的矩阵行的最有效算法是什么?这与基于多个条件的表查找相同。

语境

有限元矩阵通常又大又稀疏,因此将整个矩阵存储在内存中是对计算机内存的低效使用。因此,我将矩阵生成为 3 元组,即(行索引、列索引、矩阵值)。由于底层网格的非结构化性质,可能需要多次递增相同的矩阵元素。

为此,必须搜索现有元素以查看 (row,column) 对是否已存在。如果是这样,则可以增加该值。如果不是,则为矩阵的新非零值创建一个新的 3 元组。

我的问题是:检查 (row,column) 对是否已经存在的有效方法是什么?如果是这样,数组的索引是什么?

这本质上与找到与给定行匹配的矩阵行相同。

目前,这在 MATLAB 中实现为

% rw is the array of row indices
% cl is the array of column indices
% rv is the row being searched for
% cv is the column being searched for
% i.e. searching for (rv,cv) in (rw,cl)

possible_row = find(rw == rv);
column = find(cl(possible_row) == cv);

% check that it was found first
if isempty(column)
    % doesn't exist yet
    element_index = -1; % flag that it needs to be created
else
    element_index = possible_row(column);
end

% return element_index

由于 的性质find,此过程比手动循环遍历数组要快得多。我可以编写一个 MEX 函数来执行此操作,但在此阶段我不希望这样做。

任何帮助将不胜感激。

4个回答

如果您使用 MATLAB 从行索引、列索引和值的数组组装有限元矩阵sparse,则无需实现自己的函数来处理重复索引 -sparse将自动对具有相同行和列索引的值求和(请参阅文档,尤其是第三段)。在内部,此命令使用请求的稀疏模式和给定条目创建一个稀疏压缩列矩阵。对于那些对更多细节感兴趣的人,本文描述了 MATLAB 稀疏矩阵的原始实现。

这确实是创建一个你不知道能带结构的稀疏矩阵的最快方法(你可以使用spdiags那些);创建一个空矩阵并分配条目要慢得多,因为 MATLAB 需要在每次分配后重新创建整个数据结构。这是一个小例子来说明这一点(spalloc(n,n,3*n)分配一个空的稀疏矩阵,存储 3n 个非零元素):

n = 20000;
%% Allocate, fill row-wise
tic;
A = spalloc(n,n,3*n);
for i = 1:n
    A(i,1:3) = rand(3,1);
end
toc

经过的时间是 6.738242 秒。

%% Allocate, fill column-wise
tic;
A = spalloc(n,n,3*n);
for i = 1:n
    A(1:3,i) = rand(3,1);
end
toc

经过的时间是 1.524576 秒。

%% Using sparse
tic;
row = zeros(3*n,1); col = zeros(3*n,1); val = zeros(3*n,1);
ind = 1;
for i = 1:n
    row(ind+(0:2)) = [1,2,3];
    col(ind+(0:2)) = [i,i,i];
    val(ind+(0:2)) = rand(3,1);
    ind = ind+3;
end
B = sparse(row,col,val,n,n);
toc

经过的时间是 0.170769 秒。

如果您用 重复此n=40000操作,您会看到前两种方法按二次方缩放,而最后一种方法按线性缩放。

可能有点晚了,但我们有一些 Matlab 包,包括“经典”节点元素,甚至边缘元素,见

http://www.mathworks.com/matlabcentral/fileexchange/27826-fast-assembly-of-stiffness-and-matrices-in-finite-element-method-using-nodal-elements

http://www.mathworks.com/matlabcentral/fileexchange/46635-fast-assembly-of-stiffness-and-matrices-in-finite-element-method-using-edge-elements

还附有报告,因此实施细节应该更清楚。主要信息是,这些 FEM 组件也可以在 Matlab 中非常快速地完成。例如,您可以在几秒钟内轻松生成 100 万 x 100 万个元素的 FEM 矩阵,具体取决于您的 CPU 速度。

最好的,一月

您当前使用的数据结构和算法效率非常低。对您的算法的简单分析表明,它在时间运行,并且由于函数从数组的开头迭代,如果您正在对您的基本元素,您处于最坏的情况。O(nnz2)find

对于您在 MATLAB 中尝试执行的操作(从有限元基础组装矩阵),最有效的一般策略是spalloc使用您所拥有的矩阵结构的任何知识来预分配一个空稀疏矩阵,然后逐列遍历基础以组装矩阵并简单地使用本机 MATLAB 矩阵语法进行更新。

如果您使用 C、C++ 或 Fortran 工作,我会对如何使您的代码缓存友好的一些其他建议,但这确实不是大多数 MATLAB 开发人员应该注意的细节。

第三个答案,这只是对此时出现的其他答案(Aron的这个和Christian 的这个)的回顾。

对于 FEM 实施,我建议执行以下步骤。

  1. 旅行元素连通性和计算稀疏结构A.
  2. 如果必须实现直接求解器,请重新编号 dof 以减少填充,并执行符号因式分解A,即计算因子的稀疏结构A.
  3. 使用目标稀疏结构分配BCRS格式的零稀疏矩阵(上三角部分)A用于迭代对称求解器,用于稀疏 Cholesky 的 Cholesky 因子的上三角部分等)
  4. 行进元素,计算元素刚度矩阵,组装成全局稀疏矩阵A.

这里重要的一点是,只有在稀疏结构已知时才必须组装矩阵,这样才能使用像 BCRS 这样的高效存储方案。当然这只是一个提示:您必须选择更适合您的求解器的稀疏矩阵存储。

Wikipedia 有一篇关于稀疏矩阵的介绍性文章,而在 Tim David 的精彩网站中,您会喜欢上可以帮助您实现其中一些概念的软件宝藏。

一些笔记。

一旦知道稀疏结构,您可以按照 Christian 的建议在 Matlab中分配稀疏矩阵,然后使用标准 matlab 符号有效地组装您的刚度矩阵。

关于如何有效计算稀疏结构的问题A仍然开放。我对Matlab不是很精通,所以我的建议是在python或C中实现它,应用一些图论概念,即将矩阵中的元素视为图中的边并计算邻接矩阵的稀疏结构。列表列表或键字典是常见的选择。