//
// Class ParticleInteractLayout
// Please note: for the time being this class is *not* used! But since it
// might be used in future projects, we keep this file.
//
// Copyright (c) 2003 - 2020, Paul Scherrer Institut, Villigen PSI, Switzerland
// All rights reserved
//
// This file is part of OPAL.
//
// OPAL is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// You should have received a copy of the GNU General Public License
// along with OPAL. If not, see .
//
#include "Particle/ParticleInteractLayout.h"
#include "Particle/ParticleBConds.h"
#include "Particle/IpplParticleBase.h"
#include "Region/RegionLayout.h"
#include "FieldLayout/FieldLayout.h"
#include "Utility/IpplInfo.h"
#include "Message/Communicate.h"
#include "Message/Message.h"
#include
/////////////////////////////////////////////////////////////////////
// constructor, from a FieldLayout
template < class T, unsigned Dim, class Mesh >
ParticleInteractLayout::ParticleInteractLayout(FieldLayout&
fl)
: ParticleSpatialLayout(fl) {
setup(); // perform necessary setup
}
/////////////////////////////////////////////////////////////////////
// constructor, from a FieldLayout
template < class T, unsigned Dim, class Mesh >
ParticleInteractLayout::ParticleInteractLayout(FieldLayout&
fl, Mesh& mesh)
: ParticleSpatialLayout(fl,mesh) {
setup(); // perform necessary setup
}
/////////////////////////////////////////////////////////////////////
// constructor, from a RegionLayout
template < class T, unsigned Dim, class Mesh >
ParticleInteractLayout::ParticleInteractLayout(const
RegionLayout& rl) : ParticleSpatialLayout(rl) {
setup(); // perform necessary setup
}
/////////////////////////////////////////////////////////////////////
// default constructor ... this does not initialize the RegionLayout,
// it will be instead initialized during the first update.
template < class T, unsigned Dim, class Mesh >
ParticleInteractLayout::ParticleInteractLayout()
: ParticleSpatialLayout() {
setup(); // perform necessary setup
}
/////////////////////////////////////////////////////////////////////
// perform common constructor tasks
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::setup() {
// create storage for message pointers used in swapping particles
unsigned N = Ippl::getNodes();
InterNodeList = new bool[N];
SentToNodeList = new bool[N];
InteractionNodes = 0;
// initialize interaction radius information
InterRadius = 0;
InterRadiusArray = 0;
MaxGlobalInterRadius = 0;
}
/////////////////////////////////////////////////////////////////////
// destructor
template < class T, unsigned Dim, class Mesh >
ParticleInteractLayout::~ParticleInteractLayout() {
delete [] InterNodeList;
delete [] SentToNodeList;
for (int i=(PairList.size() - 1); i >= 0; --i)
delete (PairList[i]);
}
/////////////////////////////////////////////////////////////////////
// Return the maximum interaction radius of the local particles.
template < class T, unsigned Dim, class Mesh >
T ParticleInteractLayout::getMaxLocalInteractionRadius() {
if (InterRadiusArray != 0) {
if (InterRadiusArray->size() > 0)
return *(max_element(InterRadiusArray->begin(),
InterRadiusArray->end()));
else
return 0.0;
} else {
return InterRadius;
}
}
/////////////////////////////////////////////////////////////////////
// Retrieve a Forward-style iterator for the beginning and end of the
// Nth (local) particle's nearest-neighbor pairlist.
// If this is the first call of this
// method after update(), this must make sure up-to-date info on particles
// from neighboring nodes is available.
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::getPairlist(unsigned n,
pair_iterator& bpi, pair_iterator& epi,
IpplParticleBase< ParticleInteractLayout >& PData) {
// check if we have any particle boundary conditions
if (getUpdateFlag(ParticleLayout::BCONDS)) {
// check which boundaries, if any, are periodic
ParticleBConds& pBConds = this->getBConds();
bool periodicBC[2*Dim];
unsigned numPeriodicBC = 0;
typename ParticleBConds::ParticleBCond periodicBCond = ParticlePeriodicBCond;
for (unsigned d=0; d<2*Dim; ++d) {
periodicBC[d] = (pBConds[d] == periodicBCond);
if (periodicBC[d]) ++numPeriodicBC;
}
if (numPeriodicBC>0) {
// we need to reflect domains across all periodic boundaries
// call specialized function to update ghost particle data
swap_ghost_particles(PData.getLocalNum(), PData, periodicBC);
}
else { // no periodic boundaries, call standard function
swap_ghost_particles(PData.getLocalNum(), PData);
}
}
else { // no boundary conditions, call standard function
// update ghost particle data if necessary ... this will also build
// the pairlists if needed
swap_ghost_particles(PData.getLocalNum(), PData);
}
// get iterators for Nth particle's pairlist ... no check for array sub.
bpi = PairList[n]->begin();
epi = PairList[n]->end();
return;
}
/////////////////////////////////////////////////////////////////////
// for each dimension, calculate where neighboring Vnodes and physical
// nodes are located, and which nodes are within interaction distance
// of our own Vnodes. Save this info for use in sending ghost particles.
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::rebuild_interaction_data() {
unsigned int j, d; // loop variables
// initialize data about interaction nodes, and get the inter radius
InteractionNodes = 0;
T interRad = 2.0 * getMaxInteractionRadius();
// initialize the message list and initial node count
unsigned N = Ippl::getNodes();
for (j=0; j < N; ++j)
InterNodeList[j] = false;
// if no interaction radius, we're done
if (interRad <= 0.0)
return;
// get RegionLayout iterators
typename RegionLayout::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
// determine which physical nodes are in our interaction domain
for (localVN = this->RLayout.begin_iv(); localVN != endLocalVN; ++localVN) {
// for each local Vnode, the domain to check equals the local Vnode dom
// plus twice the interaction radius
NDRegion chkDom((*localVN).second->getDomain());
for (d=0; d < Dim; ++d) {
chkDom[d] = PRegion(chkDom[d].first() - interRad,
chkDom[d].last() + interRad);
}
// use the RegionLayout to find all remote Vnodes which touch the domain
// being checked here
typename RegionLayout::touch_range_dv touchingVN =
this->RLayout.touch_range_rdv(chkDom);
typename RegionLayout::touch_iterator_dv tVN = touchingVN.first;
for ( ; tVN != touchingVN.second; ++tVN) {
// note that we need to send a message to the node which contains
// this remote Vnode
unsigned vn = ((*tVN).second)->getNode();
if ( ! InterNodeList[vn] ) {
InterNodeList[vn] = true;
InteractionNodes++;
}
}
}
// set the flag indicating the swap ghost particle routine should
// be called the next time we try to access a pairlist or do anything
// of utility with the ghost particles
NeedGhostSwap = true;
return;
}
/////////////////////////////////////////////////////////////////////
// for each dimension, calculate where neighboring Vnodes and physical
// nodes are located, and which nodes are within interaction distance
// of our own Vnodes. Save this info for use in sending ghost particles.
// Special version to handle periodic boundary conditions
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::rebuild_interaction_data(
const bool periodicBC[2*Dim])
{
unsigned int j, d; // loop variables
unsigned pe = Ippl::myNode();
// initialize data about interaction nodes, and get the inter radius
InteractionNodes = 0;
T interRad = 2.0 * getMaxInteractionRadius();
// initialize the message list and initial node count
unsigned N = Ippl::getNodes();
for (j=0; j < N; ++j)
InterNodeList[j] = false;
// if no interaction radius, we're done
if (interRad <= 0.0)
return;
// get domain info
const NDRegion& globalDom = this->RLayout.getDomain();
// some stuff for computing reflected domains
T offset[Dim];
unsigned numRef;
bool flipBit, activeBit[Dim], refBit[Dim];
NDRegion chkDom, refDom;
// get RegionLayout iterators
typename RegionLayout::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
typename RegionLayout::iterator_iv localVN2;
typename RegionLayout::touch_range_dv touchingVN;
typename RegionLayout::touch_iterator_dv tVN;
// determine which physical nodes are in our interaction domain
for (localVN = this->RLayout.begin_iv(); localVN != endLocalVN; ++localVN) {
// for each local Vnode, the domain to check equals the local Vnode dom
// plus twice the interaction radius
chkDom = (*localVN).second->getDomain();
for (d=0; d(chkDom[d].first() - interRad,
chkDom[d].last() + interRad);
}
// use the RegionLayout to find all remote Vnodes which touch
// the domain being checked here
touchingVN = this->RLayout.touch_range_rdv(chkDom);
for (tVN = touchingVN.first; tVN != touchingVN.second; ++tVN) {
// note that we need to send a message to the node which contains
// this remote Vnode
unsigned vn = ((*tVN).second)->getNode();
if ( ! InterNodeList[vn] ) {
InterNodeList[vn] = true;
InteractionNodes++;
}
}
// look for boundary crossings and check reflected domains
numRef = 0;
for (d=0; dglobalDom[d].last()) {
// crossed upper boundary
offset[d] = -globalDom[d].length();
activeBit[d] = true;
numRef = 2 * numRef + 1;
}
else {
offset[d] = 0.0;
activeBit[d] = false;
}
refBit[d] = false; // reset reflected domain bools
}
// compute and check each domain reflection
for (j=0; jRLayout.touch_range_rdv(refDom);
for (tVN = touchingVN.first; tVN != touchingVN.second; ++tVN) {
// note that we need to send a message to the node which contains
// this remote Vnode
unsigned vn = ((*tVN).second)->getNode();
if ( ! InterNodeList[vn] ) {
InterNodeList[vn] = true;
InteractionNodes++;
}
}
if (!InterNodeList[pe]) { // check if we interact with our own domains
// for reflected domains, we also must check against local domains
bool interact = false;
localVN2 = this->RLayout.begin_iv();
while (localVN2 != endLocalVN && !interact) {
interact = refDom.touches((*localVN2).second->getDomain());
++localVN2;
}
if (interact) {
InterNodeList[pe] = true;
InteractionNodes++;
}
}
}
}
// set the flag indicating the swap ghost particle routine should
// be called the next time we try to access a pairlist or do anything
// of utility with the ghost particles
NeedGhostSwap = true;
return;
}
/////////////////////////////////////////////////////////////////////
// Update the location and indices of all atoms in the given IpplParticleBase
// object. This handles swapping particles among processors if
// needed, and handles create and destroy requests. When complete,
// all nodes have correct layout information.
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::update(
IpplParticleBase< ParticleInteractLayout >& PData,
const ParticleAttrib* canSwap) {
unsigned N = Ippl::getNodes();
unsigned myN = Ippl::myNode();
unsigned LocalNum = PData.getLocalNum();
unsigned DestroyNum = PData.getDestroyNum();
unsigned TotalNum;
T maxrad = getMaxLocalInteractionRadius();
int node;
// delete particles in destroy list, update local num
PData.performDestroy();
LocalNum -= DestroyNum;
// set up our layout, if not already done ... we could also do this if
// we needed to expand our spatial region.
if ( ! this->RLayout.initialized())
rebuild_layout(LocalNum,PData);
// apply boundary conditions to the particle positions
if (getUpdateFlag(ParticleLayout::BCONDS))
apply_bconds(LocalNum, PData.R, this->getBConds(), this->RLayout.getDomain());
// Now we can swap particles that have moved outside the region of
// local field space. This is done in several passes, one for each
// spatial dimension. The NodeCount values are updated by this routine.
if (N > 1 && getUpdateFlag(this->SWAP)) {
if (canSwap==0)
LocalNum = swap_particles(LocalNum, PData);
else
LocalNum = swap_particles(LocalNum, PData, *canSwap);
}
// flag we need to update our ghost particles
NeedGhostSwap = true;
// Save how many local particles we have.
TotalNum = this->NodeCount[myN] = LocalNum;
// there is extra work to do if there are multipple nodes, to distribute
// the particle layout data to all nodes
if (N > 1) {
// At this point, we can send our particle count updates to node 0, and
// receive back the particle layout.
int tag1 = Ippl::Comm->next_tag(P_SPATIAL_LAYOUT_TAG, P_LAYOUT_CYCLE);
int tag2 = Ippl::Comm->next_tag(P_SPATIAL_RETURN_TAG, P_LAYOUT_CYCLE);
if (myN != 0) {
Message *msg = new Message;
// put local particle count in the message
msg->put(LocalNum);
// also put in our maximum interaction radius
msg->put(maxrad);
// send this info to node 0
Ippl::Comm->send(msg, 0, tag1);
// receive back the number of particles on each node, and the maximum
// interaction radius
node = 0;
msg = Ippl::Comm->receive_block(node, tag2);
msg->get(this->NodeCount);
msg->get(maxrad);
msg->get(TotalNum);
} else { // do update tasks particular to node 0
// receive messages from other nodes describing what they have
int notrecvd = N - 1; // do not need to receive from node 0
TotalNum = LocalNum;
while (notrecvd > 0) {
// receive a message from another node. After recv, node == sender.
node = Communicate::COMM_ANY_NODE;
Message *msg = Ippl::Comm->receive_block(node, tag1);
int remNodeCount = 0;
T remMaxRad = 0.0;
msg->get(remNodeCount);
msg->get(remMaxRad);
delete msg;
notrecvd--;
// update values based on data from remote node
TotalNum += remNodeCount;
this->NodeCount[node] = remNodeCount;
if (remMaxRad > maxrad)
maxrad = remMaxRad;
}
// send info back to all the client nodes
Message *msg = new Message;
msg->put(this->NodeCount, this->NodeCount + N);
msg->put(maxrad);
msg->put(TotalNum);
Ippl::Comm->broadcast_others(msg, tag2);
}
}
// update our particle number counts
PData.setTotalNum(TotalNum); // set the total atom count
PData.setLocalNum(LocalNum); // set the number of local atoms
// if the interaction radius changed, must recalculate some things
if (maxrad != getMaxInteractionRadius()) {
setMaxInteractionRadius(maxrad);
// check if we have any particle boundary conditions
if (getUpdateFlag(ParticleLayout::BCONDS)) {
// check which boundaries, if any, are periodic
ParticleBConds& pBConds = this->getBConds();
bool periodicBC[2*Dim];
unsigned numPeriodicBC = 0;
typename ParticleBConds::ParticleBCond periodicBCond=ParticlePeriodicBCond;
for (unsigned d=0; d<2*Dim; ++d) {
periodicBC[d] = (pBConds[d] == periodicBCond);
if (periodicBC[d]) ++numPeriodicBC;
}
if (numPeriodicBC>0) {
// we need to reflect domains across all periodic boundaries
// call specialized function
rebuild_interaction_data(periodicBC);
}
else { // no periodic boundaries, call standard function
rebuild_interaction_data();
}
}
else { // no boundary conditions, call standard function
rebuild_interaction_data();
}
}
return;
}
/////////////////////////////////////////////////////////////////////
// copy particles to other nodes for pairlist computation. The arguments
// are the current number of local particles, and the ParticleBase object.
// Make sure not to send any particles to, or receive particles from,
// nodes which have no particles on them. This also takes care of
// building the pairlists.
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::swap_ghost_particles(unsigned
LocalNum,
IpplParticleBase< ParticleInteractLayout >& PData) {
unsigned int i; // loop variables
// if we've already swapped particles since the last update, we're done
if ( ! NeedGhostSwap ) return;
// clear flag indicating we need to do this ghost particle swap again
NeedGhostSwap = false;
// delete all our current ghost particles; even if we have no local
// particles now, we may have pairlists left over from earlier when we did
// have local particles
PData.ghostDestroy(PData.getGhostNum(), 0);
// find the number of nodes we need to communicate with
unsigned N = Ippl::getNodes();
unsigned sendnum = 0;
for (i=0; i < N; i++)
if (InterNodeList[i] && this->NodeCount[i] > 0)
sendnum++;
// if there are no interaction nodes, we can just compute local pairlists
// and then return
if (sendnum == 0 || LocalNum == 0) {
find_pairs(LocalNum, 0, LocalNum, true, PData);
return;
}
// get the maximum interaction radius for the particles
// we actually check twice the radius
T interRad = 2.0 * getMaxInteractionRadius();
// an NDRegion object used to store the interaction region of a particle
NDRegion pLoc;
// Ghost particles are swapped in one pass: an interaction region for a
// particle is created, and intersected with all the vnodes, and if the
// particle needs to go to that vnode, it is sent.
// create new messages to send to our neighbors
for (i=0; i < N; i++)
if (InterNodeList[i] && this->NodeCount[i] > 0)
this->SwapMsgList[i] = new Message;
// Go through the particles, find those with interaction radius
// which overlaps with a neighboring left node, and copy into a message.
// The interaction radius used to check for whether to send the particle
// is (max inter. radius of system)*2.
// for (i=0; i < LocalNum; ++i) {
// initialize the flags which indicate which node the particle will be
// sent to
// ada memset((void *)SentToNodeList, 0, N * sizeof(bool));
// get the position of the ith particle, and form an NDRegion which
// is a cube with sides of length twice the interaction radius
// ada for (j=0; j < Dim; ++j)
// ada pLoc[j] = PRegion(PData.R[i][j] - interRad,
// PData.R[i][j] + interRad);
// see which Vnodes this postion is in; if none, it is local
// ada typename RegionLayout::touch_range_dv touchingVN = this->RLayout.touch_range_rdv(pLoc);
// ada typename RegionLayout::touch_iterator_dv tVNit = touchingVN.first;
// ada for ( ; tVNit != touchingVN.second; ++tVNit) {
// ada Rnode *tVN = (*tVNit).second;
// ada unsigned node = tVN->getNode();
// the node has been found - copy particle data into a message,
// ada if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
// ada if (! InterNodeList[node]) {
// ada ERRORMSG("ParticleInteractLayout: Cannot send ghost " << i);
// ada ERRORMSG(" to node " << node << " ... skipping." << endl);
// ada }
// ada else {
// ada PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
// ada SentToNodeList[node] = true;
// }
// }
// }
// }
// send out messages with ghost particles
/*
ada: buggy BUGGY node hangs in later receive_block
int tag = Ippl::Comm->next_tag(P_SPATIAL_GHOST_TAG, P_LAYOUT_CYCLE);
for (i=0; i < N; ++i) {
if (InterNodeList[i] && this->NodeCount[i] > 0) {
// add a final 'zero' number of particles to indicate the end
PData.ghostPutMessage(*(this->SwapMsgList[i]), (unsigned)0, (unsigned)0);
// send the message
Ippl::Comm->send(this->SwapMsgList[i], i, tag);
}
}
*/
// while we're waiting for messages to arrive, calculate our local pairs
find_pairs(LocalNum, 0, LocalNum, true, PData);
// receive ghost particles from other nodes, and add them to our list
/*
while (sendnum-- > 0) {
int node = Communicate::COMM_ANY_NODE;
unsigned oldGN = PData.getGhostNum();
Message *recmsg = Ippl::Comm->receive_block(node, tag);
while (PData.ghostGetMessage(*recmsg, node) > 0);
delete recmsg;
// find pairs with these ghost particles
find_pairs(LocalNum, LocalNum + oldGN, LocalNum + PData.getGhostNum(),
false, PData);
}
*/
}
/////////////////////////////////////////////////////////////////////
// copy particles to other nodes for pairlist computation. The arguments
// are the current number of local particles, and the IpplParticleBase object.
// Make sure not to send any particles to, or receive particles from,
// nodes which have no particles on them. This also takes care of
// building the pairlists.
// special version to take care of periodic boundaries
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::swap_ghost_particles(
unsigned LocalNum,
IpplParticleBase< ParticleInteractLayout >& PData,
const bool periodicBC[2*Dim])
{
unsigned int i, j; // loop variables
unsigned d;
// if we've already swapped particles since the last update, we're done
if ( ! NeedGhostSwap ) return;
// clear flag indicating we need to do this ghost particle swap again
NeedGhostSwap = false;
// delete all our current ghost particles; even if we have no local
// particles now, we may have pairlists left over from earlier when we did
// have local particles
PData.ghostDestroy(PData.getGhostNum(), 0);
// find the number of nodes we need to communicate with
unsigned N = Ippl::getNodes();
unsigned pe = Ippl::myNode();
unsigned sendnum = 0;
for (i=0; i < N; i++)
if (InterNodeList[i] && this->NodeCount[i] > 0)
sendnum++;
// if there are no interaction nodes, we can just compute local pairlists
// and then return
if (sendnum == 0 || LocalNum == 0) {
find_pairs(LocalNum, 0, LocalNum, true, PData);
return;
}
// get the maximum interaction radius for the particles
// we actually check twice the radius
T interRad = 2.0 * getMaxInteractionRadius();
// get domain info
const NDRegion& globalDom = this->RLayout.getDomain();
// some stuff for computing reflected domains
T offset[Dim];
unsigned numRef;
bool flipBit, activeBit[Dim], refBit[Dim];
NDRegion pLoc, refLoc;
// region layout iterators
typename RegionLayout::iterator_iv localVN, endLocalVN = this->RLayout.end_iv();
typename RegionLayout::touch_range_dv touchingVN;
typename RegionLayout::touch_iterator_dv tVNit;
SingleParticlePos_t savePos; // save position of reflected ghosts
// Ghost particles are swapped in one pass: an interaction region for a
// particle is created, and intersected with all the vnodes, and if the
// particle needs to go to that vnode, it is sent.
// create new messages to send to our neighbors
for (i=0; i < N; i++)
if (InterNodeList[i] && this->NodeCount[i] > 0)
this->SwapMsgList[i] = new Message;
// Go through the particles, find those with interaction radius
// which overlaps with a neighboring left node, and copy into a message.
// The interaction radius used to check for whether to send the particle
// is (max inter. radius of system)*2.
for (i=0; i < LocalNum; ++i) {
// initialize flags indicating which nodes the particle has been sent to
memset((void *)SentToNodeList, 0, N * sizeof(bool));
// get the position of the ith particle, and form an NDRegion which
// is a cube with sides of length twice the interaction radius
for (j=0; j < (unsigned int) Dim; ++j)
pLoc[j] = PRegion(PData.R[i][j] - interRad,
PData.R[i][j] + interRad);
// see which Vnodes this postion is in; if none, it is local
touchingVN = this->RLayout.touch_range_rdv(pLoc);
for (tVNit = touchingVN.first; tVNit != touchingVN.second; ++tVNit) {
Rnode *tVN = (*tVNit).second;
unsigned node = tVN->getNode();
// the node has been found - copy particle data into a message,
if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
if (! InterNodeList[node]) {
ERRORMSG("ParticleInteractLayout: Cannot send ghost " << i);
ERRORMSG(" to node " << node << " ... skipping." << endl);
}
else {
PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
SentToNodeList[node] = true;
}
}
}
// look for boundary crossings and check reflected domains
numRef = 0;
for (d=0; dglobalDom[d].last()) {
// crossed upper boundary
offset[d] = -globalDom[d].length();
activeBit[d] = true;
numRef = 2 * numRef + 1;
}
else {
offset[d] = 0.0;
activeBit[d] = false;
}
refBit[d] = false; // reset bools indicating reflecting dims
}
if (numRef>0) savePos = PData.R[i]; // copy current particle position
// loop over reflected domains
for (j=0; jRLayout.touch_range_rdv(refLoc);
for (tVNit = touchingVN.first; tVNit != touchingVN.second; ++tVNit) {
Rnode *tVN = (*tVNit).second;
unsigned node = tVN->getNode();
// the node has been found - copy particle data into a message,
if (this->NodeCount[node] > 0 && ! SentToNodeList[node]) {
if (! InterNodeList[node]) {
ERRORMSG("ParticleInteractLayout: Cannot send ghost " << i);
ERRORMSG(" to node " << node << " ... skipping." << endl);
}
else {
PData.ghostPutMessage(*(this->SwapMsgList[node]), 1, i);
SentToNodeList[node] = true;
}
}
}
if (InterNodeList[pe]) { // we may interact with local domains
// for reflected domains, we also must check against local domains
bool interact = false;
localVN = this->RLayout.begin_iv();
while (localVN != endLocalVN && !interact) {
interact = refLoc.touches((*localVN).second->getDomain());
++localVN;
}
if (interact) {
PData.ghostPutMessage(*(this->SwapMsgList[pe]), 1, i);
}
}
}
if (numRef>0) PData.R[i] = savePos; // restore particle position data
}
// send out messages with ghost particles
int tag = Ippl::Comm->next_tag(P_SPATIAL_GHOST_TAG, P_LAYOUT_CYCLE);
for (i=0; i < N; ++i) {
if (InterNodeList[i] && this->NodeCount[i] > 0) {
// add a final 'zero' number of particles to indicate the end
PData.ghostPutMessage(*(this->SwapMsgList[i]), (unsigned)0, (unsigned)0);
// send the message
Ippl::Comm->send(this->SwapMsgList[i], i, tag);
}
}
// while we're waiting for messages to arrive, calculate our local pairs
find_pairs(LocalNum, 0, LocalNum, true, PData);
// receive ghost particles from other nodes, and add them to our list
while (sendnum-- > 0) {
int node = Communicate::COMM_ANY_NODE;
unsigned oldGN = PData.getGhostNum();
Message *recmsg = Ippl::Comm->receive_block(node, tag);
while (PData.ghostGetMessage(*recmsg, node) > 0);
delete recmsg;
// find pairs with these ghost particles
find_pairs(LocalNum, LocalNum + oldGN, LocalNum + PData.getGhostNum(),
false, PData);
}
}
/////////////////////////////////////////////////////////////////////
// find the pairs between our local particles and particles a1 ... (a2 - 1).
// if the last argument is true, initialize all the pairlists to be empty.
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::find_pairs(const unsigned LocalNum,
const unsigned a1, const unsigned a2, const bool initLists,
IpplParticleBase< ParticleInteractLayout >& PData) {
unsigned i, j; // loop variables
// initialize the pairlist storage if requested
if (initLists) {
unsigned vlen = PairList.size();
if (vlen > LocalNum)
vlen = LocalNum;
for (i=0; i < vlen; ++i)
PairList[i]->erase(PairList[i]->begin(), PairList[i]->end());
// make sure there are enough single particle pairlists
if (PairList.size() < LocalNum) {
int newamt = LocalNum - PairList.size();
PairList.reserve(newamt);
for (int k=0; k < newamt; ++k)
PairList.push_back(new std::vector);
}
}
// make sure we have something to do
if (a2 <= a1) return;
// find pairs between local particles and particles a1 ... a2
for (i=0; i < LocalNum; ++i) {
// get interaction radius of this particle
T intrad1 = getInteractionRadius(i);
// find starting index of inner loop
j = (a1 > i ? a1 : i + 1);
// do inner loop for particles up to the last local one
// (these pairs must be stored twice)
for (; j < LocalNum; ++j) {
// add interaction radius of this particle
T intrad2 = intrad1 + getInteractionRadius(j);
intrad2 *= intrad2; // (intrad1 + intrad2)^2
// find distance^2 between these two particles
Vektor rsep = PData.R[j];
rsep -= PData.R[i];
T sep2 = dot(rsep, rsep);
// if the separation is less than their interaction distance, book it
// we store the pair twice, since we know both i and j are < LocalNum
if (sep2 < intrad2) {
PairList[i]->push_back(pair_t(j, sep2));
PairList[j]->push_back(pair_t(i, sep2));
}
}
// now do rest of loop for just ghost particles (only store the
// pair once in this case)
for (; j < a2; ++j) {
// get interaction radius of this particle
T intrad2 = intrad1 + getInteractionRadius(j);
intrad2 *= intrad2; // (intrad1 + intrad2)^2
// find distance^2 between these two particles
Vektor rsep = PData.R[j];
rsep -= PData.R[i];
T sep2 = dot(rsep, rsep);
// if the separation is less than their interaction distance, book it
// we only store the pair for the local atom i once, since the other
// atom j is a ghost atom
if (sep2 < intrad2) {
PairList[i]->push_back(pair_t(j, sep2));
}
}
}
}
/////////////////////////////////////////////////////////////////////
// print it out
template < class T, unsigned Dim, class Mesh >
std::ostream& operator<<(std::ostream& out, const ParticleInteractLayout& L) {
out << "ParticleInteractLayout, with particle distribution:\n ";
for (unsigned int i=0; i < (unsigned int) Ippl::getNodes(); ++i)
out << L.getNodeCount(i) << " ";
out << "\nInteractLayout decomposition = " << L.getLayout();
return out;
}
//////////////////////////////////////////////////////////////////////
// Repartition onto a new layout, if the layout changes ... this is a
// virtual function called by a UserList, as opposed to the RepartitionLayout
// function used by the particle load balancing mechanism.
template < class T, unsigned Dim, class Mesh >
void ParticleInteractLayout::Repartition(UserList* userlist) {
// perform actions to restructure our data due to a change in the
// RegionLayout
if (userlist->getUserListID() == this->RLayout.get_Id()) {
// clear out current interaction node storage; if the next update
// indicates we have a non-zero interaction radius, this info will be
// rebuilt (by calling rebuild_interaction_data)
InteractionNodes = 0;
setMaxInteractionRadius(0);
NeedGhostSwap = true;
}
}