经过一些并行计算后,我需要将结果写入文件中,出于绘图的原因,我希望它们采用 ASCII 格式。由于性能问题,我想避免MPI_SEND
将数据发送到主进程并让他打印。因此,我想知道是否有一种方法可以使用 MPI 直接用 ASCII 编写。
Fortran 示例将非常好。
经过一些并行计算后,我需要将结果写入文件中,出于绘图的原因,我希望它们采用 ASCII 格式。由于性能问题,我想避免MPI_SEND
将数据发送到主进程并让他打印。因此,我想知道是否有一种方法可以使用 MPI 直接用 ASCII 编写。
Fortran 示例将非常好。
并行进程的输出问题在于它们都经过 ssh 隧道,并且无法保证它们到达的顺序。即使您使用 Send/Recv 对它们进行排序。
您可以执行以下操作:
mpirun -np 8 program_script
其中程序脚本:
#!/bin/bash
your_program > program.out$PMPI_RANK
然后
for i in `seq 1 8` ; do cat program.out$i ; done
这使用了大多数 MPI 实现为每个生成的进程设置一个唯一的环境变量这一事实。
可以使用 VTK 库及其并行 IO 内置机制将每个级别的文件写入不同的文件,ParaView 可以再次组合它们以显示可视化。另外,我应该说我没有 FORTRAN 示例,不幸的是 VTK 不正式支持 FORTRAN。因此,我将向您展示一个 C++ 示例,我不确定它对您有多大用处。为了实现这一点,您需要使用启用 MPI 的标志构建 VTK 才能访问vtkMPIController
类。我把整个函数是这样的:
首先,您需要定义名为的头文件VtkParallelWriter.h
:
// MPI Library
#include <mpi.h>
//VTK Library
#include <vtkXMLPStructuredGridWriter.h>
#include <vtkStructuredGrid.h>
#include <vtkSmartPointer.h>
#include <vtkDoubleArray.h>
#include <vtkPointData.h>
#include <vtkMPIController.h>
#include <vtkProgrammableFilter.h>
#include <vtkInformation.h>
struct Args {
vtkProgrammableFilter* pf;
int local_extent[6];
};
void execute (void* arg);
void vtkParallelWriter(int argc, char *argv[],std::vector<double*>colors, std::vector<char*> names, int LX, int LY, int LZ, double x_min, double x_max, double y_min, double y_max, double z_min, double z_max, double local_origin_x, double local_origin_y, double local_origin_z, int nn, int timesnapshot);
然后这个函数的主体进入VtkParallelWriter.cc
文件:
#include <vector>
#include <string>
#include <iostream>
#include "VtkParallelWriter.h"
// function to operate on the point attribute data
void execute (void* arg) {
Args* args = reinterpret_cast<Args*>(arg);
auto info = args->pf->GetOutputInformation(0);
auto output_tmp = args->pf->GetOutput();
auto input_tmp = args->pf->GetInput();
vtkStructuredGrid* output = dynamic_cast<vtkStructuredGrid*>(output_tmp);
vtkStructuredGrid* input = dynamic_cast<vtkStructuredGrid*>(input_tmp);
output->ShallowCopy(input);
output->SetExtent(args->local_extent);
}
void vtkParallelWriter(int argc, char *argv[],std::vector<double*>colors, std::vector<char*> names, int LX, int LY, int LZ, double x_min, double x_max, double y_min, double y_max, double z_min, double z_max, double local_origin_x, double local_origin_y, double local_origin_z, int nn, int timesnapshot) {
int global_extent[6] = {x_min, x_max, y_min, y_max, z_min, z_max};
bool flagX, flagY, flagZ;
int new_local_origin_x;
int new_local_origin_y;
int new_local_origin_z;
flagX = false;
flagY = false;
flagZ = false;
if (local_origin_x == 0) {
flagX = true;
}
if (local_origin_y == 0) {
flagY = true;
}
if (local_origin_z == 0) {
flagZ = true;
}
if (flagX == false) {
new_local_origin_x = local_origin_x - 1;
}
if (flagY == false) {
new_local_origin_y = local_origin_y - 1;
}
if (flagZ == false) {
new_local_origin_z = local_origin_z - 1;
}
if (flagX == true) {
new_local_origin_x = local_origin_x;
}
if (flagY == true) {
new_local_origin_y = local_origin_y;
}
if (flagZ == true) {
new_local_origin_z = local_origin_z;
}
int local_extent[6] = {new_local_origin_x, local_origin_x+LX-1, new_local_origin_y, local_origin_y+LY-1, new_local_origin_z, local_origin_z+LZ-1};
int dims[3] = {local_origin_x+LX-new_local_origin_x, local_origin_y+LY-new_local_origin_y, local_origin_z+LZ-new_local_origin_z};
// Create and Initialize vtkMPIController
auto contr = vtkSmartPointer<vtkMPIController>::New();
if (timesnapshot == 0) {
contr->Initialize(&argc, &argv, 1);
}
int nranks = contr->GetNumberOfProcesses();
int rank = contr->GetLocalProcessId();
// Create grid points, allocate memory and Insert them
auto points = vtkSmartPointer<vtkPoints>::New();
for (int k=0; k<dims[2]; ++k) {
for (int j=0; j<dims[1]; ++j) {
for (int i=0; i<dims[0]; ++i) {
points->InsertNextPoint(new_local_origin_x+i, new_local_origin_y+j, new_local_origin_z+k);
}
}
}
// Create a density field. Note that the number of cells is always less than
// number of grid points by an amount of one so we use dims[i]-1
std::vector<vtkSmartPointer<vtkDoubleArray>> vtkColors;
for (int iterator = 0; iterator < colors.size(); iterator++) {
auto tempColor = vtkSmartPointer<vtkDoubleArray>::New();
tempColor->SetNumberOfComponents(1);
tempColor->SetName(names[iterator]);
int LXP = nn+LX+nn;
int LYP = nn+LY+nn;
int LZP = nn+LZ+nn;
for (int k=0; k<dims[2]; ++k) {
int K;
if (flagZ == true) {
K = k + nn;
}
if (flagZ == false) {
K = k;
}
for (int j=0; j<dims[1]; ++j) {
int J;
if (flagY == true) {
J = j + nn;
}
if (flagY == false) {
J = j;
}
for (int i=0; i<dims[0]; ++i) {
int I;
if (flagX == true) {
I = i + nn;
}
if (flagX == false) {
I = i;
}
int IDflattened = I+J*LXP+K*LXP*LYP;
tempColor->InsertNextTuple1(colors[iterator][IDflattened]);
}
}
}
vtkColors.push_back(tempColor);
}
// Create a vtkProgrammableFilter
auto pf = vtkSmartPointer<vtkProgrammableFilter>::New();
// Initialize an instance of Args
Args args;
args.pf = pf;
for(int i=0; i<6; ++i) args.local_extent[i] = local_extent[i];
pf->SetExecuteMethod(execute, &args);
// Create a structured grid and assign point data and cell data to it
auto structuredGrid = vtkSmartPointer<vtkStructuredGrid>::New();
structuredGrid->SetExtent(global_extent);
pf->SetInputData(structuredGrid);
structuredGrid->SetPoints(points);
for (int iterator = 0; iterator < vtkColors.size(); iterator++) {
structuredGrid->GetPointData()->AddArray(vtkColors[iterator]);
}
std::string fileName = std::string("./out/output_") + std::to_string(timesnapshot) + ".pvts";
// Create the parallel writer and call some functions
auto parallel_writer = vtkSmartPointer<vtkXMLPStructuredGridWriter>::New();
parallel_writer->SetInputConnection(pf->GetOutputPort());
parallel_writer->SetController(contr);
parallel_writer->SetFileName(fileName.c_str());
parallel_writer->SetNumberOfPieces(nranks);
parallel_writer->SetStartPiece(rank);
parallel_writer->SetEndPiece(rank);
parallel_writer->SetDataModeToBinary();
parallel_writer->Update();
parallel_writer->Write();
}
此示例适用于 3D 结构化网格,但如果您设置LZ
为 1 和z_min
0 z_max
,它也应该适用于 2D 结构化网格。您基本上将color
2D 中的变量展平以存储在由定义的一维数组中double*
,如果您有其中一些,则将它们中的每一个存储在数组向量中,以基本上将其提供给std::vector<double*>colors
. 此代码已针对使用启用 MPI 构建的 VTK 8.9 的 3D 结构化网格进行测试和验证。
替代方法是让每个 MPI 进程确定它要写入的字节数,使用 MPI_Allgather 分发此信息,并使用 MPI_File_open、MPI_File_seek、MPI_File_write 和 MPI_File_close 使用此信息并行写入一个大文件.
不过,请提前考虑如何阅读此文件。设计一种独立于分区的文件格式是有意义的,因为它可以被与原始模拟大小无关的任意数量的进程读取。如果可能,将必要的标头信息放在开头,并避免让每个进程将自己的标头放在其数据的前面。然后,您可以在许多进程使用独立读取之前读取一个级别的标题并 MPI_Bcast 它。
对于 VTK 的特殊情况,每个 MPI 进程编写一个文件可能并不总是可行的。曾经接到超算中心的电话,因为我不小心每秒写了 32768 个 VTU 文件。