1 #include "cupmdevice.hpp" /* I "petscdevice.h" */ 2 #include <petsc/private/petscadvancedmacros.h> 3 4 using namespace Petsc::Device; 5 6 /* 7 note to anyone adding more classes, the name must be ALL_CAPS_SHORT_NAME + Device exactly to 8 be picked up by the switch-case macros below 9 */ 10 #if PetscDefined(HAVE_CUDA) 11 static CUPM::Device<CUPM::DeviceType::CUDA> CUDADevice(PetscDeviceContextCreate_CUDA); 12 #endif 13 #if PetscDefined(HAVE_HIP) 14 static CUPM::Device<CUPM::DeviceType::HIP> HIPDevice(PetscDeviceContextCreate_HIP); 15 #endif 16 #if PetscDefined(HAVE_SYCL) 17 #include "sycldevice.hpp" 18 static SYCL::Device SYCLDevice(PetscDeviceContextCreate_SYCL); 19 #endif 20 21 static_assert(Petsc::util::integral_value(PETSC_DEVICE_INVALID) == 0, ""); 22 static_assert(Petsc::util::integral_value(PETSC_DEVICE_CUDA) == 1, ""); 23 static_assert(Petsc::util::integral_value(PETSC_DEVICE_HIP) == 2, ""); 24 static_assert(Petsc::util::integral_value(PETSC_DEVICE_SYCL) == 3, ""); 25 static_assert(Petsc::util::integral_value(PETSC_DEVICE_MAX) == 4, ""); 26 const char *const PetscDeviceTypes[] = {"invalid", "cuda", "hip", "sycl", "max", "PetscDeviceType", "PETSC_DEVICE_", PETSC_NULLPTR}; 27 28 static_assert(Petsc::util::integral_value(PETSC_DEVICE_INIT_NONE) == 0, ""); 29 static_assert(Petsc::util::integral_value(PETSC_DEVICE_INIT_LAZY) == 1, ""); 30 static_assert(Petsc::util::integral_value(PETSC_DEVICE_INIT_EAGER) == 2, ""); 31 const char *const PetscDeviceInitTypes[] = {"none", "lazy", "eager", "PetscDeviceInitType", "PETSC_DEVICE_INIT_", PETSC_NULLPTR}; 32 static_assert(sizeof(PetscDeviceInitTypes) / sizeof(*PetscDeviceInitTypes) == 6, "Must change CUPMDevice<T>::initialize number of enum values in -device_enable_cupm to match!"); 33 34 const char *const PetscDeviceAttributes[] = {"shared_mem_per_block", "max", "PetscDeviceAttribute", "PETSC_DEVICE_ATTR_", nullptr}; 35 36 #define PETSC_DEVICE_CASE(IMPLS, func, ...) \ 37 case PetscConcat_(PETSC_DEVICE_, IMPLS): { \ 38 PetscCall(PetscConcat_(IMPLS, Device).func(__VA_ARGS__)); \ 39 } break 40 41 /* 42 Suppose you have: 43 44 CUDADevice.myFunction(arg1,arg2) 45 46 that you would like to conditionally define and call in a switch-case: 47 48 switch(PetscDeviceType) { 49 #if PetscDefined(HAVE_CUDA) 50 case PETSC_DEVICE_CUDA: { 51 PetscCall(CUDADevice.myFunction(arg1,arg2)); 52 } break; 53 #endif 54 } 55 56 then calling this macro: 57 58 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA,myFunction,arg1,arg2) 59 60 will expand to the following case statement: 61 62 case PETSC_DEVICE_CUDA: { 63 PetscCall(CUDADevice.myFunction(arg1,arg2)); 64 } break 65 66 if PetscDefined(HAVE_CUDA) evaluates to 1, and expand to nothing otherwise 67 */ 68 #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED(IMPLS, func, ...) PetscIfPetscDefined(PetscConcat_(HAVE_, IMPLS), PETSC_DEVICE_CASE, PetscExpandToNothing)(IMPLS, func, __VA_ARGS__) 69 70 /*@C 71 PetscDeviceCreate - Get a new handle for a particular device type 72 73 Not Collective, Possibly Synchronous 74 75 Input Parameters: 76 + type - The type of PetscDevice 77 - devid - The numeric ID# of the device (pass PETSC_DECIDE to assign automatically) 78 79 Output Parameter: 80 . device - The PetscDevice 81 82 Notes: 83 This routine may initialize PetscDevice. If this is the case, this will most likely cause 84 some sort of device synchronization. 85 86 devid is what you might pass to cudaSetDevice() for example. 87 88 Level: beginner 89 90 .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialize()`, 91 `PetscDeviceInitialized()`, `PetscDeviceConfigure()`, `PetscDeviceView()`, `PetscDeviceDestroy()` 92 @*/ 93 PetscErrorCode PetscDeviceCreate(PetscDeviceType type, PetscInt devid, PetscDevice *device) { 94 static PetscInt PetscDeviceCounter = 0; 95 PetscDevice dev; 96 97 PetscFunctionBegin; 98 PetscValidDeviceType(type, 1); 99 PetscValidPointer(device, 3); 100 PetscCall(PetscDeviceInitializePackage()); 101 PetscCall(PetscNew(&dev)); 102 dev->id = PetscDeviceCounter++; 103 dev->type = type; 104 dev->refcnt = 1; 105 /* 106 if you are adding a device, you also need to add it's initialization in 107 PetscDeviceInitializeTypeFromOptions_Private() below 108 */ 109 switch (type) { 110 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA, getDevice, dev, devid); 111 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP, getDevice, dev, devid); 112 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(SYCL, getDevice, dev, devid); 113 default: 114 /* in case the above macros expand to nothing this silences any unused variable warnings */ 115 (void)(devid); 116 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "PETSc was seemingly configured for PetscDeviceType %s but we've fallen through all cases in a switch", PetscDeviceTypes[type]); 117 } 118 *device = dev; 119 PetscFunctionReturn(0); 120 } 121 122 /*@C 123 PetscDeviceDestroy - Free a PetscDevice 124 125 Not Collective, Asynchronous 126 127 Input Parameter: 128 . device - The PetscDevice 129 130 Level: beginner 131 132 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceView()` 133 @*/ 134 PetscErrorCode PetscDeviceDestroy(PetscDevice *device) { 135 PetscFunctionBegin; 136 if (!*device) PetscFunctionReturn(0); 137 PetscValidDevice(*device, 1); 138 PetscCall(PetscDeviceDereference_Internal(*device)); 139 if ((*device)->refcnt) { 140 *device = PETSC_NULLPTR; 141 PetscFunctionReturn(0); 142 } 143 PetscCall(PetscFree((*device)->data)); 144 PetscCall(PetscFree(*device)); 145 PetscFunctionReturn(0); 146 } 147 148 /*@C 149 PetscDeviceConfigure - Configure a particular PetscDevice 150 151 Not Collective, Asynchronous 152 153 Input Parameter: 154 . device - The PetscDevice to configure 155 156 Notes: 157 The user should not assume that this is a cheap operation 158 159 Level: beginner 160 161 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceView()`, `PetscDeviceDestroy()` 162 @*/ 163 PetscErrorCode PetscDeviceConfigure(PetscDevice device) { 164 PetscFunctionBegin; 165 PetscValidDevice(device, 1); 166 if (PetscDefined(USE_DEBUG)) { 167 /* 168 if no available configuration is available, this cascades all the way down to default 169 and error 170 */ 171 switch (device->type) { 172 case PETSC_DEVICE_CUDA: 173 if (PetscDefined(HAVE_CUDA)) break; 174 case PETSC_DEVICE_HIP: 175 if (PetscDefined(HAVE_HIP)) break; 176 case PETSC_DEVICE_SYCL: 177 if (PetscDefined(HAVE_SYCL)) break; 178 default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "PETSc was seemingly configured for PetscDeviceType %s but we've fallen through all cases in a switch", PetscDeviceTypes[device->type]); 179 } 180 } 181 PetscUseTypeMethod(device, configure); 182 PetscFunctionReturn(0); 183 } 184 185 /*@C 186 PetscDeviceView - View a PetscDevice 187 188 Collective on viewer, Asynchronous 189 190 Input Parameters: 191 + device - The PetscDevice to view 192 - viewer - The PetscViewer to view the device with (NULL for PETSC_VIEWER_STDOUT_WORLD) 193 194 Level: beginner 195 196 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceDestroy()` 197 @*/ 198 PetscErrorCode PetscDeviceView(PetscDevice device, PetscViewer viewer) { 199 PetscFunctionBegin; 200 PetscValidDevice(device, 1); 201 if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &viewer)); 202 PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2); 203 PetscUseTypeMethod(device, view, viewer); 204 PetscFunctionReturn(0); 205 } 206 207 /*@C 208 PetscDeviceGetDeviceId - Get the device id 209 210 Not collective 211 212 Input Parameter: 213 . device - The PetscDevice 214 215 Output Parameter: 216 . id - The device id 217 218 Level: beginner 219 220 .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceDestroy()` 221 @*/ 222 PetscErrorCode PetscDeviceGetDeviceId(PetscDevice device, PetscInt *id) { 223 PetscFunctionBegin; 224 PetscValidDevice(device, 1); 225 PetscValidIntPointer(id, 2); 226 *id = device->deviceId; 227 PetscFunctionReturn(0); 228 } 229 230 static std::array<bool, PETSC_DEVICE_MAX> initializedDevice = {}; 231 static std::array<PetscDevice, PETSC_DEVICE_MAX> defaultDevices = {}; 232 static_assert(initializedDevice.size() == defaultDevices.size(), ""); 233 234 /*@C 235 PetscDeviceInitialize - Initialize PetscDevice 236 237 Not Collective, Possibly Synchronous 238 239 Input Parameter: 240 . type - The PetscDeviceType to initialize 241 242 Notes: 243 Eagerly initializes the corresponding PetscDeviceType if needed. 244 245 Level: beginner 246 247 .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialized()`, `PetscDeviceCreate()`, `PetscDeviceDestroy()` 248 @*/ 249 PetscErrorCode PetscDeviceInitialize(PetscDeviceType type) { 250 PetscFunctionBegin; 251 PetscValidDeviceType(type, 1); 252 PetscCall(PetscDeviceInitializeDefaultDevice_Internal(type, PETSC_DECIDE)); 253 PetscFunctionReturn(0); 254 } 255 256 /*@C 257 PetscDeviceInitialized - Determines whether PetscDevice is initialized for a particular 258 PetscDeviceType 259 260 Not Collective, Asynchronous 261 262 Input Parameter: 263 . type - The PetscDeviceType to check 264 265 Output Parameter: 266 . [return value] - PETSC_TRUE if type is initialized, PETSC_FALSE otherwise 267 268 Notes: 269 If one has not configured PETSc for a particular PetscDeviceType then this routine will 270 return PETSC_FALSE for that PetscDeviceType. 271 272 Level: beginner 273 274 .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialize()`, `PetscDeviceCreate()`, `PetscDeviceDestroy()` 275 @*/ 276 PetscBool PetscDeviceInitialized(PetscDeviceType type) { 277 return static_cast<PetscBool>(PetscDeviceConfiguredFor_Internal(type) && initializedDevice[type]); 278 } 279 280 /*@C 281 PetscDeviceGetAttribute - Query a particular attribute of a `PetscDevice` 282 283 Not Collective, Asynchronous 284 285 Input Parameters: 286 + device - The `PetscDevice` 287 - attr - The attribute 288 289 Output Parameter: 290 . value - The value of the attribute 291 292 Notes: 293 Since different attributes are often different types `value` is a `void *` to accommodate 294 them all. The underlying type of the attribute is therefore included in the name of the 295 `PetscDeviceAttribute` reponsible for querying it. For example, 296 `PETSC_DEVICE_ATTR_SIZE_T_SHARED_MEM_PER_BLOCK` is of type `size_t`. 297 298 .seealso: `PetscDeviceAtrtibute`, `PetscDeviceConfigure()`, `PetscDevice` 299 @*/ 300 PetscErrorCode PetscDeviceGetAttribute(PetscDevice device, PetscDeviceAttribute attr, void *value) { 301 PetscFunctionBegin; 302 PetscValidDevice(device, 1); 303 PetscValidDeviceAttribute(attr, 2); 304 PetscValidPointer(value, 3); 305 PetscUseTypeMethod(device, getattribute, attr, value); 306 PetscFunctionReturn(0); 307 } 308 309 /* 310 Actual intialization function; any functions claiming to initialize PetscDevice or 311 PetscDeviceContext will have to run through this one 312 */ 313 PetscErrorCode PetscDeviceInitializeDefaultDevice_Internal(PetscDeviceType type, PetscInt defaultDeviceId) { 314 PetscFunctionBegin; 315 PetscValidDeviceType(type, 1); 316 if (PetscLikely(PetscDeviceInitialized(type))) PetscFunctionReturn(0); 317 PetscAssert(!defaultDevices[type], PETSC_COMM_SELF, PETSC_ERR_MEM, "Trying to overwrite existing default device of type %s", PetscDeviceTypes[type]); 318 PetscCall(PetscDeviceCreate(type, defaultDeviceId, &defaultDevices[type])); 319 PetscCall(PetscDeviceConfigure(defaultDevices[type])); 320 initializedDevice[type] = true; 321 PetscFunctionReturn(0); 322 } 323 324 #if PetscDefined(USE_LOG) 325 PETSC_INTERN PetscErrorCode PetscLogInitialize(void); 326 #else 327 #define PetscLogInitialize() 0 328 #endif 329 330 static PetscErrorCode PetscDeviceInitializeTypeFromOptions_Private(MPI_Comm comm, PetscDeviceType type, PetscInt defaultDeviceId, PetscBool defaultView, PetscDeviceInitType *defaultInitType) { 331 PetscFunctionBegin; 332 if (!PetscDeviceConfiguredFor_Internal(type)) { 333 PetscCall(PetscInfo(PETSC_NULLPTR, "PetscDeviceType %s not supported\n", PetscDeviceTypes[type])); 334 defaultDevices[type] = PETSC_NULLPTR; 335 PetscFunctionReturn(0); 336 } 337 PetscCall(PetscInfo(PETSC_NULLPTR, "PetscDeviceType %s supported, initializing\n", PetscDeviceTypes[type])); 338 /* ugly switch needed to pick the right global variable... could maybe do this as a union? */ 339 switch (type) { 340 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA, initialize, comm, &defaultDeviceId, defaultInitType); 341 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP, initialize, comm, &defaultDeviceId, defaultInitType); 342 PETSC_DEVICE_CASE_IF_PETSC_DEFINED(SYCL, initialize, comm, &defaultDeviceId, defaultInitType); 343 default: SETERRQ(comm, PETSC_ERR_PLIB, "PETSc was seemingly configured for PetscDeviceType %s but we've fallen through all cases in a switch", PetscDeviceTypes[type]); 344 } 345 /* 346 defaultInitType and defaultDeviceId now represent what the individual TYPES have decided to 347 initialize as 348 */ 349 if (*defaultInitType == PETSC_DEVICE_INIT_EAGER) { 350 PetscCall(PetscLogInitialize()); 351 PetscCall(PetscInfo(PETSC_NULLPTR, "Eagerly initializing %s PetscDevice\n", PetscDeviceTypes[type])); 352 PetscCall(PetscDeviceInitializeDefaultDevice_Internal(type, defaultDeviceId)); 353 if (defaultView) { 354 PetscViewer vwr; 355 356 PetscCall(PetscViewerASCIIGetStdout(comm, &vwr)); 357 PetscCall(PetscDeviceView(defaultDevices[type], vwr)); 358 } 359 } 360 PetscFunctionReturn(0); 361 } 362 363 /* called from PetscFinalize() do not call yourself! */ 364 static PetscErrorCode PetscDeviceFinalize_Private(void) { 365 PetscFunctionBegin; 366 if (PetscDefined(USE_DEBUG)) { 367 const auto PetscDeviceCheckAllDestroyedAfterFinalize = [] { 368 PetscFunctionBegin; 369 for (auto &&device : defaultDevices) 370 PetscCheck(!device, PETSC_COMM_WORLD, PETSC_ERR_COR, "Device of type '%s' had reference count %" PetscInt_FMT " and was not fully destroyed during PetscFinalize()", PetscDeviceTypes[device->type], device->refcnt); 371 PetscFunctionReturn(0); 372 }; 373 /* 374 you might be thinking, why on earth are you registered yet another finalizer in a 375 function already called during PetscRegisterFinalizeAll()? If this seems stupid it's 376 because it is. 377 378 The crux of the problem is that the initializer (and therefore the ~finalizer~) of 379 PetscDeviceContext is guaranteed to run after PetscDevice's. So if the global context had 380 a default PetscDevice attached, that PetscDevice will have a reference count >0 and hence 381 won't be destroyed yet. So we need to repeat the check that all devices have been 382 destroyed again ~after~ the global context is destroyed. In summary: 383 384 1. This finalizer runs and destroys all devices, except it may not because the global 385 context may still hold a reference! 386 2. The global context finalizer runs and does the final reference count decrement 387 required, which actually destroys the held device. 388 3. Our newly added finalizer runs and checks that all is well. 389 */ 390 PetscCall(PetscRegisterFinalize(PetscDeviceCheckAllDestroyedAfterFinalize)); 391 } 392 for (auto &&device : defaultDevices) PetscCall(PetscDeviceDestroy(&device)); 393 PetscCallCXX(initializedDevice.fill(false)); 394 PetscFunctionReturn(0); 395 } 396 397 /* 398 Begins the init proceeedings for the entire PetscDevice stack. there are 3 stages of 399 initialization types: 400 401 1. defaultInitType - how does PetscDevice as a whole expect to initialize? 402 2. subTypeDefaultInitType - how does each PetscDevice implementation expect to initialize? 403 e.g. you may want to blanket disable PetscDevice init (and disable say Kokkos init), but 404 have all CUDA devices still initialize. 405 406 All told the following happens: 407 408 0. defaultInitType -> LAZY 409 1. Check for log_view/log_summary, if yes defaultInitType -> EAGER 410 2. PetscDevice initializes each sub type with deviceDefaultInitType. 411 2.1 Each enabled PetscDevice sub-type then does the above disable or view check in addition 412 to checking for specific device init. if view or specific device init 413 subTypeDefaultInitType -> EAGER. disabled once again overrides all. 414 */ 415 PetscErrorCode PetscDeviceInitializeFromOptions_Internal(MPI_Comm comm) { 416 auto defaultView = PETSC_FALSE; 417 auto initializeDeviceContextEagerly = PETSC_FALSE; 418 auto defaultDevice = PetscInt{PETSC_DECIDE}; 419 auto deviceContextInitDevice = PETSC_DEVICE_DEFAULT; 420 PetscDeviceInitType defaultInitType; 421 422 PetscFunctionBegin; 423 if (PetscDefined(USE_DEBUG)) { 424 int result; 425 426 PetscCallMPI(MPI_Comm_compare(comm, PETSC_COMM_WORLD, &result)); 427 /* in order to accurately assign ranks to gpus we need to get the MPI_Comm_rank of the 428 * global space */ 429 if (PetscUnlikely(result != MPI_IDENT)) { 430 char name[MPI_MAX_OBJECT_NAME] = {}; 431 int len; /* unused */ 432 433 PetscCallMPI(MPI_Comm_get_name(comm, name, &len)); 434 SETERRQ(comm, PETSC_ERR_MPI, "Default devices being initialized on MPI_Comm '%s' not PETSC_COMM_WORLD", name); 435 } 436 } 437 comm = PETSC_COMM_WORLD; /* from this point on we assume we're on PETSC_COMM_WORLD */ 438 PetscCall(PetscRegisterFinalize(PetscDeviceFinalize_Private)); 439 440 { 441 PetscInt initIdx = PETSC_DEVICE_INIT_LAZY; 442 PetscBool flg; 443 444 PetscCall(PetscOptionsHasName(PETSC_NULLPTR, PETSC_NULLPTR, "-log_view_gpu_time", &flg)); 445 if (flg) PetscCall(PetscLogGpuTime()); 446 447 /* ----------------------------------------------------------------------------------- */ 448 /* Global PetscDevice Options */ 449 /* ----------------------------------------------------------------------------------- */ 450 PetscOptionsBegin(comm, PETSC_NULLPTR, "PetscDevice Options", "Sys"); 451 PetscCall(PetscOptionsEList("-device_enable", "How (or whether) to initialize PetscDevices", "PetscDeviceInitializeFromOptions_Internal()", PetscDeviceInitTypes, 3, PetscDeviceInitTypes[initIdx], &initIdx, PETSC_NULLPTR)); 452 PetscCall(PetscOptionsRangeInt("-device_select", "Which device to use. Pass " PetscStringize(PETSC_DECIDE) " to have PETSc decide or (given they exist) [0-NUM_DEVICE) for a specific device", "PetscDeviceCreate()", defaultDevice, &defaultDevice, PETSC_NULLPTR, PETSC_DECIDE, std::numeric_limits<int>::max())); 453 PetscCall(PetscOptionsBool("-device_view", "Display device information and assignments (forces eager initialization)", PETSC_NULLPTR, defaultView, &defaultView, &flg)); 454 PetscOptionsEnd(); 455 456 if (initIdx == PETSC_DEVICE_INIT_NONE) { 457 /* disabled all device initialization if devices are globally disabled */ 458 PetscCheck(defaultDevice == PETSC_DECIDE, comm, PETSC_ERR_USER_INPUT, "You have disabled devices but also specified a particular device to use, these options are mutually exlusive"); 459 defaultView = PETSC_FALSE; 460 } else { 461 defaultView = static_cast<decltype(defaultView)>(defaultView && flg); 462 if (defaultView) initIdx = PETSC_DEVICE_INIT_EAGER; 463 } 464 defaultInitType = static_cast<decltype(defaultInitType)>(initIdx); 465 } 466 static_assert((PETSC_DEVICE_INVALID == 0) && (PETSC_DEVICE_MAX < std::numeric_limits<int>::max()), ""); 467 for (int i = 1; i < PETSC_DEVICE_MAX; ++i) { 468 const auto deviceType = static_cast<PetscDeviceType>(i); 469 auto initType = defaultInitType; 470 471 PetscCall(PetscDeviceInitializeTypeFromOptions_Private(comm, deviceType, defaultDevice, defaultView, &initType)); 472 if (PetscDeviceConfiguredFor_Internal(deviceType) && (initType == PETSC_DEVICE_INIT_EAGER)) { 473 initializeDeviceContextEagerly = PETSC_TRUE; 474 deviceContextInitDevice = deviceType; 475 PetscCall(PetscInfo(PETSC_NULLPTR, "PetscDevice %s set as default device type due to eager initialization\n", PetscDeviceTypes[deviceType])); 476 } 477 } 478 if (initializeDeviceContextEagerly) { 479 PetscDeviceContext dctx; 480 481 /* 482 somewhat inefficient here as the device context is potentially fully set up twice (once 483 when retrieved then the second time if setfromoptions makes changes) 484 */ 485 PetscCall(PetscInfo(PETSC_NULLPTR, "Eagerly initializing PetscDeviceContext with %s device\n", PetscDeviceTypes[deviceContextInitDevice])); 486 PetscCall(PetscDeviceContextSetRootDeviceType_Internal(deviceContextInitDevice)); 487 PetscCall(PetscDeviceContextGetCurrentContext(&dctx)); 488 PetscCall(PetscDeviceContextSetFromOptions(comm, "root_", dctx)); 489 PetscCall(PetscDeviceContextSetUp(dctx)); 490 } 491 PetscFunctionReturn(0); 492 } 493 494 /* Get the default PetscDevice for a particular type and constructs them if lazily initialized. */ 495 PetscErrorCode PetscDeviceGetDefaultForType_Internal(PetscDeviceType type, PetscDevice *device) { 496 PetscFunctionBegin; 497 PetscValidPointer(device, 2); 498 PetscCall(PetscDeviceInitialize(type)); 499 *device = defaultDevices[type]; 500 PetscFunctionReturn(0); 501 } 502