Initial commit

This commit is contained in:
Guilhem Lavaux 2008-12-02 11:00:28 -06:00
commit 1b8ff660c2
7 changed files with 1096 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*~
*.o
*.prog

97
config.hpp Normal file
View File

@ -0,0 +1,97 @@
#ifndef __MAK_CONFIG_HPP
#define __MAK_CONFIG_HPP
#include <stdint.h>
#include <exception>
#include <cstring>
namespace CosmoTool
{
#define NUMDIMS 3
#define NUMCUBES 8
/**
* Base type to specity at what precision we
* must achieve computations.
*/
typedef double ComputePrecision;
/**
* Coordinate type (should be a 3-array).
*/
typedef double Coordinates[NUMDIMS];
/**
* This function is used whenever one needs a general
* conversion between mass and luminosity (or the opposite).
* It should take a "mass" (or luminosity) in input, a unit is
* given to convert this mass into solar units. The output should
* be the "luminosity" (or mass), in solar units.
*/
typedef double (*BiasFunction)(double mass, double unit);
/**
* Function to copy the coordinates "a" into "b".
*/
inline void copyCoordinates(const Coordinates& a, Coordinates& b)
{
memcpy(b, a, sizeof(a));
}
/**
* Base exception class for all exceptions handled by
* this library.
*/
class Exception : public std::exception
{
public:
Exception(const char *mess = 0)
: msg(mess) {}
const char *getMessage() const { return msg; };
virtual const char *what() const throw () { return msg; };
private:
const char *msg;
};
/**
* Exception raised when an invalid argument has been
* passed to a function of the library.
*/
class InvalidArgumentException : public Exception
{
public:
InvalidArgumentException(const char *mess = 0)
: Exception(mess) {}
};
/**
*/
class InvalidRangeException : public Exception
{
public:
InvalidRangeException(const char *mess = 0)
: Exception(mess) {}
};
/**
*/
class NoSuchFileException : public Exception
{
public:
NoSuchFileException(const char *mess = 0)
: Exception(mess) {}
};
/**
*/
class InvalidFileFormatException : public Exception
{
public:
InvalidFileFormatException(const char *mess = 0)
: Exception(mess) {}
};
};
#endif

390
loadRamses.cpp Normal file
View File

