Привет! Объясни как работает данный код и где здесь используется конвейерный параллелизм 

#include <stdio.h>
#include "mpi.h"
#include <iostream>
#include <Windows.h>
#include "math.h"

using namespace std;

#define h 0.1
#define lx 100
#define ly 100
#define lmax 1000

int main(int* argc, char** argv)
{
	SetConsoleCP(1251);
	SetConsoleOutputCP(1251);

	int size, rank;
	if (MPI_Init(argc, &argv) != MPI_SUCCESS)
		return 1;
	if (MPI_Comm_size(MPI_COMM_WORLD, &size) != MPI_SUCCESS)
	{
		MPI_Finalize();
		return 2;
	}
	if (MPI_Comm_rank(MPI_COMM_WORLD, &rank) != MPI_SUCCESS)
	{
		MPI_Finalize();
		return 3;
	}


	int columns = (int)(lx / h) + 1; 
	int rows = (int)(ly / h) + 1; 

	double** umain = new double* [rows];
	for (int i = 0; i < rows; i++)
		umain[i] = new double[columns];

	for (int i = 0; i < rows; i++)
		for (int j = 0; j < columns; j++)
			umain[i][j] = 0.0;


	int* mass = new int[size];
	for (int i = 0; i < size; i++)
	{
		mass[i] = columns / size;
		if (i < columns % size)
			mass[i]++;
	}

	int localRows = mass[rank]; 
	int localColumns = rows;

	double* uBottom = new double[localColumns];
	double* uTop = new double[localColumns];

	double** u = new double* [localRows];
	for (int i = 0; i < localRows; i++)
		u[i] = new double[localColumns];

	for (int i = 0; i < localRows; i++)
		for (int j = 0; j < localColumns; j++)
			u[i][j] = 0.0;

	for (int j = 0; j < localColumns; j++)
	{
		uTop[j] = 0.0;
		uBottom[j] = 0.0;
	}

	int sum = 0;

	for (int i = 0; i < rank; i++)
		sum += mass[i];

	if (!rank)
	{
		for (int i = 0; i < localColumns; i++)
			u[0][i] = 30.0 * (1.0 - i * h);
	}
	if (rank != size - 1)
	{
		for (int i = 0; i < localColumns; i++)
			u[localRows - 1][i] = 20.0 * i * h;
	}
	for (int i = sum; i < sum + localRows; i++)
	{
		u[i - sum][localColumns - 1] = 30.0 * i * h * (100.0 - i * h);
		u[i - sum][0] = 20.0 * sqrt(i * h);
	}
	if (rank != size - 1)
	{
		uBottom[0] = 20.0 * sqrt((sum + localRows) * h);
		uBottom[localColumns - 1] = 30.0 * ((sum + localRows) * h) * (100.0 - (sum + localRows) * h);
	}
	if (rank != 0)
	{
		uTop[0] = 20 * sqrt((sum - 1) * h);
		uTop[localColumns - 1] = 30 * ((sum - 1) * h) * (100 - (sum - 1) * h);
	}

	MPI_Barrier(MPI_COMM_WORLD);


	double starttime, endtime;
	starttime = MPI_Wtime();


	for (int l = 0; l < lmax; l++)
	{

		if (rank == 0)
		{
			for (int i = 1; i < localRows - 1; i++)
				for (int j = 1; j < localColumns - 1; j++)
					u[i][j] = 0.25 * (u[i - 1][j] + u[i][j + 1] + u[i][j - 1] + u[i + 1][j]);

			for (int j = 1; j < localColumns - 1; j++)
				u[localRows - 1][j] = 0.25 * (u[localRows - 2][j] + u[localRows - 1][j + 1] + u[localRows - 1][j - 1] + uBottom[j]);

			MPI_Send(u[localRows - 1], 1, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD);

			MPI_Recv(uBottom, localColumns, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		}
		if (rank && rank != size - 1)
		{
			MPI_Recv(uTop, 1, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
			for (int j = 1; j < localColumns - 1; j++)
				u[0][j] = 0.25 * (u[0][j + 1] + u[0][j - 1] + u[1][j] + uTop[j]);
			MPI_Send(u[0], localColumns, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD);

			for (int i = 1; i < localRows - 1; i++)
				for (int j = 1; j < localColumns - 1; j++)
					u[i][j] = 0.25 * (u[i - 1][j] + u[i][j + 1] + u[i][j - 1] + u[i + 1][j]);

			for (int j = 1; j < localColumns - 1; j++)
				u[localRows - 1][j] = 0.25 * (u[localRows - 2][j] + u[localRows - 1][j + 1] + u[localRows - 1][j - 1] + uBottom[j]);
			MPI_Send(u[localRows - 1], localColumns, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD);

			MPI_Recv(uBottom, localColumns, MPI_DOUBLE, rank + 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
		}
		if (rank == size - 1)
		{
			MPI_Recv(uTop, localColumns, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
			for (int j = 1; j < localColumns - 1; j++)
				u[0][j] = 0.25 * (u[0][j + 1] + u[0][j - 1] + u[1][j] + uTop[j]);
			MPI_Send(u[0], localColumns, MPI_DOUBLE, rank - 1, 0, MPI_COMM_WORLD);

			for (int i = 1; i < localRows - 1; i++)
				for (int j = 1; j < localColumns - 1; j++)
					u[i][j] = 0.25 * (u[i - 1][j] + u[i][j + 1] + u[i][j - 1] + u[i + 1][j]);
		}
	}

	endtime = MPI_Wtime();

	for (int i = 0; i < size; i++)
	{
		if (rank == i)
		{
			for (int j = 0; j < localRows; j++)
				for (int k = 0; k < localColumns; k++)
					umain[rows - k - 1][sum + j] = u[j][k];
		}
	}

	MPI_Barrier(MPI_COMM_WORLD);

	delete[] uBottom;
	delete[] uTop;
	for (int i = 0; i < localRows; i++)
		delete[] u[i];
	delete[] u;

	MPI_Finalize();
	int count = 0;
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < columns; j++)
			if (umain[i][j] != 0)
				cout << umain[i][j] << " ";
		cout << endl;
	}
	cout << "����� ������: " << endtime - starttime << endl;

	for (int i = 0; i < rows; i++)
		delete[] umain[i];
	delete[] umain;

	system("pause");
	return 0;
}