File: ForceContainer.hpp

package info (click to toggle)
yade 2019.01a-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 16,568 kB
  • sloc: cpp: 56,330; python: 30,148; ansic: 6,463; sh: 123; makefile: 56
file content (131 lines) | stat: -rw-r--r-- 5,429 bytes parent folder | download | duplicates (3)
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 2009 © Václav Šmilauer <eudoxos@arcig.cz>
#pragma once

#include <lib/base/Math.hpp>
#include <core/Body.hpp>

#include <boost/static_assert.hpp>
// make sure that (void*)&vec[0]==(void*)&vec
BOOST_STATIC_ASSERT(sizeof(Vector3r)==3*sizeof(Real));


#ifdef YADE_OPENMP
#include <omp.h>
#endif

/*! Container for Body External Variables (forces), typically forces and torques from interactions.
 * Values should be reset at every iteration by calling ForceContainer::reset();
 * If you want to add your own force type, you need to:
 *
 * 	1. Create storage vector
 * 	2. Create accessor function
 * 	3. Update the resize function
 * 	4. Update the reset function
 * 	5. update the sync function (for the multithreaded implementation)
 *
 * This class exists in two flavors: non-parallel and parallel. The parallel one stores
 * force increments separately for every thread and sums those when sync() is called.
 * The reason of this design is that the container is not truly random-access, but rather
 * is written to everywhere in one phase and read in the next one. Adding to force/torque
 * marks the container as dirty and sync() must be performed before reading the stored data.
 * Calling getForce/getTorque when the container is not synchronized throws an exception.
 *
 * It is intentional that sync() needs to be called exlicitly, since syncs are expensive and
 * the programmer should be aware of that. Sync is however performed only if the container
 * is dirty. Every full sync increments the syncCount variable, that should ideally equal
 * the number of steps (one per step).
 *
 * The number of threads (omp_get_max_threads) may not change once ForceContainer is constructed.
 *
 * The non-parallel flavor has the same interface, but sync() is no-op and synchronization
 * is not enforced at all.
 */

//! This is the parallel flavor of ForceContainer
class ForceContainer {
	private:
		typedef std::vector<Vector3r> vvector;
#ifdef YADE_OPENMP
		std::vector<vvector> _forceData;
		std::vector<vvector> _torqueData;
		std::vector<vvector> _moveData;
		std::vector<vvector> _rotData;
		std::vector<Body::id_t> _maxId;
		std::vector<size_t> sizeOfThreads;
		void ensureSize(Body::id_t id, int threadN);
#else
		void ensureSize(Body::id_t id);
		Body::id_t _maxId=0;
#endif
		vvector _force, _torque, _move, _rot, _permForce, _permTorque;
		size_t size = 0;
		bool syncedSizes = true;
		int nThreads;
		bool synced = true;
		bool moveRotUsed = false;
		bool permForceUsed = false;
		boost::mutex globalMutex;
		const Vector3r _zero = Vector3r::Zero();

		
		void ensureSynced();
		
		// dummy function to avoid template resolution failure
		friend class boost::serialization::access;
		template<class ArchiveT> void serialize(ArchiveT & ar, unsigned int version){}
	public:
		unsigned long syncCount = 0;
		long lastReset = 0;
		ForceContainer();
		const Vector3r& getForce(Body::id_t id);
		void  addForce(Body::id_t id, const Vector3r& f);
		const Vector3r& getTorque(Body::id_t id);
		void  addTorque(Body::id_t id, const Vector3r& t);
		const Vector3r& getMove(Body::id_t id);
		void  addMove(Body::id_t id, const Vector3r& m);
		const Vector3r& getRot(Body::id_t id);
		void  addRot(Body::id_t id, const Vector3r& r);
		void  addMaxId(Body::id_t id);

		void  setPermForce(Body::id_t id, const Vector3r& f);
		void  setPermTorque(Body::id_t id, const Vector3r& t);
		const Vector3r& getPermForce(Body::id_t id);
		const Vector3r& getPermTorque(Body::id_t id);
		
		/*! Function to allow friend classes to get force even if not synced. Used for clumps by NewtonIntegrator.
		* Dangerous! The caller must know what it is doing! (i.e. don't read after write
		* for a particular body id. */
		const Vector3r& getForceUnsynced (Body::id_t id);
		const Vector3r& getTorqueUnsynced(Body::id_t id);
		void  addForceUnsynced(Body::id_t id, const Vector3r& f);
		void  addTorqueUnsynced(Body::id_t id, const Vector3r& m);
		
		/* To be benchmarked: sum thread data in getForce/getTorque upon request for each body individually instead of by the sync() function globally */
		// this function is used from python so that running simulation is not slowed down by sync'ing on occasions
		// since Vector3r writes are not atomic, it might (rarely) return wrong value, if the computation is running meanwhile
		const Vector3r getForceSingle (Body::id_t id);
		const Vector3r getTorqueSingle(Body::id_t id);
		const Vector3r getMoveSingle  (Body::id_t id);
		const Vector3r getRotSingle   (Body::id_t id);

#ifdef YADE_OPENMP
		void syncSizesOfContainers();
		void resize(size_t newSize, int threadN);
#else
		void resize(size_t newSize);
#endif
		/* Sum contributions from all threads, save to _force&_torque.
		 * Locks globalMutex, since one thread modifies common data (_force&_torque).
		 * Must be called before get* methods are used. Exception is thrown otherwise, since data are not consistent. */
		void sync();

		void resizePerm(size_t newSize);
		/*! Reset all resetable data, also reset summary forces/torques and mark the container clean.
		If resetAll, reset also user defined forces and torques*/
		// perhaps should be private and friend Scene or whatever the only caller should be
		void reset(long iter, bool resetAll=false);
		//! say for how many threads we have allocated space
		const int getNumAllocatedThreads() const;
		const bool getMoveRotUsed() const;
		const bool getPermForceUsed() const;
};