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