Initialization
在petsc4py中,需要通过下面代码进行初始化:
1 | import sys, petsc4py |
可以通过下面两种方式,来从命令行中传入参数(保存到了opt变量中),通过PETSc.Options().getAll()获取字典保存的结果,或者通过opt.get*方法,来读取(并设置默认值)命令行参数内容。更全面的函数,在PETSc的文档里可以查看到。
下面两种方法都可以进行含有参数的调用:
1 | mpirun -np 4 python test.py -n 20 -test_x 30 |
下面几个开关可以方便参数调试:
-help:查看PETSc所有的帮助选项-options_view:启动时输出全部的 options-options_left:输出没有被使用的参数列表-options_file $FILENAME:从$FILENAME中读取参数列表,并加入到PETSc程序中,但是命令行参数的优先级更高
Options
PETSc里的一些常用对象(Vec, Mat, KSP ...),都可以通过.setFromOptions()方法,来通过命令行参数进行初始化。
如果需要特定的前缀进行区分,那么可以使用.setOptionsPrefix(...),之后再进行.setFromOptions()。
1 | import sys, petsc4py |
运行结果:
1 | python test.py -vec_type mpi -y_vec_type seqcuda -vec_view ascii:stdout |
Communicator
在PETSc中,几乎所有核心对象(Vec, Mat, KSP, ...) 都绑定在某一个 MPI communicator 上。communicator 决定了哪些进程参与这个对象/数据如何分布,以及对象之间能否一起工作。
PETSc定义了常用的两个comm,分别为:
PETSc.COMM_WORLD:所有 MPI 进程共同参与PETSc.COMM_SELF:每个进程各自独立(每个 rank 都有一个“单进程 communicator”)
下面代码,给出了在COMM_WORLD和在COMM_SELF上创建相同大小的Vec对象时,PETSc分配的向量类型和每个处理器上的向量大小:
1 | import sys, petsc4py |
运行结果为:
1 | mpirun -np 2 python test.py |
可以看到,在COMM_WORLD中创建向量对象,每个处理器会平均分配到向量的一部分内容(类型为mpi);而在COMM_SELF上创建向量对象,每个处理器会分配一个完整的向量(同时,类型会被设置为序列对象seq)。
为了在处理器上,正确地进行数据间通信,PETSc要求进行相同操作的对象位于同一个communicator中,比如:
MatMult(A, x, y):A/x/y处在相同 comm 中KSP.setOperators(A):ksp与A处在相同 comm 中SNES/TS与其内部用到的Vec/Mat/DM:处在相同 comm 中
在创建PETSc对象时,可以通过.create(comm=PETSc.COMM_WORLD)来确定communicator环境。
利用communicator环境,可以方便的进行代码调试和输出,比如:
PETSc.Sys.Print("hello", comm=PETSc.COMM_WORLD)只会在comm中rank=0的处理器上进行输出PETSc.Sys.syncPrint(f"hello {rank}", comm=comm)会按照rank的顺序,依次进行输出PETSc.Sys.syncFlush(comm=comm)需要在执行完syncPrint之后,刷新输出缓存
在更复杂的应用里,可能希望把 COMM_WORLD 的进程拆成多个组,每个组运行一个独立的 PETSc 子问题,例如:
- 同一脚本里同时求解多个互不耦合的线性系统
- 多物理场:不同子模型用不同进程数
- 一个组负责计算,另一个组负责 I/O 或后处理
通过不同的communicator分组,可以将没有耦合关系的求解过程进行解耦,减少额外的同步等待时间。
通过PETSc.Comm.tompi4py()可以将PETSc中的communicator,转换成为mpi4py中comm, 并进行进一步的切割,比如:
1 | import sys, petsc4py |
运行结果如下:
1 | mpirun -np 4 python test.py |
其中,我们通过comm.Split(color=color, key=key)进行分割,保证:
color决定属于哪一组,即相同 color 的进程会进入同一个子 communicatorkey决定子 communicator 内的 rank 排序,一般直接用原 rank 即可- 如果
color = MPI.UNDEFINED,它会得到MPI_COMM_NULL,表示“不属于任何子 communicator”,可以通过这个设置来将某个处理器单独剥离出来