@ -0,0 +1,390 @@
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>
#include "loadRamses.hpp"
#include "load_data.h"
using namespace std;
#if 0
#define read_buf(b, n) \
{ \
int k; \
control_size -= n; \
for (k = (n-1); k >= 0; k--) \
infile.read(&b[k],1); \
}
#else
#define read_buf(b, n) \
{ \
int k; \
control_size -= n; \
for (k = 0; k < n; k++) \
infile.read(&b[k], 1); \
}
#endif
#define read_int(i) \
{ \
char *o = (char*)&(i); \
read_buf(o, 4); \
}
#define read_real(f) \
{ \
char *o = (char*)&(f); \
read_buf(o, 4); \
}
#define read_characters(c, n) { \
int k; \
control_size -= n; \
infile.read(c, n); \
}
#define push_dummy_control(id) \
{ int control_size = 0;
#define pop_dummy_control() }
#define push_control(id) \
{ \
int control_size = 0; \
int control_size2 = 0; \
char *intbuf = (char*)&control_size; \
read_int(control_size);
#define pop_control(id) \
assert(control_size == 0); \
read_int(control_size2); \
}
GadgetData *CosmoTool::loadRamses(const char *name)
{
GadgetData *gd = (GadgetData *)malloc(sizeof(GadgetData));
int id = 1;
uint32_t totPart = 0;
cout << "Detecting number of files and particles..." << endl;
while (1)
{
ostringstream ss_fname;
ss_fname << name << setfill('0') << setw(5) << id;
string fname = ss_fname.str();
cout << " ... " << fname << endl;
ifstream infile(fname.c_str());
if (!infile)
break;
int nCpu, ndim, nPar;
push_control(CPU);
read_int(nCpu);
pop_control(CPU);
push_control(DIM);
read_int(ndim);
pop_control(DIM);
push_control(NPAR);
read_int(nPar);
pop_control(NPAR);
cout << " NUMCPU=" << nCpu << " NUMDIM=" << ndim << " NumPart=" << nPar << endl;
totPart += nPar;
id++;
}
assert (totPart <= ((~(size_t)0)/sizeof(ParticleState)));
size_t memSize = sizeof(ParticleState)*(size_t)totPart;
cout << " Needing " << memSize / 1024 << " kBytes" << endl;
gd->particles = (ParticleState *)malloc(memSize);
assert(gd->particles != 0);
gd->NumPart = totPart;
gd->ntot_withmasses = totPart;
gd->header.npart[0] = totPart;
for (int k = 1; k < 6; k++)
{
gd->header.npart[k] = 0;
gd->header.mass[k] = 0;
}
gd->header.num_files = id;
gd->header.BoxSize = 200.0 * 1000; // kPc
gd->header.Omega0 = 0.30; // ????
gd->header.OmegaLambda = 0.70; // ????
gd->header.HubbleParam = 0.70; // ????
cout << " Total number part=" << totPart << endl
<< "Loading particles ..." << endl;
uint32_t curPos = 0;
id = 1;
while (1)
{
ostringstream ss_fname;
ss_fname << name << setfill('0') << setw(5) << id;
string fname = ss_fname.str();
cout << " ... " << id;
cout.flush();
ifstream infile(fname.c_str());
if (!infile)
break;
int nCpu, ndim, nPar;
push_control(CPU);
read_int(nCpu);
pop_control(CPU);
push_control(DIM);
read_int(ndim);
pop_control(DIM);
push_control(NPAR);
read_int(nPar);
pop_control(NPAR);
ParticleState *s = &gd->particles[curPos];
for (uint32_t i = nPar; i > 0; i--,s++)
s->Mass = 1.0;
s = &gd->particles[curPos];
push_control(POSX);
for (uint32_t i = nPar; i > 0; i--)
{
read_real(s->Pos[0]);
s->Pos[0] *= gd->header.BoxSize;
s++;
}
pop_control(POSX);
s = &gd->particles[curPos];
push_control(POSY);
for (uint32_t i = nPar; i > 0; i--)
{
read_real(s->Pos[1]);
s->Pos[1] *= gd->header.BoxSize;
s++;
}
pop_control(POSY);
s = &gd->particles[curPos];
push_control(POSZ);
for (uint32_t i = nPar; i > 0; i--)
{
read_real(s->Pos[2]);
s->Pos[2] *= gd->header.BoxSize;
s++;
}
pop_control(POSZ);
push_control(VELX);
for (uint32_t i = 0; i < nPar; i++)
{
read_real(gd->particles[curPos+i].Vel[0]);
}
pop_control(VELX);
push_control(VELY);
for (uint32_t i = 0; i < nPar; i++)
{
read_real(gd->particles[curPos+i].Vel[1]);
}
pop_control(VELY);
push_control(VELZ);
for (uint32_t i = 0; i < nPar; i++)
{
read_real(gd->particles[curPos+i].Vel[2]);
}
pop_control(VELZ);
curPos += nPar;
id++;
}
cout << endl;
return gd;
}
PurePositionData *CosmoTool::loadRamsesPosition(const char *name)
{
PurePositionData *gd = (PurePositionData *)malloc(sizeof(PurePositionData));
int id = 1;
uint32_t totPart = 0;
int nCpu = 0;
cout << "Detecting number of files and particles..." << endl;
while (1)
{
ostringstream ss_fname;
ss_fname << name << setfill('0') << setw(5) << id;
string fname = ss_fname.str();
cout << " ... " << fname << endl;
ifstream infile(fname.c_str());
if (!infile)
break;
int ndim, nPar;
push_control(CPU);
read_int(nCpu);
pop_control(CPU);
push_control(DIM);
read_int(ndim);
pop_control(DIM);
push_control(NPAR);
read_int(nPar);
pop_control(NPAR);
cout << " NUMCPU=" << nCpu << " NUMDIM=" << ndim << " NumPart=" << nPar << endl;
totPart += nPar;
id++;
}
assert (totPart <= ((~(size_t)0)/sizeof(FCoordinates)));
size_t memSize = sizeof(FCoordinates)*(size_t)(totPart+totPart/nCpu);
cout << " Needing " << memSize / 1024 << " kBytes" << endl;
gd->pos = (FCoordinates *)malloc(sizeof(FCoordinates)*totPart);
assert(gd->pos != 0);
gd->NumPart = totPart;
gd->BoxSize = 1000.0;
cout << " Total number part=" << totPart << endl
<< "Loading particles ..." << endl;
uint32_t curPos = 0;
id = 1;
while (1)
{
ostringstream ss_fname;
ss_fname << name << setfill('0') << setw(5) << id;
string fname = ss_fname.str();
int *idP;
cout << " ... " << id;
cout.flush();
ifstream infile(fname.c_str());
if (!infile)
break;
int nCpu, ndim, nPar;
push_control(CPU);
read_int(nCpu);
pop_control(CPU);
push_control(DIM);
read_int(ndim);
pop_control(DIM);
push_control(NPAR);
read_int(nPar);
pop_control(NPAR);
FCoordinates *s = new FCoordinates[nPar];
push_control(POSX);
for (uint32_t i = 0; i < nPar; i++)
{
read_real(s[i][0]);
s[i][0] *= gd->BoxSize;
}
pop_control(POSX);
push_control(POSY);
for (uint32_t i = 0; i < nPar; i++)
{
read_real(s[i][1]);
s[i][1] *= gd->BoxSize;
}
pop_control(POSY);
push_control(POSZ);
for (uint32_t i = 0; i < nPar; i++)
{
read_real(s[i][2]);
s[i][2] *= gd->BoxSize;
}
pop_control(POSZ);
// SKIP VELOCITIES
for (int k = 0; k < 3; k++) {
push_control(V);
for (uint32_t i = nPar; i > 0; i--)
{
float dummyF;
read_real(dummyF);
}
pop_control(V);
}
float minMass = INFINITY;
push_control(MASS);
for (uint32_t i = nPar; i > 0; i--)
{
float dummyF;
read_real(dummyF);
if (dummyF < minMass) minMass = dummyF;
}
pop_control(MASS);
push_control(ID);
for (uint32_t i = 0; i < nPar; i++)
{
int id;
read_int(id);
id--;
assert(id >= 0);
assert(id < totPart);
memcpy(&gd->pos[id], &s[i], sizeof(FCoordinates));
}
pop_control(ID);
delete[] s;
curPos += nPar;
id++;
}
cout << endl;
return gd;
}
#if 0
int main(int argc, char **argv)
{
GadgetData *gd = loadRamses(argv[1]);
}
#endif
#if 0
int main(int argc, char **argv)
{
GadgetData *gd = loadRamses(argv[1]);
}
#endif

13
loadRamses.hpp Normal file
View File

@ -0,0 +1,13 @@
#ifndef _LOAD_RAMSES_HPP
#define _LOAD_RAMSES_HPP
#include "load_data.h"
namespace CosmoTool {
GadgetData *loadRamses(const char *name);
PurePositionData *loadRamsesPosition(const char *fname);
};
#endif

84
load_data.h Normal file
View File

@ -0,0 +1,84 @@
#ifndef _LOAD_GADGET_DATA_H
#define _LOAD_GADGET_DATA_H
typedef struct io_header_1
{
int npart[6];
double mass[6];
double time;
double redshift;
int flag_sfr;
int flag_feedback;
int npartTotal[6];
int flag_cooling;
int num_files;
double BoxSize;
double Omega0;
double OmegaLambda;
double HubbleParam;
char fill[256- 6*4- 6*8- 2*8- 2*4- 6*4- 2*4 - 4*8]; /* fills to 256 Bytes */
} GadgetHeader;
typedef struct particle_data
{
float Pos[3];
float Init[3];
float Vel[3];
float VelInit[3];
float Mass;
int Type;
float Rho, U, Temp, Ne;
int Id;
} ParticleState;
typedef struct {
GadgetHeader header;
ParticleState *particles;
int NumPart;
int ntot_withmasses;
} GadgetData;
typedef struct {
int Ntypes;
float BoxSize;
float RedShift;
char header[256 - 4 - 2*4];
} ParticleSetHeader;
typedef struct {
ParticleSetHeader header;
// Particle description
int *Npart;
ParticleState **particles;
} ParticleSet;
typedef float FCoordinates[3];
typedef struct {
unsigned int NumPart;
double BoxSize;
FCoordinates *pos;
} PurePositionData;
#ifdef __cplusplus
extern "C" {
#endif
void writeGadget(GadgetData *data, const char *fname);
GadgetData *loadGadget(const char *fname);
void freeGadget(GadgetData *data);
GadgetData *loadSimulationData(const char *fname);
GadgetData *loadHydra(const char *fname);
void writePersoSet(ParticleSet *set, const char *fname);
ParticleSet *loadPersoSet(const char *fname);
void freePersoSet(ParticleSet *set);
#ifdef __cplusplus
}
#endif
#endif

298
yorick.cpp Normal file
View File

@ -0,0 +1,298 @@
#include "config.hpp"
#include <netcdfcpp.h>
#include <fstream>
#include "yorick.hpp"
#include <assert.h>
using namespace CosmoTool;
using namespace std;
OutputNetCDF::OutputNetCDF(NcFile *f, NcVar *v, long *dimList, uint32_t rank)
{
this->outFile = f;
this->curVar = v;
this->dimList = dimList;
this->rank = rank;
this->counts = new long[rank];
this->curPos = new long[rank];
for (long i = 0; i < rank; i++)
this->curPos[i] = 0;
for (long i = 0; i < rank; i++)
this->counts[i] = 1;
}
void OutputNetCDF::addDouble(double a)
{
curVar->set_cur(curPos);
curVar->put(&a, counts);
curPos[rank-1]++;
for (long i = rank-1; i >= 1; i--)
{
if (curPos[i] == dimList[i])
{
curPos[i-1]++;
curPos[i] = 0;
}
}
}
OutputNetCDF::~OutputNetCDF()
{
delete counts;
delete dimList;
delete curPos;
delete outFile;
}
class NetCDF_handle
{
public:
NcFile *outFile;
NcVar *curVar;
long *curPos;
long *counts;
long *dimList;
uint32_t rank;
NetCDF_handle(NcFile *f, NcVar *v, long *dimList, uint32_t rank);
virtual ~NetCDF_handle();
};
NetCDF_handle::NetCDF_handle(NcFile *f, NcVar *v, long *dimList, uint32_t rank)
{
this->outFile = f;
this->curVar = v;
this->dimList = dimList;
this->rank = rank;
this->counts = new long[rank];
this->curPos = new long[rank];
for (long i = 0; i < rank; i++)
this->curPos[i] = 0;
for (long i = 0; i < rank; i++)
this->counts[i] = 1;
}
NetCDF_handle::~NetCDF_handle()
{
delete[] dimList;
delete outFile;
}
template<typename T>
class InputGenCDF: public NetCDF_handle, public ProgressiveInputImpl<T>
{
public:
InputGenCDF(NcFile *f, NcVar *v, long *dimList, uint32_t rank)
: NetCDF_handle(f,v,dimList,rank)
{}
virtual ~InputGenCDF() {}
virtual T read()
{
T a;
curVar->set_cur(curPos);
curVar->get(&a, counts);
curPos[rank-1]++;
for (long i = rank-1; i >= 1; i--)
{
if (curPos[i] == dimList[i])
{
curPos[i-1]++;
curPos[i] = 0;
}
}
return a;
}
virtual void seek(uint32_t *pos)
{
for (long i = rank-1; i >= 0; i--)
curPos[i] = pos[rank-1-i];
}
};
template<typename T>
class OutputGenCDF: public NetCDF_handle, public ProgressiveOutputImpl<T>
{
public:
OutputGenCDF(NcFile *f, NcVar *v, long *dimList, uint32_t rank)
: NetCDF_handle(f,v,dimList,rank)
{}
virtual ~OutputGenCDF() {}
virtual void put(T a)
{
curVar->set_cur(curPos);
curVar->put(&a, counts);
curPos[rank-1]++;
for (long i = rank-1; i >= 1; i--)
{
if (curPos[i] == dimList[i])
{
curPos[i-1]++;
curPos[i] = 0;
}
}
}
};
template<typename T>
class NetCDF_type
{
public:
static const NcType t = (NcType)-1;
};
template<>
class NetCDF_type<int>
{
public:
static const NcType t = ncInt;
};
template<>
class NetCDF_type<float>
{
public:
static const NcType t = ncFloat;
};
template<>
class NetCDF_type<double>
{
public:
static const NcType t = ncDouble;
};
namespace CosmoTool {
template<typename T>
ProgressiveOutput<T>
ProgressiveOutput<T>::saveArrayProgressive(const char *fname, uint32_t *dimList,
uint32_t rank)
{
NcFile *f = new NcFile(fname, NcFile::Replace);
assert(f->is_valid());
const NcDim **dimArray = new const NcDim *[rank];
for (uint32_t i = 0; i < rank; i++)
{
char dimName[255];
sprintf(dimName, "dim%d", i);
dimArray[i] = f->add_dim(dimName, dimList[rank-1-i]);
}
NcVar *v = f->add_var("array", NetCDF_type<T>::t, rank, dimArray);
long *ldimList = new long[rank];
for (uint32_t i = 0; i < rank; i++)
ldimList[rank-1-i] = dimList[i];
OutputGenCDF<T> *impl = new OutputGenCDF<T>(f, v, ldimList, rank);
return ProgressiveOutput<T>(impl);
}
template<typename T>
ProgressiveInput<T>
ProgressiveInput<T>::loadArrayProgressive(const char *fname, uint32_t *&dimList,
uint32_t& rank)
{
NcFile *f = new NcFile(fname, NcFile::ReadOnly);
assert(f->is_valid());
NcVar *v = f->get_var("array");
rank = v->num_dims();
long *ldimList = v->edges();
dimList = new uint32_t[rank];
for (uint32_t i = 0; i < rank; i++)
{
dimList[rank-i-1] = ldimList[i];
}
InputGenCDF<T> *impl = new InputGenCDF<T>(f, v, ldimList, rank);
return ProgressiveInput<T>(impl);
}
template<typename T>
void saveArray(const char *fname,
T *array, uint32_t *dimList, uint32_t rank)
{
NcFile f(fname, NcFile::Replace);
assert(f.is_valid());
const NcDim **dimArray = new const NcDim *[rank];
for (uint32_t i = 0; i < rank; i++)
{
char dimName[255];
sprintf(dimName, "dim%d", i);
dimArray[i] = f.add_dim(dimName, dimList[i]);
}
NcVar *v = f.add_var("array", NetCDF_type<T>::t, rank, dimArray);
long *edge = v->edges();
v->put(array, edge);
delete[] edge;
}
template<typename T>
void loadArray(const char *fname,
T*&array, uint32_t *&dimList, uint32_t& rank)
{
NcFile f(fname, NcFile::ReadOnly);
if (!f.is_valid())
throw NoSuchFileException(fname);
NcVar *v = f.get_var("array");
rank = v->num_dims();
long *edge = v->edges();
uint32_t fullSize = 1;
dimList = new uint32_t[rank];
for (int i = 0; i < rank; i++)
{
dimList[i] = edge[i];
fullSize *= edge[i];
}
array = new T[fullSize];
v->get(array, edge);
delete[] edge;
}
template class ProgressiveInput<int>;
template class ProgressiveInput<float>;
template class ProgressiveInput<double>;
template class ProgressiveOutput<int>;
template class ProgressiveOutput<float>;
template class ProgressiveOutput<double>;
template void loadArray<int>(const char *fname,
int*& array, uint32_t *&dimList, uint32_t& rank);
template void loadArray<float>(const char *fname,
float*& array, uint32_t *&dimList, uint32_t& rank);
template void loadArray<double>(const char *fname,
double*& array, uint32_t *&dimList, uint32_t& rank);
template void saveArray<int>(const char *fname,
int *array, uint32_t *dimList, uint32_t rank);
template void saveArray<float>(const char *fname,
float *array, uint32_t *dimList, uint32_t rank);
template void saveArray<double>(const char *fname,
double *array, uint32_t *dimList, uint32_t rank);
}

211
yorick.hpp Normal file
View File

@ -0,0 +1,211 @@
#ifndef __YORICK_HELPERS_HPP
#define __YORICK_HELPERS_HPP
#include "config.hpp"
#include <stdint.h>
#include <fstream>
namespace CosmoTool
{
class ProgressiveDoubleOutputImpl
{
public:
virtual ~ProgressiveDoubleOutputImpl();
virtual void addDouble(double d) = 0;
};
template<class T>
class ProgressiveInputImpl
{
public:
virtual ~ProgressiveInputImpl() {}
virtual T read() = 0;
virtual void seek(uint32_t *pos) = 0;
};
template<class T>
class ProgressiveOutputImpl
{
public:
virtual ~ProgressiveOutputImpl() {}
virtual void put(T a) = 0;
};
class ProgressiveDoubleOutput
{
private:
bool initialized;
int *ref;
ProgressiveDoubleOutputImpl *impl;
friend ProgressiveDoubleOutput saveDoubleArrayProgressive(const char *fname, uint32_t *dimList, uint32_t rank);
void decRef();
public:
ProgressiveDoubleOutput();
ProgressiveDoubleOutput(ProgressiveDoubleOutputImpl *i);
ProgressiveDoubleOutput(const ProgressiveDoubleOutput& o);
~ProgressiveDoubleOutput();
virtual void addDouble(double a);
const ProgressiveDoubleOutput& operator=(const ProgressiveDoubleOutput& b);
};
template<class T>
class ProgressiveInput
{
private:
int *ref;
ProgressiveInputImpl<T> *impl;
void decRef()
{
if (ref == 0)
return;
(*ref)--;
if (*ref == 0)
{
delete ref;
delete impl;
}
impl = 0;
ref = 0;
}
public:
static ProgressiveInput<T>
loadArrayProgressive(const char *fname, uint32_t *&dimList,
uint32_t& rank);
ProgressiveInput() {
impl = 0;
ref = 0;
}
ProgressiveInput(ProgressiveInputImpl<T> *i) {
impl = i;
ref = new int;
*ref = 1;
}
ProgressiveInput(const ProgressiveInput<T>& o) {
ref = o.ref;
impl = o.impl;
(*ref)++;
}
~ProgressiveInput() {
decRef();
}
T read()
{
return impl->read();
}
void seek(uint32_t *pos)
{
impl->seek(pos);
}
const ProgressiveInput<T>& operator=(const ProgressiveInput<T>& b)
{
decRef();
ref = b.ref;
impl = b.impl;
if (ref != 0)
(*ref)++;
return *this;
}
};
template<class T>
class ProgressiveOutput
{
private:
int *ref;
ProgressiveOutputImpl<T> *impl;
void decRef()
{
if (ref == 0)
return;
(*ref)--;
if (*ref == 0)
{
delete ref;
delete impl;
}
impl = 0;
ref = 0;
}
public:
static ProgressiveOutput<T>
saveArrayProgressive(const char *fname, uint32_t *dimList,
uint32_t rank);
ProgressiveOutput() {
impl = 0;
ref = 0;
}
ProgressiveOutput(ProgressiveOutputImpl<T> *i) {
impl = i;
ref = new int;
*ref = 1;
}
ProgressiveOutput(const ProgressiveOutput<T>& o) {
ref = o.ref;
impl = o.impl;
(*ref)++;
}
~ProgressiveOutput() {
decRef();
}
void put(T a)
{
impl->put(a);
}
const ProgressiveOutput<T>& operator=(const ProgressiveOutput<T>& b)
{
decRef();
ref = b.ref;
impl = b.impl;
if (ref != 0)
(*ref)++;
return *this;
}
};
template<typename T>
void saveArray(const char *fname,
T *array, uint32_t *dimList, uint32_t rank);
template<typename T>
void loadArray(const char *fname,
T*& array, uint32_t *& dimList, uint32_t& rank);
void saveFloatArray(const char *fname,
float *array, uint32_t *dimList, uint32_t rank);
void loadFloatArray(const char *fname,
float *&array, uint32_t *&dimList, uint32_t& rank)
throw (NoSuchFileException);
void saveDoubleArray(const char *fname,
double *array, uint32_t *dimList, uint32_t rank);
void loadDoubleArray(const char *fname,
double *&array, uint32_t *&dimList, uint32_t& rank)
throw (NoSuchFileException);
void loadIntArray(const char *fname,
int *&array, uint32_t *&dimList, uint32_t& rank)
throw (NoSuchFileException);
void saveIntArray(const char *fname,
int *array, uint32_t *dimList, uint32_t rank);
ProgressiveDoubleOutput saveDoubleArrayProgressive(const char *fname, uint32_t *dimList, uint32_t rank);
};
#endif