Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend the number of unique particles per cpu we can have at once. #1315

Merged
merged 10 commits into from
Aug 27, 2020
167 changes: 143 additions & 24 deletions Src/Particle/AMReX_Particle.H
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,127 @@ namespace amrex {

namespace
{
constexpr int GhostParticleID = std::numeric_limits<int>::max();
constexpr int VirtualParticleID = std::numeric_limits<int>::max()-1;
constexpr int LastParticleID = std::numeric_limits<int>::max()-2;
constexpr int DoSplitParticleID = std::numeric_limits<int>::max()-3;
constexpr int NoSplitParticleID = std::numeric_limits<int>::max()-4;
constexpr Long GhostParticleID = 549755813887L; // 2**39-1
constexpr Long VirtualParticleID = GhostParticleID-1;
constexpr Long LastParticleID = GhostParticleID-2;
constexpr Long DoSplitParticleID = GhostParticleID-3;
constexpr Long NoSplitParticleID = GhostParticleID-4;
}

struct ParticleIDWrapper
{
uint64_t& m_idata;

AMREX_GPU_HOST_DEVICE
ParticleIDWrapper (uint64_t& idata) noexcept
: m_idata(idata)
{}

AMREX_GPU_HOST_DEVICE
ParticleIDWrapper& operator= (const Long id) noexcept
{
// zero out the 40 leftmost bits, which store the sign and the abs of the id;
m_idata &= 0x00FFFFFF;

uint64_t val;
uint64_t sign = id >= 0;
if (sign)
{
// 2**39-1, the max value representible in this fashion
AMREX_ASSERT(id <= 549755813887L);
val = id;
}
else
{
// -2**39-1, the min value representible in this fashion
AMREX_ASSERT(id >= -549755813887L);
val = -id;
}

m_idata |= (sign << 63); // put the sign in the leftmost bit
m_idata |= (val << 24); // put the val in the next 39
return *this;
}

AMREX_GPU_HOST_DEVICE
operator Long () const noexcept
{
Long r = 0;

uint64_t sign = m_idata >> 63; // extract leftmost sign bit
uint64_t val = ((m_idata >> 24) & 0x7FFFFFFFFF); // extract next 39 id bits

Long lval = static_cast<Long>(val); // bc we take -
r = (sign) ? lval : -lval;
return r;
}
};

struct ParticleCPUWrapper
{
uint64_t& m_idata;

AMREX_GPU_HOST_DEVICE
ParticleCPUWrapper (uint64_t& idata) noexcept
: m_idata(idata)
{}

AMREX_GPU_HOST_DEVICE
ParticleCPUWrapper& operator= (const int cpu) noexcept
{
// zero out the first 24 bits, which are used to store the cpu number
m_idata &= (~ 0x00FFFFFF);

AMREX_ASSERT(cpu >= 0);
AMREX_ASSERT(cpu <= 16777215); // 2**24-1, the max representable number

m_idata |= cpu;
return *this;
}

AMREX_GPU_HOST_DEVICE
operator int () noexcept
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be a const function.

{
return (m_idata & 0x00FFFFFF);
}
};

struct ConstParticleIDWrapper
{
const uint64_t& m_idata;

AMREX_GPU_HOST_DEVICE
ConstParticleIDWrapper (const uint64_t& idata) noexcept
: m_idata(idata)
{}

AMREX_GPU_HOST_DEVICE
operator Long () const noexcept
{
Long r = 0;

uint64_t sign = m_idata >> 63; // extract leftmost sign bit
uint64_t val = ((m_idata >> 24) & 0x7FFFFFFFFF); // extract next 39 id bits

Long lval = static_cast<Long>(val); // bc we take -
r = (sign) ? lval : -lval;
return r;
}
};

struct ConstParticleCPUWrapper
{
const uint64_t& m_idata;

AMREX_GPU_HOST_DEVICE
ConstParticleCPUWrapper (const uint64_t& idata) noexcept
: m_idata(idata)
{}

AMREX_GPU_HOST_DEVICE
operator int () noexcept { return (m_idata & 0x00FFFFFF); }
};

/** \brief The struct used to store particles.
*
* \tparam T_NReal The number of extra Real components
Expand Down Expand Up @@ -54,14 +168,19 @@ struct Particle
* The integer data. We always have id and cpu, and optionally we
* have NInt additional integer attributes.
*/
int m_idata[2+NInt];
union im_t
{
uint64_t ids = 0;
int arr[2+NInt];
};
im_t m_idata;

static int the_next_id;
static Long the_next_id;

AMREX_GPU_HOST_DEVICE int& id () & {return m_idata[0];}
AMREX_GPU_HOST_DEVICE int id () const & {return m_idata[0];}
AMREX_GPU_HOST_DEVICE int& cpu () & {return m_idata[1];}
AMREX_GPU_HOST_DEVICE int cpu () const & {return m_idata[1];}
AMREX_GPU_HOST_DEVICE ParticleCPUWrapper cpu () & { return ParticleCPUWrapper(m_idata.ids); }
AMREX_GPU_HOST_DEVICE ParticleIDWrapper id () & { return ParticleIDWrapper(m_idata.ids); }
AMREX_GPU_HOST_DEVICE ConstParticleCPUWrapper cpu () const & { return ConstParticleCPUWrapper(m_idata.ids); }
AMREX_GPU_HOST_DEVICE ConstParticleIDWrapper id () const & { return ConstParticleIDWrapper(m_idata.ids); }

AMREX_GPU_HOST_DEVICE RealVect pos () const &
{return RealVect(AMREX_D_DECL(m_rdata.pos[0], m_rdata.pos[1], m_rdata.pos[2]));}
Expand Down Expand Up @@ -105,12 +224,12 @@ struct Particle
AMREX_GPU_HOST_DEVICE int& idata (int index) &
{
AMREX_ASSERT(index < NInt);
return m_idata[2 + index];
return m_idata.arr[2 + index];
}
AMREX_GPU_HOST_DEVICE int idata (int index) const &
{
AMREX_ASSERT(index < NInt);
return m_idata[2 + index];
return m_idata.arr[2 + index];
}

static Real InterpDoit (const FArrayBox& fab, const Real* fracs, const IntVect* cells, int comp);
Expand Down Expand Up @@ -138,21 +257,21 @@ struct Particle
* across all processors must be checkpointed and then restored on restart
* so that we don't reuse particle IDs.
*/
static int NextID ();
static Long NextID ();


/**
* \brief This version can only be used inside omp critical.
*/
static int UnprotectedNextID ();
static Long UnprotectedNextID ();


/**
* \brief Reset on restart.
*
* \param nextid
*/
static void NextID (int nextid);
static void NextID (Long nextid);

static void CIC_Fracs (const Real* frac, Real* fracs);

Expand Down Expand Up @@ -207,7 +326,7 @@ struct Particle
Vector<Real>& fracs,
Vector<IntVect>& cells);
};
template <int NReal, int NInt> int Particle<NReal, NInt>::the_next_id = 1;
template <int NReal, int NInt> Long Particle<NReal, NInt>::the_next_id = 1;

