-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathAPI.h
More file actions
1959 lines (1773 loc) · 118 KB
/
Copy pathAPI.h
File metadata and controls
1959 lines (1773 loc) · 118 KB
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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2021, SBEL GPU Development Team
// Copyright (c) 2021, University of Wisconsin - Madison
//
// SPDX-License-Identifier: BSD-3-Clause
#ifndef DEME_API
#define DEME_API
#include <vector>
#include <set>
#include <cfloat>
#include <functional>
#include <memory>
#include "kT.h"
#include "dT.h"
#include "../core/utils/CudaAllocator.hpp"
#include "../core/utils/ThreadManager.h"
#include "../core/utils/GpuManager.h"
#include "../core/utils/DEMEPaths.h"
#include "Defines.h"
#include "Structs.h"
#include "BdrsAndObjs.h"
#include "Models.h"
#include "AuxClasses.h"
/// Main namespace for the DEM-Engine package.
namespace deme {
// class DEMKinematicThread;
// class DEMDynamicThread;
// class ThreadManager;
class DEMInspector;
class DEMTracker;
//////////////////////////////////////////////////////////////
// TODO LIST: 1. Variable ts size (MAX_VEL flavor uses tracked max cp vel)
// 2. Allow ext obj init CoM setting
// 3. Instruct how many dT steps should at LEAST do before receiving kT update
// 4. Sleepers that don't participate CD or integration
// 5. Update the game of life demo (it's about model ingredient usage)
// 7. Set the device numbers to use
// 9. wT takes care of an extra output when it crashes
// 10. Recover sph--mesh contact pairs in restarted sim by mesh name
// 11. A dry-run to map contact pair file with current clump batch based on cnt points location
// (this is done by fake an initialization with this batch)
//////////////////////////////////////////////////////////////
/// Main DEM-Engine solver.
class DEMSolver {
public:
/// Default constructor: scans available GPUs and uses at most 2.
DEMSolver(unsigned int nGPUs = 2);
/// Construct using explicit GPU device IDs. Errors if any ID is unavailable. Warns and uses the first 2 if more
/// than 2 are given.
DEMSolver(std::vector<int> device_ids);
~DEMSolver();
/// Set output detail level.
void SetVerbosity(VERBOSITY verbose) { verbosity = verbose; }
/// Instruct the dimension of the `world'. On initialization, this info will be used to figure out how to assign the
/// num of voxels in each direction. If your `useful' domain is not box-shaped, then define a box that contains your
/// domian.
void InstructBoxDomainDimension(float x, float y, float z, const std::string& dir_exact = "none");
/// @brief Set the size of the simulation `world'.
/// @param x Lower and upper limit for X coordinate.
/// @param y Lower and upper limit for Y coordinate.
/// @param z Lower and upper limit for Z coordinate.
/// @param dir_exact The direction for which the user-instructed size must strictly agree with the actual generated
/// size. Pick between "X", "Y", "Z" or "none".
void InstructBoxDomainDimension(const std::pair<float, float>& x,
const std::pair<float, float>& y,
const std::pair<float, float>& z,
const std::string& dir_exact = "none");
/// Instruct if and how we should add boundaries to the simulation world upon initialization. Choose between `none',
/// `all' (add 6 boundary planes) and `top_open' (add 5 boundary planes and leave the z-directon top open). Also
/// specifies the material that should be assigned to those bounding boundaries.
void InstructBoxDomainBoundingBC(const std::string& inst, const std::shared_ptr<DEMMaterial>& mat) {
m_user_add_bounding_box = inst;
m_bounding_box_material = mat;
}
/// Set gravitational pull.
void SetGravitationalAcceleration(float3 g) { G = g; }
void SetGravitationalAcceleration(const std::vector<float>& g) {
assertThreeElements(g, "SetGravitationalAcceleration", "g");
G = make_float3(g[0], g[1], g[2]);
}
/// Set the initial time step size. If using constant step size, then this will be used throughout; otherwise, the
/// actual step size depends on the variable step strategy.
void SetInitTimeStep(double ts_size) { m_ts_size = ts_size; }
/// Return the number of clumps that are currently in the simulation. Must be used after initialization.
size_t GetNumClumps() const { return nOwnerClumps; }
/// Return the total number of owners (clumps + meshes + analytical objects) that are currently in the simulation.
/// Must be used after initialization.
size_t GetNumOwners() const { return nOwnerBodies; }
/// @brief Get the number of kT-reported potential contact pairs.
/// @return Number of potential contact pairs.
size_t GetNumContacts() const { return dT->getNumContacts(); }
/// Get the current time step size in simulation.
double GetTimeStepSize() const { return m_ts_size; }
/// Get the current expand factor in simulation.
float GetExpandFactor() const;
/// Set the number of dT steps before it waits for a contact-pair info update from kT.
void SetCDUpdateFreq(int freq) {
m_updateFreq = freq;
m_suggestedFutureDrift = 2 * freq;
if (freq < 0) {
DisableAdaptiveUpdateFreq();
}
}
/// Get the simulation time passed since the start of simulation.
double GetSimTime() const;
/// Set the simulation time manually.
void SetSimTime(double time);
/// @brief Set the strategy for auto-adapting time step size (NOT implemented, no effect yet).
/// @param type "none" or "max_vel" or "int_diff".
void SetAdaptiveTimeStepType(const std::string& type);
/// @brief Set the time integrator for this simulator.
/// @param intg "forward_euler" or "extended_taylor" or "centered_difference".
void SetIntegrator(const std::string& intg);
/// @brief Set the time integrator for this simulator.
void SetIntegrator(TIME_INTEGRATOR intg) { m_integrator = intg; }
/// Return whether this simulation system is initialized
bool GetInitStatus() const { return sys_initialized; }
/// Get the jitification string substitution laundary list. It is needed by some of this simulation system's friend
/// classes.
std::unordered_map<std::string, std::string> GetJitStringSubs() const { return m_subs; }
/// Get current jitification options. It is needed by some of this simulation system's friend classes.
std::vector<std::string> GetJitifyOptions() const { return m_jitify_options; }
/// Set the jitification options. It is only needed by advanced users.
void SetJitifyOptions(const std::vector<std::string>& options) { m_jitify_options = options; }
/// Explicitly instruct the bin size (for contact detection) that the solver should use.
void SetInitBinSize(double bin_size) {
use_user_defined_bin_size = INIT_BIN_SIZE_TYPE::EXPLICIT;
m_binSize = bin_size;
}
/// Explicitly instruct the bin size (for contact detection) that the solver should use, as a multiple of the radius
/// of the smallest sphere in simulation.
void SetInitBinSizeAsMultipleOfSmallestSphere(float bin_size) {
use_user_defined_bin_size = INIT_BIN_SIZE_TYPE::MULTI_MIN_SPH;
m_binSize_as_multiple = bin_size;
}
/// @brief Set the target number of bins (for contact detection) at the start of the simulation upon initialization.
void SetInitBinNumTarget(size_t num) {
use_user_defined_bin_size = INIT_BIN_SIZE_TYPE::TARGET_NUM;
m_target_init_bin_num = num;
}
/// Explicitly instruct the sizes for the arrays at initialization time. This is useful when the number of owners
/// tends to change (especially gradually increase) frequently in the simulation, by reducing the need for
/// reallocation. Note however, whatever instruction the user gives here it won't affect the correctness of the
/// simulation, since if the arrays are not long enough they will always be auto-resized. This is not implemented
/// yet :/
void InstructNumOwners(size_t numOwners) { m_instructed_num_owners = numOwners; }
/// Instruct the solver to use frictonal (history-based) Hertzian contact force model.
std::shared_ptr<DEMForceModel> UseFrictionalHertzianModel();
/// Instruct the solver to use frictonless Hertzian contact force model.
std::shared_ptr<DEMForceModel> UseFrictionlessHertzianModel();
/// Define a custom contact force model by a string. Returns a shared_ptr to the force model in use.
std::shared_ptr<DEMForceModel> DefineContactForceModel(const std::string& model);
/// Read user custom contact force model from a file (which by default should reside in kernel/DEMUserScripts).
/// Returns a shared_ptr to the force model in use.
std::shared_ptr<DEMForceModel> ReadContactForceModel(const std::string& filename);
/// Get the current force model.
std::shared_ptr<DEMForceModel> GetContactForceModel() { return m_force_model; }
/// Instruct the solver if contact pair arrays should be sorted (based on the types of contacts) before usage.
void SetSortContactPairs(bool use_sort) { should_sort_contacts = use_sort; }
/// Instruct the solver to rearrange and consolidate clump templates information, then jitify it into GPU kernels
/// (if set to true), rather than using flattened sphere component configuration arrays whose entries are associated
/// with individual spheres.
void SetJitifyClumpTemplates(bool use = true) { jitify_clump_templates = use; }
/// Use flattened sphere component configuration arrays whose entries are associated with individual spheres, rather
/// than jitifying them it into GPU kernels.
void DisableJitifyClumpTemplates() { jitify_clump_templates = false; }
/// Instruct the solver to rearrange and consolidate mass property information (for all owner types), then jitify it
/// into GPU kernels (if set to true), rather than using flattened mass property arrays whose entries are associated
/// with individual owners.
void SetJitifyMassProperties(bool use = true) { jitify_mass_moi = use; }
/// Use flattened mass property arrays whose entries are associated with individual spheres, rather than jitifying
/// them it into GPU kernels.
void DisableJitifyMassProperties() { jitify_mass_moi = false; }
// NOTE: compact force calculation (in the hope to use shared memory) is not implemented
void UseCompactForceKernel(bool use_compact);
/// (Explicitly) set the amount by which the radii of the spheres (and the thickness of the boundaries) are expanded
/// for the purpose of contact detection (safe, and creates false positives). If fix is set to true, then this
/// expand factor does not change even if the user uses variable time step size.
void SetExpandFactor(float beta, bool fix = true) {
m_expand_factor = beta;
use_user_defined_expand_factor = fix;
}
/// Input the maximum expected particle velocity. If `force' is set to false, the solver will not use a velocity
/// larger than max_vel for determining the margin thickness; if `force' is set to true, the solver will not
/// calculate maximum system velocity and will always use max_vel to calculate the margin thickness.
/// @brief Set the maximum expected particle velocity. The solver will not use a velocity larger than this for
/// determining the margin thickness, and velocity larger than this will be considered a system anomaly.
/// @param max_vel Expected max velocity.
void SetMaxVelocity(float max_vel);
/// @brief Set the method this solver uses to derive current system velocity (for safety purposes in contact
/// detection).
/// @param insp_type A string. If "auto": the solver automatically derives.
void SetExpandSafetyType(const std::string& insp_type);
// void SetExpandSafetyType(const std::shared_ptr<DEMInspector>& insp) {
// m_max_v_finder_type = MARGIN_FINDER_TYPE::DEM_INSPECTOR;
// m_approx_max_vel_func = insp;
// }
/// Assign a multiplier to our estimated maximum system velocity, when deriving the thinckness of the contact
/// `safety' margin. This can be greater than one if the simulation velocity can increase significantly in one kT
/// update cycle, but this is not common and should be close to 1 in general.
void SetExpandSafetyMultiplier(float param) { m_expand_safety_multi = param; }
/// Set a `base' velocity, which we will always add to our estimated maximum system velocity, when deriving the
/// thinckness of the contact `safety' margin. This need not to be large unless the simulation velocity can increase
/// significantly in one kT update cycle.
void SetExpandSafetyAdder(float vel) { m_expand_base_vel = vel; }
/// @brief Used to force the solver to error out when there are too many spheres in a bin. A huge number can be used
/// to discourage this error type.
/// @param max_sph Max number of spheres in a bin.
void SetMaxSphereInBin(unsigned int max_sph) { threshold_too_many_spheres_in_bin = max_sph; }
/// @brief Used to force the solver to error out when there are too many spheres in a bin. A huge number can be used
/// to discourage this error type.
/// @param max_tri Max number of triangles in a bin.
void SetMaxTriangleInBin(unsigned int max_tri) { threshold_too_many_tri_in_bin = max_tri; }
/// @brief Set the velocity which when exceeded, the solver errors out. A huge number can be used to discourage this
/// error type. Defaulted to 5e4.
/// @param vel Error-out velocity.
void SetErrorOutVelocity(float vel) { threshold_error_out_vel = vel; }
/// @brief Set the average number of contacts a sphere has, before the solver errors out. A huge number can be used
/// to discourage this error type. Defaulted to 100.
/// @param num_cnts Error-out contact number.
void SetErrorOutAvgContacts(float num_cnts) { threshold_error_out_num_cnts = num_cnts; }
/// @brief Get the current number of contacts each sphere has.
/// @return Number of contacts.
float GetAvgSphContacts() const { return kT->stateParams.avgCntsPerSphere; }
/// @brief Enable or disable the use of adaptive bin size (by default it is on).
/// @param use Enable or disable.
void UseAdaptiveBinSize(bool use = true) { auto_adjust_bin_size = use; }
/// @brief Disable the use of adaptive bin size (always use initial size).
void DisableAdaptiveBinSize() { auto_adjust_bin_size = false; }
/// @brief Enable or disable the use of adaptive max update step count (by default it is on).
/// @param use Enable or disable.
void UseAdaptiveUpdateFreq(bool use = true) { auto_adjust_update_freq = use; }
/// @brief Disable the use of adaptive max update step count (always use initial update frequency).
void DisableAdaptiveUpdateFreq() { auto_adjust_update_freq = false; }
/// @brief Adjust how frequent kT updates the bin size.
/// @param n Number of contact detections before kT makes one adjustment to bin size.
void SetAdaptiveBinSizeDelaySteps(unsigned int n) {
if (n < NUM_STEPS_RESERVED_AFTER_CHANGING_BIN_SIZE)
DEME_WARNING(
"SetAdaptiveBinSizeDelaySteps is called with argument %u.\nThis is probably sub-optimal and may cause "
"kT to try adjusting bin size before enough knowledge is gained.",
n);
auto_adjust_observe_steps = (n >= 1) ? n : 1;
}
/// @brief Set the max rate that the bin size can change in one adjustment.
/// @param rate 0: never changes; 1: can double or halve size in one go; suggest using default.
void SetAdaptiveBinSizeMaxRate(float rate) { auto_adjust_max_rate = (rate > 0) ? rate : 0; }
/// @brief Set how fast kT changes the direction of bin size adjustmemt when there's a more beneficial direction.
/// @param acc 0.01: slowly change direction; 1: quickly change direction
void SetAdaptiveBinSizeAcc(float acc) { auto_adjust_acc = clampBetween(acc, 0.01, 1.0); }
/// @brief Set how proactive the solver is in avoiding the bin being too big (leading to too many geometries in a
/// bin).
/// @param ratio 0: not proavtive; 1: very proactive.
void SetAdaptiveBinSizeUpperProactivity(float ratio) {
auto_adjust_upper_proactive_ratio = clampBetween(ratio, 0.0, 1.0);
}
/// @brief Set how proactive the solver is in avoiding the bin being too small (leading to too many bins in domain).
/// @param ratio 0: not proavtive; 1: very proactive.
void SetAdaptiveBinSizeLowerProactivity(float ratio) {
auto_adjust_lower_proactive_ratio = clampBetween(ratio, 0.0, 1.0);
}
/// @brief Get the current bin (for contact detection) size. Must be called from synchronized stance.
/// @return Bin size.
double GetBinSize() { return kT->simParams->binSize; }
// NOTE: No need to get binSize from the device, as binSize is only changed on the host
/// @brief Get the current number of bins (for contact detection). Must be called from synchronized stance.
/// @return Number of bins.
size_t GetBinNum() { return kT->stateParams.numBins; }
/// @brief Set the upper bound of kT update frequency (when it is adjusted automatically).
/// @details This only affects when the update freq is updated automatically. To manually control the freq, use
/// SetCDUpdateFreq then call DisableAdaptiveUpdateFreq.
/// @param max_freq dT will not receive updates less frequently than 1 update per max_freq steps.
void SetCDMaxUpdateFreq(unsigned int max_freq) { upper_bound_future_drift = 2 * max_freq; }
/// @brief Set the number of steps dT configures its max drift more than average drift steps.
/// @param n Number of steps. Suggest using default.
void SetCDNumStepsMaxDriftAheadOfAvg(float n) { max_drift_ahead_of_avg_drift = n; }
/// @brief Set the multiplier which dT configures its max drift to be w.r.t. the average drift steps.
/// @param m The multiplier. Suggest using default.
void SetCDNumStepsMaxDriftMultipleOfAvg(float m) { max_drift_multiple_of_avg_drift = m; }
/// @brief Set the number of past kT updates that dT will use to calibrate the max future drift limit.
/// @param n Number of kT updates. Suggest using default.
void SetCDNumStepsMaxDriftHistorySize(unsigned int n);
/// @brief Get the current update frequency used by the solver.
/// @return The current update frequency.
float GetUpdateFreq() const;
/// Set the number of threads per block in force calculation (default 256).
void SetForceCalcThreadsPerBlock(unsigned int nTh) { dT->DT_FORCE_CALC_NTHREADS_PER_BLOCK = nTh; }
/// @brief Load a clump type into the API-level cache.
/// @return the shared ptr to the clump type just loaded.
std::shared_ptr<DEMClumpTemplate> LoadClumpType(float mass,
float3 moi,
const std::vector<float>& sp_radii,
const std::vector<float3>& sp_locations_xyz,
const std::vector<std::shared_ptr<DEMMaterial>>& sp_materials);
std::shared_ptr<DEMClumpTemplate> LoadClumpType(float mass,
const std::vector<float>& moi,
const std::vector<float>& sp_radii,
const std::vector<std::vector<float>>& sp_locations_xyz,
const std::vector<std::shared_ptr<DEMMaterial>>& sp_materials) {
assertThreeElements(moi, "LoadClumpType", "moi");
assertThreeElementsVector(sp_locations_xyz, "LoadClumpType", "sp_locations_xyz");
std::vector<float3> loc_xyz(sp_locations_xyz.size());
for (size_t i = 0; i < sp_locations_xyz.size(); i++) {
loc_xyz[i] = make_float3(sp_locations_xyz[i][0], sp_locations_xyz[i][1], sp_locations_xyz[i][2]);
}
return LoadClumpType(mass, make_float3(moi[0], moi[1], moi[2]), sp_radii, loc_xyz, sp_materials);
}
/// An overload of LoadClumpType where all components use the same material
std::shared_ptr<DEMClumpTemplate> LoadClumpType(float mass,
float3 moi,
const std::vector<float>& sp_radii,
const std::vector<float3>& sp_locations_xyz,
const std::shared_ptr<DEMMaterial>& sp_material);
std::shared_ptr<DEMClumpTemplate> LoadClumpType(float mass,
const std::vector<float>& moi,
const std::vector<float>& sp_radii,
const std::vector<std::vector<float>>& sp_locations_xyz,
const std::shared_ptr<DEMMaterial>& sp_material) {
assertThreeElements(moi, "LoadClumpType", "moi");
assertThreeElementsVector(sp_locations_xyz, "LoadClumpType", "sp_locations_xyz");
std::vector<float3> loc_xyz(sp_locations_xyz.size());
for (size_t i = 0; i < sp_locations_xyz.size(); i++) {
loc_xyz[i] = make_float3(sp_locations_xyz[i][0], sp_locations_xyz[i][1], sp_locations_xyz[i][2]);
}
return LoadClumpType(mass, make_float3(moi[0], moi[1], moi[2]), sp_radii, loc_xyz, sp_material);
}
/// An overload of LoadClumpType where the user builds the DEMClumpTemplate struct themselves then supply it
std::shared_ptr<DEMClumpTemplate> LoadClumpType(DEMClumpTemplate& clump);
/// An overload of LoadClumpType which loads sphere components from a file
std::shared_ptr<DEMClumpTemplate> LoadClumpType(float mass,
float3 moi,
const std::string filename,
const std::vector<std::shared_ptr<DEMMaterial>>& sp_materials);
std::shared_ptr<DEMClumpTemplate> LoadClumpType(float mass,
const std::vector<float>& moi,
const std::string filename,
const std::vector<std::shared_ptr<DEMMaterial>>& sp_materials) {
assertThreeElements(moi, "LoadClumpType", "moi");
return LoadClumpType(mass, make_float3(moi[0], moi[1], moi[2]), filename, sp_materials);
}
/// An overload of LoadClumpType which loads sphere components from a file and all components use the same material
std::shared_ptr<DEMClumpTemplate> LoadClumpType(float mass,
float3 moi,
const std::string filename,
const std::shared_ptr<DEMMaterial>& sp_material);
std::shared_ptr<DEMClumpTemplate> LoadClumpType(float mass,
const std::vector<float>& moi,
const std::string filename,
const std::shared_ptr<DEMMaterial>& sp_material) {
assertThreeElements(moi, "LoadClumpType", "moi");
return LoadClumpType(mass, make_float3(moi[0], moi[1], moi[2]), filename, sp_material);
}
/// A simplified version of LoadClumpType: it just loads a one-sphere clump template
std::shared_ptr<DEMClumpTemplate> LoadSphereType(float mass,
float radius,
const std::shared_ptr<DEMMaterial>& material);
/// @brief Load materials properties (Young's modulus, Poisson's ratio...) into the system.
/// @param mat_prop Property name--value pairs, as an unordered_map.
/// @return A shared pointer for this material.
std::shared_ptr<DEMMaterial> LoadMaterial(const std::unordered_map<std::string, float>& mat_prop);
/// @brief Load materials properties into the system.
/// @param a_material A DEMMaterial object.
/// @return A shared pointer for this material.
std::shared_ptr<DEMMaterial> LoadMaterial(DEMMaterial& a_material);
/// @brief Duplicate a material that is loaded into the system.
/// @param ptr Shared pointer for the object to duplicate.
/// @return A duplicate of the object (with effectively a deep copy).
std::shared_ptr<DEMMaterial> Duplicate(const std::shared_ptr<DEMMaterial>& ptr);
/// @brief Duplicate a clump template that is loaded into the system.
/// @param ptr Shared pointer for the object to duplicate.
/// @return A duplicate of the object (with effectively a deep copy).
std::shared_ptr<DEMClumpTemplate> Duplicate(const std::shared_ptr<DEMClumpTemplate>& ptr);
/// @brief Duplicate a batch of clumps that is loaded into the system.
/// @param ptr Shared pointer for the object to duplicate.
/// @return A duplicate of the object (with effectively a deep copy).
std::shared_ptr<DEMClumpBatch> Duplicate(const std::shared_ptr<DEMClumpBatch>& ptr);
/// @brief Set the value for a material property that by nature involves a pair of a materials (e.g. friction
/// coefficient).
/// @param name The name of this property (which should have already been referred to in a previous LoadMaterial
/// call).
/// @param mat1 Material 1 that is involved in this pair.
/// @param mat2 Material 2 that is involved in this pair.
/// @param val The value.
void SetMaterialPropertyPair(const std::string& name,
const std::shared_ptr<DEMMaterial>& mat1,
const std::shared_ptr<DEMMaterial>& mat2,
float val);
/// @brief Get the clumps that are in contact with this owner as a vector.
/// @details No multi-owner bulk version. This is due to efficiency concerns. If getting multiple owners' contacting
/// clumps is needed, use family-based GetContacts method, then the owner ID list-based c method if you further
/// need the contact forces information.
/// @param ownerID The ID of the owner that is being queried.
/// @return Clump owner IDs in contact with this owner.
std::vector<bodyID_t> GetOwnerContactClumps(bodyID_t ownerID) const;
/// Get position of n consecutive owners.
std::vector<float3> GetOwnerPosition(bodyID_t ownerID, bodyID_t n = 1) const;
/// Get angular velocity of n consecutive owners.
std::vector<float3> GetOwnerAngVel(bodyID_t ownerID, bodyID_t n = 1) const;
/// Get quaternion of n consecutive owners.
std::vector<float4> GetOwnerOriQ(bodyID_t ownerID, bodyID_t n = 1) const;
/// Get velocity of n consecutive owners.
std::vector<float3> GetOwnerVelocity(bodyID_t ownerID, bodyID_t n = 1) const;
/// Get the acceleration of n consecutive owners.
std::vector<float3> GetOwnerAcc(bodyID_t ownerID, bodyID_t n = 1) const;
/// Get the angular acceleration of n consecutive owners.
std::vector<float3> GetOwnerAngAcc(bodyID_t ownerID, bodyID_t n = 1) const;
/// @brief Get the family number of n consecutive owners.
/// @param ownerID First owner's ID.
/// @param n The number of consecutive owners.
/// @return The family number.
std::vector<unsigned int> GetOwnerFamily(bodyID_t ownerID, bodyID_t n = 1) const;
/// @brief Get the mass of n consecutive owners.
/// @param ownerID First owner's ID.
/// @param n The number of consecutive owners.
/// @return The mass.
std::vector<float> GetOwnerMass(bodyID_t ownerID, bodyID_t n = 1) const;
/// @brief Get the moment of inertia (in principal axis frame) of n consecutive owners.
/// @param ownerID First owner's ID.
/// @param n The number of consecutive owners.
/// @return The moment of inertia (in principal axis frame).
std::vector<float3> GetOwnerMOI(bodyID_t ownerID, bodyID_t n = 1) const;
/// @brief Set position of consecutive owners starting from ownerID, based on input position vector. N (the size of
/// the input vector) elements will be modified.
void SetOwnerPosition(bodyID_t ownerID, const std::vector<float3>& pos);
/// Set angular velocity of consecutive owners starting from ownerID, based on input angular velocity vector. N (the
/// size of the input vector) elements will be modified.
void SetOwnerAngVel(bodyID_t ownerID, const std::vector<float3>& angVel);
/// Set velocity of consecutive owners starting from ownerID, based on input velocity vector. N (the size of the
/// input vector) elements will be modified.
void SetOwnerVelocity(bodyID_t ownerID, const std::vector<float3>& vel);
/// Set quaternion of consecutive owners starting from ownerID, based on input quaternion vector. N (the size of the
/// input vector) elements will be modified.
void SetOwnerOriQ(bodyID_t ownerID, const std::vector<float4>& oriQ);
/// @brief Set the family number of consecutive owners.
/// @param ownerID The ID of the owner.
/// @param fam Family number.
/// @param n Number of consecutive owners.
void SetOwnerFamily(bodyID_t ownerID, unsigned int fam, bodyID_t n = 1);
/// @brief Add an extra accelerations to consecutive owners for the next time step.
/// @param ownerID The number of the starting owner.
/// @param acc The extra acceleration to add. N (the size of this vector) elements will be modified based on its
/// values.
void AddOwnerNextStepAcc(bodyID_t ownerID, const std::vector<float3>& acc);
/// @brief Add an extra angular accelerations to consecutive owners for the next time step.
/// @param ownerID The number of the starting owner.
/// @param acc The extra angular acceleration to add. N (the size of this vector) elements will be modified based on
/// its values.
void AddOwnerNextStepAngAcc(bodyID_t ownerID, const std::vector<float3>& angAcc);
/// @brief Rewrite the relative positions of the flattened triangle soup.
void SetTriNodeRelPos(size_t owner, size_t triID, const std::vector<float3>& new_nodes);
/// @brief Update the relative positions of the flattened triangle soup.
void UpdateTriNodeRelPos(size_t owner, size_t triID, const std::vector<float3>& updates);
/// @brief Get a handle for the mesh this tracker is tracking.
/// @return Pointer to the mesh.
std::shared_ptr<DEMMeshConnected>& GetCachedMesh(bodyID_t ownerID);
/// @brief Get the current locations of all the nodes in the mesh being tracked.
/// @param ownerID The ownerID of the mesh.
/// @return A vector of float3 representing the global coordinates of the mesh nodes.
std::vector<float3> GetMeshNodesGlobal(bodyID_t ownerID);
/// @brief Get all clump--clump contact ID pairs in the simulation system. Note all GetContact-like methods reports
/// potential contacts (not necessarily confirmed contacts), meaning they are similar to what
/// WriteContactFileIncludingPotentialPairs does, not what WriteContactFile does.
/// @details Do not call this method with high frequency, as it is not efficient.
/// @return A sorted (based on contact body A's owner ID) vector of contact pairs. First is the owner ID of contact
/// body A, and Second is that of contact body B.
std::vector<std::pair<bodyID_t, bodyID_t>> GetClumpContacts() const;
/// @brief Get all clump--clump contact ID pairs in the simulation system. Note all GetContact-like methods reports
/// potential contacts (not necessarily confirmed contacts), meaning they are similar to what
/// WriteContactFileIncludingPotentialPairs does, not what WriteContactFile does.
/// @details Do not call this method with high frequency, as it is not efficient.
/// @param family_to_include Contacts that involve a body in a family not listed in this argument are ignored.
/// @return A sorted (based on contact body A's owner ID) vector of contact pairs. First is the owner ID of contact
/// body A, and Second is that of contact body B.
std::vector<std::pair<bodyID_t, bodyID_t>> GetClumpContacts(const std::set<family_t>& family_to_include) const;
/// @brief Get all clump--clump contact ID pairs in the simulation system. Note all GetContact-like methods reports
/// potential contacts (not necessarily confirmed contacts), meaning they are similar to what
/// WriteContactFileIncludingPotentialPairs does, not what WriteContactFile does.
/// @details Do not call this method with high frequency, as it is not efficient.
/// @param family_pair Functions returns a vector of contact body family number pairs. First is the family number of
/// contact body A, and Second is that of contact body B.
/// @return A sorted (based on contact body A's owner ID) vector of contact pairs. First is the owner ID of contact
/// body A, and Second is that of contact body B.
std::vector<std::pair<bodyID_t, bodyID_t>> GetClumpContacts(
std::vector<std::pair<family_t, family_t>>& family_pair) const;
/// @brief Get all contact ID pairs in the simulation system. Note all GetContact-like methods reports potential
/// contacts (not necessarily confirmed contacts), meaning they are similar to what
/// WriteContactFileIncludingPotentialPairs does, not what WriteContactFile does.
/// @details Do not call this method with high frequency, as it is not efficient.
/// @return A sorted (based on contact body A's owner ID) vector of contact pairs. First is the owner ID of contact
/// body A, and Second is that of contact body B.
std::vector<std::pair<bodyID_t, bodyID_t>> GetContacts() const;
/// @brief Get all contact ID pairs in the simulation system. Note all GetContact-like methods reports potential
/// contacts (not necessarily confirmed contacts), meaning they are similar to what
/// WriteContactFileIncludingPotentialPairs does, not what WriteContactFile does.
/// @details Do not call this method with high frequency, as it is not efficient.
/// @param family_to_include Contacts that involve a body in a family not listed in this argument are ignored.
/// @return A sorted (based on contact body A's owner ID) vector of contact pairs. First is the owner ID of contact
/// body A, and Second is that of contact body B.
std::vector<std::pair<bodyID_t, bodyID_t>> GetContacts(const std::set<family_t>& family_to_include) const;
/// @brief Get all contact ID pairs in the simulation system. Note all GetContact-like methods reports potential
/// contacts (not necessarily confirmed contacts), meaning they are similar to what
/// WriteContactFileIncludingPotentialPairs does, not what WriteContactFile does.
/// @details Do not call this method with high frequency, as it is not efficient.
/// @param family_pair Functions returns a vector of contact body family number pairs. First is the family number of
/// contact body A, and Second is that of contact body B.
/// @return A sorted (based on contact body A's owner ID) vector of contact pairs. First is the owner ID of contact
/// body A, and Second is that of contact body B.
std::vector<std::pair<bodyID_t, bodyID_t>> GetContacts(
std::vector<std::pair<family_t, family_t>>& family_pair) const;
/// @brief Get all contact pairs' detailed information (actual content based on the setting with
/// SetContactOutputContent; default are owner IDs, contact point location, contact force, and associated wildcard
/// values) in the simulation system. Note all GetContact-like methods reports potential contacts (not necessarily
/// confirmed contacts), meaning they are similar to what WriteContactFileIncludingPotentialPairs does, not what
/// WriteContactFile does.
/// @details Do not call this method with high frequency, as it is not efficient.
/// @param force_thres Only contacts with force larger than this value are returned. Setting it to a small positive
/// number to, instead of getting all potential contacts, only get the ones that are currently confirmed to generate
/// force.
/// @return A map that may have the following keys: "ContactType", "Point", "AOwner", "BOwner", "AOwnerFamily",
/// "BOwnerFamily", "Force", "Torque", "Normal" and wildcard names, each corresponding to a vector of values. The
/// "ContactType" is a vector of strings, each indicating the type of contact (e.g., "sphere-sphere",
/// "sphere-triangle", etc.). The "Point" is a vector of float3s, each indicating the contact point location in
/// global coordinates. The "AOwner" and "BOwner" are vectors of body IDs for the two bodies in contact. The "Force"
/// is a vector of float3s, each indicating the contact force at the contact point. The "Torque" is a vector of
/// float3s, each indicating the torque at the contact point. The "Normal" is a vector of float3s, each indicating
/// the normal direction at the contact point.
std::shared_ptr<ContactInfoContainer> GetContactDetailedInfo(float force_thres = -1.0) const;
/// @brief Get the host memory usage (in bytes) on dT.
/// @return Number of bytes.
size_t GetHostMemUsageDynamic() const { return dT->estimateHostMemUsage(); }
/// @brief Get the device memory usage (in bytes) on dT.
/// @return Number of bytes.
size_t GetDeviceMemUsageDynamic() const { return dT->estimateDeviceMemUsage(); }
/// @brief Get the host memory usage (in bytes) on kT.
/// @return Number of bytes.
size_t GetHostMemUsageKinematic() const { return kT->estimateHostMemUsage(); }
/// @brief Get the device memory usage (in bytes) on kT.
/// @return Number of bytes.
size_t GetDeviceMemUsageKinematic() const { return kT->estimateDeviceMemUsage(); }
/// @brief Print the current memory usage in pretty format.
void ShowMemStats() const;
/// Load input clumps (topology types and initial locations) on a per-pair basis. Note that the initial location
/// means the location of the clumps' CoM coordinates in the global frame.
std::shared_ptr<DEMClumpBatch> AddClumps(DEMClumpBatch& input_batch);
/// @brief Load clumps into the simulation.
/// @param input_types Vector of the types of the clumps (vector of shared pointers).
/// @param input_xyz Vector of the initial locations of the clumps.
/// @return Handle to the loaded batch of clumps.
std::shared_ptr<DEMClumpBatch> AddClumps(const std::vector<std::shared_ptr<DEMClumpTemplate>>& input_types,
const std::vector<float3>& input_xyz);
std::shared_ptr<DEMClumpBatch> AddClumps(const std::vector<std::shared_ptr<DEMClumpTemplate>>& input_types,
const std::vector<std::vector<float>>& input_xyz) {
assertThreeElementsVector(input_xyz, "AddClumps", "input_xyz");
std::vector<float3> loc_xyz(input_xyz.size());
for (size_t i = 0; i < input_xyz.size(); i++) {
loc_xyz[i] = make_float3(input_xyz[i][0], input_xyz[i][1], input_xyz[i][2]);
}
return AddClumps(input_types, loc_xyz);
}
/// @brief Load a clump into the simulation.
/// @param input_type The type (shared pointer pointing to the clump type handle).
/// @param input_xyz Initial location of the clump.
/// @return Handle to the clump.
std::shared_ptr<DEMClumpBatch> AddClumps(std::shared_ptr<DEMClumpTemplate>& input_type, float3 input_xyz) {
return AddClumps(std::vector<std::shared_ptr<DEMClumpTemplate>>(1, input_type),
std::vector<float3>(1, input_xyz));
}
std::shared_ptr<DEMClumpBatch> AddClumps(std::shared_ptr<DEMClumpTemplate>& input_type,
const std::vector<float>& input_xyz) {
assertThreeElements(input_xyz, "AddClumps", "input_xyz");
return AddClumps(input_type, make_float3(input_xyz[0], input_xyz[1], input_xyz[2]));
}
/// @brief Load clumps (of the same template) into the simulation.
/// @param input_types The type (shared pointer pointing to the clump type handle).
/// @param input_xyz Vector of the initial locations of the clumps.
/// @return Handle to the loaded batch of clumps.
std::shared_ptr<DEMClumpBatch> AddClumps(std::shared_ptr<DEMClumpTemplate>& input_type,
const std::vector<float3>& input_xyz) {
return AddClumps(std::vector<std::shared_ptr<DEMClumpTemplate>>(input_xyz.size(), input_type), input_xyz);
}
std::shared_ptr<DEMClumpBatch> AddClumps(std::shared_ptr<DEMClumpTemplate>& input_type,
const std::vector<std::vector<float>>& input_xyz) {
assertThreeElementsVector(input_xyz, "AddClumps", "input_xyz");
std::vector<float3> loc_xyz(input_xyz.size());
for (size_t i = 0; i < input_xyz.size(); i++) {
loc_xyz[i] = make_float3(input_xyz[i][0], input_xyz[i][1], input_xyz[i][2]);
}
return AddClumps(input_type, loc_xyz);
}
/// Load a mesh-represented object
std::shared_ptr<DEMMeshConnected> AddWavefrontMeshObject(const std::string& filename,
const std::shared_ptr<DEMMaterial>& mat,
bool load_normals = true,
bool load_uv = false);
std::shared_ptr<DEMMeshConnected> AddWavefrontMeshObject(const std::string& filename,
bool load_normals = true,
bool load_uv = false);
std::shared_ptr<DEMMeshConnected> AddWavefrontMeshObject(DEMMeshConnected& mesh);
/// @brief Create a DEMTracker to allow direct control/modification/query to this external object/batch of
/// clumps/triangle mesh object.
/// @details By default, it refers to the first clump in this batch. The user can refer to other clumps in this
/// batch by supplying an offset when using this tracker's querying or assignment methods.
template <typename T>
std::shared_ptr<DEMTracker> Track(const std::shared_ptr<T>& obj) {
// Create a middle man: DEMTrackedObj. The reason we use it is because a simple struct should be used to
// transfer to dT for owner-number processing. If we cut the middle man and use things such as DEMExtObj, there
// will not be a universal treatment that dT can apply, besides we may have some include-related issues.
DEMTrackedObj tracked_obj;
tracked_obj.load_order = obj->load_order;
tracked_obj.obj_type = obj->obj_type;
m_tracked_objs.push_back(std::make_shared<DEMTrackedObj>(std::move(tracked_obj)));
// Create a Tracker for this tracked object
DEMTracker tracker(this);
tracker.obj = m_tracked_objs.back();
return std::make_shared<DEMTracker>(std::move(tracker));
}
/// @brief Create a DEMTracker to allow direct control/modification/query to this external object/batch of
/// clumps/triangle mesh object.
/// @details C++ users do not have to use this method. Using Track is enough. This method is for Python wrapper.
std::shared_ptr<DEMTracker> PythonTrack(const std::shared_ptr<DEMInitializer>& obj) {
return Track<DEMInitializer>(obj);
}
/// Create a inspector object that can help query some statistical info of the clumps in the simulation
std::shared_ptr<DEMInspector> CreateInspector(const std::string& quantity = "clump_max_z");
std::shared_ptr<DEMInspector> CreateInspector(const std::string& quantity, const std::string& region);
/// Instruct the solver that the 2 input families should not have contacts (a.k.a. ignored, if such a pair is
/// encountered in contact detection). These 2 families can be the same (which means no contact within members of
/// that family).
void DisableContactBetweenFamilies(unsigned int ID1, unsigned int ID2);
/// Re-enable contact between 2 families after the system is initialized.
void EnableContactBetweenFamilies(unsigned int ID1, unsigned int ID2);
/// Prevent entites associated with this family to be outputted to files.
void DisableFamilyOutput(unsigned int ID);
/// Mark all entities in this family to be fixed.
void SetFamilyFixed(unsigned int ID);
/// If dictate is set to true, then
/// @brief Set the prescribed linear velocity to all entities in a family.
/// @param ID Family number.
/// @param velX X component of velocity.
/// @param velY Y component of velocity.
/// @param velZ Z component of velocity.
/// @param dictate If true, this family will not be influenced by the force exerted from other simulation entites
/// (both linear and rotational motions); if false, only specified components (that is, not specified with "none")
/// will not be influenced by the force exerted from other simulation entites.
/// @param pre Prerequisite code. For example, you can generate a float3 with this prerequisite code, then assign
/// XYZ components based on this float3.
void SetFamilyPrescribedLinVel(unsigned int ID,
const std::string& velX,
const std::string& velY,
const std::string& velZ,
bool dictate = true,
const std::string& pre = "none");
/// Let the linear velocities of all entites in this family always keep `as is', and not influenced by the force
/// exerted from other simulation entites.
void SetFamilyPrescribedLinVel(unsigned int ID);
/// Let the X component of the linear velocities of all entites in this family always keep `as is', and not
/// influenced by the force exerted from other simulation entites.
void SetFamilyPrescribedLinVelX(unsigned int ID);
/// Let the Y component of the linear velocities of all entites in this family always keep `as is', and not
/// influenced by the force exerted from other simulation entites.
void SetFamilyPrescribedLinVelY(unsigned int ID);
/// Let the Z component of the linear velocities of all entites in this family always keep `as is', and not
/// influenced by the force exerted from other simulation entites.
void SetFamilyPrescribedLinVelZ(unsigned int ID);
/// @brief Set the prescribed angular velocity to all entities in a family.
/// @param ID Family number.
/// @param velX X component of angular velocity.
/// @param velY Y component of angular velocity.
/// @param velZ Z component of angular velocity.
/// @param dictate If true, this family will not be influenced by the force exerted from other simulation entites
/// (both linear and rotational motions); if false, only specified components (that is, not specified with "none")
/// will not be influenced by the force exerted from other simulation entites.
/// @param pre Prerequisite code. For example, you can generate a float3 with this prerequisite code, then assign
/// XYZ components based on this float3.
void SetFamilyPrescribedAngVel(unsigned int ID,
const std::string& velX,
const std::string& velY,
const std::string& velZ,
bool dictate = true,
const std::string& pre = "none");
/// Let the linear velocities of all entites in this family always keep `as is', and not influenced by the force
/// exerted from other simulation entites.
void SetFamilyPrescribedAngVel(unsigned int ID);
/// Let the X component of the angular velocities of all entites in this family always keep `as is', and not
/// influenced by the force exerted from other simulation entites.
void SetFamilyPrescribedAngVelX(unsigned int ID);
/// Let the Y component of the angular velocities of all entites in this family always keep `as is', and not
/// influenced by the force exerted from other simulation entites.
void SetFamilyPrescribedAngVelY(unsigned int ID);
/// Let the Z component of the angular velocities of all entites in this family always keep `as is', and not
/// influenced by the force exerted from other simulation entites.
void SetFamilyPrescribedAngVelZ(unsigned int ID);
/// @brief Keep the positions of all entites in this family to remain exactly the user-specified values.
/// @param ID Family number.
/// @param X X coordinate (can be an expression).
/// @param Y Y coordinate (can be an expression).
/// @param Z Z coordinate (can be an expression).
/// @param dictate If true, prevent entities in this family to have (both linear and rotational) positional updates
/// resulted from the `simulation physics'; if false, only specified components (that is, not specified with "none")
/// will not be influenced by the force exerted from other simulation entites.
/// @param pre Prerequisite code. For example, you can generate a float3 with this prerequisite code, then assign
/// XYZ components based on this float3.
void SetFamilyPrescribedPosition(unsigned int ID,
const std::string& X,
const std::string& Y,
const std::string& Z,
bool dictate = true,
const std::string& pre = "none");
/// @brief Let the linear positions of all entites in this family always keep `as is'.
void SetFamilyPrescribedPosition(unsigned int ID);
/// @brief Let the X component of the linear positions of all entites in this family always keep `as is'.
void SetFamilyPrescribedPositionX(unsigned int ID);
/// @brief Let the Y component of the linear positions of all entites in this family always keep `as is'.
void SetFamilyPrescribedPositionY(unsigned int ID);
/// @brief Let the Z component of the linear positions of all entites in this family always keep `as is'.
void SetFamilyPrescribedPositionZ(unsigned int ID);
/// @brief Keep the orientation quaternions of all entites in this family to remain exactly the user-specified
/// values.
/// @param ID Family number.
/// @param q_formula The code from which the quaternion should be calculated. Must `return' a float4. For example,
/// "float tmp=make_float4(1,1,1,1); return tmp;".
/// @param dictate If true, prevent entities in this family to have (both linear and rotational) positional updates
/// resulted from the `simulation physics'; otherwise, the `simulation physics' still takes effect.
void SetFamilyPrescribedQuaternion(unsigned int ID, const std::string& q_formula, bool dictate = true);
/// @brief Let the orientation quaternions of all entites in this family always keep `as is'.
void SetFamilyPrescribedQuaternion(unsigned int ID);
/// @brief The entities in this family will always experience an extra acceleration defined using this method.
/// @param pre Prerequisite code. For example, you can generate a float3 with this prerequisite code, then assign
/// XYZ components based on this float3.
void AddFamilyPrescribedAcc(unsigned int ID,
const std::string& X,
const std::string& Y,
const std::string& Z,
const std::string& pre = "none");
/// @brief The entities in this family will always experience an extra angular acceleration defined using this
/// method.
/// @param pre Prerequisite code. For example, you can generate a float3 with this prerequisite code, then assign
/// XYZ components based on this float3.
void AddFamilyPrescribedAngAcc(unsigned int ID,
const std::string& X,
const std::string& Y,
const std::string& Z,
const std::string& pre = "none");
/// @brief The entities in this family will always experience an added linear-velocity correction defined using this
/// method. At the same time, they are still subject to the `simulation physics'.
/// @param pre Prerequisite code. For example, you can generate a float3 with this prerequisite code, then assign
/// XYZ components based on this float3.
void CorrectFamilyLinVel(unsigned int ID,
const std::string& X,
const std::string& Y,
const std::string& Z,
const std::string& pre = "none");
/// @brief The entities in this family will always experience an added angular-velocity correction defined using
/// this method. At the same time, they are still subject to the `simulation physics'.
/// @param pre Prerequisite code. For example, you can generate a float3 with this prerequisite code, then assign
/// XYZ components based on this float3.
void CorrectFamilyAngVel(unsigned int ID,
const std::string& X,
const std::string& Y,
const std::string& Z,
const std::string& pre = "none");
/// @brief The entities in this family will always experience an added positional correction defined using this
/// method. At the same time, they are still subject to the `simulation physics'.
/// @param pre Prerequisite code. For example, you can generate a float3 with this prerequisite code, then assign
/// XYZ components based on this float3.
void CorrectFamilyPosition(unsigned int ID,
const std::string& X,
const std::string& Y,
const std::string& Z,
const std::string& pre = "none");
/// @brief The entities in this family will always experience an added quaternion correction defined using this
/// method. At the same time, they are still subject to the `simulation physics'.
/// @param q_formula The code from which the quaternion should be calculated. Must `return' a float4. For example,
/// "float tmp=make_float4(1,1,1,1); return tmp;".
void CorrectFamilyQuaternion(unsigned int ID, const std::string& q_formula);
/// @brief Set the names for the extra quantities that will be associated with each contact pair.
void SetContactWildcards(const std::set<std::string>& wildcards);
/// @brief Set the names for the extra quantities that will be associated with each owner.
void SetOwnerWildcards(const std::set<std::string>& wildcards);
/// @brief Set the names for the extra quantities that will be associated with each geometry entity (such as sphere,
/// triangle).
void SetGeometryWildcards(const std::set<std::string>& wildcards);
/// @brief Change the value of contact wildcards to val if either of the contact geometries is in family N.
/// @param N Family number. If one contact geometry is in N, this contact wildcard is modified.
/// @param name Name of the contact wildcard to modify.
/// @param val The value to change to.
void SetFamilyContactWildcardValueEither(unsigned int N, const std::string& name, float val);
/// @brief Change the value of contact wildcards to val if both of the contact geometries are in family N.
/// @param N Family number. Only if both contact geometries are in N, this contact wildcard is modified.
/// @param name Name of the contact wildcard to modify.
/// @param val The value to change to.
void SetFamilyContactWildcardValueBoth(unsigned int N, const std::string& name, float val);
/// @brief Change the value of contact wildcards to val if one of the contact geometry is in family N1, and the
/// other is in N2.
/// @param N1 First family number.
/// @param N2 Second family number.
/// @param name Name of the contact wildcard to modify.
/// @param val The value to change to.
void SetFamilyContactWildcardValue(unsigned int N1, unsigned int N2, const std::string& name, float val);
/// @brief Change the value of contact wildcards to val. Apply to all simulation bodies that are present.
/// @param name Name of the contact wildcard to modify.
/// @param val The value to change to.
void SetContactWildcardValue(const std::string& name, float val);
/// @brief Make it so that for any currently-existing contact, if one of its contact geometries is in family N, then
/// this contact will never be removed.
/// @details This contact might be created through contact detection, or being a contact the user manually loaded at
/// the start of simulation. Note this is a one-time assignment and will not continuously mark future emerging
/// contact to be persistent.
/// @param N Family number.
void MarkFamilyPersistentContactEither(unsigned int N);
/// @brief Make it so that for any currently-existing contact, if both of its contact geometries are in family N,
/// then this contact will never be removed.
/// @details This contact might be created through contact detection, or being a contact the user manually loaded at
/// the start of simulation. Note this is a one-time assignment and will not continuously mark future emerging
/// contact to be persistent.
/// @param N Family number.
void MarkFamilyPersistentContactBoth(unsigned int N);
/// @brief Make it so that if for any currently-existing contact, if its two contact geometries are in family N1 and
/// N2 respectively, this contact will never be removed.
/// @details This contact might be created through contact detection, or being a contact the user manually loaded at
/// the start of simulation. Note this is a one-time assignment and will not continuously mark future emerging
/// contact to be persistent.
/// @param N1 Family number 1.
/// @param N2 Family number 2.
void MarkFamilyPersistentContact(unsigned int N1, unsigned int N2);
/// @brief Make it so that all currently-existing contacts in this simulation will never be removed.
/// @details The contacts might be created through contact detection, or being manually loaded at the start of
/// simulation. Note this is a one-time assignment and will not continuously mark future emerging contact to be
/// persistent.
void MarkPersistentContact();
/// @brief Cancel contact persistence qualification. Work like the inverse of MarkFamilyPersistentContactEither.
void RemoveFamilyPersistentContactEither(unsigned int N);
/// @brief Cancel contact persistence qualification. Work like the inverse of MarkFamilyPersistentContactBoth.
void RemoveFamilyPersistentContactBoth(unsigned int N);
/// @brief Cancel contact persistence qualification. Work like the inverse of MarkFamilyPersistentContact.
void RemoveFamilyPersistentContact(unsigned int N1, unsigned int N2);
/// @brief Cancel contact persistence qualification. Work like the inverse of MarkPersistentContact.
void RemovePersistentContact();
/// @brief Get all contact forces that concern a list of owners.
/// @param ownerIDs The IDs of the owners.
/// @param points Fill this vector of float3 with the XYZ components of the contact points.
/// @param forces Fill this vector of float3 with the XYZ components of the forces.
/// @return Number of force pairs.
size_t GetOwnerContactForces(const std::vector<bodyID_t>& ownerIDs,
std::vector<float3>& points,
std::vector<float3>& forces);
/// @brief Get all contact forces that concern a list of owners.
/// @details If a contact involves at least one of the owner IDs provided as the first arg this method, it will be
/// outputted. Note if a contact involves two IDs of the user-provided list, then the force for that contact will be
/// given as the force experienced by whichever owner that appears earlier in the ID list.
/// @param ownerIDs The IDs of the owners.
/// @param points Fill this vector of float3 with the XYZ components of the contact points.
/// @param forces Fill this vector of float3 with the XYZ components of the forces.
/// @param torques Fill this vector of float3 with the XYZ components of the torques (in local frame).
/// @param torque_in_local If true, output torque in this body's local ref frame.
/// @return Number of force pairs.
size_t GetOwnerContactForces(const std::vector<bodyID_t>& ownerIDs,
std::vector<float3>& points,
std::vector<float3>& forces,
std::vector<float3>& torques,
bool torque_in_local = false);
/// @brief Set the wildcard values of some triangles.
/// @param geoID The ID of the starting (first) triangle that needs to be modified.
/// @param name The name of the wildcard.
/// @param vals A vector of values that will be assigned to the triangles starting from geoID.
void SetTriWildcardValue(bodyID_t geoID, const std::string& name, const std::vector<float>& vals);
/// @brief Set the wildcard values of some spheres.
/// @param geoID The ID of the starting (first) sphere that needs to be modified.
/// @param name The name of the wildcard.
/// @param vals A vector of values that will be assigned to the spheres starting from geoID.
void SetSphereWildcardValue(bodyID_t geoID, const std::string& name, const std::vector<float>& vals);
/// @brief Set the wildcard values of some analytical components.
/// @param geoID The ID of the starting (first) analytical component that needs to be modified.
/// @param name The name of the wildcard.
/// @param vals A vector of values that will be assigned to the analytical components starting from geoID.
void SetAnalWildcardValue(bodyID_t geoID, const std::string& name, const std::vector<float>& vals);
/// @brief Set the wildcard values of some owners.
/// @param ownerID The ID of the starting (first) owner that needs to be modified.
/// @param name The name of the wildcard.
/// @param vals A vector of values that will be assigned to the owners starting from ownerID.
void SetOwnerWildcardValue(bodyID_t ownerID, const std::string& name, const std::vector<float>& vals);
/// @brief Set the wildcard values of some owners.
/// @param ownerID The ID of the starting (first) owner that needs to be modified.
/// @param name The name of the wildcard.
/// @param val The value to set.
/// @param n The number of owners starting from the first that will be modified by this call. Default is 1.
void SetOwnerWildcardValue(bodyID_t ownerID, const std::string& name, float val, size_t n = 1) {
SetOwnerWildcardValue(ownerID, name, std::vector<float>(n, val));
}
/// Modify the owner wildcard's values of all entities in family N
void SetFamilyOwnerWildcardValue(unsigned int N, const std::string& name, const std::vector<float>& vals);
void SetFamilyOwnerWildcardValue(unsigned int N, const std::string& name, float val) {
SetFamilyOwnerWildcardValue(N, name, std::vector<float>(1, val));
}
/// @brief Set all clumps in this family to have this material.
/// @param N Family number.
/// @param mat Material type.
void SetFamilyClumpMaterial(unsigned int N, const std::shared_ptr<DEMMaterial>& mat);
/// @brief Set all meshes in this family to have this material.
/// @param N Family number.
/// @param mat Material type.
void SetFamilyMeshMaterial(unsigned int N, const std::shared_ptr<DEMMaterial>& mat);
/// @brief Add an extra contact margin to entities in a family so they are registered as potential contact pairs
/// earlier.
/// @details You typically need this method when the custom force model contains non-contact forces. The solver
/// needs this extra margin to preemptively registered contact pairs. If the extra margin is not added, then the
/// contact pair will not be computed until entities are in physical contact. Note this margin should be as small as
/// needed, since it potentially increases the total number of contact pairs greatly.
/// @param N Family number.
/// @param extra_size The thickness of the extra contact margin.
void SetFamilyExtraMargin(unsigned int N, float extra_size);
/// @brief Get the owner wildcard's values of some owners.
/// @param ownerID Starting owner's ID.
/// @param name Wildcard's name.
/// @param n Total number of owners to query, starting from ownerID.
/// @return Value of this wildcard.
std::vector<float> GetOwnerWildcardValue(bodyID_t ownerID, const std::string& name, bodyID_t n = 1);
/// @brief Get the owner wildcard's values of all entities.
std::vector<float> GetAllOwnerWildcardValue(const std::string& name);
/// @brief Get the owner wildcard's values of all entities in family N.
std::vector<float> GetFamilyOwnerWildcardValue(unsigned int N, const std::string& name);
/// @brief Get the geometry wildcard's values of a series of triangles.
/// @param geoID The ID of the first triangle.
/// @param name Wildcard's name.
/// @param n The number of triangles to query following the ID of the first one.