PETSc
PETSc
PETSc 是用于开发并行计算程序的基础库,完全建立在 MPI 上。
安装与编译
- 下载 源码
基本结构
PETSc 模块
- index set (IS):向量的指标
- vectors (Vec)
- matrices (Mat): 稀疏矩阵
- Krylov subspace methods (KSP): 线性方程组求解
- preconditioners (PC): 预条件器和直接求解器
- nonlinear solvers(SNES)
- timesteppers for solving time-dependent PDES(TS): 时间演化求解器
- managing data structures(DM): 管理数据结构
- scalable optimization algorithms(Tao)
环境变量
- 所有 PETSc 程序都依赖环境变量
$PETSC_DIR
找到 PETSc 的目录 - 还需要环境变量
$PETSC_ARCH
找到安装路径
命令行
PETSc 程序需要用 MPI 执行,通过 mpiexec
或者 PETSc 提供的脚本 $PETSC_DIR/lib/petsc/bin/petscmpiexec
常用的命令行选项
-help
-version
-
-log_view
显示性能摘要 -
-fp_trap
浮点异常时退出 -
-malloc_dump
内存追踪 -
-malloc_debug
内存debug -start_in_debugger [noxterm, gdb, lldb]
-on_error_attach_debugger
-info
主函数
- PETSc 主函数需要调用
PetscInitialize()
作为开始,来初始化 PETSc 和 MPI - 程序退出时调用
PetscFinalize()
, 来结束 PETSc 和 MPI
示例如下:
#include "petsc.h"
/* 自定义帮助信息 */
static char help[] = "Solves a tridiagonal linear system with KSP.\n\n";
int
main(int argc, char** args)
{
/* 初始化 */
PetscErrorCode ierr;
PetscMPIInt size;
ierr = PetscInitialize(&argc, &args, (char*)0, help);
if (ierr)
return ierr;
ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size);
CHKERRMPI(ierr);
if (size != 1)
SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "This is a uniprocessor example only!");
/* 主程序
* ......
*/
/* 退出 */
ierr = PetscFinalize();
return ierr;
}
错误处理
所有 PETSc 函数都返回整数值 ierr
作为消息,使用 CHECKRRQ(ierr)
宏来处理错误。
向量和并行数据
向量是 PETSc 最基本的结构,用于保存计算结果等
创建和组装向量
PETSc 提供两种向量:顺序(Seq) 和并行(MPI)
-
VecCreateSeq(PETSC_COMM_SELF,PetscInt m,Vec *x);
- 创建顺序向量
-
VecCreateMPI(MPI_Comm comm,PetscInt m,PetscInt M,Vec *x);
- 创建并行向量
-
VecCreate(MPI_Comm comm,Vec *x);
- 创建向量, 自动选择顺序或并行
-
MPI_Comm comm
是 MPI 通信器
VecCreateSeqWithArray(PETSC_COMM_SELF,PetscInt bs,PetscInt n,PetscScalar *array,Vec *V);
-
VecCreateMPIWithArray(MPI_Comm comm,PetscInt bs,PetscInt n,PetscInt N,PetscScalar *array,Vec *vv);
- 根据输入的数据创建向量
-
VecSetSizes(Vec x, PetscInt m, PetscInt M);
- 设置向量的大小
-
m
是可选的局域大小,可以设为PETSC_DECIDE
-
M
是总大小
VecSetType()
-
VecSetFromOptions()
- 设置向量的类型
-
VecSet(Vec x,PetscScalar value);
- 将向量所有值都设成
value
- 将向量所有值都设成
-
VecSetValues(Vec x,PetscInt n,PetscInt *indices,PetscScalar *values,INSERT_VALUES);
- 设置向量的部分值
-
PetscInt n
要设置的值的个数 -
PetscInt *indices
设置的值的指标 -
PetscScalar *values
设置的值 -
InsertMode iota
设置值的方式:-
INSERT_VALUES
代替旧值 -
ADD_VALUES
将旧值加上新值
-
-
VecAssemblyBegin(Vec x);
- 开始组装向量, 在
VecSetValues()
之后调用
- 开始组装向量, 在
-
VecAssemblyEnd(Vec x);
- 结束组装向量,在
VecAssemblyBegin()
之后调用
- 结束组装向量,在
-
VecView(Vec x,PetscViewer v);
- 打印输出向量
-
VecDuplicate(Vec old, Vec *new);
- 创建一个新的向量,其类型与旧的相同
-
VecDuplicateVecs(Vec old,PetscInt n,Vec **new);
- 创建多个向量,与原向量类型相同
-
VecDestroy(Vec *x);
- 销毁向量
-
VecDestroyVecs(PetscInt n,Vec **vecs);
- 销毁多个向量
Vec x;
ierr = VecCreate(PETSC_COMM_WORLD, &x);
CHKERRQ(ierr);
ierr = PetscObjectSetName((PetscObject)x, "test");
CHKERRQ(ierr);
ierr = VecSetSizes(x, PETSC_DECIDE, H_len);
CHKERRQ(ierr);
ierr = VecSetFromOptions(x);
CHKERRQ(ierr);
Vec y;
ierr = VecDuplicate(x, &y);
int ix[1] = { 0 };
double complex vx[1] = { 1 };
ierr = VecSetValues(x, 1, ix, vx, INSERT_VALUES);
CHKERRQ(ierr);
ierr = VecAssemblyBegin(x);
CHKERRQ(ierr);
ierr = VecAssemblyEnd(x);
CHKERRQ(ierr);
struct timespec t0_mvm;
timespec_get(&t0_mvm, TIME_UTC);
ierr = MatMult(H, x, y);
CHKERRQ(ierr);
ierr = VecDestroy(&x);
CHKERRQ(ierr);
ierr = VecDestroy(&y);
CHKERRQ(ierr);
基本向量操作
加减乘除之类的操作,参考函数表
-
VecGetOwnershipRange(Vec vec,PetscInt *low,PetscInt *high);
- 获取当前局域操作的范围
-
low
是第一个元素 -
high
是最后一个再 +1
-
VecGetArray(Vec v,PetscScalar **array);
- 获取当前局域的数组的所有权
- 这个函数直接获得了裸数据的指针
-
VecRestoreArray(Vec v, PetscScalar **array);
- 恢复当前局域的数组的所有权
-
VecGetArrayRead(Vec v, const PetscScalar **array);
- 只读地获取当前局域的数组
-
VecRestoreArrayRead(Vec v, const PetscScalar **array);
- 恢复获取的只读数组的所有权
-
VecGetLocalSize(Vec v,PetscInt *size);
- 获取当前局域的大小
-
VecGetSize(Vec v,PetscInt *size);
- 获取向量的总大小
索引和排序
PETSc 提供了一组工具用于处理向量指标的顺序,称作 Application Orderings(AO)
矩阵
PETSc 支持多种矩阵类型,包括稠密矩阵、稀疏矩阵等
创建和组装矩阵
-
MatCreate(MPI_Comm comm,Mat *A);
- 创建矩阵
- 默认的矩阵类似是 稀疏 AIJ
-
MatSetSizes(Mat A,PETSC_DECIDE,PETSC_DECIDE,PetscInt M,PetscInt N);
- 设置矩阵大小
- 第二、三个参数是局域的行列大小
-
MatSetType(A,MatType matype);
- 设置矩阵类型
- 矩阵类型
MatType
有很多
-
MatSetValues(Mat A,PetscInt m,PetscInt *im,PetscInt n,PetscInt *in,PetscScalar *values,INSERT_VALUES);
- 设置矩阵的值
- 规则类似
Vec
-
MatSetOption(Mat A,MAT_ROW_ORIENTED,PETSC_FALSE);
- 设置加入值的方式,行先或列先等
-
MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY);
- 开始组装矩阵,在所有的
MatSetValues()
调用完成后使用
- 开始组装矩阵,在所有的
-
MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY);
- 完成组装矩阵,在
MatAssemblyBegin()
之后调用
- 完成组装矩阵,在
-
MatGetOwnershipRange(Mat A,PetscInt *first_row,PetscInt *last_row);
- 获取矩阵的一部分
Mat A;
ierr = MatCreate(PETSC_COMM_WORLD, &A);
CHKERRQ(ierr);
ierr = MatSetSizes(A, PETSC_DECIDE, PETSC_DECIDE, A_len, A_len);
CHKERRQ(ierr);
ierr = MatSetFromOptions(A);
CHKERRQ(ierr);
ierr = MatSetUp(A);
CHKERRQ(ierr);
PetscScalar value[3] = {1,2,3};
PetscInt col[3] = {0,1,2};
PetscInt row[1] = {1};
ierr = MatSetValues(A, 1, row, 2, col, value, INSERT_VALUES);
CHKERRQ(ierr);
ierr = MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY);
CHKERRQ(ierr);
ierr = MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY);
CHKERRQ(ierr);
MatView(A,PETSC_VIEWER_STDOUT_WORLD);
ierr = MatDestroy(&A);
CHKERRQ(ierr);
矩阵类型
PETSc默认的矩阵类型是 AIJ(也就是 CSR)
- 稀疏矩阵
- Limited-Memory Variable Metric (LMVM) Matrices
- 稠密矩阵
- 块矩阵
基本矩阵操作
参考函数表
无矩阵的矩阵
-
MatCreateShell(MPI_Comm comm,PetscInt m,PetscInt n,PetscInt M,PetscInt N,void *ctx,Mat *mat);
- 创建矩阵的结构,无需添加矩阵元
-
M, N
是矩阵总维数 -
m, n
是局域维数 -
ctx
是用户定义的矩阵操作
-
MatShellSetOperation(Mat mat,MatOperation MATOP_MULT, (void(*)(void)) PetscErrorCode (*UserMult)(Mat,Vec,Vec));
- 注册用户定义的矩阵运算
-
MATOP_MULT
指的是矩阵乘向量
/* 定义每个矩阵都需要用到的一些数据 */
typedef struct A_data {} A_data_t;
/* 自定义的矩阵乘向量运算函数, y = A x */
PetscErrorCode
A_MatVec(Mat A, Vec x, Vec y)
{
/* 获取数据 */
A_data_t* A_data;
PetscErrorCode ierr;
MatShellGetContext(A, &A_data);
/* 获得本地的 x 向量数据 */
PetscInt x_start, x_end;
ierr = VecGetOwnershipRange(x, &x_start, &x_end);
CAKERRQ(ierr);
const PetscScalar* x_arr;
ierr = VecGetArrayRead(x, &x_arr);
CAKERRQ(ierr);
/* y 向量要先置零 */
ierr = VecSet(y, 0);
CAKERRQ(ierr);
/* 设置计算后 y 的值 */
{
PetscScalar new_value;
ierr = VecSetValues(y, 1, &new_value, ADD_VALUES);
CAKERRQ(ierr);
}
ierr = VecAssemblyBegin(y);
CAKERRQ(ierr);
ierr = VecAssemblyEnd(y);
CAKERRQ(ierr);
/* 结束计算要返还 x 的所有权 */
ierr = VecRestoreArrayRead(x, &x_arr);
CAKERRQ(ierr);
return 0;
}
/* 创建一个临时向量,来获取 PETSc 自动分配的局域大小, 这里是方矩阵,长方形矩阵要分别获取两边的尺寸 */
int A_local_size = 0;
{
Vec x;
VecCreateMPI(PETSC_COMM_WORLD,PETSC_DECIDE,A_len,&x);
VecGetLocalSize(x,&A_local_size);
VecDestroy(&x);
}
/* 创建无矩阵的矩阵 A,必须设置每个局域块 A 的大小 */
Mat A;
A_data_t A_data = {};
ierr = MatCreateShell(PETSC_COMM_WORLD, A_local_size, A_local_size, A_len, A_len, &A_data, &A);
CAKERRQ(ierr);
/* 为 A 绑定矩阵乘向量运算 */
ierr = MatShellSetOperation(Afree, MATOP_MULT, (void (*)(void))A_MatVec);
CAKERRQ(ierr);
线性求解器
使用线性求解器 KSP
解线性方程 \(Ax=b\)
-
KSPCreate(MPI_Comm comm,KSP *ksp);
- 初始化 KSP
-
KSPSetOperators(KSP ksp,Mat Amat,Mat Pmat);
- 设置矩阵
-
KSPSetFromOptions(KSP ksp);
- 设置求解器选项
-
KSPSolve(KSP ksp,Vec b,Vec x);
- 运行求解器
-
KSPDestroy(KSP ksp);
- 运算结束销毁求解器
petsc4py
基本使用
# 初始化
import sys
import petsc4py
petsc4py.init(sys.argv)
from petsc4py import PETSc
# 矩阵
A = PETSc.Mat().create(comm=comm)
A.setSizes((m*n,m*n))
A.setSizes(((m,M),(n,N))) # m,n: local, M,N: global
A.setFromOptions()
A.setValues(3, 4, 5.0, addv=True)
A.assemblyBegin(A.AssemblyType.FINAL)
A.assemblyEnd(A.AssemblyType.FINAL)
# 向量
u = PETSc.Vec().create(comm=comm)
u.setSizes(m*n)
u.setSizes((n,N), bsize=1) # n: local, N: global
u.setFromOptions()
# 矩阵乘向量
b = u.duplicate()
u.set(1.0)
b = A(u)
无矩阵的矩阵
class H():
def __init__(self):
pass
def mult(self, mat, X, Y):
Y = sth(X)
shell = H()
A = PETSc.Mat().createPython(
[x_len, y_len], comm=comm)
A.setPythonContext(shell)
A.setUp()
遍历向量
x = PETSc.Vec().create(comm=comm)
x.setSizes(n)
x.setFromOptions()
rstart,rend = x.getOwnershipRange()
nlocal = x.getLocalSize()
x_arr: np.ndarray = x.getArray(readonly=True)
其它问题
复数支持
只能通过编译选项 --with-scalar-type=complex
将 PetscScalar
设成复数
编译选项
debug 模式:
./configure PETSC_ARCH=arch-complex-debug --with-scalar-type=complex
release 模式:
./configure PETSC_ARCH=arch-complex-release --with-cc=gcc --with-cxx=g++ --with-fc=gfortran --with-debugging=0 COPTFLAGS='-O3 -march=native -mtune=native' CXXOPTFLAGS='-O3 -march=native -mtune=native' FOPTFLAGS='-O3 -march=native -mtune=native' --with-scalar-type=complex --download-mpich
64 位支持:
--with-64-bit-indices
python 支持:
–with-petsc4py=1
混合线程
PETSc 是纯 MPI 框架,不提供混合线程的函数,同时所有函数也不保证线程安全。
在指定 MPI 通信器上使用 PETSc
在调用 PetscInitialize()
之前设置
PETSC_COMM_WORLD = MPI_COMM_WORLD; // 或其它 comm
只使用 c 编译
依赖越复杂,莫名其妙的问题就越多,只需要 c 的话就简单不少。
./configure PETSC_ARCH=arch-complex-release --with-cc=mpicc --with-cxx=0 --with-fc=0 --with-debugging=0 COPTFLAGS='-O3 -march=native -mtune=native' --with-scalar-type=complex --with-64-bit-indices --with-make-dir=$HOME/app/make --with-blas-lapack-dir=/opt/intel/composer_xe_2015.1.133/mkl/lib/intel64
评论
Comments powered by Disqus