template<int NReal, int NInt>
inline
Expand Down Expand Up @@ -559,17 +678,17 @@ Particle<NReal, NInt>::Version ()
}

template <int NReal, int NInt>
int
Long
Particle<NReal, NInt>::NextID ()
{
int next;
Long next;
// we should be able to test on _OPENMP < 201107 for capture (version 3.1)
// but we must work around a bug in gcc < 4.9
#if defined(_OPENMP) && _OPENMP < 201307
#pragma omp critical (amrex_particle_nextid)
#elif defined(_OPENMP)
#pragma omp atomic capture
#endif
#endif
next = the_next_id++;

if (next > LastParticleID)
Expand All @@ -579,18 +698,18 @@ Particle<NReal, NInt>::NextID ()
}

template <int NReal, int NInt>
int
Long
Particle<NReal, NInt>::UnprotectedNextID ()
{
int next = the_next_id++;
Long next = the_next_id++;
if (next > LastParticleID)
amrex::Abort("Particle<NReal, NInt>::NextID() -- too many particles");
return next;
}

template <int NReal, int NInt>
void
Particle<NReal, NInt>::NextID (int nextid)
Particle<NReal, NInt>::NextID (Long nextid)
{
the_next_id = nextid;
}
Expand Down Expand Up @@ -694,7 +813,7 @@ operator<< (std::ostream& os, const Particle<NReal, NInt>& p)
os << p.m_rdata.arr[i] << ' ';

for (int i = 2; i < 2 + NInt; i++)
os << p.m_idata[i] << ' ';
os << p.m_idata.arr[i] << ' ';

if (!os.good())
amrex::Error("operator<<(ostream&,Particle<NReal, NInt>&) failed");
Expand Down
7 changes: 4 additions & 3 deletions Src/Particle/AMReX_ParticleContainerI.H
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ ParticleContainer<NStructReal, NStructInt, NArrayReal, NArrayInt> :: Initialize
for (int i=0; i<AMREX_SPACEDIM; ++i) tile_size[i] = tilesize[i];
}

static_assert(std::is_standard_layout<ParticleType>::value
&& std::is_trivial<ParticleType>::value,
"Particle type must be standard layout and trivial.");
static_assert(std::is_standard_layout<ParticleType>::value,
"Particle type must be standard layout");
// && std::is_trivial<ParticleType>::value,
// "Particle type must be standard layout and trivial.");

pp.query("use_prepost", usePrePost);
pp.query("do_unlink", doUnlink);
Expand Down