Skip to content
Snippets Groups Projects
Commit 5fbab031 authored by Podhorszki, Norbert's avatar Podhorszki, Norbert
Browse files

Refactored heat transfer example. A config file is now command line argument....

Refactored heat transfer example. A config file is now command line argument. Reader's decomposition is also command line argument.
parent e5f561b0
No related branches found
No related tags found
1 merge request!326Refactored heat transfer example. A config file is now command line a…
examples/heatTransfer examples/heatTransfer
Test that solves a 2D Poisson equation for temperature in homogeneous media This example solves a 2D Poisson equation for temperature in homogeneous media
using finite differences. This examples shows a straight-forward way to hook using finite differences. This examples shows a straight-forward way to hook
an application to the ADIOS2 library for its IO. an application to the ADIOS2 library for its IO.
1. read: illustrates the Read API that allows running the reader either as 1. write: illustrates the Write API as well as has implementations of other IO libraries
* post-mortem to read all output steps
* in situ to read step by step as the writer outputs them
2. write: illustrates the Write API as well as has implementations of other IO libraries
* adios 1.x * adios 1.x
* hdf5 * hdf5
* phdf5 * phdf5
2. read: illustrates the Read API that allows running the reader either as
* post-mortem to read all output steps
* in situ to read step by step as the writer outputs them
(need to run with a suitable engine)
3. read_fileonly: illustrates reading all output steps at once (a single read 3. read_fileonly: illustrates reading all output steps at once (a single read
statement) into a single contiguous memory block. This approach only works statement) into a single contiguous memory block. This approach only works
for post-mortem processing. for post-mortem processing.
Example
1. Produce an output
Writer usage: heatTransfer config output N M nx ny steps iterations
config: XML config file to use
output: name of output data file/stream
N: number of processes in X dimension
M: number of processes in Y dimension
nx: local array size in X dimension per processor
ny: local array size in Y dimension per processor
steps: the total number of steps to output
iterations: one step consist of this many iterations
$ mpirun -np 12 ./bin/heatTransfer_write_adios2 ../examples/heatTransfer/heat.xml heat 4 3 5 10 10 10
2. Read the output step-by-step and print data into text files (data.<rank> per reader process)
Reader Usage: heatRead config input N M
config: XML config file to use
input: name of input data file/stream
N: number of processes in X dimension
M: number of processes in Y dimension
$ mpirun -np 2 ./bin/heatTransfer_read ../examples/heatTransfer/heat.xml heat 2 1
Notes:
1. Engines for file-based output and post-mortem reading: i
* BPFileWriter/BPFileReader
* HDF5Writer/HDF5Reader
* ADIOS1Writer/ADIOS1Reader
2. Engines for in situ execution
* DataManWriter/DataManReader
(Must run writer and reader with the same number of processes and same decomposition)
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
#------------------------------------------------------------------------------# #------------------------------------------------------------------------------#
if(ADIOS2_HAVE_MPI) if(ADIOS2_HAVE_MPI)
add_executable(heatTransfer_read heatRead.cpp PrintDataStep.h) add_executable(heatTransfer_read
heatRead.cpp
PrintDataStep.h
ReadSettings.cpp
)
target_link_libraries(heatTransfer_read adios2 MPI::MPI_C) target_link_libraries(heatTransfer_read adios2 MPI::MPI_C)
target_compile_definitions(heatTransfer_read PRIVATE target_compile_definitions(heatTransfer_read PRIVATE
-DDEFAULT_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/../heat.xml) -DDEFAULT_CONFIG=${CMAKE_CURRENT_SOURCE_DIR}/../heat.xml)
......
...@@ -22,29 +22,26 @@ ...@@ -22,29 +22,26 @@
#include <vector> #include <vector>
#include "PrintDataStep.h" #include "PrintDataStep.h"
#include "ReadSettings.h"
#define str_helper(X) #X void printUsage()
#define str(X) str_helper(X) {
#ifndef DEFAULT_CONFIG std::cout << "Usage: heatRead config input N M \n"
#define DEFAULT_CONFIG "../heat.xml" << " config: XML config file to use\n"
#endif << " input: name of input data file/stream\n"
#define DEFAULT_CONFIG_STR str(DEFAULT_CONFIG) << " N: number of processes in X dimension\n"
<< " M: number of processes in Y dimension\n\n";
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
MPI_Init(&argc, &argv); MPI_Init(&argc, &argv);
if (argc < 2) /* When writer and reader is launched together with a single mpirun command,
{ the world comm spans all applications. We have to split and create the
std::cout << "Not enough arguments: need an input file\n"; local 'world' communicator for the reader only.
return 1; When writer and reader is launched separately, the mpiReaderComm
} communicator will just equal the MPI_COMM_WORLD.
const char *inputfile = argv[1];
/* World comm spans all applications started with the same aprun command
on a Cray XK6. So we have to split and create the local
'world' communicator for the reader only.
In normal start-up, the communicator will just equal the MPI_COMM_WORLD.
*/ */
int wrank, wnproc; int wrank, wnproc;
...@@ -60,100 +57,87 @@ int main(int argc, char *argv[]) ...@@ -60,100 +57,87 @@ int main(int argc, char *argv[])
MPI_Comm_rank(mpiReaderComm, &rank); MPI_Comm_rank(mpiReaderComm, &rank);
MPI_Comm_size(mpiReaderComm, &nproc); MPI_Comm_size(mpiReaderComm, &nproc);
adios2::ADIOS ad(std::string(DEFAULT_CONFIG_STR), mpiReaderComm, try
adios2::DebugON);
// Define method for engine creation
// 1. Get method def from config file or define new one
adios2::IO &bpReaderIO = ad.DeclareIO("reader");
if (!bpReaderIO.InConfigFile())
{ {
// if not defined by user, we can change the default settings ReadSettings settings(argc, argv, rank, nproc);
// BPFileWriter is the default engine adios2::ADIOS ad(settings.configfile, mpiReaderComm, adios2::DebugON);
bpReaderIO.SetEngine("ADIOS1Reader");
bpReaderIO.SetParameters({{"num_threads", "2"}});
// ISO-POSIX file output is the default transport (called "File")
// Passing parameters to the transport
bpReaderIO.AddTransport("File", {{"verbose", "4"}});
}
adios2::Engine &bpReader = // Define method for engine creation
bpReaderIO.Open(inputfile, adios2::Mode::Read, mpiReaderComm); // 1. Get method def from config file or define new one
unsigned int gndx; adios2::IO &bpReaderIO = ad.DeclareIO("reader");
unsigned int gndy; if (!bpReaderIO.InConfigFile())
double *T; {
adios2::Dims readsize; // if not defined by user, we can change the default settings
adios2::Dims offset; // BPFileWriter is the default engine
adios2::Variable<double> *vT = nullptr; bpReaderIO.SetEngine("ADIOS1Reader");
bool firstStep = true; bpReaderIO.SetParameters({{"num_threads", "2"}});
int step = 0;
// ISO-POSIX file output is the default transport (called "File")
// Passing parameters to the transport
bpReaderIO.AddTransport("File", {{"verbose", "4"}});
}
while (true) adios2::Engine &bpReader = bpReaderIO.Open(
{ settings.inputfile, adios2::Mode::Read, mpiReaderComm);
adios2::StepStatus status =
bpReader.BeginStep(adios2::StepMode::NextAvailable);
if (status != adios2::StepStatus::OK)
break;
if (firstStep) double *T;
{ adios2::Variable<double> *vT = nullptr;
adios2::Variable<unsigned int> *vgndx = bool firstStep = true;
bpReaderIO.InquireVariable<unsigned int>("gndx"); int step = 0;
gndx = vgndx->m_Value;
// bpReader.GetSync<unsigned int>("gndx", gndx);
adios2::Variable<unsigned int> *vgndy = while (true)
bpReaderIO.InquireVariable<unsigned int>("gndy"); {
gndy = vgndy->m_Value; adios2::StepStatus status =
// bpReader.GetSync<unsigned int>("gndy", gndy); bpReader.BeginStep(adios2::StepMode::NextAvailable);
if (status != adios2::StepStatus::OK)
break;
if (rank == 0) if (firstStep)
{ {
std::cout << "gndx = " << gndx << std::endl; vT = bpReaderIO.InquireVariable<double>("T");
std::cout << "gndy = " << gndy << std::endl; unsigned int gndx = vT->m_Shape[0];
unsigned int gndy = vT->m_Shape[1];
if (rank == 0)
{
std::cout << "gndx = " << gndx << std::endl;
std::cout << "gndy = " << gndy << std::endl;
}
settings.DecomposeArray(gndx, gndy);
T = new double[settings.readsize[0] * settings.readsize[1]];
// Create a 2D selection for the subset
vT->SetSelection(adios2::Box<adios2::Dims>(settings.offset,
settings.readsize));
firstStep = false;
MPI_Barrier(mpiReaderComm); // sync processes just for stdout
} }
// 1D decomposition of the columns, which is inefficient for if (!rank)
// reading!
readsize.push_back(gndx);
readsize.push_back(gndy / nproc);
offset.push_back(0LL);
offset.push_back(rank * readsize[1]);
if (rank == nproc - 1)
{ {
// last process should read all the rest of columns std::cout << "Processing step " << step << std::endl;
readsize[1] = gndy - readsize[1] * (nproc - 1);
} }
// Arrays are read by scheduling one or more of them
std::cout << "rank " << rank << " reads " << readsize[1] // and performing the reads at once
<< " columns from offset " << offset[1] << std::endl; bpReader.GetDeferred<double>(*vT, T);
bpReader.PerformGets();
vT = bpReaderIO.InquireVariable<double>("T");
T = new double[readsize[0] * readsize[1]]; printDataStep(T, settings.readsize.data(), settings.offset.data(),
rank, step);
// Create a 2D selection for the subset bpReader.EndStep();
vT->SetSelection(adios2::Box<adios2::Dims>(offset, readsize)); step++;
firstStep = false;
} }
bpReader.Close();
if (!rank) delete[] T;
{
std::cout << "Processing step " << step << std::endl;
}
// Arrays are read by scheduling one or more of them
// and performing the reads at once
bpReader.GetDeferred<double>(*vT, T);
bpReader.PerformGets();
printDataStep(T, readsize.data(), offset.data(), rank, step);
bpReader.EndStep();
step++;
} }
bpReader.Close(); catch (std::invalid_argument &e) // command-line argument errors
delete[] T; {
std::cout << e.what() << std::endl;
printUsage();
}
MPI_Finalize(); MPI_Finalize();
return 0; return 0;
} }
...@@ -34,7 +34,7 @@ IO::IO(const Settings &s, MPI_Comm comm) ...@@ -34,7 +34,7 @@ IO::IO(const Settings &s, MPI_Comm comm)
// Define method for engine creation // Define method for engine creation
adios2::IO &bpio = *ad->InquireIO("output"); adios2::IO &bpio = ad->DeclareIO("output");
if (!bpio.InConfigFile()) if (!bpio.InConfigFile())
{ {
// if not defined by user, we can change the default settings // if not defined by user, we can change the default settings
...@@ -75,8 +75,6 @@ IO::~IO() ...@@ -75,8 +75,6 @@ IO::~IO()
void IO::write(int step, const HeatTransfer &ht, const Settings &s, void IO::write(int step, const HeatTransfer &ht, const Settings &s,
MPI_Comm comm) MPI_Comm comm)
{ {
#if 1
bpWriter->BeginStep(); bpWriter->BeginStep();
/* This selection is redundant and not required, since we defined /* This selection is redundant and not required, since we defined
* the selection already in DefineVariable(). It is here just as an example. * the selection already in DefineVariable(). It is here just as an example.
...@@ -87,29 +85,17 @@ void IO::write(int step, const HeatTransfer &ht, const Settings &s, ...@@ -87,29 +85,17 @@ void IO::write(int step, const HeatTransfer &ht, const Settings &s,
varT->SetSelection( varT->SetSelection(
adios2::Box<adios2::Dims>({s.offsx, s.offsy}, {s.ndx, s.ndy})); adios2::Box<adios2::Dims>({s.offsx, s.offsy}, {s.ndx, s.ndy}));
/* Select the area that we want to write from the data pointer we pass to if (!step)
the {
writer. int rank;
Think HDF5 memspace, just not hyperslabs, only a bounding box selection. MPI_Comm_rank(comm, &rank);
Engine will copy this bounding box from the data pointer into the output if (!rank)
buffer. {
Size of the bounding box should match the "space" selection which was bpWriter->PutSync<unsigned int>(*varGndx, s.gndx);
given bpWriter->PutSync<unsigned int>("gndy", s.gndy);
above. }
Default memspace is always the full selection. }
*/
varT->SetMemorySelection(adios2::Box<adios2::Dims>({1, 1}, {s.ndx, s.ndy}));
bpWriter->PutSync<unsigned int>(*varGndx, s.gndx);
bpWriter->PutSync<unsigned int>("gndy", s.gndy);
bpWriter->PutSync<double>(*varT, ht.data_noghost().data());
bpWriter->EndStep();
#else
bpWriter->PutSync<double>(*varT, ht.data_noghost().data()); bpWriter->PutSync<double>(*varT, ht.data_noghost().data());
// bpWriter->PerformPuts();
bpWriter->EndStep(); bpWriter->EndStep();
#endif
} }
...@@ -35,37 +35,20 @@ static unsigned int convertToUint(std::string varName, char *arg) ...@@ -35,37 +35,20 @@ static unsigned int convertToUint(std::string varName, char *arg)
Settings::Settings(int argc, char *argv[], int rank, int nproc) : rank{rank} Settings::Settings(int argc, char *argv[], int rank, int nproc) : rank{rank}
{ {
if (argc < 8) if (argc < 9)
{ {
throw std::invalid_argument("Not enough arguments"); throw std::invalid_argument("Not enough arguments");
} }
this->nproc = (unsigned int)nproc; this->nproc = (unsigned int)nproc;
outputfile = argv[1]; configfile = argv[1];
npx = convertToUint("N", argv[2]); outputfile = argv[2];
npy = convertToUint("M", argv[3]); npx = convertToUint("N", argv[3]);
ndx = convertToUint("nx", argv[4]); npy = convertToUint("M", argv[4]);
ndy = convertToUint("ny", argv[5]); ndx = convertToUint("nx", argv[5]);
steps = convertToUint("steps", argv[6]); ndy = convertToUint("ny", argv[6]);
iterations = convertToUint("iterations", argv[7]); steps = convertToUint("steps", argv[7]);
iterations = convertToUint("iterations", argv[8]);
if (argc == 9)
{
const std::string asyncArg(argv[8]);
if (asyncArg == "ON" || asyncArg == "on")
{
async = true;
}
else if (asyncArg == "OFF" || asyncArg == "off")
{
// nothing off is default
}
else
{
throw std::invalid_argument("ERROR: wrong async argument " +
asyncArg + " must be on or off\n");
}
}
if (npx * npy != this->nproc) if (npx * npy != this->nproc)
{ {
......
...@@ -18,6 +18,7 @@ class Settings ...@@ -18,6 +18,7 @@ class Settings
public: public:
// user arguments // user arguments
std::string configfile;
std::string outputfile; std::string outputfile;
unsigned int npx; // Number of processes in X (slow) dimension unsigned int npx; // Number of processes in X (slow) dimension
unsigned int npy; // Number of processes in Y (fast) dimension unsigned int npy; // Number of processes in Y (fast) dimension
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
*/ */
#include <mpi.h> #include <mpi.h>
#include <future> //std::future, std::async
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
...@@ -24,26 +23,28 @@ ...@@ -24,26 +23,28 @@
void printUsage() void printUsage()
{ {
std::cout << "Usage: heatTransfer output N M nx ny steps " std::cout << "Usage: heatTransfer config output N M nx ny steps "
"iterations async\n" "iterations\n"
<< " output: name of output file\n" << " config: XML config file to use\n"
<< " output: name of output data file/stream\n"
<< " N: number of processes in X dimension\n" << " N: number of processes in X dimension\n"
<< " M: number of processes in Y dimension\n" << " M: number of processes in Y dimension\n"
<< " nx: local array size in X dimension per processor\n" << " nx: local array size in X dimension per processor\n"
<< " ny: local array size in Y dimension per processor\n" << " ny: local array size in Y dimension per processor\n"
<< " steps: the total number of steps to output\n" << " steps: the total number of steps to output\n"
<< " iterations: one step consist of this many iterations\n" << " iterations: one step consist of this many iterations\n\n";
<< " async: on or off (default) \n\n";
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
MPI_Init(&argc, &argv); MPI_Init(&argc, &argv);
/* World comm spans all applications started with the same aprun command
on a Cray XK6. So we have to split and create the local /* When writer and reader is launched together with a single mpirun command,
'world' communicator for heat_transfer only. the world comm spans all applications. We have to split and create the
In normal start-up, the communicator will just equal the MPI_COMM_WORLD. local 'world' communicator mpiHeatTransferComm for the writer only.
*/ When writer and reader is launched separately, the mpiHeatTransferComm
communicator will just equal the MPI_COMM_WORLD.
*/
int wrank, wnproc; int wrank, wnproc;
MPI_Comm_rank(MPI_COMM_WORLD, &wrank); MPI_Comm_rank(MPI_COMM_WORLD, &wrank);
...@@ -71,17 +72,7 @@ int main(int argc, char *argv[]) ...@@ -71,17 +72,7 @@ int main(int argc, char *argv[])
ht.exchange(mpiHeatTransferComm); ht.exchange(mpiHeatTransferComm);
// ht.printT("Heated T:", mpiHeatTransferComm); // ht.printT("Heated T:", mpiHeatTransferComm);
std::future<void> futureWrite; io.write(0, ht, settings, mpiHeatTransferComm);
if (settings.async)
{
futureWrite =
std::async(std::launch::async, &IO::write, &io, 0, std::ref(ht),
std::ref(settings), mpiHeatTransferComm);
}
else
{
io.write(0, ht, settings, mpiHeatTransferComm);
}
for (unsigned int t = 1; t <= settings.steps; ++t) for (unsigned int t = 1; t <= settings.steps; ++t)
{ {
...@@ -94,21 +85,7 @@ int main(int argc, char *argv[]) ...@@ -94,21 +85,7 @@ int main(int argc, char *argv[])
ht.heatEdges(); ht.heatEdges();
} }
if (settings.async) io.write(t, ht, settings, mpiHeatTransferComm);
{
futureWrite.get();
futureWrite = std::async(std::launch::async, &IO::write, &io, t,
std::ref(ht), std::ref(settings),
mpiHeatTransferComm);
}
else
{
io.write(t, ht, settings, mpiHeatTransferComm);
}
}
if (settings.async)
{
futureWrite.get();
} }
MPI_Barrier(mpiHeatTransferComm); MPI_Barrier(mpiHeatTransferComm);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment