Skip to content

Commit ec2e97e

Browse files
author
William Tobin
committed
Implementation of AggregateNumbering as a read-only field, also adding several operations to PCU including PCU_Allgather and rank translation between MPI_Comms as needed in the implementation of AggregateNumberings.
1 parent 6f3c392 commit ec2e97e

22 files changed

+741
-83
lines changed

apf/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ endif()
66
# Package sources
77
set(SOURCES
88
apf.cc
9+
apfAggregateNumbering.cc
910
apfCavityOp.cc
1011
apfElement.cc
1112
apfField.cc
@@ -51,6 +52,7 @@ set(SOURCES
5152
# Package headers
5253
set(HEADERS
5354
apf.h
55+
apfAggregateNumbering.h
5456
apfMesh.h
5557
apfMesh2.h
5658
apfMatrix.h

apf/apf.cc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -453,10 +453,6 @@ bool isFrozen(Field* f)
453453
return f->getData()->isFrozen();
454454
}
455455

456-
Function::~Function()
457-
{
458-
}
459-
460456
Field* createUserField(Mesh* m, const char* name, int valueType, FieldShape* s,
461457
Function* f)
462458
{

apf/apf.h

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -654,19 +654,22 @@ double* getArrayData(Field* f);
654654
/** \brief Initialize all nodal values with all-zero components */
655655
void zeroField(Field* f);
656656

657-
/** \brief User-defined Analytic Function. */
658-
struct Function
657+
/** \brief User-defined Analytic Function for arbitrary scalar type. */
658+
template <class T>
659+
struct FunctionBase
659660
{
660661
/** \brief Possible user-defined cleanup */
661-
virtual ~Function();
662+
virtual ~FunctionBase() {}
662663
/** \brief The user's analytic function call.
663-
\details For simplicity, this
664-
currently only supports one node per entity.
665-
\param e the entity on which the node is
666-
\param result the field component values for that node
667-
*/
668-
virtual void eval(MeshEntity* e, double* result) = 0;
664+
\details For simplicity, this
665+
currently only supports one node per entity.
666+
\param e the entity on which the node is
667+
\param result the field component values for that node
668+
*/
669+
virtual void eval(MeshEntity * e, T * result) = 0;
669670
};
671+
typedef FunctionBase<double> Function;
672+
670673

671674
/* \brief Create a Field from a user's analytic function.
672675
* \details This field will use no memory, has values on all

apf/apfAggregateNumbering.cc

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
#include "apfAggregateNumbering.h"
2+
#include "apfAggregateNumberingClass.h"
3+
#include "apfField.h"
4+
#include "apfNumberingClass.h"
5+
#include "apfTagData.h"
6+
#include "apfUserData.h"
7+
#include <PCU.h>
8+
#include <pcu_util.h>
9+
#include <set>
10+
namespace apf
11+
{
12+
// explicit instantiations
13+
template class AggregateNumberingOf<int>;
14+
template class AggregateNumberingOf<long>;
15+
// this assumes that the ranks returned by a sharing
16+
// do not change when the pcu comm changes
17+
// if the sharing can handle the pcu comm changing
18+
// then this isn't needed
19+
class StaticSharing : public NormalSharing
20+
{
21+
protected:
22+
Sharing * shr;
23+
// original comm
24+
MPI_Comm old_cm;
25+
int old_rnk;
26+
// new comm
27+
MPI_Comm new_cm;
28+
public:
29+
StaticSharing(Mesh * m,
30+
Sharing * s,
31+
MPI_Comm ncm,
32+
MPI_Comm pcm = MPI_COMM_NULL)
33+
: NormalSharing(m)
34+
, shr(s)
35+
, old_cm(pcm)
36+
, old_rnk(-1)
37+
, new_cm(ncm)
38+
{
39+
if(old_cm == MPI_COMM_NULL)
40+
old_cm = PCU_Get_Comm();
41+
new_cm = PCU_Get_Comm();
42+
old_rnk = PCU_Comm_Self();
43+
}
44+
virtual bool isOwned(MeshEntity * e)
45+
{
46+
int old_ownr = shr->getOwner(e);
47+
if(old_ownr == old_rnk)
48+
return true;
49+
return false;
50+
}
51+
virtual int getOwner(MeshEntity * e)
52+
{
53+
int old_ownr = shr->getOwner(e);
54+
return PCU_Foreign_To_Local(old_ownr,old_cm);
55+
}
56+
virtual void getCopies(MeshEntity * e, CopyArray & copies)
57+
{
58+
shr->getCopies(e,copies);
59+
for(int ii = 0; ii < copies.getSize(); ++ii)
60+
copies[ii].peer = PCU_Foreign_To_Local(copies[ii].peer,old_cm);
61+
}
62+
virtual bool isShared(MeshEntity * e)
63+
{
64+
return shr->isShared(e);
65+
}
66+
};
67+
template <class T>
68+
struct AggDataOfFunc : public FunctionBase<T>
69+
{
70+
AggDataOfFunc(AggregateNumberingOf<T> * n,
71+
TagDataOf<T> * i)
72+
: num(n)
73+
, nd_ids(i)
74+
{ }
75+
virtual void eval(MeshEntity * e, T * dat)
76+
{
77+
int nds = num->getShape()->countNodesOn(num->getMesh()->getType(e));
78+
int blks_per_nd = num->countBlocks();
79+
int dofs_per_blk = num->blockSize();
80+
int cmps = blks_per_nd * dofs_per_blk;
81+
int ownr = num->getSharing()->getOwner(e);
82+
int strd = num->getStride(ownr);
83+
int frst = num->getFirstDof(ownr);
84+
for(int nd = 0; nd < nds; ++nd)
85+
{
86+
T nd_id = -1;
87+
nd_ids->get(e,&nd_id);
88+
for(int blk = 0; blk < blks_per_nd; ++blk)
89+
for(int dof = 0; dof < dofs_per_blk; ++dof)
90+
dat[nd * cmps + blk * dofs_per_blk + dof] =
91+
frst + (nd_id * dofs_per_blk) + (blk * strd) + dof;
92+
}
93+
}
94+
AggregateNumberingOf<T> * num;
95+
TagDataOf<T> * nd_ids;
96+
};
97+
template <class T>
98+
T exscanT(T v);
99+
template <>
100+
int exscanT(int v)
101+
{
102+
return PCU_Exscan_Int(v);
103+
}
104+
template <>
105+
long exscanT(long v)
106+
{
107+
return PCU_Exscan_Long(v);
108+
}
109+
template <class T>
110+
void allgatherT(T i, T* o);
111+
template <>
112+
void allgatherT(int i, int* o)
113+
{
114+
PCU_Allgather_Int(i,o);
115+
}
116+
template <>
117+
void allgatherT(long i, long* o)
118+
{
119+
PCU_Allgather_Long(i,o);
120+
}
121+
template <class T>
122+
void AggregateNumberingOf<T>::init(const char * n,
123+
Mesh * m,
124+
Sharing * sh,
125+
FieldShape * s,
126+
int blks,
127+
int bs)
128+
{
129+
// trick AggFieldDataOf into only allocating one int per node
130+
shr = sh;
131+
//FieldBase::init(n,m,s,new AggDataOf<T>(this));
132+
NumberingOf<T>::components = 1;
133+
FieldBase::shape = s;
134+
FieldBase::mesh = m;
135+
FieldBase::name = n;
136+
nd_ids = new TagDataOf<T>;
137+
nd_ids->init(this);
138+
FieldBase::init(n,m,s,new UserDataBase<T>(new AggDataOfFunc<T>(this,static_cast<TagDataOf<T>*>(nd_ids))));
139+
NumberingOf<T>::components = blks * bs;
140+
blks_per_nd = blks;
141+
dofs_per_blk = bs;
142+
int dim = m->getDimension();
143+
for(int dd = 0; dd < dim; ++dd)
144+
lcl_strd += countOwnedNodes(m,dd,s,sh);
145+
int sz = PCU_Comm_Peers();
146+
slf = PCU_Comm_Self();
147+
// do we bother setting strds and frsts since they
148+
// are reset in globalize which must be called after
149+
// init for the numbering to be valid
150+
strds.resize(static_cast<unsigned>(sz));
151+
// all peers in the aggregation scope necessarily
152+
// have the same stride
153+
int strd = PCU_Add_Int(lcl_strd);
154+
for(int ii = 0; ii < sz; ++ii)
155+
strds[ii] = strd;
156+
// the first dof id of the first local node is the # of
157+
// nodes before the first local node times the # of
158+
// dofs in the first block on a node
159+
T dof_id_offset = lcl_strd * dofs_per_blk;
160+
T frst = exscanT<T>(dof_id_offset);
161+
frsts.resize(static_cast<unsigned>(sz));
162+
allgatherT(frst,&frsts[0]);
163+
T nd_id = 0;
164+
for(int dd = 0; dd < dim; ++dd)
165+
{
166+
MeshIterator * it = m->begin(dd);
167+
MeshEntity * e = NULL;
168+
while((e = m->iterate(it)))
169+
{
170+
int nds = FieldBase::shape->countNodesOn(m->getType(e));
171+
// only really works if nds == 1 or 0
172+
// due to the above comment ^
173+
for(int nd = 0; nd < nds; ++nd)
174+
{
175+
if(shr->isOwned(e))
176+
{
177+
nd_ids->set(e,&nd_id);
178+
++nd_id;
179+
}
180+
}
181+
}
182+
m->end(it);
183+
}
184+
}
185+
template <class T>
186+
void AggregateNumberingOf<T>::init(Field * fld,
187+
Sharing * sh,
188+
int blks,
189+
int bs)
190+
{
191+
PCU_ALWAYS_ASSERT(fld->countComponents() == blks * bs);
192+
std::string nm = fld->getName();
193+
nm += "_num";
194+
init(nm.c_str(),
195+
fld->getMesh(),
196+
sh,
197+
fld->getShape(),
198+
blks,
199+
bs);
200+
}
201+
template <class T>
202+
void AggregateNumberingOf<T>::globalize()
203+
{
204+
synchronizeFieldData(nd_ids,shr,false);
205+
// any rank with frst == 0 was the zero rank
206+
// in the aggregation scope under which init
207+
// was called, the offsets from these
208+
// zero-ranked processes are what we need to
209+
// sum since they include in their strides
210+
// all the nodes from the local aggregation scope
211+
int strd = getScopeStride();
212+
int frst = getLocalFirstDof();
213+
int scp_offset = (int)(!(bool)frst) * (strd * dofs_per_blk * blks_per_nd);
214+
int lcl_offset = PCU_Exscan_Int(scp_offset);
215+
lcl_offset += scp_offset; // make the excan a scan
216+
// remove the contribution from the local
217+
// aggregation scope, because the scope can be
218+
// any comm and only rank 0 in each scope
219+
// contributes to the above scan
220+
lcl_offset -= (strd * dofs_per_blk * blks_per_nd);
221+
frst += lcl_offset;
222+
// strds and frsts needs to be resized/updated
223+
// since we can have adjacent parts that were
224+
// not in the aggregation scope under which
225+
// init was called
226+
int sz = PCU_Comm_Peers();
227+
frsts.setSize(sz);
228+
allgatherT<T>(frst,&frsts[0]);
229+
// each aggregation scope has its own stride
230+
// we store per-rank since that is vastly
231+
// less complicated than keeping track
232+
// of the initial scopes
233+
strds.setSize(sz);
234+
allgatherT<T>(strd,&strds[0]);
235+
}
236+
template <class T>
237+
void AggregateNumberingOf<T>::getAll(MeshEntity * e, T * dat)
238+
{
239+
reinterpret_cast<FieldDataOf<T>*>(FieldBase::data)->get(e,dat);
240+
}
241+
template <class T>
242+
T AggregateNumberingOf<T>::get(MeshEntity * e, int nd, int cmp)
243+
{
244+
int cmps = blks_per_nd * dofs_per_blk;
245+
int nds = FieldBase::shape->countNodesOn(FieldBase::mesh->getType(e));
246+
NewArray<T> data(nds*cmps);
247+
getAll(e,&data[0]);
248+
return data[nd*cmps + cmp];
249+
}
250+
AggNumbering * createAggNumbering(Field * f,
251+
int blocks,
252+
int dofs_per_block,
253+
MPI_Comm cm,
254+
Sharing * share)
255+
{
256+
bool dlt = false;
257+
if(!share)
258+
{
259+
share = getSharing(getMesh(f));
260+
dlt = true;
261+
}
262+
AggNumbering * n = new AggNumbering;
263+
MPI_Comm pcu_cm = PCU_Get_Comm();
264+
bool swtch = pcu_cm != cm;
265+
Sharing * init_share = share;
266+
if(swtch)
267+
{
268+
init_share = new StaticSharing(getMesh(f),share,cm);
269+
PCU_Switch_Comm(cm);
270+
}
271+
n->init(f,init_share,blocks,dofs_per_block);
272+
if(swtch)
273+
{
274+
PCU_Switch_Comm(pcu_cm);
275+
delete init_share;
276+
}
277+
// during init the static sharing may be used which
278+
// only provides binary ownership information, not
279+
// the owner rank of unowned entities, so we replace
280+
// it with the passed-in sharing here before globalization
281+
// which requires the rank information
282+
n->setSharing(share);
283+
n->globalize();
284+
f->getMesh()->addNumbering(n);
285+
return n;
286+
}
287+
AggNumbering * createAggNumbering(Mesh * m,
288+
const char * name,
289+
FieldShape * shape,
290+
int blocks,
291+
int dofs_per_block,
292+
MPI_Comm cm,
293+
Sharing * share)
294+
{
295+
bool dlt = false;
296+
if(!share)
297+
{
298+
share = getSharing(m);
299+
dlt = true;
300+
}
301+
AggNumbering * n = new AggNumbering;
302+
MPI_Comm pcu_cm = PCU_Get_Comm();
303+
bool swtch = pcu_cm != cm;
304+
Sharing * init_share = share;
305+
if(swtch)
306+
{
307+
init_share = new StaticSharing(m,share,cm);
308+
PCU_Switch_Comm(cm);
309+
}
310+
n->init(name,m,init_share,shape,blocks,dofs_per_block);
311+
if(swtch)
312+
{
313+
PCU_Switch_Comm(pcu_cm);
314+
delete init_share;
315+
}
316+
n->setSharing(share);
317+
n->globalize();
318+
m->addNumbering(n);
319+
return n;
320+
}
321+
/* Public API */
322+
Numbering * getNumbering(AggNumbering * n)
323+
{
324+
return static_cast<Numbering*>(n);
325+
}
326+
int countBlocks(AggNumbering * n)
327+
{
328+
return n->blockSize();
329+
}
330+
int countDOFsPerBlock(AggNumbering * n)
331+
{
332+
return n->blockSize();
333+
}
334+
}

0 commit comments

Comments
 (0)