1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
|
// 2004 © Olivier Galizzi <olivier.galizzi@imag.fr>
// 2004,2019 © Janek Kozicki <cosurgi@berlios.de>
// 2010 © Václav Šmilauer <eudoxos@arcig.cz>
// 2019 © Anton Gladky <gladk@debian.org>
// 2018 © Bruno Chareyre <bruno.chareyre@grenoble-inp.fr>
#pragma once
#include <lib/serialization/Serializable.hpp>
#include <core/Body.hpp>
#include <boost/iterator/filter_iterator.hpp>
namespace yade { // Cannot have #include directive inside.
class Body;
class InteractionContainer;
#ifdef YADE_OPENMP
#define YADE_PARALLEL_FOREACH_BODY_BEGIN(b_, bodies) \
bodies->updateRealBodies(); \
const vector<Body::id_t>& realBodies = bodies->realBodies; \
const bool redirect = bodies->useRedirection; \
const Body::id_t _sz(redirect ? realBodies.size() : bodies->size()); \
_Pragma("omp parallel for") for (int kParallelForeachIndexCounter = 0; kParallelForeachIndexCounter < _sz; kParallelForeachIndexCounter++) \
{ \
if (not redirect and not(*bodies)[kParallelForeachIndexCounter]) continue; \
b_((*bodies)[redirect ? realBodies[kParallelForeachIndexCounter] : kParallelForeachIndexCounter]);
#else
#define YADE_PARALLEL_FOREACH_BODY_BEGIN(b_, bodies) \
bodies->updateRealBodies(); \
const vector<Body::id_t>& realBodies = bodies->realBodies; \
const bool redirect = bodies->useRedirection; \
const Body::id_t _sz(redirect ? realBodies.size() : bodies->size()); \
for (int kParallelForeachIndexCounter = 0; kParallelForeachIndexCounter < _sz; kParallelForeachIndexCounter++) { \
if (not redirect and not(*bodies)[kParallelForeachIndexCounter]) continue; \
b_((*bodies)[redirect ? realBodies[kParallelForeachIndexCounter] : kParallelForeachIndexCounter]);
#endif
#define YADE_PARALLEL_FOREACH_BODY_END() }
/*
Container of bodies implemented as flat std::vector.
The iterator will silently skip null body pointers which may exist after removal. The null pointers can still be accessed via the [] operator.
When the container is sparse (after erasing bodies or mpi decomposition) the container supports redirection, i.e. looping on the indices of the non-void elements of the container (realBodies) rather than on elements of the container. This is integrated in the above YADE_PARALLEL_FOREACH macros.
Any alternative implementation should use the same API.
*/
class BodyContainer : public Serializable {
private:
using ContainerT = std::vector<shared_ptr<Body>>;
public:
friend class InteractionContainer; // accesses the body vector directly
//An iterator that will automatically jump slots with null bodies
struct isNonEmptySharedPtr {
bool operator()(const shared_ptr<Body>& b) const { return b.operator bool(); }
};
using iterator = boost::filter_iterator<isNonEmptySharedPtr, ContainerT::iterator>;
Body::id_t insert(shared_ptr<Body>);
Body::id_t insertAtId(shared_ptr<Body> b, Body::id_t candidate);
// Container operations
void clear();
iterator begin();
iterator end();
size_t size() const;
shared_ptr<Body>& operator[](unsigned int id);
const shared_ptr<Body>& operator[](unsigned int id) const;
bool exists(Body::id_t id) const;
bool erase(Body::id_t id, bool eraseClumpMembers);
void updateRealBodies();
// clang-format off
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(BodyContainer,Serializable,"Standard body container for a scene",
((ContainerT,body,,,"The underlying vector<shared_ptr<Body> >"))
((bool,dirty,true,(Attr::noSave|Attr::readonly|Attr::hidden),"true if after insertion/removal of bodies, used only if collider::keepListsShort=true"))
((bool,checkedByCollider,false,(Attr::noSave|Attr::readonly|Attr::hidden),""))
((vector<Body::id_t>,insertedBodies,vector<Body::id_t>(),Attr::readonly,"The list of newly bodies inserted, to be used and purged by collider"))
((vector<Body::id_t>,erasedBodies,vector<Body::id_t>(),Attr::readonly,"The list of erased bodies, to be used and purged by collider"))
((vector<Body::id_t>,realBodies,vector<Body::id_t>(),Attr::readonly,"Redirection vector to non-null bodies, used to optimize loops after numerous insertion/erase. In MPI runs the list is restricted to bodies and neighbors present in current subdomain."))
((bool,useRedirection,false,,"true if the scene uses up-to-date lists for boundedBodies and realBodies; turned true automatically 1/ after removal of bodies if :yref:`enableRedirection=True <BodyContainer.enableRedirection>`, and 2/ in MPI execution. |yupdate|"))
((bool,enableRedirection,true,,"let collider switch to optimized algorithm with body redirection when bodies are erased - true by default"))
#ifdef YADE_MPI
((vector<Body::id_t>,subdomainBodies,vector<Body::id_t>(),,"The list of bounded bodies in the subdomain"))
#endif
,/*ctor*/,
.def("updateRealBodies",&BodyContainer::updateRealBodies,"update lists realBodies and subdomainBodies. This function is called automatically by e.g. ForceContainer::reset(), it is safe to call multiple times from many places since if the lists are up-to-date he function will just return.")
)
// clang-format on
DECLARE_LOGGER;
// mutual exclusion to avoid crashes in the rendering loop
std::mutex drawloopmutex;
private:
bool eraseAlreadyLocked(Body::id_t id, bool eraseClumpMembers);
};
REGISTER_SERIALIZABLE(BodyContainer);
} // namespace yade
|