File: GeneralIntegratorInsertionSortCollider.cpp

package info (click to toggle)
yade 2026.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 34,448 kB
  • sloc: cpp: 97,645; python: 52,173; sh: 677; makefile: 162
file content (316 lines) | stat: -rw-r--r-- 11,151 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// 2009 © Václav Šmilauer <eudoxos@arcig.cz>

#include "GeneralIntegratorInsertionSortCollider.hpp"
#include <core/Dispatching.hpp>
#include <core/Interaction.hpp>
#include <core/InteractionContainer.hpp>
#include <core/Scene.hpp>
#include <pkg/common/Sphere.hpp>
#include <pkg/dem/Integrator.hpp>

#include <algorithm>
#include <boost/static_assert.hpp>
#include <vector>

namespace yade { // Cannot have #include directive inside.

using math::max;
using math::min; // using inside .cpp file is ok.

YADE_PLUGIN((GeneralIntegratorInsertionSortCollider))
CREATE_LOGGER(GeneralIntegratorInsertionSortCollider);

// STRIDE
bool GeneralIntegratorInsertionSortCollider::isActivated()
{
	// activated if number of bodies changes (hence need to refresh collision information)
	// or the time of scheduled run already came, or we were never scheduled yet
	if (!strideActive) return true;
	if (!integrator) return true;
	if (fastestBodyMaxDist < 0) {
		fastestBodyMaxDist = 0;
		return true;
	}
	fastestBodyMaxDist = integrator->maxVelocitySq;
	if (fastestBodyMaxDist >= 1 || fastestBodyMaxDist == 0) return true;
	if (BB[0].size() != 2 * scene->bodies->size()) return true;
	if (scene->interactions->dirty) return true;
	if (scene->doSort) {
		scene->doSort = false;
		return true;
	}
	return false;
}

void GeneralIntegratorInsertionSortCollider::action()
{
#ifdef ISC_TIMING
	timingDeltas->start();
#endif

	const size_t          nBodies            = scene->bodies->size();
	InteractionContainer* interactions       = scene->interactions.get();
	scene->interactions->iterColliderLastRun = -1;

	// periodicity changed, force reinit
	if (scene->isPeriodic != periodic) {
		for (int i = 0; i < 3; i++)
			BB[i].clear();
		periodic = scene->isPeriodic;
	}
	// pre-conditions
	// adjust storage size
	bool doInitSort = false;
	if (doSort) {
		doInitSort = true;
		doSort     = false;
	}
	if (BB[0].size() != 2 * nBodies) {
		size_t BBsize = BB[0].size();
		LOG_DEBUG("Resize bounds containers from " << BBsize << " to " << nBodies * 2 << ", will std::sort.");
		// bodies deleted; clear the container completely, and do as if all bodies were added (rather slow…)
		// future possibility: insertion sort with such operator that deleted bodies would all go to the end, then just trim bounds
		if (2 * nBodies < BBsize) {
			for (int i = 0; i < 3; i++)
				BB[i].clear();
		}
		// more than 100 bodies was added, do initial sort again
		// maybe: should rather depend on ratio of added bodies to those already present...?
		if (2 * nBodies - BBsize > 200 || BBsize == 0) doInitSort = true;
		assert((BBsize % 2) == 0);
		for (int i = 0; i < 3; i++) {
			BB[i].reserve(2 * nBodies);
			// add lower and upper bounds; coord is not important, will be updated from bb shortly
			for (size_t id = BBsize / 2; id < nBodies; id++) {
				BB[i].push_back(Bounds(0, id, /*isMin=*/true));
				BB[i].push_back(Bounds(0, id, /*isMin=*/false));
			}
		}
	}
	if (minima.size() != (size_t)3 * nBodies) {
		minima.resize(3 * nBodies);
		maxima.resize(3 * nBodies);
	}
	assert(BB[0].size() == 2 * scene->bodies->size());

	// update periodicity
	assert(BB[0].axis == 0);
	assert(BB[1].axis == 1);
	assert(BB[2].axis == 2);
	if (periodic)
		for (int i = 0; i < 3; i++)
			BB[i].updatePeriodicity(scene);

	if (verletDist < 0) {
		Real minR = std::numeric_limits<Real>::infinity();
		for (const auto& b : *scene->bodies) {
			if (!b || !b->shape) continue;
			Sphere* s = dynamic_cast<Sphere*>(b->shape.get());
			if (!s) continue;
			minR = min(s->radius, minR);
		}
		if (math::isinf(minR))
			LOG_ERROR("verletDist is set to 0 because no spheres were found. It will result in suboptimal performances, consider setting a "
			          "positive verletDist in your script.");
		// if no spheres, disable stride
		verletDist = math::isinf(minR) ? 0 : math::abs(verletDist) * minR;
	}

	// update bounds via boundDispatcher
	boundDispatcher->scene              = scene;
	boundDispatcher->sweepDist          = verletDist;
	boundDispatcher->minSweepDistFactor = minSweepDistFactor;
	boundDispatcher->targetInterv       = targetInterv;
	boundDispatcher->updatingDispFactor = updatingDispFactor;
	boundDispatcher->action();

	// if interactions are dirty, force reinitialization
	if (scene->interactions->dirty) {
		doInitSort                 = true;
		scene->interactions->dirty = false;
	}

	// STRIDE
	if (verletDist > 0) {
		// get the Integrator, to ask for the maximum velocity value
		if (!integrator) {
			FOREACH(shared_ptr<Engine> & e, scene->engines)
			{
				integrator = YADE_PTR_DYN_CAST<Integrator>(e);
				if (integrator) break;
			}
			if (!integrator) { throw runtime_error("InsertionSortCollider.verletDist>0, but unable to locate any Integrator within O.engines."); }
		}
	}
	ISC_CHECKPOINT("init");

	// STRIDE
	// get us ready for strides, if they were deactivated
	if (!strideActive && verletDist > 0 && integrator->maxVelocitySq >= 0) { // maxVelocitySq is a really computed value
		strideActive = true;
	}
	if (strideActive) {
		assert(verletDist > 0);
		assert(strideActive);
		assert(integrator->maxVelocitySq >= 0);
		integrator->updatingDispFactor = updatingDispFactor;
	} else { /* !strideActive */
		boundDispatcher->sweepDist = 0;
	}

	ISC_CHECKPOINT("bound");

	// copy bounds along given axis into our arrays
	for (size_t i = 0; i < 2 * nBodies; i++) {
		for (int j = 0; j < 3; j++) {
			VecBounds&              BBj = BB[j];
			const Body::id_t        id  = BBj[i].id;
			const shared_ptr<Body>& b   = Body::byId(id, scene);
			if (b) {
				const shared_ptr<Bound>& bv = b->bound;
				// coordinate is min/max if has bounding volume, otherwise both are the position. Add periodic shift so that we are inside the cell
				// watch out for the parentheses around ?: within ?: (there was unwanted conversion of the Reals to bools!)

				BBj[i].coord = ((BBj[i].flags.hasBB = ((bool)bv)) ? (BBj[i].flags.isMin ? bv->min[j] : bv->max[j]) : (b->state->pos[j]))
				        - (periodic ? BBj.cellDim * BBj[i].period : 0.);

			} else {
				BBj[i].flags.hasBB = false; /* for vanished body, keep the coordinate as-is, to minimize inversions. */
			}
			// if initializing periodic, shift coords & record the period into BBj[i].period
			if (doInitSort && periodic) { BBj[i].coord = cellWrap(BBj[i].coord, 0, BBj.cellDim, BBj[i].period); }
		}
	}
	// for each body, copy its minima and maxima, for quick checks of overlaps later
	BOOST_STATIC_ASSERT(sizeof(Vector3r) == 3 * sizeof(Real));
#if (YADE_REAL_BIT <= 64)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
// this is to remove warning about manipulating raw memory
#pragma GCC diagnostic ignored "-Wclass-memaccess"
	for (size_t id = 0; id < nBodies; id++) {
		const shared_ptr<Body>& b = Body::byId(id, scene);
		if (b) {
			const shared_ptr<Bound>& bv = b->bound;
			if (bv) {
				memcpy(&minima[3 * id], &bv->min, 3 * sizeof(Real));
				memcpy(&maxima[3 * id], &bv->max, 3 * sizeof(Real));
			} // ⇐ faster than 6 assignments
			else {
				const Vector3r& pos = b->state->pos;
				memcpy(&minima[3 * id], &pos, 3 * sizeof(Real));
				memcpy(&maxima[3 * id], &pos, 3 * sizeof(Real));
			}
		} else {
			memset(&minima[3 * id], 0, 3 * sizeof(Real));
			memset(&maxima[3 * id], 0, 3 * sizeof(Real));
		}
	}
#pragma GCC diagnostic pop
#else
	for (size_t id = 0; id < nBodies; id++) {
		const shared_ptr<Body>& b = Body::byId(id, scene);
		if (b) {
			const shared_ptr<Bound>& bv = b->bound;
			if (bv) {
				minima[3 * id]     = bv->min[0];
				minima[3 * id + 1] = bv->min[1];
				minima[3 * id + 2] = bv->min[2];
				maxima[3 * id]     = bv->max[0];
				maxima[3 * id + 1] = bv->max[1];
				maxima[3 * id + 2] = bv->max[2];
			} else {
				const Vector3r& pos = b->state->pos;
				minima[3 * id]      = pos[0];
				minima[3 * id + 1]  = pos[1];
				minima[3 * id + 2]  = pos[2];
				maxima[3 * id]      = pos[0];
				maxima[3 * id + 1]  = pos[1];
				maxima[3 * id + 2]  = pos[2];
			}
		} else {
			std::fill(minima.begin() + 3 * id, minima.begin() + 3 * id + 3, 0);
			std::fill(maxima.begin() + 3 * id, maxima.begin() + 3 * id + 3, 0);
		}
	}
#endif

	ISC_CHECKPOINT("copy");

	// process interactions that the constitutive law asked to be erased
	// 	interactions->erasePending(*this,scene);
	interactions->conditionalyEraseNonReal(*this, scene);

	ISC_CHECKPOINT("erase");

	// sort
	// the regular case
	if (!doInitSort && !sortThenCollide) {
		/* each inversion in insertionSort calls handleBoundInversion, which in turns may add/remove interaction */
		if (!periodic)
			for (int i = 0; i < 3; i++)
				insertionSort(BB[i], interactions, scene);
		else
			for (int i = 0; i < 3; i++)
				insertionSortPeri(BB[i], interactions, scene);
	}
	// create initial interactions (much slower)
	else {
		if (doInitSort) {
			// the initial sort is in independent in 3 dimensions, may be run in parallel; it seems that there is no time gain running in parallel, though
			// important to reset loInx for periodic simulation (!!)
			for (int i = 0; i < 3; i++) {
				BB[i].loIdx = 0;
				BB[i].sort();
			}
			numReinit++;
		} else { // sortThenCollide
			if (!periodic)
				for (int i = 0; i < 3; i++)
					insertionSort(BB[i], interactions, scene, false);
			else
				for (int i = 0; i < 3; i++)
					insertionSortPeri(BB[i], interactions, scene, false);
		}
		// traverse the container along requested axis
		assert(sortAxis == 0 || sortAxis == 1 || sortAxis == 2);
		VecBounds& V = BB[sortAxis];
		// go through potential aabb collisions, create interactions as necessary
		if (!periodic) {
			for (size_t i = 0; i < 2 * nBodies; i++) {
				// start from the lower bound (i.e. skipping upper bounds)
				// skip bodies without bbox, because they don't collide
				if (!(V[i].flags.isMin && V[i].flags.hasBB)) continue;
				const Body::id_t& iid = V[i].id;
				// go up until we meet the upper bound
				for (size_t j = i + 1; /* handle case 2. of swapped min/max */ j < 2 * nBodies && V[j].id != iid; j++) {
					const Body::id_t& jid = V[j].id;
					// take 2 of the same condition (only handle collision [min_i..max_i]+min_j, not [min_i..max_i]+min_i (symmetric)
					if (!V[j].flags.isMin) continue;
					/* abuse the same function here; since it does spatial overlap check first, it is OK to use it */
					handleBoundInversion(iid, jid, interactions, scene);
					assert(j < 2 * nBodies - 1);
				}
			}
		} else { // periodic case: see comments above
			for (size_t i = 0; i < 2 * nBodies; i++) {
				if (!(V[i].flags.isMin && V[i].flags.hasBB)) continue;
				const Body::id_t& iid = V[i].id;
				long              cnt = 0;
				// we might wrap over the periodic boundary here; that's why the condition is different from the aperiodic case
				for (long j = V.norm(i + 1); V[j].id != iid; j = V.norm(j + 1)) {
					const Body::id_t& jid = V[j].id;
					if (!V[j].flags.isMin) continue;
					handleBoundInversionPeri(iid, jid, interactions, scene);
					if (cnt++ > 2 * (long)nBodies) {
						LOG_FATAL("Uninterrupted loop in the initial sort?");
						throw std::logic_error("loop??");
					}
				}
			}
		}
	}
	ISC_CHECKPOINT("sort&collide");
}

} // namespace yade