diff --git a/apf/apf.cc b/apf/apf.cc index c7b60a1a2..bbc444fde 100644 --- a/apf/apf.cc +++ b/apf/apf.cc @@ -21,6 +21,7 @@ #include "apfNumberingClass.h" #include #include +#include #include #include @@ -140,6 +141,46 @@ bool hasEntity(Field* f, MeshEntity* e) return f->getData()->hasEntity(e); } +int countLocalNodes(Field* f, Sharing* shr) +{ + Mesh * msh = getMesh(f); + FieldShape * shp = getShape(f); + return countLocalNodes(msh,shp,shr); +} + +int countLocalNodes(Mesh* m, FieldShape * shp, Sharing * shr) +{ + // if shr is NULL, the default behavior of + // countEntitiesOfType is to count ALL + // entities on the local part of type tp + int nds = 0; + for(int tp = Mesh::VERTEX; tp != Mesh::TYPES; ++tp) + nds += countEntitiesOfType(m,tp,shr) * shp->countNodesOn(tp); + return nds; +} + +int countGlobalNodes(Field* f, Sharing* shr) +{ + Mesh * msh = getMesh(f); + FieldShape * shp = getShape(f); + return countGlobalNodes(msh,shp,shr); +} + +int countGlobalNodes(Mesh* m, FieldShape* shp, Sharing* shr) +{ + bool del = false; + if(shr == NULL) + { + shr = getSharing(m); + del = true; + } + int lcl_nds = countLocalNodes(m,shp,shr); + int gbl_nds = PCU_Add_Int(lcl_nds); + if(del) + delete shr; + return gbl_nds; +} + const char* getName(Field* f) { return f->getName(); diff --git a/apf/apf.h b/apf/apf.h index 1ea32d2df..7fc3aa431 100644 --- a/apf/apf.h +++ b/apf/apf.h @@ -204,6 +204,24 @@ Mesh* getMesh(Field* f); */ bool hasEntity(Field* f, MeshEntity* e); +/** \brief Count all field nodes owned according to shr, on all mesh + entities on the local part, default is all to count every node, + even those on owned entities + */ +int countLocalNodes(Field* f, Sharing* shr = NULL); +/** \brief same as countLocalNodes but you don't need to allocate a field + */ +int countLocalNodes(Mesh* m, FieldShape* shp, Sharing* shr = NULL); + +/** \brief Count all field nodes owned according to shr, on all mesh + entities on all parts in PCU_COMM_WORLD, default is to use the + NormalSharing to define ownership. + */ +int countGlobalNodes(Field* f, Sharing* shr = NULL); +/** \brief same as countGlobalNodes but you don't need to allocate a field + */ +int countGlobalNodes(Mesh* m, FieldShape* shp, Sharing* shr = NULL); + /** \brief Get the name of a Field. * * \details Both for use convenience and for technical reasons @@ -682,7 +700,7 @@ void synchronize(Field* f, Sharing* shr = 0); */ void accumulate(Field* f, Sharing* shr = 0, bool delete_shr = false); -/** \brief Apply a reudction operator along partition boundaries +/** \brief Apply a reduction operator along partition boundaries \details Using the copies described by an apf::Sharing object, applied the specified operation pairwise to the values of the field on each partition. No guarantee is made about hte order of the pairwise diff --git a/apf/apfMesh.cc b/apf/apfMesh.cc index 02a34f193..ad10ec053 100644 --- a/apf/apfMesh.cc +++ b/apf/apfMesh.cc @@ -676,13 +676,16 @@ MeshEntity* getEdgeVertOppositeVert(Mesh* m, MeshEntity* edge, MeshEntity* v) return ev[0]; } -int countEntitiesOfType(Mesh* m, int type) + +int countEntitiesOfType(Mesh* m, int type, Sharing * shr) { + if(shr == NULL) + shr = getNoSharing(); MeshIterator* it = m->begin(Mesh::typeDimension[type]); MeshEntity* e; int count = 0; while ((e = m->iterate(it))) - if (m->getType(e)==type) + if (m->getType(e)==type && shr->isOwned(e)) ++count; m->end(it); return count; @@ -996,6 +999,36 @@ bool MatchedSharing::isShared(MeshEntity* e) { return false; } +// treat all entities as if they are not shared, +// used to count all local entities instead of +// all owned entities as the default +// The ONLY justification for this being a +// singleton is that it is essentially a functor +// at this point, as it retains no state, so there +// only ever needs to be a single instance of this +struct NoSharing : public Sharing +{ +private: + static NoSharing * instance; + NoSharing() {} + ~NoSharing() {} +public: + static NoSharing * Instance() + { + if(instance == NULL) + instance = new NoSharing; + return instance; + } + virtual int getOwner(MeshEntity*) { return PCU_Comm_Self(); } + virtual bool isOwned(MeshEntity*) { return true; } + virtual void getCopies(MeshEntity*,CopyArray&) { } + virtual bool isShared(MeshEntity*) { return false; } +}; +NoSharing * NoSharing::instance = NULL; + +// obfuscate the singleton just a bit +Sharing* getNoSharing() { return NoSharing::Instance(); } + Sharing* getSharing(Mesh* m) { if (m->hasMatching()) diff --git a/apf/apfMesh.h b/apf/apfMesh.h index bdba056a1..a7d28ce5f 100644 --- a/apf/apfMesh.h +++ b/apf/apfMesh.h @@ -430,9 +430,6 @@ MeshEntity* getEdgeVertOppositeVert(Mesh* m, MeshEntity* edge, MeshEntity* v); void getBridgeAdjacent(Mesh* m, MeshEntity* origin, int bridgeDimension, int targetDimension, Adjacent& result); -/** \brief count all on-part entities of one topological type */ -int countEntitiesOfType(Mesh* m, int type); - /** \brief return true if the topological type is a simplex */ bool isSimplex(int type); @@ -516,6 +513,8 @@ struct MatchedSharing : public Sharing std::map countMap; }; +Sharing* getNoSharing(); + /** \brief create a default sharing object for this mesh \details for normal meshes, the sharing object just describes remote copies. For matched meshes, the @@ -523,6 +522,12 @@ struct MatchedSharing : public Sharing and remote copies for other entities */ Sharing* getSharing(Mesh* m); +/** \brief count all on-part entities of one topological type, + \details if a sharing is provided, only 'owned' entities of + the specified type are counted, otherwise all on-part entities + are counted */ +int countEntitiesOfType(Mesh* m, int type, Sharing * shr = NULL); + /** \brief map from triangle edge order to triangle vertex order */ extern int const tri_edge_verts[3][2]; /** \brief map from quad edge order to quad vertex order */ diff --git a/apf/apfNumbering.cc b/apf/apfNumbering.cc index 8d6f7fe38..afe52e70c 100644 --- a/apf/apfNumbering.cc +++ b/apf/apfNumbering.cc @@ -221,14 +221,6 @@ void synchronize(Numbering * n, Sharing* shr, bool delete_shr) synchronizeFieldData(n->getData(), shr, delete_shr); } -struct NoSharing : public Sharing -{ - int getOwner(MeshEntity*) {return PCU_Comm_Self();} - bool isOwned(MeshEntity*) {return true;} - virtual void getCopies(MeshEntity*, CopyArray&) {} - bool isShared(MeshEntity*) {return false;} -}; - Numbering* numberNodes( Mesh* mesh, const char* name, @@ -258,14 +250,13 @@ Numbering* numberNodes( return n; } -Numbering* numberOwnedDimension(Mesh* mesh, const char* name, int dim, - Sharing* shr) +Numbering* numberOwnedDimension(Mesh* mesh, const char* name, int dim, Sharing * shr) { - bool delete_shr=false; - if (!shr) + bool delete_shr = false; + if (!shr) { shr = getSharing(mesh); - delete_shr=true; + delete_shr = true; } return numberNodes(mesh, name, getConstant(dim), shr, delete_shr); } @@ -273,8 +264,8 @@ Numbering* numberOwnedDimension(Mesh* mesh, const char* name, int dim, Numbering* numberOverlapDimension(Mesh* mesh, const char* name, int dim) { FieldShape* s = getConstant(dim); - Sharing* shr = new NoSharing(); - return numberNodes(mesh, name, s, shr, true); + Sharing* shr = getNoSharing(); + return numberNodes(mesh, name, s, shr, false); } Numbering* numberElements(Mesh* mesh, const char* name) @@ -286,8 +277,7 @@ Numbering* numberOverlapNodes(Mesh* mesh, const char* name, FieldShape* s) { if (!s) s = mesh->getShape(); - Sharing* shr = new NoSharing(); - return numberNodes(mesh, name, s, shr, true); + return numberNodes(mesh, name, s, getNoSharing(), false); } Numbering* numberOwnedNodes( diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e1562cef1..efb635671 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ test_exe_func(quality quality.cc) test_exe_func(writeVtxPtn writeVtxPtn.cc) test_exe_func(verify_2nd_order_shapes verify_2nd_order_shapes.cc) test_exe_func(verify_convert verify_convert.cc) +test_exe_func(count_entities count_entities.cc) # Geometric model utilities if(ENABLE_SIMMETRIX) diff --git a/test/count_entities.cc b/test/count_entities.cc new file mode 100644 index 000000000..93c7a2550 --- /dev/null +++ b/test/count_entities.cc @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int ac, char * av[]) +{ + + MPI_Init(&ac,&av); + PCU_Comm_Init(); + if (ac < 2) + { + if (PCU_Comm_Self() == 0) + printf("USAGE1: %s \n", av[0]); + MPI_Finalize(); + exit(EXIT_FAILURE); + } + + gmi_register_mesh(); + gmi_register_null(); + + apf::Mesh * msh = apf::loadMdsMesh(".null",av[1]); + + int lcl_vrt = apf::countEntitiesOfType(msh,apf::Mesh::VERTEX); + int own_vrt = apf::countEntitiesOfType(msh,apf::Mesh::VERTEX,apf::getSharing(msh)); + + int lcl_vrt2 = apf::countOwned(msh,0,apf::getNoSharing()); + int own_vrt2 = apf::countOwned(msh,0); + + int lcl_edg = apf::countEntitiesOfType(msh,apf::Mesh::EDGE); + int own_edg = apf::countEntitiesOfType(msh,apf::Mesh::EDGE,apf::getSharing(msh)); + + int lcl_edg2 = apf::countOwned(msh,1,apf::getNoSharing()); + int own_edg2 = apf::countOwned(msh,1); + + // higher dimension mesh entities is where counting specific types gets tricky + // using the default interfaces, + // which is the entire reason for this function in the first place. + + PCU_ALWAYS_ASSERT((lcl_vrt == lcl_vrt2) && (own_vrt == own_vrt2)); + PCU_ALWAYS_ASSERT((lcl_edg == lcl_edg2) && (own_edg == own_edg2)); + + apf::Field * fld = apf::createPackedField(msh,"scalar",1); + apf::Sharing * shr = apf::getSharing(msh); + + int lcl_nds = apf::countLocalNodes(fld); + int own_nds = apf::countLocalNodes(fld,shr); + + PCU_ALWAYS_ASSERT((lcl_vrt == lcl_nds) && (own_vrt == own_nds)); + + int gbl_nds = apf::countGlobalNodes(fld); + int all_nds = apf::countGlobalNodes(fld,apf::getNoSharing()); + + int gbl_nds2 = PCU_Add_Int(own_nds); + int all_nds2 = PCU_Add_Int(lcl_nds); + + PCU_ALWAYS_ASSERT((gbl_nds == gbl_nds2) && (all_nds == all_nds2)); + + delete shr; + + msh->destroyNative(); + apf::destroyMesh(msh); + + PCU_Comm_Free(); + MPI_Finalize(); +} + diff --git a/test/testing.cmake b/test/testing.cmake index a619319f2..6d124155f 100644 --- a/test/testing.cmake +++ b/test/testing.cmake @@ -35,6 +35,12 @@ mpi_test(qr_test 1 ./qr) mpi_test(base64 1 ./base64) mpi_test(tensor_test 1 ./tensor) mpi_test(verify_convert 1 ./verify_convert) +mpi_test(count_serial 1 + ./count_entities + "${MESHES}/cube/pumi11/cube.smb") +mpi_test(count_parallel 2 + ./count_entities + "${MESHES}/cube/pumi670/2/cube.smb") mpi_test(test_integrator 1 ./test_integrator "${MESHES}/cube/cube.dmg"