Skip to content

Commit 11d76ab

Browse files
authored
Merge pull request #45 from uwsbel/Mesh_Particles
Mesh particles performance update
2 parents 99f00dd + eb8f71a commit 11d76ab

68 files changed

Lines changed: 4701 additions & 1578 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmake/CudaSupportedArchitectures.cmake

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ function(cuda_supported_architectures)
3131
set(cu10 30 35 50 52 60 61 70 72 75)
3232
set(cu11 35 50 52 60 61 70 72 75 80)
3333
set(cu11_x 35 50 52 60 61 70 72 75 80 86)
34-
set(cu12_x 50 52 60 61 70 72 75 80 86)
34+
set(cu12_x 50 52 60 61 70 72 75 80 86 89 120)
35+
set(cu13_x 75 80 86 89 90 100 120 121)
3536

3637
if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 7)
3738
set(CUDASUP_ARCHITECTURES ${cu7} CACHE INTERNAL "")
@@ -60,6 +61,10 @@ function(cuda_supported_architectures)
6061
if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 12)
6162
set(CUDASUP_ARCHITECTURES ${cu12_x} CACHE INTERNAL "")
6263
endif()
64+
65+
if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 13)
66+
set(CUDASUP_ARCHITECTURES ${cu13_x} CACHE INTERNAL "")
67+
endif()
6368

6469
if (NOT DEFINED CUDASUP_ARCHITECTURES)
6570
message(SEND_ERROR "[CUDASUP] Could not determine device architectures supported by the CUDA toolkit!")

