mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
2247 lines
78 KiB
C++
2247 lines
78 KiB
C++
/*=========================================================================
|
|
|
|
Copyright (c) 2007, Los Alamos National Security, LLC
|
|
|
|
All rights reserved.
|
|
|
|
Copyright 2007. Los Alamos National Security, LLC.
|
|
This software was produced under U.S. Government contract DE-AC52-06NA25396
|
|
for Los Alamos National Laboratory (LANL), which is operated by
|
|
Los Alamos National Security, LLC for the U.S. Department of Energy.
|
|
The U.S. Government has rights to use, reproduce, and distribute this software.
|
|
NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY,
|
|
EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.
|
|
If software is modified to produce derivative works, such modified software
|
|
should be clearly marked, so as not to confuse it with the version available
|
|
from LANL.
|
|
|
|
Additionally, redistribution and use in source and binary forms, with or
|
|
without modification, are permitted provided that the following conditions
|
|
are met:
|
|
- Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
- Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
- Neither the name of Los Alamos National Security, LLC, Los Alamos National
|
|
Laboratory, LANL, the U.S. Government, nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software
|
|
without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY LOS ALAMOS NATIONAL SECURITY, LLC AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL SECURITY, LLC OR
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
=========================================================================*/
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
#include "Partition.h"
|
|
#include "ParticleDistribute.h"
|
|
#include "GenericIOMPIReader.h"
|
|
#include "SimpleTimings.h"
|
|
|
|
#include <cstring>
|
|
using namespace std;
|
|
|
|
namespace cosmotk {
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Particle data space is partitioned for the number of processors
|
|
// which currently is a factor of two but is easily extended. Particles
|
|
// are read in from files where each processor reads one file into a buffer,
|
|
// extracts the particles which really belong on the processor (ALIVE) and
|
|
// those in a buffer region around the edge (DEAD). The buffer is then
|
|
// passed round robin to every other processor so that all particles are
|
|
// examined by all processors. All dead particles are tagged with the
|
|
// neighbor zone (26 neighbors in 3D) so that later halos can be associated
|
|
// with zones.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
ParticleDistribute::ParticleDistribute()
|
|
{
|
|
// Get the number of processors running this problem and rank
|
|
this->numProc = Partition::getNumProc();
|
|
this->myProc = Partition::getMyProc();
|
|
|
|
// Get the number of processors in each dimension
|
|
Partition::getDecompSize(this->layoutSize);
|
|
|
|
// Get my position within the Cartesian topology
|
|
Partition::getMyPosition(this->layoutPos);
|
|
|
|
// Get neighbors of this processor including the wraparound
|
|
Partition::getNeighbors(this->neighbor);
|
|
|
|
this->numberOfAliveParticles = 0;
|
|
this->massConvertFactor = 1.0;
|
|
this->distConvertFactor = 1.0;
|
|
}
|
|
|
|
ParticleDistribute::~ParticleDistribute()
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set parameters for particle distribution
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::setParameters(
|
|
const string& baseName,
|
|
POSVEL_T rL,
|
|
string dataType)
|
|
{
|
|
// Base file name which will have processor id appended for actual files
|
|
this->baseFile = baseName;
|
|
|
|
// Physical total space and amount of physical space to use for dead particles
|
|
this->boxSize = rL;
|
|
|
|
// RECORD format is the binary .cosmo of one particle with all information
|
|
if (dataType == "RECORD")
|
|
this->inputType = RECORD;
|
|
|
|
// BLOCK format is Gadget format with a header and x,y,z locations for
|
|
// all particles, then x,y,z velocities for all particles, and all tags
|
|
else if (dataType == "BLOCK")
|
|
this->inputType = BLOCK;
|
|
|
|
if (this->myProc == MASTER) {
|
|
cout << endl << "------------------------------------" << endl;
|
|
cout << "boxSize: " << this->boxSize << endl;
|
|
}
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set parameters for particle unit conversion
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::setConvertParameters(
|
|
POSVEL_T massFactor,
|
|
POSVEL_T distFactor)
|
|
{
|
|
this->massConvertFactor = massFactor;
|
|
this->distConvertFactor = distFactor;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set box sizes for determining if a particle is in the alive or dead
|
|
// region of this processor. Data space is a DIMENSION torus.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::initialize()
|
|
{
|
|
#ifdef DEBUG
|
|
if (this->myProc == MASTER)
|
|
cout << "Decomposition: [" << this->layoutSize[0] << ":"
|
|
<< this->layoutSize[1] << ":" << this->layoutSize[2] << "]" << endl;
|
|
#endif
|
|
|
|
// Set subextents on particle locations for this processor
|
|
POSVEL_T boxStep[DIMENSION];
|
|
for (int dim = 0; dim < DIMENSION; dim++) {
|
|
boxStep[dim] = this->boxSize / this->layoutSize[dim];
|
|
|
|
// Alive particles
|
|
this->minAlive[dim] = this->layoutPos[dim] * boxStep[dim];
|
|
this->maxAlive[dim] = this->minAlive[dim] + boxStep[dim];
|
|
if (this->maxAlive[dim] > this->boxSize)
|
|
this->maxAlive[dim] = this->boxSize;
|
|
}
|
|
}
|
|
|
|
|
|
void ParticleDistribute::setParticles(vector<POSVEL_T>* xLoc,
|
|
vector<POSVEL_T>* yLoc,
|
|
vector<POSVEL_T>* zLoc,
|
|
vector<POSVEL_T>* xVel,
|
|
vector<POSVEL_T>* yVel,
|
|
vector<POSVEL_T>* zVel,
|
|
vector<POSVEL_T>* mass,
|
|
vector<ID_T>* id)
|
|
{
|
|
this->xx = xLoc;
|
|
this->yy = yLoc;
|
|
this->zz = zLoc;
|
|
this->vx = xVel;
|
|
this->vy = yVel;
|
|
this->vz = zVel;
|
|
this->ms = mass;
|
|
this->tag = id;
|
|
}
|
|
|
|
#ifndef USE_SERIAL_COSMO
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Each processor reads 0 or more files, a buffer at a time, and shares
|
|
// the particles using an all-to-all with every other processor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readParticlesAllToAll(int reserveQ, bool useAlltoallv)
|
|
{
|
|
// Find how many input files there are and deal them between the processors
|
|
// Calculates the max number of files per processor and max number of
|
|
// particles per file so that buffering can be done
|
|
// For round robin sharing determine where to send and receive buffers from
|
|
partitionInputFiles(true);
|
|
|
|
// Compute the total number of particles in the problem
|
|
// Compute the maximum number of particles in any one file to set buffer size
|
|
findFileParticleCount();
|
|
|
|
// MPI buffer size might limit the number of particles read from a file
|
|
// and passed round robin
|
|
// Largest file will have a number of buffer chunks to send if it is too large
|
|
// Every processor must send that number of chunks even if its own file
|
|
// does not have that much information
|
|
|
|
if (ENFORCE_MAX_READ == true && this->maxParticles > MAX_READ) {
|
|
this->maxRead = MAX_READ;
|
|
this->maxReadsPerFile = (this->maxParticles / this->maxRead) + 1;
|
|
} else {
|
|
this->maxRead = this->maxParticles;
|
|
this->maxReadsPerFile = 1;
|
|
}
|
|
|
|
MPI_Datatype CosmoParticleType;
|
|
|
|
{
|
|
MPI_Datatype type[3] = { MPI_FLOAT, MPI_LONG, MPI_UB };
|
|
int blocklen[3] = { COSMO_FLOAT, COSMO_INT, 1 };
|
|
MPI_Aint disp[3] = { offsetof(CosmoParticle, floatData),
|
|
offsetof(CosmoParticle, intData),
|
|
sizeof(CosmoParticle) };
|
|
MPI_Type_struct(3, blocklen, disp, type, &CosmoParticleType);
|
|
MPI_Type_commit(&CosmoParticleType);
|
|
}
|
|
|
|
std::vector<int> pCounts(numProc), pRecvCounts(numProc),
|
|
pDisp(numProc), pRecvDisp(numProc);
|
|
std::vector< std::vector<CosmoParticle> > pByProc(numProc);
|
|
std::vector<CosmoParticle> pBuffer, pRecvBuffer;
|
|
|
|
// Allocate space to hold buffer information for reading of files
|
|
// Mass is constant use that float to store the tag
|
|
// Number of particles is the first integer in the buffer
|
|
|
|
// Allocate space for the data read from the file
|
|
POSVEL_T *fBlock = 0;
|
|
POSVEL_T *lBlock = 0;
|
|
POSVEL_T *vBlock = 0;
|
|
ID_T* iBlock = 0;
|
|
|
|
// RECORD format reads one particle at a time
|
|
if (this->inputType == RECORD) {
|
|
fBlock = new POSVEL_T[COSMO_FLOAT];
|
|
iBlock = new ID_T[COSMO_INT];
|
|
}
|
|
|
|
// BLOCK format reads all particles at one time for triples
|
|
else if (this->inputType == BLOCK) {
|
|
lBlock = new POSVEL_T[this->maxRead * DIMENSION];
|
|
vBlock = new POSVEL_T[this->maxRead * DIMENSION];
|
|
iBlock = new ID_T[this->maxRead];
|
|
}
|
|
|
|
// Reserve particle storage to minimize reallocation
|
|
int reserveSize = (int) (this->maxFiles * this->maxParticles * DEAD_FACTOR);
|
|
|
|
// If multiple processors are reading the same file we can reduce size
|
|
reserveSize /= this->processorsPerFile;
|
|
|
|
if(reserveQ) {
|
|
cout << "readParticlesRoundRobin reserving vectors" << endl;
|
|
|
|
this->xx->reserve(reserveSize);
|
|
this->yy->reserve(reserveSize);
|
|
this->zz->reserve(reserveSize);
|
|
this->vx->reserve(reserveSize);
|
|
this->vy->reserve(reserveSize);
|
|
this->vz->reserve(reserveSize);
|
|
this->ms->reserve(reserveSize);
|
|
this->tag->reserve(reserveSize);
|
|
}
|
|
|
|
// Running total and index into particle data on this processor
|
|
this->particleCount = 0;
|
|
|
|
// Using the input files assigned to this processor, read the input
|
|
// and push all-to-all to every other processor
|
|
// this->maxFiles is the maximum number to read on any processor
|
|
// Some processors may have no files to read but must still participate
|
|
// in the round robin distribution
|
|
|
|
for (int file = 0; file < this->maxFiles; file++) {
|
|
|
|
// Open file to read the data if any for this processor
|
|
ifstream* inStream = 0;
|
|
int firstParticle = 0;
|
|
int numberOfParticles = 0;
|
|
int remainingParticles = 0;
|
|
|
|
if ((int)this->inFiles.size() > file) {
|
|
inStream = new ifstream(this->inFiles[file].c_str(), ios::in|ios::binary);
|
|
|
|
cout << "Rank " << this->myProc << " open file " << inFiles[file]
|
|
<< " with " << this->fileParticles[file] << " particles" << endl;
|
|
|
|
// Number of particles read at one time depends on MPI buffer size
|
|
numberOfParticles = this->fileParticles[file];
|
|
if (numberOfParticles > this->maxRead)
|
|
numberOfParticles = this->maxRead;
|
|
|
|
// If a file is too large to be passed as an MPI message divide it up
|
|
remainingParticles = this->fileParticles[file];
|
|
|
|
} else {
|
|
cout << "Rank " << this->myProc << " no file to open " << endl;
|
|
}
|
|
|
|
for (int piece = 0; piece < this->maxReadsPerFile; piece++) {
|
|
// Reset the comm buffers
|
|
for (int i = 0; i < numProc; ++i) {
|
|
pByProc[i].clear();
|
|
}
|
|
|
|
pBuffer.clear();
|
|
pRecvBuffer.clear();
|
|
|
|
// Processor has a file to read and share via round robin with others
|
|
if (file < (int)this->inFiles.size()) {
|
|
if (this->inputType == RECORD) {
|
|
readFromRecordFile(inStream, firstParticle, numberOfParticles,
|
|
fBlock, iBlock, pByProc);
|
|
} else {
|
|
readFromBlockFile(inStream, firstParticle, numberOfParticles,
|
|
this->fileParticles[file],
|
|
lBlock, vBlock, iBlock, pByProc);
|
|
}
|
|
firstParticle += numberOfParticles;
|
|
remainingParticles -= numberOfParticles;
|
|
if (remainingParticles <= 0)
|
|
numberOfParticles = 0;
|
|
else if (remainingParticles < numberOfParticles)
|
|
numberOfParticles = remainingParticles;
|
|
}
|
|
|
|
// Record all sizes into a single buffer and send this to all ranks
|
|
long totalToSend = 0;
|
|
for (int i = 0; i < numProc; ++i) {
|
|
int sz = (int) pByProc[i].size();
|
|
pCounts[i] = sz;
|
|
totalToSend += sz;
|
|
}
|
|
|
|
MPI_Alltoall(&pCounts[0], 1, MPI_INT,
|
|
&pRecvCounts[0], 1, MPI_INT,
|
|
Partition::getComm());
|
|
|
|
// pRecvCounts now holds the number of particles that this rank should
|
|
// get from every other rank
|
|
long totalToRecv = 0;
|
|
for (int i = 0; i < numProc; ++i) {
|
|
totalToRecv += pRecvCounts[i];
|
|
}
|
|
|
|
// Allocate and pack the buffer with all particles to send
|
|
pBuffer.reserve(totalToSend);
|
|
for (int i = 0; i < numProc; ++i)
|
|
for (int j = 0; j < (int) pByProc[i].size(); ++j) {
|
|
pBuffer.push_back(pByProc[i][j]);
|
|
}
|
|
|
|
// Calculate displacements
|
|
pDisp[0] = pRecvDisp[0] = 0;
|
|
for (int i = 1; i < numProc; ++i) {
|
|
pDisp[i] = pDisp[i-1] + pCounts[i-1];
|
|
pRecvDisp[i] = pRecvDisp[i-1] + pRecvCounts[i-1];
|
|
}
|
|
|
|
// Send all particles to their new homes
|
|
pRecvBuffer.resize(totalToRecv);
|
|
if (useAlltoallv) {
|
|
MPI_Alltoallv(&pBuffer[0], &pCounts[0], &pDisp[0], CosmoParticleType,
|
|
&pRecvBuffer[0], &pRecvCounts[0], &pRecvDisp[0], CosmoParticleType,
|
|
Partition::getComm());
|
|
} else {
|
|
vector<MPI_Request> requests;
|
|
|
|
for (int i = 0; i < numProc; ++i) {
|
|
if (pRecvCounts[i] == 0)
|
|
continue;
|
|
|
|
requests.resize(requests.size()+1);
|
|
MPI_Irecv(&pRecvBuffer[pRecvDisp[i]], pRecvCounts[i], CosmoParticleType, i, 0,
|
|
Partition::getComm(), &requests[requests.size()-1]);
|
|
}
|
|
|
|
MPI_Barrier(Partition::getComm());
|
|
|
|
for (int i = 0; i < numProc; ++i) {
|
|
if (pCounts[i] == 0)
|
|
continue;
|
|
|
|
requests.resize(requests.size()+1);
|
|
MPI_Isend(&pBuffer[pDisp[i]], pCounts[i], CosmoParticleType, i, 0,
|
|
Partition::getComm(), &requests[requests.size()-1]);
|
|
}
|
|
|
|
vector<MPI_Status> status(requests.size());
|
|
MPI_Waitall((int) requests.size(), &requests[0], &status[0]);
|
|
}
|
|
|
|
// We now have all of our particles, put them in our local arrays
|
|
for (long i = 0; i < totalToRecv; ++i) {
|
|
POSVEL_T loc[DIMENSION], vel[DIMENSION], mass;
|
|
ID_T id;
|
|
|
|
int j = 0;
|
|
for (int dim = 0; dim < DIMENSION; dim++) {
|
|
loc[dim] = pRecvBuffer[i].floatData[j++];
|
|
}
|
|
for (int dim = 0; dim < DIMENSION; dim++) {
|
|
vel[dim] = pRecvBuffer[i].floatData[j++];
|
|
}
|
|
id = pRecvBuffer[i].intData[0];
|
|
|
|
// Is the particle ALIVE on this processor
|
|
if ((loc[0] >= minAlive[0] && loc[0] < maxAlive[0]) &&
|
|
(loc[1] >= minAlive[1] && loc[1] < maxAlive[1]) &&
|
|
(loc[2] >= minAlive[2] && loc[2] < maxAlive[2])) {
|
|
|
|
this->xx->push_back(loc[0]);
|
|
this->yy->push_back(loc[1]);
|
|
this->zz->push_back(loc[2]);
|
|
this->vx->push_back(vel[0]);
|
|
this->vy->push_back(vel[1]);
|
|
this->vz->push_back(vel[2]);
|
|
this->ms->push_back(mass);
|
|
this->tag->push_back(id);
|
|
|
|
this->numberOfAliveParticles++;
|
|
this->particleCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Can delete the read buffers as soon as last file is read because
|
|
// information has been transferred into the double buffer1
|
|
if (file == (this->maxFiles - 1)) {
|
|
if (this->inputType == RECORD) {
|
|
delete [] fBlock;
|
|
delete [] iBlock;
|
|
} else if (this->inputType == BLOCK) {
|
|
delete [] lBlock;
|
|
delete [] vBlock;
|
|
delete [] iBlock;
|
|
}
|
|
}
|
|
|
|
if ((int)this->inFiles.size() > file)
|
|
inStream->close();
|
|
}
|
|
|
|
// Count the particles across processors
|
|
long totalAliveParticles = 0;
|
|
MPI_Allreduce((void*) &this->numberOfAliveParticles,
|
|
(void*) &totalAliveParticles,
|
|
1, MPI_LONG, MPI_SUM, Partition::getComm());
|
|
|
|
#ifdef DEBUG
|
|
cout << "Rank " << setw(3) << this->myProc
|
|
<< " #alive = " << this->numberOfAliveParticles << endl;
|
|
#endif
|
|
|
|
if (this->myProc == MASTER) {
|
|
cout << "TotalAliveParticles " << totalAliveParticles << endl;
|
|
}
|
|
|
|
MPI_Type_free(&CosmoParticleType);
|
|
}
|
|
#endif // USE_SERIAL_COSMO
|
|
|
|
|
|
|
|
void ParticleDistribute::readParticlesGIO(int reserveQ, bool useAlltoallv)
|
|
{
|
|
gio::GenericIOMPIReader GIO;
|
|
GIO.SetCommunicator(Partition::getComm());
|
|
GIO.SetFileName(this->baseFile);
|
|
GIO.OpenAndReadHeader();
|
|
|
|
this->particleCount = GIO.GetNumberOfElements();
|
|
this->xx->resize(particleCount + gio::CRCSize/sizeof(POSVEL_T));
|
|
this->yy->resize(particleCount + gio::CRCSize/sizeof(POSVEL_T));
|
|
this->zz->resize(particleCount + gio::CRCSize/sizeof(POSVEL_T));
|
|
this->vx->resize(particleCount + gio::CRCSize/sizeof(POSVEL_T));
|
|
this->vy->resize(particleCount + gio::CRCSize/sizeof(POSVEL_T));
|
|
this->vz->resize(particleCount + gio::CRCSize/sizeof(POSVEL_T));
|
|
this->tag->resize(particleCount + gio::CRCSize/sizeof(ID_T));
|
|
|
|
if (reserveQ)
|
|
this->ms->reserve(particleCount);
|
|
|
|
// FIXME: We don't read the mask here, and we might want to do so!
|
|
|
|
GIO.AddVariable("x", *this->xx, gio::GenericIOBase::ValueHasExtraSpace);
|
|
GIO.AddVariable("y", *this->yy, gio::GenericIOBase::ValueHasExtraSpace);
|
|
GIO.AddVariable("z", *this->zz, gio::GenericIOBase::ValueHasExtraSpace);
|
|
GIO.AddVariable("vx", *this->vx, gio::GenericIOBase::ValueHasExtraSpace);
|
|
GIO.AddVariable("vy", *this->vy, gio::GenericIOBase::ValueHasExtraSpace);
|
|
GIO.AddVariable("vz", *this->vz, gio::GenericIOBase::ValueHasExtraSpace);
|
|
GIO.AddVariable("id", *this->tag, gio::GenericIOBase::ValueHasExtraSpace);
|
|
GIO.ReadData();
|
|
|
|
this->xx->resize(particleCount);
|
|
this->yy->resize(particleCount);
|
|
this->zz->resize(particleCount);
|
|
this->vx->resize(particleCount);
|
|
this->vy->resize(particleCount);
|
|
this->vz->resize(particleCount);
|
|
this->tag->resize(particleCount);
|
|
|
|
distributeGIOParticles(reserveQ,useAlltoallv);
|
|
}
|
|
|
|
void ParticleDistribute::distributeGIOParticles(int reserveQ, bool useAlltoallv)
|
|
{
|
|
MPI_Datatype CosmoParticleType;
|
|
|
|
{
|
|
MPI_Datatype type[3] = { MPI_FLOAT, MPI_LONG, MPI_UB };
|
|
int blocklen[3] = { COSMO_FLOAT, COSMO_INT, 1 };
|
|
MPI_Aint disp[3] = { offsetof(CosmoParticle, floatData),
|
|
offsetof(CosmoParticle, intData),
|
|
sizeof(CosmoParticle) };
|
|
MPI_Type_struct(3, blocklen, disp, type, &CosmoParticleType);
|
|
MPI_Type_commit(&CosmoParticleType);
|
|
}
|
|
|
|
std::vector<int> pCounts(numProc), pRecvCounts(numProc),
|
|
pDisp(numProc), pRecvDisp(numProc);
|
|
std::vector< std::vector<CosmoParticle> > pByProc(numProc);
|
|
std::vector<CosmoParticle> pBuffer, pRecvBuffer;
|
|
|
|
this->particleCount = this->xx->size();
|
|
|
|
// Find a home for every particle...
|
|
for (int i = 0; i < particleCount; ++i) {
|
|
if ((*this->xx)[i] >= this->boxSize)
|
|
(*this->xx)[i] -= this->boxSize;
|
|
if ((*this->yy)[i] >= this->boxSize)
|
|
(*this->yy)[i] -= this->boxSize;
|
|
if ((*this->zz)[i] >= this->boxSize)
|
|
(*this->zz)[i] -= this->boxSize;
|
|
|
|
if ((*this->xx)[i] < 0)
|
|
(*this->xx)[i] += this->boxSize;
|
|
if ((*this->yy)[i] < 0)
|
|
(*this->yy)[i] += this->boxSize;
|
|
if ((*this->zz)[i] < 0)
|
|
(*this->zz)[i] += this->boxSize;
|
|
|
|
// Figure out to which rank this particle belongs
|
|
float sizeX = this->boxSize / this->layoutSize[0];
|
|
float sizeY = this->boxSize / this->layoutSize[1];
|
|
float sizeZ = this->boxSize / this->layoutSize[2];
|
|
int coords[3] = { (int) ((*this->xx)[i]/sizeX),
|
|
(int) ((*this->yy)[i]/sizeY),
|
|
(int) ((*this->zz)[i]/sizeZ)
|
|
};
|
|
|
|
int home;
|
|
MPI_Cart_rank(Partition::getComm(), coords, &home);
|
|
|
|
CosmoParticle particle = { { (*this->xx)[i],
|
|
(*this->yy)[i],
|
|
(*this->zz)[i],
|
|
(*this->vx)[i],
|
|
(*this->vy)[i],
|
|
(*this->vz)[i],
|
|
0.0f
|
|
}, {
|
|
(*this->tag)[i]
|
|
|
|
}
|
|
};
|
|
|
|
pByProc[home].push_back(particle);
|
|
}
|
|
|
|
this->xx->resize(0);
|
|
this->yy->resize(0);
|
|
this->zz->resize(0);
|
|
this->vx->resize(0);
|
|
this->vy->resize(0);
|
|
this->vz->resize(0);
|
|
this->tag->resize(0);
|
|
|
|
if (reserveQ) {
|
|
this->xx->reserve(particleCount);
|
|
this->yy->reserve(particleCount);
|
|
this->zz->reserve(particleCount);
|
|
this->vx->reserve(particleCount);
|
|
this->vy->reserve(particleCount);
|
|
this->vz->reserve(particleCount);
|
|
this->tag->reserve(particleCount);
|
|
this->ms->reserve(particleCount);
|
|
}
|
|
|
|
SimpleTimings::TimerRef t_ala2a = SimpleTimings::getTimer("ala2a");
|
|
SimpleTimings::startTimer(t_ala2a);
|
|
|
|
this->particleCount = 0;
|
|
|
|
// Record all sizes into a single buffer and send this to all ranks
|
|
long totalToSend = 0;
|
|
for (int i = 0; i < numProc; ++i) {
|
|
int sz = (int) pByProc[i].size();
|
|
pCounts[i] = sz;
|
|
totalToSend += sz;
|
|
}
|
|
|
|
MPI_Alltoall(&pCounts[0], 1, MPI_INT,
|
|
&pRecvCounts[0], 1, MPI_INT,
|
|
Partition::getComm());
|
|
// pRecvCounts now holds the number of particles that this rank should
|
|
// get from every other rank
|
|
long totalToRecv = 0;
|
|
for (int i = 0; i < numProc; ++i) {
|
|
totalToRecv += pRecvCounts[i];
|
|
}
|
|
|
|
// Allocate and pack the buffer with all particles to send
|
|
pBuffer.reserve(totalToSend);
|
|
for (int i = 0; i < numProc; ++i)
|
|
for (int j = 0; j < (int) pByProc[i].size(); ++j) {
|
|
pBuffer.push_back(pByProc[i][j]);
|
|
}
|
|
|
|
// Calculate displacements
|
|
pDisp[0] = pRecvDisp[0] = 0;
|
|
for (int i = 1; i < numProc; ++i) {
|
|
pDisp[i] = pDisp[i-1] + pCounts[i-1];
|
|
pRecvDisp[i] = pRecvDisp[i-1] + pRecvCounts[i-1];
|
|
}
|
|
|
|
// Send all particles to their new homes
|
|
pRecvBuffer.resize(totalToRecv);
|
|
if (useAlltoallv) {
|
|
MPI_Alltoallv(&pBuffer[0], &pCounts[0], &pDisp[0], CosmoParticleType,
|
|
&pRecvBuffer[0], &pRecvCounts[0], &pRecvDisp[0], CosmoParticleType,
|
|
Partition::getComm());
|
|
} else {
|
|
vector<MPI_Request> requests;
|
|
|
|
for (int i = 0; i < numProc; ++i) {
|
|
if (pRecvCounts[i] == 0)
|
|
continue;
|
|
|
|
requests.resize(requests.size()+1);
|
|
MPI_Irecv(&pRecvBuffer[pRecvDisp[i]], pRecvCounts[i], CosmoParticleType, i, 0,
|
|
Partition::getComm(), &requests[requests.size()-1]);
|
|
}
|
|
|
|
MPI_Barrier(Partition::getComm());
|
|
|
|
for (int i = 0; i < numProc; ++i) {
|
|
if (pCounts[i] == 0)
|
|
continue;
|
|
|
|
requests.resize(requests.size()+1);
|
|
MPI_Isend(&pBuffer[pDisp[i]], pCounts[i], CosmoParticleType, i, 0,
|
|
Partition::getComm(), &requests[requests.size()-1]);
|
|
}
|
|
|
|
vector<MPI_Status> status(requests.size());
|
|
MPI_Waitall((int) requests.size(), &requests[0], &status[0]);
|
|
}
|
|
|
|
// We now have all of our particles, put them in our local arrays
|
|
for (long i = 0; i < totalToRecv; ++i) {
|
|
POSVEL_T loc[DIMENSION], vel[DIMENSION], mass = 1.0;
|
|
ID_T id;
|
|
|
|
int j = 0;
|
|
for (int dim = 0; dim < DIMENSION; dim++) {
|
|
loc[dim] = pRecvBuffer[i].floatData[j++];
|
|
}
|
|
for (int dim = 0; dim < DIMENSION; dim++) {
|
|
vel[dim] = pRecvBuffer[i].floatData[j++];
|
|
}
|
|
id = pRecvBuffer[i].intData[0];
|
|
|
|
// Is the particle ALIVE on this processor
|
|
if ((loc[0] >= minAlive[0] && loc[0] < maxAlive[0]) &&
|
|
(loc[1] >= minAlive[1] && loc[1] < maxAlive[1]) &&
|
|
(loc[2] >= minAlive[2] && loc[2] < maxAlive[2])) {
|
|
|
|
this->xx->push_back(loc[0]);
|
|
this->yy->push_back(loc[1]);
|
|
this->zz->push_back(loc[2]);
|
|
this->vx->push_back(vel[0]);
|
|
this->vy->push_back(vel[1]);
|
|
this->vz->push_back(vel[2]);
|
|
this->ms->push_back(mass);
|
|
this->tag->push_back(id);
|
|
|
|
this->numberOfAliveParticles++;
|
|
this->particleCount++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// Count the particles across processors
|
|
long totalAliveParticles = 0;
|
|
MPI_Allreduce((void*) &this->numberOfAliveParticles,
|
|
(void*) &totalAliveParticles,
|
|
1, MPI_LONG, MPI_SUM, Partition::getComm());
|
|
|
|
#ifdef DEBUG
|
|
cout << "Rank " << setw(3) << this->myProc
|
|
<< " #alive = " << this->numberOfAliveParticles << endl;
|
|
#endif
|
|
|
|
MPI_Type_free(&CosmoParticleType);
|
|
|
|
if (this->myProc == MASTER) {
|
|
cout << "ReadParticlesGIO TotalAliveParticles "
|
|
<< totalAliveParticles << endl;
|
|
}
|
|
|
|
SimpleTimings::stopTimerStats(t_ala2a);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Each processor reads 0 or more files, a buffer at a time, and shares
|
|
// the particles by passing the buffer round robin to every other processor
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readParticlesRoundRobin(int reserveQ)
|
|
{
|
|
// Find how many input files there are and deal them between the processors
|
|
// Calculates the max number of files per processor and max number of
|
|
// particles per file so that buffering can be done
|
|
// For round robin sharing determine where to send and receive buffers from
|
|
partitionInputFiles();
|
|
|
|
// Compute the total number of particles in the problem
|
|
// Compute the maximum number of particles in any one file to set buffer size
|
|
findFileParticleCount();
|
|
|
|
// If there is only one input file we don't have to do MPI messaging
|
|
// because each processor will read that same file and extract only
|
|
// the particles in range
|
|
if (this->numberOfFiles == 1) {
|
|
if (this->inputType == RECORD) {
|
|
readFromRecordFile();
|
|
} else {
|
|
readFromBlockFile();
|
|
}
|
|
} else {
|
|
|
|
// MPI buffer size might limit the number of particles read from a file
|
|
// and passed round robin
|
|
// Largest file will have a number of buffer chunks to send if it is too large
|
|
// Every processor must send that number of chunks even if its own file
|
|
// does not have that much information
|
|
|
|
if (ENFORCE_MAX_READ == true && this->maxParticles > MAX_READ) {
|
|
this->maxRead = MAX_READ;
|
|
this->maxReadsPerFile = (this->maxParticles / this->maxRead) + 1;
|
|
} else {
|
|
this->maxRead = this->maxParticles;
|
|
this->maxReadsPerFile = 1;
|
|
}
|
|
|
|
// Allocate space to hold buffer information for reading of files
|
|
// Mass is constant use that float to store the tag
|
|
// Number of particles is the first integer in the buffer
|
|
int bufferSize = sizeof(int) + (this->maxRead * RECORD_SIZE);
|
|
Message* message1 = new Message(bufferSize);
|
|
Message* message2 = new Message(bufferSize);
|
|
|
|
// Allocate space for the data read from the file
|
|
POSVEL_T *fBlock = 0;
|
|
POSVEL_T *lBlock = 0;
|
|
POSVEL_T *vBlock = 0;
|
|
ID_T* iBlock = 0;
|
|
|
|
// RECORD format reads one particle at a time
|
|
if (this->inputType == RECORD) {
|
|
fBlock = new POSVEL_T[COSMO_FLOAT];
|
|
iBlock = new ID_T[COSMO_INT];
|
|
}
|
|
|
|
// BLOCK format reads all particles at one time for triples
|
|
else if (this->inputType == BLOCK) {
|
|
lBlock = new POSVEL_T[this->maxRead * DIMENSION];
|
|
vBlock = new POSVEL_T[this->maxRead * DIMENSION];
|
|
iBlock = new ID_T[this->maxRead];
|
|
}
|
|
|
|
// Reserve particle storage to minimize reallocation
|
|
int reserveSize = (int) (this->maxFiles * this->maxParticles * DEAD_FACTOR);
|
|
|
|
// If multiple processors are reading the same file we can reduce size
|
|
reserveSize /= this->processorsPerFile;
|
|
|
|
if(reserveQ) {
|
|
cout << "readParticlesRoundRobin reserving vectors" << endl;
|
|
this->xx->reserve(reserveSize);
|
|
this->yy->reserve(reserveSize);
|
|
this->zz->reserve(reserveSize);
|
|
this->vx->reserve(reserveSize);
|
|
this->vy->reserve(reserveSize);
|
|
this->vz->reserve(reserveSize);
|
|
this->ms->reserve(reserveSize);
|
|
this->tag->reserve(reserveSize);
|
|
}
|
|
|
|
// Running total and index into particle data on this processor
|
|
this->particleCount = 0;
|
|
|
|
// Using the input files assigned to this processor, read the input
|
|
// and push round robin to every other processor
|
|
// this->maxFiles is the maximum number to read on any processor
|
|
// Some processors may have no files to read but must still participate
|
|
// in the round robin distribution
|
|
|
|
for (int file = 0; file < this->maxFiles; file++) {
|
|
|
|
// Open file to read the data if any for this processor
|
|
ifstream* inStream = 0;
|
|
int firstParticle = 0;
|
|
int numberOfParticles = 0;
|
|
int remainingParticles = 0;
|
|
|
|
if ((int)this->inFiles.size() > file) {
|
|
inStream = new ifstream(this->inFiles[file].c_str(), ios::in|ios::binary);
|
|
|
|
cout << "Rank " << this->myProc << " open file " << inFiles[file]
|
|
<< " with " << this->fileParticles[file] << " particles" << endl;
|
|
|
|
// Number of particles read at one time depends on MPI buffer size
|
|
numberOfParticles = this->fileParticles[file];
|
|
if (numberOfParticles > this->maxRead)
|
|
numberOfParticles = this->maxRead;
|
|
|
|
// If a file is too large to be passed as an MPI message divide it up
|
|
remainingParticles = this->fileParticles[file];
|
|
|
|
} else {
|
|
cout << "Rank " << this->myProc << " no file to open " << endl;
|
|
}
|
|
|
|
for (int piece = 0; piece < this->maxReadsPerFile; piece++) {
|
|
|
|
// Reset each MPI message for each file read
|
|
message1->reset();
|
|
message2->reset();
|
|
|
|
// Processor has a file to read and share via round robin with others
|
|
if (file < (int)this->inFiles.size()) {
|
|
if (this->inputType == RECORD) {
|
|
readFromRecordFile(inStream, firstParticle, numberOfParticles,
|
|
fBlock, iBlock, message1);
|
|
} else {
|
|
readFromBlockFile(inStream, firstParticle, numberOfParticles,
|
|
this->fileParticles[file],
|
|
lBlock, vBlock, iBlock, message1);
|
|
}
|
|
firstParticle += numberOfParticles;
|
|
remainingParticles -= numberOfParticles;
|
|
if (remainingParticles <= 0)
|
|
numberOfParticles = 0;
|
|
else if (remainingParticles < numberOfParticles)
|
|
numberOfParticles = remainingParticles;
|
|
}
|
|
|
|
// Processor does not have a file to open but must participate in the
|
|
// round robin with an empty buffer
|
|
else {
|
|
// Store number of particles used in first position
|
|
int zero = 0;
|
|
message1->putValue(&zero);
|
|
}
|
|
|
|
// Particles belonging to this processor are put in vectors
|
|
distributeParticles(message1, message2);
|
|
}
|
|
|
|
// Can delete the read buffers as soon as last file is read because
|
|
// information has been transferred into the double buffer1
|
|
if (file == (this->maxFiles - 1)) {
|
|
if (this->inputType == RECORD) {
|
|
delete [] fBlock;
|
|
delete [] iBlock;
|
|
} else if (this->inputType == BLOCK) {
|
|
delete [] lBlock;
|
|
delete [] vBlock;
|
|
delete [] iBlock;
|
|
}
|
|
}
|
|
|
|
if ((int)this->inFiles.size() > file)
|
|
inStream->close();
|
|
}
|
|
|
|
// After all particles have been distributed to vectors the double
|
|
// buffers can be deleted
|
|
delete message1;
|
|
delete message2;
|
|
|
|
// Count the particles across processors
|
|
long totalAliveParticles = 0;
|
|
#ifdef USE_SERIAL_COSMO
|
|
totalAliveParticles = this->numberOfAliveParticles;
|
|
#else
|
|
MPI_Allreduce((void*) &this->numberOfAliveParticles,
|
|
(void*) &totalAliveParticles,
|
|
1, MPI_LONG, MPI_SUM, Partition::getComm());
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
cout << "Rank " << setw(3) << this->myProc
|
|
<< " #alive = " << this->numberOfAliveParticles << endl;
|
|
#endif
|
|
|
|
if (this->myProc == MASTER) {
|
|
cout << "TotalAliveParticles " << totalAliveParticles << endl;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Using the base name of the data, go to the subdirectory and determine
|
|
// how many input files there are. Parcel those files between all the
|
|
// processors which will be responsible for actually reading 0 or more.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::partitionInputFiles(bool force1PPF)
|
|
{
|
|
// Find number of input files for this problem given the base input name
|
|
// Get the subdirectory name containing the input files
|
|
string::size_type dirPos = this->baseFile.rfind("/");
|
|
string subdirectory;
|
|
string baseName;
|
|
|
|
// If the directory is not given use the current directory
|
|
if (dirPos == string::npos) {
|
|
subdirectory = "./";
|
|
baseName = this->baseFile;
|
|
} else {
|
|
subdirectory = this->baseFile.substr(0, dirPos + 1);
|
|
baseName = this->baseFile.substr(dirPos + 1);
|
|
}
|
|
|
|
// strip everything back to the first non-number
|
|
string::size_type pos = baseName.size() - 1;
|
|
int numbersOK = 1;
|
|
|
|
while(numbersOK)
|
|
{
|
|
if(baseName[pos] >= '0' && baseName[pos] <= '9')
|
|
{
|
|
if(pos > 0)
|
|
{
|
|
pos = pos - 1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
numbersOK = 0;
|
|
}
|
|
}
|
|
|
|
// base name is everything up to the numbers
|
|
baseName = baseName.substr(0, pos + 1);
|
|
|
|
// Open the subdirectory and make a list of input files
|
|
DIR* directory = opendir(subdirectory.c_str());
|
|
struct dirent* directoryEntry;
|
|
vector<string> files;
|
|
|
|
if (directory != NULL) {
|
|
while ((directoryEntry = readdir(directory)))
|
|
{
|
|
// get the name
|
|
string fileName = directoryEntry->d_name;
|
|
pos = fileName.find(baseName.c_str());
|
|
|
|
// if it starts with the base name
|
|
if(pos == 0)
|
|
{
|
|
// check to see if it is all numbers on the end
|
|
pos = baseName.size() + 1;
|
|
numbersOK = 1;
|
|
|
|
while(pos < fileName.size())
|
|
{
|
|
if(fileName[pos] < '0' || fileName[pos] > '9')
|
|
{
|
|
numbersOK = 0;
|
|
break;
|
|
}
|
|
|
|
pos = pos + 1;
|
|
}
|
|
|
|
if(numbersOK)
|
|
{
|
|
fileName = subdirectory + fileName;
|
|
files.push_back(fileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(directory);
|
|
}
|
|
|
|
this->numberOfFiles = (int)files.size();
|
|
|
|
if (this->numberOfFiles == 0) {
|
|
cout << "Rank " << this->myProc << " found no input files" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (this->myProc == MASTER) {
|
|
for (int i = 0; i < this->numberOfFiles; i++)
|
|
cout << " File " << i << ": " << files[i] << endl;
|
|
}
|
|
#endif
|
|
|
|
// Divide the files between all the processors
|
|
// If there are 1 or more files per processor set the
|
|
// buffering up with a full round robin between all processors
|
|
if (this->numberOfFiles >= this->numProc || force1PPF) {
|
|
|
|
// Number of round robin sends to share all the files
|
|
this->processorsPerFile = 1;
|
|
this->numberOfFileSends = this->numProc - 1;
|
|
this->maxFileSends = this->numberOfFileSends;
|
|
|
|
// Which files does this processor read
|
|
for (int i = 0; i < this->numberOfFiles; i++)
|
|
if ((i % this->numProc) == this->myProc)
|
|
this->inFiles.push_back(files[i]);
|
|
|
|
// Where is the file sent, and where is it received
|
|
if (this->myProc == this->numProc - 1)
|
|
this->nextProc = 0;
|
|
else
|
|
this->nextProc = this->myProc + 1;
|
|
if (this->myProc == 0)
|
|
this->prevProc = this->numProc - 1;
|
|
else
|
|
this->prevProc = this->myProc - 1;
|
|
}
|
|
|
|
// If there are more processors than file set up as many round robin loops
|
|
// as possible so that multiple processors read the same file. If the number
|
|
// of files does not divide evenly into the number of processors the last
|
|
// round robin loop will be bigger and some processors will contribute
|
|
// buffers of 0 size to send
|
|
|
|
else {
|
|
|
|
// Assign the round robin circle (last circle is bigger than others)
|
|
this->processorsPerFile = this->numProc / this->numberOfFiles;
|
|
int numberOfRoundRobinCircles = this->processorsPerFile;
|
|
int myCircle = this->myProc / this->numberOfFiles;
|
|
int extraProcessors = this->numProc -
|
|
(numberOfRoundRobinCircles * this->numberOfFiles);
|
|
if (myCircle == numberOfRoundRobinCircles)
|
|
myCircle--;
|
|
|
|
int firstInCircle = myCircle * this->numberOfFiles;
|
|
int lastInCircle = firstInCircle + this->numberOfFiles - 1;
|
|
if (myCircle == (numberOfRoundRobinCircles - 1))
|
|
lastInCircle += extraProcessors;
|
|
|
|
// How big is the round robin circle this processor is in
|
|
// What is the biggest round robin circle (needed because of MPI_Barrier)
|
|
this->numberOfFileSends = lastInCircle - firstInCircle;
|
|
this->maxFileSends = this->numberOfFiles + extraProcessors;
|
|
|
|
// Which file does this processor read
|
|
int index = this->myProc % this->numberOfFiles;
|
|
if (myCircle == (this->myProc / this->numberOfFiles))
|
|
this->inFiles.push_back(files[index]);
|
|
|
|
// Where is the file sent, and where is it received
|
|
if (this->myProc == lastInCircle)
|
|
this->nextProc = firstInCircle;
|
|
else
|
|
this->nextProc = this->myProc + 1;
|
|
if (this->myProc == firstInCircle)
|
|
this->prevProc = lastInCircle;
|
|
else
|
|
this->prevProc = this->myProc - 1;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Open each input file belonging to this processor and find the number
|
|
// of particles for setting buffer sizes
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::findFileParticleCount()
|
|
{
|
|
// Compute the total number of particles in the problem
|
|
// Compute the maximum number of particles in any one file to set buffer size
|
|
long numberOfParticles = 0;
|
|
long maxNumberOfParticles = 0;
|
|
int numberOfMyFiles = (int)this->inFiles.size();
|
|
|
|
// Each processor counts the particles in its own files
|
|
for (int i = 0; i < numberOfMyFiles; i++) {
|
|
|
|
// Open my file
|
|
ifstream *inStream = new ifstream(this->inFiles[i].c_str(), ios::in);
|
|
if (inStream->fail()) {
|
|
delete inStream;
|
|
cout << "File: " << this->inFiles[i] << " cannot be opened" << endl;
|
|
exit (-1);
|
|
}
|
|
|
|
if (this->inputType == RECORD) {
|
|
|
|
// Compute the number of particles from file size
|
|
inStream->seekg(0L, ios::end);
|
|
int numberOfRecords = inStream->tellg() / RECORD_SIZE;
|
|
this->fileParticles.push_back(numberOfRecords);
|
|
|
|
numberOfParticles += numberOfRecords;
|
|
if (maxNumberOfParticles < numberOfRecords)
|
|
maxNumberOfParticles = numberOfRecords;
|
|
}
|
|
|
|
else if (this->inputType == BLOCK) {
|
|
|
|
// Find the number of particles in the header
|
|
readGadgetHeader(inStream);
|
|
|
|
int numberOfRecords = this->gadgetParticleCount;
|
|
this->fileParticles.push_back(numberOfRecords);
|
|
|
|
numberOfParticles += numberOfRecords;
|
|
if (maxNumberOfParticles < numberOfRecords)
|
|
maxNumberOfParticles = numberOfRecords;
|
|
}
|
|
|
|
inStream->close();
|
|
delete inStream;
|
|
}
|
|
|
|
// If multiple processors read the same file, just do the reduce on one set
|
|
if (this->processorsPerFile > 1) {
|
|
if (this->myProc >= this->numberOfFiles) {
|
|
numberOfParticles = 0;
|
|
maxNumberOfParticles = 0;
|
|
}
|
|
}
|
|
|
|
// Share the information about total particles
|
|
#ifdef USE_SERIAL_COSMO
|
|
this->totalParticles = numberOfParticles;
|
|
#else
|
|
MPI_Allreduce((void*) &numberOfParticles,
|
|
(void*) &this->totalParticles,
|
|
1, MPI_LONG, MPI_SUM, Partition::getComm());
|
|
#endif
|
|
|
|
// Share the information about max particles in a file for setting buffer size
|
|
#ifdef USE_SERIAL_COSMO
|
|
this->maxParticles = maxNumberOfParticles;
|
|
#else
|
|
MPI_Allreduce((void*) &maxNumberOfParticles,
|
|
(void*) &this->maxParticles,
|
|
1, MPI_LONG, MPI_MAX, Partition::getComm());
|
|
#endif
|
|
|
|
// Share the maximum number of files on a processor for setting the loop
|
|
#ifdef USE_SERIAL_COSMO
|
|
this->maxFiles = numberOfMyFiles;
|
|
#else
|
|
MPI_Allreduce((void*) &numberOfMyFiles,
|
|
(void*) &this->maxFiles,
|
|
1, MPI_INT, MPI_MAX, Partition::getComm());
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
if (this->myProc == MASTER) {
|
|
cout << "Total particle count: " << this->totalParticles << endl;
|
|
cout << "Max particle count: " << this->maxParticles << endl;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Each processor reads 0 or more files, a buffer at a time.
|
|
// The particles are processed by seeing if they are in the subextent of
|
|
// this processor and are tagged either ALIVE or if dead, by the index of
|
|
// the neighbor zone which contains that particle. That buffer is sent
|
|
// round robin to (myProc + 1) % numProc where it is processed and sent on.
|
|
// After each processor reads one buffer and sends and receives numProc - 1
|
|
// times the next buffer from the file is read. Must use a double buffering
|
|
// scheme so that on each send/recv we switch buffers.
|
|
//
|
|
// Input files may be BLOCK or RECORD structured
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::distributeParticles(
|
|
Message* message1, // Send/receive buffers
|
|
Message* message2) // Send/receive buffers
|
|
{
|
|
// Each processor has filled a buffer with particles read from a file
|
|
// or had no particles to read but set the count in the buffer to 0
|
|
// Process the buffer to keep only those within range
|
|
Message* recvMessage = message1;
|
|
Message* sendMessage = message2;
|
|
|
|
// Process the original send buffer of particles from the file
|
|
collectLocalParticles(recvMessage, sendMessage);
|
|
|
|
// Distribute buffer round robin so that all processors see it
|
|
for (int step = 0; step < this->maxFileSends; step++) {
|
|
|
|
if (step < this->numberOfFileSends)
|
|
{
|
|
// Send buffer to the next processor if round robin loop is still active
|
|
sendMessage->send(this->nextProc);
|
|
|
|
// Receive buffer from the previous processor
|
|
recvMessage->receive(this->prevProc);
|
|
sendMessage->waitOnSend();
|
|
}
|
|
|
|
#ifndef USE_SERIAL_COSMO
|
|
MPI_Barrier(Partition::getComm());
|
|
#endif
|
|
|
|
// Process the send buffer for alive and dead before sending on
|
|
// the particles that were not claimed by this processor
|
|
if (step < this->numberOfFileSends)
|
|
collectLocalParticles(recvMessage, sendMessage);
|
|
|
|
#ifndef USE_SERIAL_COSMO
|
|
MPI_Barrier(Partition::getComm());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Input file is RECORD structured so read each particle record and populate
|
|
// the double buffer in particle order for the rest of the processing
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readFromRecordFile(
|
|
ifstream* inStream, // Stream to read from
|
|
int firstParticle, // First particle index
|
|
int numberOfParticles, // Number to read this time
|
|
POSVEL_T* fBlock, // Buffer for read in data
|
|
ID_T* iBlock, // Buffer for read in data
|
|
Message* message) // Reordered data
|
|
{
|
|
// Store number of particles used in first position
|
|
message->putValue(&numberOfParticles);
|
|
if (numberOfParticles == 0)
|
|
return;
|
|
|
|
// Seek to the first particle locations and read
|
|
int skip = RECORD_SIZE * firstParticle;
|
|
inStream->seekg(skip, ios::beg);
|
|
|
|
// Store each particle location, velocity, mass and tag (as float) in buffer
|
|
int changeCount = 0;
|
|
for (int p = 0; p < numberOfParticles; p++) {
|
|
|
|
// Set file pointer to the requested particle
|
|
inStream->read(reinterpret_cast<char*>(fBlock),
|
|
COSMO_FLOAT * sizeof(POSVEL_T));
|
|
|
|
if (inStream->gcount() != COSMO_FLOAT * sizeof(POSVEL_T)) {
|
|
cout << "Premature end-of-file" << endl;
|
|
exit (-1);
|
|
}
|
|
|
|
// Convert units if requested
|
|
fBlock[0] *= this->distConvertFactor;
|
|
fBlock[2] *= this->distConvertFactor;
|
|
fBlock[4] *= this->distConvertFactor;
|
|
fBlock[6] *= this->massConvertFactor;
|
|
|
|
inStream->read(reinterpret_cast<char*>(iBlock),
|
|
COSMO_INT * sizeof(ID_T));
|
|
|
|
if (inStream->gcount() != COSMO_INT * sizeof(ID_T)) {
|
|
cout << "Premature end-of-file" << endl;
|
|
exit (-1);
|
|
}
|
|
|
|
// If the location is not within the bounding box wrap around
|
|
for (int i = 0; i <= 4; i = i + 2) {
|
|
if (fBlock[i] >= this->boxSize) {
|
|
#ifdef DEBUG
|
|
cout << "Location at " << i << " changed from " << fBlock[i] << endl;
|
|
#endif
|
|
fBlock[i] -= this->boxSize;
|
|
changeCount++;
|
|
}
|
|
}
|
|
|
|
// Store location and velocity and mass in message buffer
|
|
// Reorder so that location vector is followed by velocity vector
|
|
message->putValue(&fBlock[0]);
|
|
message->putValue(&fBlock[2]);
|
|
message->putValue(&fBlock[4]);
|
|
message->putValue(&fBlock[1]);
|
|
message->putValue(&fBlock[3]);
|
|
message->putValue(&fBlock[5]);
|
|
message->putValue(&fBlock[6]);
|
|
|
|
// Store the integer tag
|
|
message->putValue(&iBlock[0]);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Input file is BLOCK structured so read head and each block of data.
|
|
// Gadget format:
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_H 4 bytes (size of header)
|
|
// Header (6 types of particles with counts and masses)
|
|
// SKIP_H 4 bytes (size of header)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_L 4 bytes (size of location block in bytes)
|
|
// Block of location data where each particle's x,y,z is stored together
|
|
// SKIP_L 4 bytes (size of location block in bytes)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_V 4 bytes (size of velocity block in bytes)
|
|
// Block of velocity data where each particle's xv,yv,zv is stored together
|
|
// SKIP_V 4 bytes (size of velocity block in bytes)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_T 4 bytes (size of tag block in bytes)
|
|
// Block of tag data
|
|
// SKIP_T 4 bytes (size of tag block in bytes)
|
|
//
|
|
// Reorder the data after it is read into the same structure as the
|
|
// RECORD data so that the rest of the code does not have to be changed
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readFromBlockFile(
|
|
ifstream* inStream, // Stream to read from
|
|
int firstParticle, // First particle index
|
|
int numberOfParticles, // Number to read this time
|
|
int totParticles, // Total particles in file
|
|
POSVEL_T* lBlock, // Buffer for read of location
|
|
POSVEL_T* vBlock, // Buffer for read of velocity
|
|
ID_T* iBlock, // Buffer for read in data
|
|
Message* message) // Reordered data
|
|
{
|
|
// Store number of particles used in first position
|
|
message->putValue(&numberOfParticles);
|
|
if (numberOfParticles == 0)
|
|
return;
|
|
|
|
// Calculate skips to first location, velocity and tag
|
|
int skipToLocation = 0;
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToLocation += GADGET_SKIP; // Size of header
|
|
skipToLocation += GADGET_HEADER_SIZE; // Header
|
|
skipToLocation += GADGET_SKIP; // Size of header
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToLocation += GADGET_SKIP; // Size of location block
|
|
|
|
int skipToVelocity = skipToLocation;
|
|
skipToVelocity += DIMENSION * sizeof(POSVEL_T) * totParticles;
|
|
skipToVelocity += GADGET_SKIP; // Size of location block
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToVelocity += GADGET_SKIP; // Size of velocity block
|
|
|
|
int skipToTag = skipToVelocity;
|
|
skipToTag += DIMENSION * sizeof(POSVEL_T) * totParticles;
|
|
skipToTag += GADGET_SKIP; // Size of velocity block
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToTag += GADGET_SKIP; // Size of tag block
|
|
|
|
// Seek to the first requested particle location and read triples
|
|
inStream->seekg(skipToLocation, ios::beg);
|
|
int skip = (DIMENSION * sizeof(POSVEL_T) * firstParticle);
|
|
inStream->seekg(skip, ios::cur);
|
|
|
|
readData(this->gadgetSwap, (void*) lBlock, sizeof(POSVEL_T),
|
|
DIMENSION * numberOfParticles, inStream);
|
|
|
|
// Convert units of distance
|
|
for (int i = 0; i < DIMENSION*numberOfParticles; i++)
|
|
lBlock[i] *= this->distConvertFactor;
|
|
|
|
// If the location is not within the bounding box wrap around
|
|
for (int i = 0; i < DIMENSION*numberOfParticles; i++) {
|
|
if (lBlock[i] >= this->boxSize)
|
|
lBlock[i] -= this->boxSize;
|
|
}
|
|
|
|
// Seek to first requested particle velocity and read triples
|
|
inStream->seekg(skipToVelocity, ios::beg);
|
|
skip = (DIMENSION * sizeof(POSVEL_T) * firstParticle); // skip to velocity
|
|
inStream->seekg(skip, ios::cur);
|
|
|
|
readData(this->gadgetSwap, (void*) vBlock, sizeof(POSVEL_T),
|
|
DIMENSION * numberOfParticles, inStream);
|
|
|
|
// Seek to first requested particle tag and read
|
|
inStream->seekg(skipToTag, ios::beg);
|
|
skip = sizeof(int32_t) * firstParticle; // skip to tag
|
|
inStream->seekg(skip, ios::cur);
|
|
|
|
#ifdef ID_64
|
|
{
|
|
vector<int32_t> iBlock32(numberOfParticles);
|
|
readData(this->gadgetSwap, (void*) &iBlock32[0], sizeof(int32_t),
|
|
numberOfParticles, inStream);
|
|
std::copy(iBlock32.begin(), iBlock32.end(), iBlock);
|
|
}
|
|
#else
|
|
readData(this->gadgetSwap, (void*) iBlock, sizeof(ID_T),
|
|
numberOfParticles, inStream);
|
|
#endif
|
|
|
|
// Store the locations in the message buffer in record order
|
|
// so that the same distribution method for RECORD will work
|
|
int particlesRemaining = numberOfParticles;
|
|
int curParticle = firstParticle;
|
|
int type = 0;
|
|
int indx = 0;
|
|
int tagindx = 0;
|
|
|
|
// When more than one gadget particle type is in the file we must
|
|
// know what mass to assign to the current piece read
|
|
while (particlesRemaining > 0) {
|
|
|
|
// Set particle type and mass based on current particle
|
|
while (type < NUM_GADGET_TYPES && curParticle >= this->gadgetStart[type])
|
|
type++;
|
|
type--;
|
|
|
|
POSVEL_T particleMass =
|
|
(POSVEL_T) this->gadgetHeader.mass[type] * this->massConvertFactor;
|
|
|
|
// Place particles of this type and mass in the buffer
|
|
int numLeftInType = this->gadgetHeader.npart[type] -
|
|
(curParticle - this->gadgetStart[type]);
|
|
int count = min(particlesRemaining, numLeftInType);
|
|
|
|
for (int p = 0; p < count; p++) {
|
|
|
|
// Locations
|
|
message->putValue(&lBlock[indx]); // X location
|
|
message->putValue(&lBlock[indx+1]); // Y location
|
|
message->putValue(&lBlock[indx+2]); // Z location
|
|
|
|
// Velocities
|
|
message->putValue(&vBlock[indx]); // X velocity
|
|
message->putValue(&vBlock[indx+1]); // Y velocity
|
|
message->putValue(&vBlock[indx+2]); // Z velocity
|
|
|
|
// Mass
|
|
message->putValue(&particleMass);
|
|
|
|
// Id tag
|
|
message->putValue(&iBlock[tagindx]);
|
|
indx += DIMENSION;
|
|
tagindx++;
|
|
}
|
|
|
|
// Do we have more particles of a different type
|
|
particlesRemaining -= count;
|
|
curParticle += count;
|
|
}
|
|
}
|
|
|
|
#ifndef USE_SERIAL_COSMO
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Input file is RECORD structured so read each particle record and populate
|
|
// the double buffer in particle order for the rest of the processing
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readFromRecordFile(
|
|
ifstream* inStream, // Stream to read from
|
|
int firstParticle, // First particle index
|
|
int numberOfParticles, // Number to read this time
|
|
POSVEL_T* fBlock, // Buffer for read in data
|
|
ID_T* iBlock, // Buffer for read in data
|
|
std::vector< std::vector<CosmoParticle> > &pByProc)
|
|
{
|
|
if (numberOfParticles == 0)
|
|
return;
|
|
|
|
// Seek to the first particle locations and read
|
|
int skip = RECORD_SIZE * firstParticle;
|
|
inStream->seekg(skip, ios::beg);
|
|
|
|
// Store each particle location, velocity, mass and tag (as float) in buffer
|
|
int changeCount = 0;
|
|
for (int p = 0; p < numberOfParticles; p++) {
|
|
|
|
// Set file pointer to the requested particle
|
|
inStream->read(reinterpret_cast<char*>(fBlock),
|
|
COSMO_FLOAT * sizeof(POSVEL_T));
|
|
|
|
if (inStream->gcount() != COSMO_FLOAT * sizeof(POSVEL_T)) {
|
|
cout << "Premature end-of-file" << endl;
|
|
exit (-1);
|
|
}
|
|
|
|
// Convert units if requested
|
|
fBlock[0] *= this->distConvertFactor;
|
|
fBlock[2] *= this->distConvertFactor;
|
|
fBlock[4] *= this->distConvertFactor;
|
|
fBlock[6] *= this->massConvertFactor;
|
|
|
|
inStream->read(reinterpret_cast<char*>(iBlock),
|
|
COSMO_INT * sizeof(ID_T));
|
|
|
|
if (inStream->gcount() != COSMO_INT * sizeof(ID_T)) {
|
|
cout << "Premature end-of-file" << endl;
|
|
exit (-1);
|
|
}
|
|
|
|
// If the location is not within the bounding box wrap around
|
|
for (int i = 0; i <= 4; i = i + 2) {
|
|
if (fBlock[i] >= this->boxSize) {
|
|
#ifdef DEBUG
|
|
cout << "Location at " << i << " changed from " << fBlock[i] << endl;
|
|
#endif
|
|
fBlock[i] -= this->boxSize;
|
|
changeCount++;
|
|
} else if (fBlock[i] < 0) {
|
|
#ifdef DEBUG
|
|
cout << "Location at " << i << " changed from " << fBlock[i] << endl;
|
|
#endif
|
|
fBlock[i] += this->boxSize;
|
|
changeCount++;
|
|
}
|
|
}
|
|
|
|
// Figure out to which rank this particle belongs
|
|
float sizeX = this->boxSize / this->layoutSize[0];
|
|
float sizeY = this->boxSize / this->layoutSize[1];
|
|
float sizeZ = this->boxSize / this->layoutSize[2];
|
|
int coords[3] = { (int) (fBlock[0]/sizeX),
|
|
(int) (fBlock[2]/sizeY),
|
|
(int) (fBlock[4]/sizeZ)
|
|
};
|
|
int home;
|
|
MPI_Cart_rank(Partition::getComm(), coords, &home);
|
|
|
|
// Store location and velocity and mass
|
|
// Reorder so that location vector is followed by velocity vector
|
|
CosmoParticle particle = { { fBlock[0],
|
|
fBlock[2],
|
|
fBlock[4],
|
|
fBlock[1],
|
|
fBlock[3],
|
|
fBlock[5],
|
|
fBlock[6]
|
|
}, {
|
|
iBlock[0]
|
|
}
|
|
};
|
|
pByProc[home].push_back(particle);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Input file is BLOCK structured so read head and each block of data.
|
|
// Gadget format:
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_H 4 bytes (size of header)
|
|
// Header (6 types of particles with counts and masses)
|
|
// SKIP_H 4 bytes (size of header)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_L 4 bytes (size of location block in bytes)
|
|
// Block of location data where each particle's x,y,z is stored together
|
|
// SKIP_L 4 bytes (size of location block in bytes)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_V 4 bytes (size of velocity block in bytes)
|
|
// Block of velocity data where each particle's xv,yv,zv is stored together
|
|
// SKIP_V 4 bytes (size of velocity block in bytes)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_T 4 bytes (size of tag block in bytes)
|
|
// Block of tag data
|
|
// SKIP_T 4 bytes (size of tag block in bytes)
|
|
//
|
|
// Reorder the data after it is read into the same structure as the
|
|
// RECORD data so that the rest of the code does not have to be changed
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readFromBlockFile(
|
|
ifstream* inStream, // Stream to read from
|
|
int firstParticle, // First particle index
|
|
int numberOfParticles, // Number to read this time
|
|
int totParticles, // Total particles in file
|
|
POSVEL_T* lBlock, // Buffer for read of location
|
|
POSVEL_T* vBlock, // Buffer for read of velocity
|
|
ID_T* iBlock, // Buffer for read in data
|
|
std::vector< std::vector<CosmoParticle> > &pByProc)
|
|
{
|
|
if (numberOfParticles == 0)
|
|
return;
|
|
|
|
// Calculate skips to first location, velocity and tag
|
|
int skipToLocation = 0;
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToLocation += GADGET_SKIP; // Size of header
|
|
skipToLocation += GADGET_HEADER_SIZE; // Header
|
|
skipToLocation += GADGET_SKIP; // Size of header
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToLocation += GADGET_SKIP; // Size of location block
|
|
|
|
int skipToVelocity = skipToLocation;
|
|
skipToVelocity += DIMENSION * sizeof(POSVEL_T) * totParticles;
|
|
skipToVelocity += GADGET_SKIP; // Size of location block
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToVelocity += GADGET_SKIP; // Size of velocity block
|
|
|
|
int skipToTag = skipToVelocity;
|
|
skipToTag += DIMENSION * sizeof(POSVEL_T) * totParticles;
|
|
skipToTag += GADGET_SKIP; // Size of velocity block
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToTag += GADGET_SKIP; // Size of tag block
|
|
|
|
// Seek to the first requested particle location and read triples
|
|
inStream->seekg(skipToLocation, ios::beg);
|
|
int skip = (DIMENSION * sizeof(POSVEL_T) * firstParticle);
|
|
inStream->seekg(skip, ios::cur);
|
|
|
|
readData(this->gadgetSwap, (void*) lBlock, sizeof(POSVEL_T),
|
|
DIMENSION * numberOfParticles, inStream);
|
|
|
|
// Convert units of distance
|
|
for (int i = 0; i < DIMENSION*numberOfParticles; i++)
|
|
lBlock[i] *= this->distConvertFactor;
|
|
|
|
// If the location is not within the bounding box wrap around
|
|
for (int i = 0; i < DIMENSION*numberOfParticles; i++) {
|
|
if (lBlock[i] >= this->boxSize)
|
|
lBlock[i] -= this->boxSize;
|
|
else if (lBlock[i] < 0)
|
|
lBlock[i] += this->boxSize;
|
|
}
|
|
|
|
// Seek to first requested particle velocity and read triples
|
|
inStream->seekg(skipToVelocity, ios::beg);
|
|
skip = (DIMENSION * sizeof(POSVEL_T) * firstParticle); // skip to velocity
|
|
inStream->seekg(skip, ios::cur);
|
|
|
|
readData(this->gadgetSwap, (void*) vBlock, sizeof(POSVEL_T),
|
|
DIMENSION * numberOfParticles, inStream);
|
|
|
|
// Seek to first requested particle tag and read
|
|
inStream->seekg(skipToTag, ios::beg);
|
|
skip = sizeof(int32_t) * firstParticle; // skip to tag
|
|
inStream->seekg(skip, ios::cur);
|
|
|
|
#ifdef ID_64
|
|
{
|
|
vector<int32_t> iBlock32(numberOfParticles);
|
|
readData(this->gadgetSwap, (void*) &iBlock32[0], sizeof(int32_t),
|
|
numberOfParticles, inStream);
|
|
std::copy(iBlock32.begin(), iBlock32.end(), iBlock);
|
|
}
|
|
#else
|
|
readData(this->gadgetSwap, (void*) iBlock, sizeof(ID_T),
|
|
numberOfParticles, inStream);
|
|
#endif
|
|
|
|
// Store the locations in the message buffer in record order
|
|
// so that the same distribution method for RECORD will work
|
|
int particlesRemaining = numberOfParticles;
|
|
int curParticle = firstParticle;
|
|
int type = 0;
|
|
int indx = 0;
|
|
int tagindx = 0;
|
|
|
|
// When more than one gadget particle type is in the file we must
|
|
// know what mass to assign to the current piece read
|
|
while (particlesRemaining > 0) {
|
|
|
|
// Set particle type and mass based on current particle
|
|
while (type < NUM_GADGET_TYPES && curParticle >= this->gadgetStart[type])
|
|
type++;
|
|
type--;
|
|
|
|
POSVEL_T particleMass =
|
|
(POSVEL_T) this->gadgetHeader.mass[type] * this->massConvertFactor;
|
|
|
|
// Place particles of this type and mass in the buffer
|
|
int numLeftInType = this->gadgetHeader.npart[type] -
|
|
(curParticle - this->gadgetStart[type]);
|
|
int count = min(particlesRemaining, numLeftInType);
|
|
|
|
float sizeX = this->boxSize / this->layoutSize[0];
|
|
float sizeY = this->boxSize / this->layoutSize[1];
|
|
float sizeZ = this->boxSize / this->layoutSize[2];
|
|
|
|
for (int p = 0; p < count; p++) {
|
|
// Figure out to which rank this particle belongs
|
|
int coords[3] = { (int) (lBlock[indx]/sizeX),
|
|
(int) (lBlock[indx+1]/sizeY),
|
|
(int) (lBlock[indx+2]/sizeZ)
|
|
};
|
|
int home;
|
|
MPI_Cart_rank(Partition::getComm(), coords, &home);
|
|
|
|
// Store location and velocity and mass
|
|
// Reorder so that location vector is followed by velocity vector
|
|
CosmoParticle particle = { { lBlock[indx], // X location
|
|
lBlock[indx+1], // Y location
|
|
lBlock[indx+2], // Z location
|
|
vBlock[indx], // X velocity
|
|
vBlock[indx+1], // Y velocity
|
|
vBlock[indx+2], // Z velocity
|
|
particleMass
|
|
}, {
|
|
iBlock[tagindx]
|
|
}
|
|
};
|
|
pByProc[home].push_back(particle);
|
|
|
|
indx += DIMENSION;
|
|
tagindx++;
|
|
}
|
|
|
|
// Do we have more particles of a different type
|
|
particlesRemaining -= count;
|
|
curParticle += count;
|
|
}
|
|
}
|
|
#endif // USE_SERIAL_COSMO
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Process the data buffer of particles to choose those which are ALIVE
|
|
// or DEAD on this processor. Do wraparound tests to populate as for a
|
|
// 3D torus. Dead particle status is the zone id of the neighbor processor
|
|
// which contains it as an ALIVE particle.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::collectLocalParticles(
|
|
Message* recvMessage, // Read particles and extract
|
|
Message* sendMessage) // Other particles copied here
|
|
{
|
|
// In order to read a buffer, reset position to the beginning
|
|
recvMessage->reset();
|
|
sendMessage->reset();
|
|
|
|
int recvParticles;
|
|
int sendParticles = 0;
|
|
recvMessage->getValue(&recvParticles);
|
|
sendMessage->putValue(&sendParticles);
|
|
|
|
POSVEL_T loc[DIMENSION], vel[DIMENSION], mass;
|
|
ID_T id;
|
|
|
|
// Test each particle in the buffer to see if it is ALIVE or DEAD
|
|
// If it is DEAD assign it to the neighbor zone that it is in
|
|
// Check all combinations of wraparound
|
|
|
|
for (int i = 0; i < recvParticles; i++) {
|
|
for (int dim = 0; dim < DIMENSION; dim++)
|
|
recvMessage->getValue(&loc[dim]);
|
|
for (int dim = 0; dim < DIMENSION; dim++)
|
|
recvMessage->getValue(&vel[dim]);
|
|
recvMessage->getValue(&mass);
|
|
recvMessage->getValue(&id);
|
|
|
|
// Is the particle ALIVE on this processor
|
|
if ((loc[0] >= minAlive[0] && loc[0] < maxAlive[0]) &&
|
|
(loc[1] >= minAlive[1] && loc[1] < maxAlive[1]) &&
|
|
(loc[2] >= minAlive[2] && loc[2] < maxAlive[2])) {
|
|
|
|
this->xx->push_back(loc[0]);
|
|
this->yy->push_back(loc[1]);
|
|
this->zz->push_back(loc[2]);
|
|
this->vx->push_back(vel[0]);
|
|
this->vy->push_back(vel[1]);
|
|
this->vz->push_back(vel[2]);
|
|
this->ms->push_back(mass);
|
|
this->tag->push_back(id);
|
|
|
|
this->numberOfAliveParticles++;
|
|
this->particleCount++;
|
|
} else {
|
|
|
|
// Pass the particle along to the next processor in send buffer
|
|
sendParticles++;
|
|
for (int dim = 0; dim < DIMENSION; dim++)
|
|
sendMessage->putValue(&loc[dim]);
|
|
for (int dim = 0; dim < DIMENSION; dim++)
|
|
sendMessage->putValue(&vel[dim]);
|
|
sendMessage->putValue(&mass);
|
|
sendMessage->putValue(&id);
|
|
}
|
|
}
|
|
// Overwrite the send buffer first word with the known number of particles
|
|
sendMessage->putValueAtPosition(&sendParticles, 0);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Each processor reads 1 file or gets a pointer to data eventually
|
|
// As the particle is read it will be stored as an alive particle on this
|
|
// processor and will be checked about neighbor ranges to see if it must
|
|
// be exchanged
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readParticlesOneToOne(int reserveQ)
|
|
{
|
|
// File name is the base file name with processor id appended
|
|
// Because MPI Cartesian topology is used the arrangement of files in
|
|
// physical space must follow the rule of last dimension varies fastest
|
|
ostringstream fileName;
|
|
fileName << this->baseFile << this->myProc;
|
|
this->inFiles.push_back(fileName.str());
|
|
|
|
// Compute the total number of particles in the problem
|
|
// Compute the maximum number of particles in any one file to set buffer size
|
|
findFileParticleCount();
|
|
|
|
// Reserve particle storage to minimize reallocation
|
|
int reserveSize = (int) (this->maxParticles * DEAD_FACTOR);
|
|
|
|
if(reserveQ) {
|
|
cout << "readParticlesOneToOne reserving vectors" << endl;
|
|
this->xx->reserve(reserveSize);
|
|
this->yy->reserve(reserveSize);
|
|
this->zz->reserve(reserveSize);
|
|
this->vx->reserve(reserveSize);
|
|
this->vy->reserve(reserveSize);
|
|
this->vz->reserve(reserveSize);
|
|
this->ms->reserve(reserveSize);
|
|
this->tag->reserve(reserveSize);
|
|
}
|
|
|
|
// Running total and index into particle data on this processor
|
|
this->particleCount = 0;
|
|
|
|
// Read the input file storing particles immediately because all are alive
|
|
if (this->inputType == RECORD) {
|
|
readFromRecordFile();
|
|
} else {
|
|
readFromBlockFile();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Input file is RECORD structured so read each particle record and populate
|
|
// the vectors of particles marking all as ALIVE
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readFromRecordFile()
|
|
{
|
|
// Only one file per processor named in index 0
|
|
ifstream inStream(this->inFiles[0].c_str(), ios::in);
|
|
int numberOfParticles = this->fileParticles[0];
|
|
|
|
cout << "Rank " << this->myProc << " open file " << this->inFiles[0]
|
|
<< " with " << numberOfParticles << " particles" << endl;
|
|
|
|
POSVEL_T* fBlock = new POSVEL_T[COSMO_FLOAT];
|
|
ID_T* iBlock = new ID_T[COSMO_INT];
|
|
|
|
// Store each particle location, velocity and tag
|
|
for (int i = 0; i < numberOfParticles; i++) {
|
|
|
|
// Set file pointer to the requested particle
|
|
inStream.read(reinterpret_cast<char*>(fBlock),
|
|
COSMO_FLOAT * sizeof(POSVEL_T));
|
|
|
|
if (inStream.gcount() != COSMO_FLOAT * sizeof(POSVEL_T)) {
|
|
cout << "Premature end-of-file" << endl;
|
|
exit (-1);
|
|
}
|
|
|
|
// Convert units if requested
|
|
fBlock[0] *= this->distConvertFactor;
|
|
fBlock[2] *= this->distConvertFactor;
|
|
fBlock[4] *= this->distConvertFactor;
|
|
fBlock[6] *= this->massConvertFactor;
|
|
|
|
inStream.read(reinterpret_cast<char*>(iBlock),
|
|
COSMO_INT * sizeof(ID_T));
|
|
|
|
if (inStream.gcount() != COSMO_INT * sizeof(ID_T)) {
|
|
cout << "Premature end-of-file" << endl;
|
|
exit (-1);
|
|
}
|
|
|
|
// Store information in buffer if within range on this processor
|
|
if ((fBlock[0] >= minAlive[0] && fBlock[0] <= maxAlive[0]) &&
|
|
(fBlock[2] >= minAlive[1] && fBlock[2] <= maxAlive[1]) &&
|
|
(fBlock[4] >= minAlive[2] && fBlock[4] <= maxAlive[2])) {
|
|
|
|
this->xx->push_back(fBlock[0]);
|
|
this->vx->push_back(fBlock[1]);
|
|
this->yy->push_back(fBlock[2]);
|
|
this->vy->push_back(fBlock[3]);
|
|
this->zz->push_back(fBlock[4]);
|
|
this->vz->push_back(fBlock[5]);
|
|
this->ms->push_back(fBlock[6]);
|
|
this->tag->push_back(iBlock[0]);
|
|
|
|
this->numberOfAliveParticles++;
|
|
this->particleCount++;
|
|
}
|
|
}
|
|
|
|
inStream.close();
|
|
delete [] fBlock;
|
|
delete [] iBlock;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Input file is BLOCK structured so read head and each block of data.
|
|
// Gadget format:
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_H 4 bytes (size of header)
|
|
// Header (6 types of particles with counts and masses)
|
|
// SKIP_H 4 bytes (size of header)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_L 4 bytes (size of location block in bytes)
|
|
// Block of location data where each particle's x,y,z is stored together
|
|
// SKIP_L 4 bytes (size of location block in bytes)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_V 4 bytes (size of velocity block in bytes)
|
|
// Block of velocity data where each particle's xv,yv,zv is stored together
|
|
// SKIP_V 4 bytes (size of velocity block in bytes)
|
|
//
|
|
// SKIP_GADGET_2 has extra 16 bytes
|
|
// SKIP_T 4 bytes (size of tag block in bytes)
|
|
// Block of tag data
|
|
// SKIP_T 4 bytes (size of tag block in bytes)
|
|
//
|
|
// Reorder the data after it is read into the same structure as the
|
|
// RECORD data so that the rest of the code does not have to be changed
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readFromBlockFile()
|
|
{
|
|
// Only one file per processor named in index 0
|
|
ifstream inStream(this->inFiles[0].c_str(), ios::in);
|
|
int numberOfParticles = this->fileParticles[0];
|
|
|
|
cout << "Rank " << this->myProc << " open file " << this->inFiles[0]
|
|
<< " with " << numberOfParticles << " particles" << endl;
|
|
|
|
// Calculate skips to first location, velocity and tag
|
|
int skipToLocation = 0;
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToLocation += GADGET_SKIP; // Size of header
|
|
skipToLocation += GADGET_HEADER_SIZE; // Header
|
|
skipToLocation += GADGET_SKIP; // Size of header
|
|
if (this->gadgetFormat == GADGET_2)
|
|
skipToLocation += GADGET_2_SKIP;
|
|
skipToLocation += GADGET_SKIP; // Size of location block
|
|
|
|
// Allocate blocks to read into
|
|
POSVEL_T* lBlock = new POSVEL_T[numberOfParticles * DIMENSION];
|
|
POSVEL_T* vBlock = new POSVEL_T[numberOfParticles * DIMENSION];
|
|
ID_T* iBlock = new ID_T[numberOfParticles];
|
|
|
|
// Seek to particle locations and read triples
|
|
inStream.seekg(skipToLocation, ios::beg);
|
|
readData(this->gadgetSwap, (void*) lBlock, sizeof(POSVEL_T),
|
|
DIMENSION * numberOfParticles, &inStream);
|
|
|
|
// Convert locations
|
|
for (int p = 0; p < DIMENSION * numberOfParticles; p++)
|
|
lBlock[p] *= this->distConvertFactor;
|
|
|
|
// Seek to particle velocities and read triples
|
|
inStream.seekg((2 * GADGET_SKIP), ios::cur);
|
|
readData(this->gadgetSwap, (void*) vBlock, sizeof(POSVEL_T),
|
|
DIMENSION * numberOfParticles, &inStream);
|
|
|
|
|
|
// Seek to particle tags and read
|
|
inStream.seekg((2 * GADGET_SKIP), ios::cur);
|
|
#ifdef ID_64
|
|
{
|
|
vector<int32_t> iBlock32(numberOfParticles);
|
|
readData(this->gadgetSwap, (void*) &iBlock32[0], sizeof(int32_t),
|
|
numberOfParticles, &inStream);
|
|
std::copy(iBlock32.begin(), iBlock32.end(), iBlock);
|
|
}
|
|
#else
|
|
readData(this->gadgetSwap, (void*) iBlock, sizeof(ID_T),
|
|
numberOfParticles, &inStream);
|
|
#endif
|
|
|
|
// Store mass, locations, velocities and tags into arrays if in range
|
|
// Range test is needed because this code is used for ONE_TO_ONE where all
|
|
// particles must be added, and by one single input file over many
|
|
// processors where messaging is not needed, but some particles don't belong
|
|
int indx = 0;
|
|
int tagindx = 0;
|
|
for (int type = 0; type < NUM_GADGET_TYPES; type++) {
|
|
|
|
POSVEL_T particleMass =
|
|
(POSVEL_T) this->gadgetHeader.mass[type] * this->massConvertFactor;
|
|
|
|
for (int p = 0; p < this->gadgetHeader.npart[type]; p++) {
|
|
|
|
if ((lBlock[indx] >= minAlive[0] && lBlock[indx] < maxAlive[0]) &&
|
|
(lBlock[indx+1] >= minAlive[1] && lBlock[indx+1] < maxAlive[1]) &&
|
|
(lBlock[indx+2] >= minAlive[2] && lBlock[indx+2] < maxAlive[2])) {
|
|
|
|
this->xx->push_back(lBlock[indx]);
|
|
this->yy->push_back(lBlock[indx+1]);
|
|
this->zz->push_back(lBlock[indx+2]);
|
|
this->vx->push_back(vBlock[indx]);
|
|
this->vy->push_back(vBlock[indx+1]);
|
|
this->vz->push_back(vBlock[indx+2]);
|
|
this->ms->push_back(particleMass);
|
|
this->tag->push_back(iBlock[tagindx]);
|
|
|
|
this->numberOfAliveParticles++;
|
|
this->particleCount++;
|
|
}
|
|
indx += DIMENSION;
|
|
tagindx++;
|
|
}
|
|
}
|
|
|
|
delete [] lBlock;
|
|
delete [] vBlock;
|
|
delete [] iBlock;
|
|
inStream.close();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Read the Gadget header from the stream
|
|
// Gadget file may be Gadget-1 format with no block indicators or
|
|
// Gadget-2 format with size of block 4 byte integers surrounding each block
|
|
// Data may be big or little endian which we can tell by checking that
|
|
// the header size is 256 in the first 4 bytes
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readGadgetHeader(ifstream* gStr)
|
|
{
|
|
this->gadgetSwap = false;
|
|
this->gadgetFormat = 1;
|
|
int blockSize, blockSize2;
|
|
string gadget2;
|
|
|
|
// Set the gadget format type by reading the first 4 byte integer
|
|
// If it is not "256" or "65536" then gadget-2 format with 16 bytes in front
|
|
readData(this->gadgetSwap, (void*) &blockSize, GADGET_SKIP, 1, gStr);
|
|
if (blockSize != GADGET_HEADER_SIZE && blockSize != GADGET_HEADER_SIZE_SWP) {
|
|
this->gadgetFormat = GADGET_2;
|
|
gadget2 = readString(gStr, GADGET_2_SKIP - GADGET_SKIP);
|
|
readData(this->gadgetSwap, (void*) &blockSize, GADGET_SKIP, 1, gStr);
|
|
}
|
|
|
|
// Set the swap type
|
|
if (blockSize != GADGET_HEADER_SIZE) {
|
|
this->gadgetSwap = true;
|
|
blockSize = GADGET_HEADER_SIZE;
|
|
}
|
|
|
|
// Read the Gadget header
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.npart[0],
|
|
sizeof(int), NUM_GADGET_TYPES, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.mass[0],
|
|
sizeof(double), NUM_GADGET_TYPES, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.time,
|
|
sizeof(double), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.redshift,
|
|
sizeof(double), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.flag_sfr,
|
|
sizeof(int), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.flag_feedback,
|
|
sizeof(int), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.npartTotal[0],
|
|
sizeof(int), NUM_GADGET_TYPES, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.flag_cooling,
|
|
sizeof(int), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.num_files,
|
|
sizeof(int), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.BoxSize,
|
|
sizeof(double), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.Omega0,
|
|
sizeof(double), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.OmegaLambda,
|
|
sizeof(double), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.HubbleParam,
|
|
sizeof(double), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.flag_stellarage,
|
|
sizeof(int), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.flag_metals,
|
|
sizeof(int), 1, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.HighWord[0],
|
|
sizeof(int), NUM_GADGET_TYPES, gStr);
|
|
readData(this->gadgetSwap, (void*) &this->gadgetHeader.flag_entropy,
|
|
sizeof(int), 1, gStr);
|
|
string fill = readString(gStr, GADGET_FILL);
|
|
strcpy(&this->gadgetHeader.fill[0], fill.c_str());
|
|
|
|
// Read the Gadget header size to verify block
|
|
readData(this->gadgetSwap, (void*) &blockSize2, GADGET_SKIP, 1, gStr);
|
|
if (blockSize != blockSize2)
|
|
cout << "Mismatch of header size and header structure" << endl;
|
|
|
|
|
|
// Every type particle will have location, velocity and tag so sum up
|
|
this->gadgetParticleCount = 0;
|
|
for (int i = 0; i < NUM_GADGET_TYPES; i++) {
|
|
this->gadgetStart[i] = this->gadgetParticleCount;
|
|
this->gadgetParticleCount += this->gadgetHeader.npart[i];
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Read in the requested number of characters
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
string ParticleDistribute::readString(ifstream* inStr, int size)
|
|
{
|
|
char* buffer = new char[size + 1];
|
|
inStr->read(buffer, size);
|
|
buffer[size] = '\0';
|
|
|
|
// Make sure string has legal values
|
|
if (isalnum(buffer[0]) == 0)
|
|
buffer[0] = '\0';
|
|
for (int i = 1; i < size; i++)
|
|
if (isprint(buffer[i]) == 0)
|
|
buffer[i] = '\0';
|
|
|
|
string retString = buffer;
|
|
delete [] buffer;
|
|
return retString;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Read in the number of items from the file pointer and
|
|
// byte swap if necessary
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
void ParticleDistribute::readData(
|
|
bool swap,
|
|
void* data,
|
|
unsigned long dataSize,
|
|
unsigned long dataCount,
|
|
ifstream* inStr)
|
|
{
|
|
// Read all the data from the file
|
|
inStr->read(reinterpret_cast<char*>(data), dataSize*dataCount);
|
|
|
|
if (swap == true) {
|
|
|
|
// Byte swap each integer
|
|
char* dataPtr = (char*) data;
|
|
char temp;
|
|
for (unsigned long item = 0; item < dataCount; item++) {
|
|
|
|
// Do a byte-by-byte swap, reversing the order.
|
|
for (unsigned int i = 0; i < dataSize / 2; i++) {
|
|
temp = dataPtr[i];
|
|
dataPtr[i] = dataPtr[dataSize - 1 - i];
|
|
dataPtr[dataSize - 1 - i] = temp;
|
|
}
|
|
dataPtr += dataSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // END namespace cosmotk
|