|
21 | 21 | namespace deme { |
22 | 22 |
|
23 | 23 | DEMSolver::DEMSolver(unsigned int nGPUs) { |
24 | | - dTkT_InteractionManager = new ThreadManager(); |
25 | | - kTMain_InteractionManager = new WorkerReportChannel(); |
26 | | - dTMain_InteractionManager = new WorkerReportChannel(); |
| 24 | + if (nGPUs == 0) { |
| 25 | + DEME_ERROR("DEMSolver was set to use 0 GPUs and that is currently not supported."); |
| 26 | + } |
27 | 27 |
|
28 | | - // 2 means 2 threads (nGPUs is currently not used) |
29 | | - dTkT_GpuManager = new GpuManager(2); |
| 28 | + dTkT_InteractionManager = std::make_unique<ThreadManager>(); |
| 29 | + kTMain_InteractionManager = std::make_unique<WorkerReportChannel>(); |
| 30 | + dTMain_InteractionManager = std::make_unique<WorkerReportChannel>(); |
30 | 31 |
|
31 | 32 | // Set default solver params |
32 | 33 | setDefaultSolverParams(); |
33 | 34 |
|
| 35 | + // Determine which device IDs to use: scan available GPUs, use at most 2 |
| 36 | + int detected = GpuManager::scanNumDevices(); |
| 37 | + if (nGPUs > 2) { |
| 38 | + DEME_WARNING("DEMSolver was requested to use %u GPUs, but at most 2 are supported. Using 2 GPUs.", nGPUs); |
| 39 | + nGPUs = 2; |
| 40 | + } |
| 41 | + // Build a 2-element device ID list (one per thread); both threads share the same device when using 1 GPU |
| 42 | + unsigned int nToUse = std::min((unsigned int)detected, nGPUs); |
| 43 | + std::vector<int> device_ids; |
| 44 | + if (nToUse >= 2) { |
| 45 | + device_ids = {0, 1}; |
| 46 | + } else { |
| 47 | + device_ids = {0, 0}; |
| 48 | + } |
| 49 | + // Always use id list to avoid GpuManager deciding device usage by itself |
| 50 | + dTkT_GpuManager = std::make_unique<GpuManager>(device_ids); |
| 51 | + |
34 | 52 | // Thread-based worker creation may be needed as the workers allocate DualStructs on construction |
35 | 53 | std::thread dT_construct([&]() { |
36 | 54 | // Get a device/stream ID to use from the GPU Manager |
37 | 55 | const GpuManager::StreamInfo dT_stream_info = dTkT_GpuManager->getAvailableStream(); |
38 | 56 | DEME_GPU_CALL(cudaSetDevice(dT_stream_info.device)); |
39 | | - dT = new DEMDynamicThread(dTMain_InteractionManager, dTkT_InteractionManager, dT_stream_info); |
| 57 | + dT = std::make_unique<DEMDynamicThread>(dTMain_InteractionManager.get(), dTkT_InteractionManager.get(), |
| 58 | + dT_stream_info); |
| 59 | + }); |
| 60 | + |
| 61 | + std::thread kT_construct([&]() { |
| 62 | + const GpuManager::StreamInfo kT_stream_info = dTkT_GpuManager->getAvailableStream(); |
| 63 | + DEME_GPU_CALL(cudaSetDevice(kT_stream_info.device)); |
| 64 | + kT = std::make_unique<DEMKinematicThread>(kTMain_InteractionManager.get(), dTkT_InteractionManager.get(), |
| 65 | + kT_stream_info); |
| 66 | + }); |
| 67 | + |
| 68 | + dT_construct.join(); |
| 69 | + kT_construct.join(); |
| 70 | + |
| 71 | + // Make friends |
| 72 | + dT->kT = kT.get(); |
| 73 | + kT->dT = dT.get(); |
| 74 | +} |
| 75 | + |
| 76 | +DEMSolver::DEMSolver(std::vector<int> device_ids) { |
| 77 | + dTkT_InteractionManager = std::make_unique<ThreadManager>(); |
| 78 | + kTMain_InteractionManager = std::make_unique<WorkerReportChannel>(); |
| 79 | + dTMain_InteractionManager = std::make_unique<WorkerReportChannel>(); |
| 80 | + |
| 81 | + // Set default solver params |
| 82 | + setDefaultSolverParams(); |
| 83 | + |
| 84 | + // Validate device IDs against the number of physically available GPUs |
| 85 | + int detected = GpuManager::scanNumDevices(); |
| 86 | + if (device_ids.empty()) { |
| 87 | + DEME_ERROR("DEMSolver was given an empty device ID list. Please provide at least one device ID."); |
| 88 | + } |
| 89 | + for (int id : device_ids) { |
| 90 | + if (id < 0 || id >= detected) { |
| 91 | + DEME_ERROR("DEMSolver was given device ID %d, but only %d GPU device(s) are available.", id, detected); |
| 92 | + } |
| 93 | + } |
| 94 | + if (device_ids.size() > 2) { |
| 95 | + DEME_WARNING("DEMSolver was given %zu device IDs, but at most 2 are supported. Using only the first 2.", |
| 96 | + device_ids.size()); |
| 97 | + device_ids.resize(2); |
| 98 | + } |
| 99 | + // When only one device is specified, both threads run on that device |
| 100 | + if (device_ids.size() == 1) { |
| 101 | + device_ids = {device_ids[0], device_ids[0]}; |
| 102 | + } |
| 103 | + // Always use id list to avoid GpuManager deciding device usage by itself |
| 104 | + dTkT_GpuManager = std::make_unique<GpuManager>(device_ids); |
| 105 | + |
| 106 | + // Thread-based worker creation may be needed as the workers allocate DualStructs on construction |
| 107 | + std::thread dT_construct([&]() { |
| 108 | + const GpuManager::StreamInfo dT_stream_info = dTkT_GpuManager->getAvailableStream(); |
| 109 | + DEME_GPU_CALL(cudaSetDevice(dT_stream_info.device)); |
| 110 | + dT = std::make_unique<DEMDynamicThread>(dTMain_InteractionManager.get(), dTkT_InteractionManager.get(), |
| 111 | + dT_stream_info); |
40 | 112 | }); |
41 | 113 |
|
42 | 114 | std::thread kT_construct([&]() { |
43 | 115 | const GpuManager::StreamInfo kT_stream_info = dTkT_GpuManager->getAvailableStream(); |
44 | 116 | DEME_GPU_CALL(cudaSetDevice(kT_stream_info.device)); |
45 | | - kT = new DEMKinematicThread(kTMain_InteractionManager, dTkT_InteractionManager, kT_stream_info); |
| 117 | + kT = std::make_unique<DEMKinematicThread>(kTMain_InteractionManager.get(), dTkT_InteractionManager.get(), |
| 118 | + kT_stream_info); |
46 | 119 | }); |
47 | 120 |
|
48 | 121 | dT_construct.join(); |
49 | 122 | kT_construct.join(); |
50 | 123 |
|
51 | 124 | // Make friends |
52 | | - dT->kT = kT; |
53 | | - kT->dT = dT; |
| 125 | + dT->kT = kT.get(); |
| 126 | + kT->dT = dT.get(); |
54 | 127 | } |
55 | 128 |
|
56 | 129 | DEMSolver::~DEMSolver() { |
57 | 130 | if (sys_initialized) |
58 | 131 | DoDynamicsThenSync(0.0); |
59 | | - delete kT; |
60 | | - delete dT; |
61 | | - delete kTMain_InteractionManager; |
62 | | - delete dTMain_InteractionManager; |
63 | | - delete dTkT_InteractionManager; |
64 | | - delete dTkT_GpuManager; |
| 132 | + kT.reset(); |
| 133 | + dT.reset(); |
| 134 | + kTMain_InteractionManager.reset(); |
| 135 | + dTMain_InteractionManager.reset(); |
| 136 | + dTkT_InteractionManager.reset(); |
| 137 | + dTkT_GpuManager.reset(); |
65 | 138 | } |
66 | 139 |
|
67 | 140 | void DEMSolver::SetVerbosity(const std::string& verbose) { |
@@ -1910,13 +1983,13 @@ std::shared_ptr<DEMMeshConnected> DEMSolver::AddWavefrontMeshObject(const std::s |
1910 | 1983 | } |
1911 | 1984 |
|
1912 | 1985 | std::shared_ptr<DEMInspector> DEMSolver::CreateInspector(const std::string& quantity) { |
1913 | | - DEMInspector insp(this, this->dT, quantity); |
| 1986 | + DEMInspector insp(this, this->dT.get(), quantity); |
1914 | 1987 | m_inspectors.push_back(std::make_shared<DEMInspector>(std::move(insp))); |
1915 | 1988 | return m_inspectors.back(); |
1916 | 1989 | } |
1917 | 1990 |
|
1918 | 1991 | std::shared_ptr<DEMInspector> DEMSolver::CreateInspector(const std::string& quantity, const std::string& region) { |
1919 | | - DEMInspector insp(this, this->dT, quantity, region); |
| 1992 | + DEMInspector insp(this, this->dT.get(), quantity, region); |
1920 | 1993 | m_inspectors.push_back(std::make_shared<DEMInspector>(std::move(insp))); |
1921 | 1994 | return m_inspectors.back(); |
1922 | 1995 | } |
|
0 commit comments