@@ -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
326332void 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
11511163void 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
13291392void 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