@@ -520,7 +520,7 @@ void DEMDynamicThread::changeOwnerSizes(const std::vector<bodyID_t>& IDs, const
520520 size_t ownerFactorSize = (size_t )simParams->nOwnerBodies * sizeof (float );
521521 // Bool table for whether this owner should change
522522 notStupidBool_t* idBool = (notStupidBool_t*)solverScratchSpace.allocateTempVector (" idBool" , idBoolSize);
523- DEME_GPU_CALL (cudaMemsetAsync (idBool, 0 , idBoolSize, streamInfo. stream ));
523+ DEME_GPU_CALL (cudaMemset (idBool, 0 , idBoolSize));
524524 float * ownerFactors = (float *)solverScratchSpace.allocateTempVector (" ownerFactors" , ownerFactorSize);
525525
526526 // Mark on the bool array those owners that need a change
@@ -638,7 +638,7 @@ void DEMDynamicThread::allocateGPUArrays(size_t nOwnerBodies,
638638 // maxTriTriPenetration usually keeps the max tri--tri penetration during the on-going simulation. But after
639639 // initialization, when it stores no meaningful values, dT will send a work order to kT, so maxTriTriPenetration's
640640 // value has to be initialized.
641- DEME_GPU_CALL (cudaMemsetAsync (maxTriTriPenetration.getDevicePointer (), 0 , sizeof (double ), streamInfo. stream ));
641+ DEME_GPU_CALL (cudaMemset (maxTriTriPenetration.getDevicePointer (), 0 , sizeof (double )));
642642
643643 // Resize to the number of analytical geometries
644644 DEME_DUAL_ARRAY_RESIZE (ownerAnalBody, nAnalGM, 0 );
@@ -2499,25 +2499,8 @@ inline void DEMDynamicThread::dispatchPatchBasedForceCorrections(
24992499 const ContactTypeMap<std::pair<contactPairs_t, contactPairs_t>>& typeStartCountPatchMap,
25002500 const ContactTypeMap<std::vector<std::pair<std::shared_ptr<JitHelper::CachedProgram>, std::string>>>&
25012501 typeKernelMap) {
2502- // Reset max tri-tri penetration only when mesh contacts are present (avoid needless clears), async to overlap
2503- bool needTriPenReset = false ;
2504- auto markIfTri = [&](contact_t c) {
2505- auto itP = typeStartCountPrimitiveMap.find (c);
2506- if (itP != typeStartCountPrimitiveMap.end () && itP->second .second > 0 ) {
2507- needTriPenReset = true ;
2508- return ;
2509- }
2510- auto itPatch = typeStartCountPatchMap.find (c);
2511- if (itPatch != typeStartCountPatchMap.end () && itPatch->second .second > 0 ) {
2512- needTriPenReset = true ;
2513- }
2514- };
2515- markIfTri (SPHERE_TRIANGLE_CONTACT );
2516- markIfTri (TRIANGLE_TRIANGLE_CONTACT );
2517- markIfTri (TRIANGLE_ANALYTICAL_CONTACT );
2518- if (needTriPenReset) {
2519- DEME_GPU_CALL (cudaMemsetAsync (maxTriTriPenetration.getDevicePointer (), 0 , sizeof (double ), streamInfo.stream ));
2520- }
2502+ // Reset max tri-tri penetration for this timestep on device (kT may need this info)
2503+ DEME_GPU_CALL (cudaMemset (maxTriTriPenetration.getDevicePointer (), 0 , sizeof (double )));
25212504
25222505 // For each contact type that exists, check if it is patch(mesh)-related type...
25232506 for (size_t i = 0 ; i < m_numExistingTypes; i++) {
@@ -2531,112 +2514,74 @@ inline void DEMDynamicThread::dispatchPatchBasedForceCorrections(
25312514 contactPairs_t startOffsetPatch = start_count_patch.first ;
25322515 contactPairs_t countPatch = start_count_patch.second ;
25332516
2534- // Vote for the contact direction; voting power depends on the contact area
2535- // This reduce-by-key operation reduces primitive-recorded force pairs into patch/convex part-based
2536- // force pairs. All elements that share the same geomToPatchMap value vote together.
25372517 if (countPrimitive > 0 ) {
25382518 contactPairs_t* keys = granData->geomToPatchMap + startOffsetPrimitive;
2539- // Allocate temporary arrays for the voting process
2540- float3* weightedNormals =
2541- (float3*)solverScratchSpace.allocateTempVector (" weightedNormals" , countPrimitive * sizeof (float3));
2542-
25432519 // Allocate arrays for reduce-by-key results (uniqueKeys uses contactPairs_t, not patchIDPair_t)
25442520 contactPairs_t* uniqueKeys = (contactPairs_t*)solverScratchSpace.allocateTempVector (
25452521 " uniqueKeys" , countPrimitive * sizeof (contactPairs_t));
2546- float3* votedWeightedNormals = (float3*)solverScratchSpace.allocateTempVector (
2547- " votedWeightedNormals" , countPrimitive * sizeof (float3));
25482522 solverScratchSpace.allocateDualStruct (" numUniqueKeys" );
25492523 size_t * numUniqueKeys = solverScratchSpace.getDualStructDevice (" numUniqueKeys" );
25502524
2551- // Step 1: Prepare weighted normals. Keys are read directly from geomToPatchMap.
2525+ // Step 1: Area-weighted normals for voting
2526+ float3* weightedNormals =
2527+ (float3*)solverScratchSpace.allocateTempVector (" weightedNormals" , countPrimitive * sizeof (float3));
25522528 prepareWeightedNormalsForVoting (&granData, weightedNormals, startOffsetPrimitive, countPrimitive,
25532529 streamInfo.stream );
25542530
2555- // Step 2: Reduce-by-key for weighted normals (sum)
2556- // The keys are geomToPatchMap values (contactPairs_t), which group primitives by patch pair
2531+ float3* votedWeightedNormals = (float3*)solverScratchSpace. allocateTempVector (
2532+ " votedWeightedNormals " , countPrimitive * sizeof (float3));
25572533 cubSumReduceByKey<contactPairs_t, float3>(keys, uniqueKeys, weightedNormals, votedWeightedNormals,
25582534 numUniqueKeys, countPrimitive, streamInfo.stream ,
25592535 solverScratchSpace);
25602536 solverScratchSpace.finishUsingTempVector (" weightedNormals" );
2561- // For extra safety
2537+
2538+ // Normalize the voted normals using unique keys and scatter to patch-local storage.
2539+ float3* votedNormals =
2540+ (float3*)solverScratchSpace.allocateTempVector (" votedNormals" , countPatch * sizeof (float3));
25622541 solverScratchSpace.syncDualStructDeviceToHost (" numUniqueKeys" );
25632542 size_t numUniqueKeysHost = *(solverScratchSpace.getDualStructHost (" numUniqueKeys" ));
2564- // std::cout << "Keys:" << std::endl;
2565- // displayDeviceArray<contactPairs_t>(keys, countPrimitive);
2566- // std::cout << "Unique Keys:" << std::endl;
2567- // displayDeviceArray<contactPairs_t>(uniqueKeys, numUniqueKeysHost);
2543+ normalizeAndScatterVotedNormalsFromUniqueKeys (votedWeightedNormals, uniqueKeys, votedNormals,
2544+ startOffsetPatch, numUniqueKeysHost, streamInfo.stream );
2545+ solverScratchSpace.finishUsingTempVector (" votedWeightedNormals" );
25682546 if (numUniqueKeysHost != countPatch) {
25692547 DEME_ERROR (
25702548 " Patch-based contact voting produced %zu unique patch pairs, but expected %zu pairs for "
25712549 " contact type %d!" ,
25722550 numUniqueKeysHost, countPatch, contact_type);
25732551 }
25742552
2575- // Normalize the voted normals using unique keys and scatter to patch-local storage.
2576- float3* votedNormals =
2577- (float3*)solverScratchSpace.allocateTempVector (" votedNormals" , countPatch * sizeof (float3));
2578- normalizeAndScatterVotedNormalsFromUniqueKeys (votedWeightedNormals, uniqueKeys, votedNormals,
2579- startOffsetPatch, numUniqueKeysHost, streamInfo.stream );
2580- solverScratchSpace.finishUsingTempVector (" votedWeightedNormals" );
2581- // displayDeviceFloat3(votedNormals, countPatch);
2582-
2583- // Project penetration/area and accumulate weighted contact points in a single pass.
2584- PatchContactAccum* primitivePatchAccumulators =
2585- (PatchContactAccum*)solverScratchSpace.allocateTempVector (
2586- " primitivePatchAccumulators" , countPrimitive * sizeof (PatchContactAccum));
2587- computePatchContactAccumulators (&granData, votedNormals, keys, primitivePatchAccumulators,
2588- startOffsetPrimitive, startOffsetPatch, countPrimitive,
2589- streamInfo.stream );
2553+ // Step 2: Fused accumulation (sum + max) in a single reduce-by-key.
2554+ FusedPatchAccum* primitiveAccums = (FusedPatchAccum*)solverScratchSpace.allocateTempVector (
2555+ " fusedPrimitiveAccums" , countPrimitive * sizeof (FusedPatchAccum));
2556+ computeFusedPatchContactAccumulators (&granData, votedNormals, keys, primitiveAccums,
2557+ startOffsetPrimitive, startOffsetPatch, countPrimitive,
2558+ streamInfo.stream );
25902559
2591- PatchContactAccum* patchContactAccumulators = (PatchContactAccum *)solverScratchSpace.allocateTempVector (
2592- " patchContactAccumulators " , numUniqueKeysHost * sizeof (PatchContactAccum ));
2593- cubSumReduceByKey<contactPairs_t, PatchContactAccum>(
2594- keys, uniqueKeys, primitivePatchAccumulators, patchContactAccumulators, numUniqueKeys ,
2595- countPrimitive, streamInfo. stream , solverScratchSpace);
2596- solverScratchSpace.finishUsingTempVector (" primitivePatchAccumulators " );
2560+ FusedPatchAccum* patchAccums = (FusedPatchAccum *)solverScratchSpace.allocateTempVector (
2561+ " fusedPatchAccums " , numUniqueKeysHost * sizeof (FusedPatchAccum ));
2562+ cubSumReduceByKey<contactPairs_t, FusedPatchAccum>(keys, uniqueKeys, primitiveAccums, patchAccums,
2563+ numUniqueKeys, countPrimitive, streamInfo. stream ,
2564+ solverScratchSpace);
2565+ solverScratchSpace.finishUsingTempVector (" fusedPrimitiveAccums " );
25972566
25982567 double * totalProjectedAreas =
25992568 (double *)solverScratchSpace.allocateTempVector (" totalProjectedAreas" , countPatch * sizeof (double ));
26002569 double * maxProjectedPenetrations = (double *)solverScratchSpace.allocateTempVector (
26012570 " maxProjectedPenetrations" , countPatch * sizeof (double ));
26022571 double3* votedContactPoints =
26032572 (double3*)solverScratchSpace.allocateTempVector (" votedContactPoints" , countPatch * sizeof (double3));
2604- scatterPatchContactAccumulators (patchContactAccumulators, uniqueKeys, totalProjectedAreas,
2605- maxProjectedPenetrations, votedContactPoints, startOffsetPatch,
2606- numUniqueKeysHost, streamInfo.stream );
2607- solverScratchSpace.finishUsingTempVector (" patchContactAccumulators" );
2608-
2609- // Step 9: Handle zero-area patches (all primitive areas are 0)
2610- // For these patches, we need to find the max penetration primitive and use its normal/penetration
2611-
2612- // 9a: Extract primitive penetrations for max-reduce
2613- double * primitivePenetrations = (double *)solverScratchSpace.allocateTempVector (
2614- " primitivePenetrations" , countPrimitive * sizeof (double ));
2615- extractPrimitivePenetrations (&granData, primitivePenetrations, startOffsetPrimitive, countPrimitive,
2616- streamInfo.stream );
2617-
2618- // 9b: Max-negative-reduce-by-key to get max negative penetration per patch
2619- // This finds the largest negative value (smallest absolute value among negatives)
2620- // Positive values are treated as very negative to indicate invalid/non-physical state
2621- double * maxPenetrations =
2622- (double *)solverScratchSpace.allocateTempVector (" maxPenetrations" , countPatch * sizeof (double ));
2623- cubMaxNegativeReduceByKey<contactPairs_t, double >(keys, uniqueKeys, primitivePenetrations,
2624- maxPenetrations, numUniqueKeys, countPrimitive,
2625- streamInfo.stream , solverScratchSpace);
2626- solverScratchSpace.finishUsingTempVector (" primitivePenetrations" );
2627-
2628- // 9c: Find max-penetration primitives for zero-area patches and extract their normals, penetrations,
2629- // and contact points
26302573 float3* zeroAreaNormals =
26312574 (float3*)solverScratchSpace.allocateTempVector (" zeroAreaNormals" , countPatch * sizeof (float3));
26322575 double * zeroAreaPenetrations =
26332576 (double *)solverScratchSpace.allocateTempVector (" zeroAreaPenetrations" , countPatch * sizeof (double ));
26342577 double3* zeroAreaContactPoints = (double3*)solverScratchSpace.allocateTempVector (
26352578 " zeroAreaContactPoints" , countPatch * sizeof (double3));
2636- findMaxPenetrationPrimitiveForZeroAreaPatches (
2637- &granData, maxPenetrations, zeroAreaNormals, zeroAreaPenetrations, zeroAreaContactPoints, keys,
2638- startOffsetPrimitive, startOffsetPatch, countPrimitive, streamInfo.stream );
2639- solverScratchSpace.finishUsingTempVector (" maxPenetrations" );
2579+
2580+ scatterFusedPatchAccumulators (patchAccums, uniqueKeys, totalProjectedAreas, maxProjectedPenetrations,
2581+ votedContactPoints, votedNormals, zeroAreaNormals, zeroAreaPenetrations,
2582+ zeroAreaContactPoints, startOffsetPatch, numUniqueKeysHost,
2583+ streamInfo.stream );
2584+ solverScratchSpace.finishUsingTempVector (" fusedPatchAccums" );
26402585
26412586 // Step 9d: Check if each patch has any SAT-satisfying primitive (for tri-tri contacts)
26422587 // If no primitive satisfies SAT, the patch contact is non-physical and should use Step 9 fallback
@@ -3417,7 +3362,7 @@ float* DEMDynamicThread::inspectCall(const std::shared_ptr<JitHelper::CachedProg
34173362 // If this boolArrExclude is 1 at an element, that means this element is exluded in the reduction
34183363 notStupidBool_t* boolArrExclude =
34193364 (notStupidBool_t*)solverScratchSpace.allocateTempVector (" boolArrExclude" , regionTempSize);
3420- DEME_GPU_CALL (cudaMemsetAsync (boolArrExclude, 0 , regionTempSize, streamInfo. stream ));
3365+ DEME_GPU_CALL (cudaMemset (boolArrExclude, 0 , regionTempSize));
34213366
34223367 // We may actually have 2 reduced returns: in regional reduction, key 0 and 1 give one return each.
34233368 size_t returnSize = sizeof (float ) * 2 ;
0 commit comments