src/DEM/API.h

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class DEMSolver {
108108
/// Set the simulation time manually.
109109
void SetSimTime(double time);
110110
/// @brief Set the strategy for auto-adapting time step size (NOT implemented, no effect yet).
111-
/// @param type "none" or "max_vel" or "int_diff".
111+
/// @param type "none" or "hertz_const" or "max_vel" or "int_diff".
112112
void SetAdaptiveTimeStepType(const std::string& type);
113113

114114
/// @brief Set the time integrator for this simulator.
@@ -222,6 +222,10 @@ class DEMSolver {
222222
/// thinckness of the contact `safety' margin. This need not to be large unless the simulation velocity can increase
223223
/// significantly in one kT update cycle.
224224
void SetExpandSafetyAdder(float vel) { m_expand_base_vel = vel; }
225+
/// @brief Control whether angular velocity contributes to the contact detection margin.
226+
/// @details Default is auto: false for pure single-sphere clumps; true when multi-sphere clumps or meshes exist.
227+
/// If disabled, sphere--sphere contact uses only linear velocity when computing damping/friction terms.
228+
void SetUseAngularVelocityMargin(bool use);
225229

226230
/// @brief Manually set the current triangle--triangle penetration value in dT.
227231
/// @details This allows the user to directly control the maxTriTriPenetration value which will ONLY be used in the
@@ -325,7 +329,7 @@ class DEMSolver {
325329
}
326330
/// @brief Get the current bin (for contact detection) size. Must be called from synchronized stance.
327331
/// @return Bin size.
328-
double GetBinSize() { return kT->simParams->binSize; }
332+
double GetBinSize() { return kT->simParams->dyn.binSize; }
329333
// NOTE: No need to get binSize from the device, as binSize is only changed on the host
330334

331335
/// @brief Get the current number of bins (for contact detection). Must be called from synchronized stance.
@@ -346,6 +350,19 @@ class DEMSolver {
346350
/// @brief Set the number of past kT updates that dT will use to calibrate the max future drift limit.
347351
/// @param n Number of kT updates. Suggest using default.
348352
void SetCDNumStepsMaxDriftHistorySize(unsigned int n);
353+
/// @brief Inflate observed kT lag (in units of dT steps) by this factor, for drift scheduling/calibration only.
354+
/// @details Values < 1 can be unsafe. Default is 1.1.
355+
void SetCDFutureDriftEffDriftSafetyFactor(float factor) {
356+
future_drift_eff_drift_safety_factor = clampBetween(factor, 1.0f, 10.0f);
357+
}
358+
/// @brief Set bounds (as a fraction of max drift) that clamp dT's waiting behavior before sending a new kT work order.
359+
/// @details Default is lower=0, upper=1 (send as late as safely possible). `upper=0` makes dT send immediately
360+
/// (close to the old behavior, but still with the safety factor). Values must satisfy 0 <= lower <= upper <= 1.
361+
void SetCDFutureDriftSendBounds(float lower_ratio, float upper_ratio) {
362+
future_drift_send_upper_bound_ratio = clampBetween(upper_ratio, 0.0f, 1.0f);
363+
future_drift_send_lower_bound_ratio =
364+
clampBetween(lower_ratio, 0.0f, static_cast<float>(future_drift_send_upper_bound_ratio));
365+
}
349366
/// @brief Get the current update frequency used by the solver.
350367
/// @return The current update frequency.
351368
float GetUpdateFreq() const;
@@ -1376,6 +1393,13 @@ class DEMSolver {
13761393
/// Show the wall time and percentages of wall time spend on various solver tasks.
13771394
void ShowTimingStats();
13781395

1396+
/// Enable/disable GPU event-based timing (StartGpuTimer/StopGpuTimer/AccumulateGpuTimer). Disabling this can
1397+
/// improve performance by avoiding per-step cudaEventRecord/cudaEventElapsedTime overhead. Call only when the
1398+
/// solver is not actively running (best: right after constructing the solver, or after DoDynamicsThenSync).
1399+
void SetGPUTimersEnabled(bool enabled);
1400+
/// Query whether GPU event-based timing is enabled.
1401+
bool GetGPUTimersEnabled() const { return m_gpu_timers_enabled; }
1402+
13791403
/// Reset the collaboration stats between dT and kT back to the initial value (0). You should call this if you want
13801404
/// to start over and re-inspect the stats of the new run; otherwise, it is generally not needed, you can go ahead
13811405
/// and destroy DEMSolver.
@@ -1471,28 +1495,28 @@ class DEMSolver {
14711495
void PrintKinematicScratchSpaceUsage() const { kT->printScratchSpaceUsage(); }
14721496

14731497
/// Let dT do this call and return the reduce value of the inspected quantity.
1474-
float dTInspectReduce(const std::shared_ptr<jitify::Program>& inspection_kernel,
1498+
float dTInspectReduce(const std::shared_ptr<JitHelper::CachedProgram>& inspection_kernel,
14751499
const std::string& kernel_name,
14761500
INSPECT_ENTITY_TYPE thing_to_insp,
14771501
CUB_REDUCE_FLAVOR reduce_flavor,
14781502
bool all_domain,
14791503
DualArray<scratch_t>& reduceResArr,
14801504
DualArray<scratch_t>& reduceRes);
1481-
float* dTInspectNoReduce(const std::shared_ptr<jitify::Program>& inspection_kernel,
1505+
float* dTInspectNoReduce(const std::shared_ptr<JitHelper::CachedProgram>& inspection_kernel,
14821506
const std::string& kernel_name,
14831507
INSPECT_ENTITY_TYPE thing_to_insp,
14841508
CUB_REDUCE_FLAVOR reduce_flavor,
14851509
bool all_domain,
14861510
DualArray<scratch_t>& reduceResArr,
14871511
DualArray<scratch_t>& reduceRes);
1488-
float dTInspectReduceDevice(const std::shared_ptr<jitify::Program>& inspection_kernel,
1512+
float dTInspectReduceDevice(const std::shared_ptr<JitHelper::CachedProgram>& inspection_kernel,
14891513
const std::string& kernel_name,
14901514
INSPECT_ENTITY_TYPE thing_to_insp,
14911515
CUB_REDUCE_FLAVOR reduce_flavor,
14921516
bool all_domain,
14931517
DualArray<scratch_t>& reduceResArr,
14941518
DualArray<scratch_t>& reduceRes);
1495-
float* dTInspectNoReduceDevice(const std::shared_ptr<jitify::Program>& inspection_kernel,
1519+
float* dTInspectNoReduceDevice(const std::shared_ptr<JitHelper::CachedProgram>& inspection_kernel,
14961520
const std::string& kernel_name,
14971521
INSPECT_ENTITY_TYPE thing_to_insp,
14981522
CUB_REDUCE_FLAVOR reduce_flavor,
@@ -1599,6 +1623,9 @@ class DEMSolver {
15991623
// The `base' velocity we always consider entities to have, when determining the thickness of the margin to add for
16001624
// contact detection.
16011625
float m_expand_base_vel = 3.f;
1626+
// Whether angular velocity contributes to the contact margin (auto-detected if not user-set).
1627+
bool m_use_angvel_margin = true;
1628+
bool m_use_angvel_margin_user_set = false;
16021629

16031630
// The method of determining the thickness of the margin added to CD
16041631
// Default is using a max_vel inspector of the clumps to decide it
@@ -1664,12 +1691,18 @@ class DEMSolver {
16641691
float max_drift_ahead_of_avg_drift = 4.;
16651692
float max_drift_multiple_of_avg_drift = 1.05;
16661693
unsigned int max_drift_gauge_history_size = 200;
1694+
float future_drift_eff_drift_safety_factor = 1.1;
1695+
float future_drift_send_upper_bound_ratio = 1.0;
1696+
float future_drift_send_lower_bound_ratio = 0.0;
16671697

16681698
// See SetNoForceRecord
16691699
bool no_recording_contact_forces = false;
16701700
// See SetCollectAccRightAfterForceCalc
16711701
bool collect_force_in_force_kernel = false;
16721702

1703+
// Controls event-based GPU timing for kT/dT timers.
1704+
bool m_gpu_timers_enabled = true;
1705+
16731706
// Error-out avg num contacts
16741707
float threshold_error_out_num_cnts = 300.;
16751708

src/DEM/APIPrivate.cpp

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ void DEMSolver::jitifyKernels() {
295295
equipIntegrationScheme(m_subs);
296296
equipKernelIncludes(m_subs);
297297

298-
// Jitify may require a defined device to derive the arch
298+
// Jitify may require a defined device to derive the arch
299299
std::thread kT_build([&]() {
300300
DEME_GPU_CALL(cudaSetDevice(kT->streamInfo.device));
301301
kT->jitifyKernels(m_subs, m_jitify_options);
@@ -304,12 +304,6 @@ void DEMSolver::jitifyKernels() {
304304
std::thread dT_build([&]() {
305305
DEME_GPU_CALL(cudaSetDevice(dT->streamInfo.device));
306306
dT->jitifyKernels(m_subs, m_jitify_options);
307-
308-
// Now, inspectors need to be jitified too... but the current design jitify inspector kernels at the first time
309-
// they are used. for (auto& insp : m_inspectors) {
310-
// insp->Initialize(m_subs);
311-
// }
312-
313307
// Solver system's own max vel inspector should be init-ed. Don't bother init-ing it while using, because it is
314308
// called at high frequency, let's save an if check. Forced initialization (since doing it before system
315309
// completes init).
@@ -319,8 +313,20 @@ void DEMSolver::jitifyKernels() {
319313
m_approx_angvel_func->Initialize(m_subs, m_jitify_options, true);
320314
dT->approxAngVelFunc = m_approx_angvel_func;
321315
});
316+
322317
kT_build.join();
323318
dT_build.join();
319+
320+
// Eagerly initialize user-created inspectors so their kernels compile before first use
321+
for (auto& insp : m_inspectors) {
322+
if (insp) {
323+
insp->Initialize(m_subs, m_jitify_options, true);
324+
if (insp->inspection_kernel) {
325+
insp->inspection_kernel->kernel(insp->kernel_name).instantiate();
326+
}
327+
}
328+
}
329+
324330
}
325331

326332
void DEMSolver::getContacts_impl(std::vector<bodyID_t>& idA,
@@ -1145,7 +1151,13 @@ void DEMSolver::setSolverParams() {
11451151
dT->solverFlags.upperBoundFutureDrift = upper_bound_future_drift;
11461152
dT->solverFlags.targetDriftMoreThanAvg = max_drift_ahead_of_avg_drift;
11471153
dT->solverFlags.targetDriftMultipleOfAvg = max_drift_multiple_of_avg_drift;
1148-
dT->accumStepUpdater.SetCacheSize(max_drift_gauge_history_size);
1154+
kT->solverFlags.futureDriftEffDriftSafetyFactor = future_drift_eff_drift_safety_factor;
1155+
dT->solverFlags.futureDriftEffDriftSafetyFactor = future_drift_eff_drift_safety_factor;
1156+
kT->solverFlags.futureDriftSendUpperBoundRatio = future_drift_send_upper_bound_ratio;
1157+
dT->solverFlags.futureDriftSendUpperBoundRatio = future_drift_send_upper_bound_ratio;
1158+
kT->solverFlags.futureDriftSendLowerBoundRatio = future_drift_send_lower_bound_ratio;
1159+
dT->solverFlags.futureDriftSendLowerBoundRatio = future_drift_send_lower_bound_ratio;
1160+
// accumStepUpdater removed; FutureDriftRegulator manages its own window size.
11491161
}
11501162

11511163
void DEMSolver::setSimParams() {
@@ -1179,20 +1191,71 @@ void DEMSolver::setSimParams() {
11791191
DEME_MAX_WILDCARD_NUM);
11801192
}
11811193
DEME_DEBUG_PRINTF("%u contact wildcards are in the force model.", nContactWildcards);
1182-
11831194
// Error-out velocity should be no smaller than the max velocity we can expect
11841195
if (threshold_error_out_vel < m_approx_max_vel) {
11851196
// Silently bring down m_approx_max_vel
11861197
m_approx_max_vel = threshold_error_out_vel;
11871198
}
1188-
1199+
{ // Adaptive Timestep -- currently only Hertz const.
1200+
if (adapt_ts_type == ADAPT_TS_TYPE::HERTZ_CONST) {
1201+
auto sqr = [](double x) { return x * x; };
1202+
auto effective_E = [&](double E1, double nu1, double E2, double nu2) -> double {
1203+
if (E1 <= 0.0 || E2 <= 0.0) return 0.0;
1204+
return 1.0 / ( ((1.0 - sqr(nu1)) / E1) + ((1.0 - sqr(nu2)) / E2) );
1205+
}; // max effektive E* over all material contacts
1206+
double Eeff_max = 0.0;
1207+
for (size_t i = 0; i < m_loaded_materials.size(); ++i) {
1208+
const auto& A = m_loaded_materials[i]->mat_prop;
1209+
const double E1 = (A.count("E") ? (double)A.at("E") : 0.0);
1210+
const double nu1 = (A.count("nu") ? (double)A.at("nu") : 0.3);
1211+
for (size_t j = i; j < m_loaded_materials.size(); ++j) {
1212+
const auto& B = m_loaded_materials[j]->mat_prop;
1213+
const double E2 = (B.count("E") ? (double)B.at("E") : 0.0);
1214+
const double nu2 = (B.count("nu") ? (double)B.at("nu") : 0.3);
1215+
const double Ee = effective_E(E1, nu1, E2, nu2);
1216+
if (Ee > Eeff_max) Eeff_max = Ee;
1217+
}
1218+
} // lowest mass
1219+
double min_mass = std::numeric_limits<double>::infinity();
1220+
for (double m : m_template_clump_mass)
1221+
if (m > 0.0 && m < min_mass) min_mass = m;
1222+
const double r_min = (double)m_smallest_radius;
1223+
if (std::isfinite(min_mass) && r_min > 0.0 && Eeff_max > 0.0) {
1224+
const double R_eff = 0.5 * r_min;
1225+
const double m_eff = 0.5 * min_mass;
1226+
const double KH = FOUR_OVER_THREE * std::sqrt(0.1) * Eeff_max * R_eff;
1227+
const double dt_hertz = (PI / (2.0 * N_DT)) * std::sqrt(m_eff / KH);
1228+
if (dt_hertz > 0.0 && std::isfinite(dt_hertz)) {
1229+
m_ts_size = dt_hertz; // <- set const. timestep
1230+
DEME_INFO("Adaptive time step 'hertz_const': dt = %.9g (m_min=%.6g, r_min=%.6g, E*=%.6g, N_DT=%.1f)",
1231+
m_ts_size, min_mass, r_min, Eeff_max, N_DT);
1232+
} else {
1233+
DEME_WARNING("hertz_const erzeugte ungueltigen dt; behalte bisherigen Wert %.7g.", m_ts_size);
1234+
}
1235+
} else {
1236+
DEME_WARNING("hertz_const: fehlende/ungueltige Daten (m_min=%g, r_min=%g, E*=%g); dt bleibt %.7g.",
1237+
min_mass, r_min, Eeff_max, m_ts_size);
1238+
}
1239+
}
1240+
}
1241+
if (!m_use_angvel_margin_user_set) {
1242+
bool has_multi_sphere_clump = false;
1243+
for (const auto& radii : m_template_sp_radii) {
1244+
if (radii.size() > 1) {
1245+
has_multi_sphere_clump = true;
1246+
break;
1247+
}
1248+
}
1249+
const bool has_mesh = nTriGM > 0;
1250+
m_use_angvel_margin = has_multi_sphere_clump || has_mesh;
1251+
}
11891252
dT->setSimParams(nvXp2, nvYp2, nvZp2, l, m_voxelSize, m_binSize, nbX, nbY, nbZ, m_boxLBF, m_user_box_min,
11901253
m_user_box_max, G, m_ts_size, m_expand_factor, m_approx_max_vel, m_max_tritri_penetration,
1191-
m_expand_safety_multi, m_expand_base_vel, m_force_model->m_contact_wildcards,
1254+
m_expand_safety_multi, m_expand_base_vel, m_use_angvel_margin, m_force_model->m_contact_wildcards,
11921255
m_force_model->m_owner_wildcards, m_force_model->m_geo_wildcards);
11931256
kT->setSimParams(nvXp2, nvYp2, nvZp2, l, m_voxelSize, m_binSize, nbX, nbY, nbZ, m_boxLBF, m_user_box_min,
11941257
m_user_box_max, G, m_ts_size, m_expand_factor, m_approx_max_vel, m_max_tritri_penetration,
1195-
m_expand_safety_multi, m_expand_base_vel, m_force_model->m_contact_wildcards,
1258+
m_expand_safety_multi, m_expand_base_vel, m_use_angvel_margin, m_force_model->m_contact_wildcards,
11961259
m_force_model->m_owner_wildcards, m_force_model->m_geo_wildcards);
11971260
}
11981261

@@ -1327,7 +1390,10 @@ void DEMSolver::packDataPointers() {
13271390
}
13281391

13291392
void DEMSolver::migrateSimParamsToDevice() {
1393+
DEME_GPU_CALL(cudaSetDevice(dT->streamInfo.device));
13301394
dT->simParams.toDevice();
1395+
1396+
DEME_GPU_CALL(cudaSetDevice(kT->streamInfo.device));
13311397
kT->simParams.toDevice();
13321398
}
13331399

@@ -1562,6 +1628,14 @@ inline void DEMSolver::equipForceModel(std::unordered_map<std::string, std::stri
15621628
if (!no_recording_contact_forces) {
15631629
contact_info_write_strat = FORCE_INFO_WRITE_BACK_STRAT();
15641630
}
1631+
std::string contact_info_clear_strat = " ";
1632+
if (!no_recording_contact_forces) {
1633+
contact_info_clear_strat =
1634+
"granData->contactPointGeometryA[myContactID] = make_float3(0,0,0);"
1635+
"granData->contactPointGeometryB[myContactID] = make_float3(0,0,0);"
1636+
"granData->contactForces[myContactID] = make_float3(0,0,0);"
1637+
"granData->contactTorque_convToForce[myContactID] = make_float3(0,0,0)";
1638+
}
15651639

15661640
if (ensure_kernel_line_num) {
15671641
model = compact_code(model);
@@ -1570,6 +1644,7 @@ inline void DEMSolver::equipForceModel(std::unordered_map<std::string, std::stri
15701644
ingredient_acquisition_B = compact_code(ingredient_acquisition_B);
15711645
whether_reduce_in_kernel = compact_code(whether_reduce_in_kernel);
15721646
contact_info_write_strat = compact_code(contact_info_write_strat);
1647+
contact_info_clear_strat = compact_code(contact_info_clear_strat);
15731648
}
15741649
strMap["_DEMForceModel_;"] = model;
15751650
strMap["_forceModelPrerequisites_;"] = model_prerequisites;
@@ -1590,8 +1665,9 @@ inline void DEMSolver::equipForceModel(std::unordered_map<std::string, std::stri
15901665
strMap["_forceModelContactWildcardWrite_;"] = cnt_wildcard_write_back;
15911666
strMap["_forceModelContactWildcardDestroy_;"] = cnt_wildcard_destroy_record;
15921667

1593-
strMap["_forceCollectInPlaceStrat_;"] = whether_reduce_in_kernel;
1594-
strMap["_contactInfoWrite_;"] = contact_info_write_strat;
1668+
strMap["_forceCollectInPlaceStrat_"] = whether_reduce_in_kernel;
1669+
strMap["_contactInfoWrite_"] = contact_info_write_strat;
1670+
strMap["_contactInfoClear_"] = contact_info_clear_strat;
15951671

15961672
DEME_DEBUG_PRINTF("Model ingredient definition:\n%s", ingredient_definition.c_str());
15971673

0 commit comments

Comments
 (0)