xref: /petsc/src/sys/objects/device/interface/device.cxx (revision bbfde98d24007149e2e73427fb24bfdd802107d3)
10e6b6b59SJacob Faibussowitsch #include "petscdevice_interface_internal.hpp" /*I <petscdevice.h> I*/
217f48955SJacob Faibussowitsch #include <petsc/private/petscadvancedmacros.h>
3030f984aSJacob Faibussowitsch 
4*bbfde98dSJacob Faibussowitsch #include <petsc/private/cpp/register_finalize.hpp>
5*bbfde98dSJacob Faibussowitsch 
60e6b6b59SJacob Faibussowitsch #include "../impls/host/hostdevice.hpp"
7*bbfde98dSJacob Faibussowitsch #if PetscDefined(HAVE_CUPM)
80e6b6b59SJacob Faibussowitsch   #include "../impls/cupm/cupmdevice.hpp"
9*bbfde98dSJacob Faibussowitsch #endif
10*bbfde98dSJacob Faibussowitsch #if PetscDefined(HAVE_SYCL)
110e6b6b59SJacob Faibussowitsch   #include "../impls/sycl/sycldevice.hpp"
12*bbfde98dSJacob Faibussowitsch #endif
130e6b6b59SJacob Faibussowitsch 
140e6b6b59SJacob Faibussowitsch #include <utility> // std::make_pair
150e6b6b59SJacob Faibussowitsch 
160e6b6b59SJacob Faibussowitsch using namespace Petsc::device;
17030f984aSJacob Faibussowitsch 
18*bbfde98dSJacob Faibussowitsch namespace
19*bbfde98dSJacob Faibussowitsch {
20*bbfde98dSJacob Faibussowitsch 
21cf3a2253SJacob Faibussowitsch /*
22cf3a2253SJacob Faibussowitsch   note to anyone adding more classes, the name must be ALL_CAPS_SHORT_NAME + Device exactly to
23cf3a2253SJacob Faibussowitsch   be picked up by the switch-case macros below
24cf3a2253SJacob Faibussowitsch */
25*bbfde98dSJacob Faibussowitsch host::Device HOSTDevice{PetscDeviceContextCreate_HOST};
26030f984aSJacob Faibussowitsch #if PetscDefined(HAVE_CUDA)
27*bbfde98dSJacob Faibussowitsch cupm::Device<cupm::DeviceType::CUDA> CUDADevice{PetscDeviceContextCreate_CUDA};
28030f984aSJacob Faibussowitsch #endif
29030f984aSJacob Faibussowitsch #if PetscDefined(HAVE_HIP)
30*bbfde98dSJacob Faibussowitsch cupm::Device<cupm::DeviceType::HIP> HIPDevice{PetscDeviceContextCreate_HIP};
31030f984aSJacob Faibussowitsch #endif
32a2158755SJunchao Zhang #if PetscDefined(HAVE_SYCL)
33*bbfde98dSJacob Faibussowitsch sycl::Device SYCLDevice{PetscDeviceContextCreate_SYCL};
34a2158755SJunchao Zhang #endif
35030f984aSJacob Faibussowitsch 
36*bbfde98dSJacob Faibussowitsch } // namespace
37*bbfde98dSJacob Faibussowitsch 
3817f48955SJacob Faibussowitsch #define PETSC_DEVICE_CASE(IMPLS, func, ...) \
3917f48955SJacob Faibussowitsch   case PetscConcat_(PETSC_DEVICE_, IMPLS): { \
409566063dSJacob Faibussowitsch     PetscCall(PetscConcat_(IMPLS, Device).func(__VA_ARGS__)); \
4117f48955SJacob Faibussowitsch   } break
42a4af0ceeSJacob Faibussowitsch 
43394bf645SJacob Faibussowitsch #define PETSC_VOID_0(...) ((void)0)
44394bf645SJacob Faibussowitsch 
45cf3a2253SJacob Faibussowitsch /*
46cf3a2253SJacob Faibussowitsch   Suppose you have:
47cf3a2253SJacob Faibussowitsch 
48cf3a2253SJacob Faibussowitsch   CUDADevice.myFunction(arg1,arg2)
49cf3a2253SJacob Faibussowitsch 
50cf3a2253SJacob Faibussowitsch   that you would like to conditionally define and call in a switch-case:
51cf3a2253SJacob Faibussowitsch 
52cf3a2253SJacob Faibussowitsch   switch(PetscDeviceType) {
53cf3a2253SJacob Faibussowitsch   #if PetscDefined(HAVE_CUDA)
54cf3a2253SJacob Faibussowitsch   case PETSC_DEVICE_CUDA: {
559566063dSJacob Faibussowitsch     PetscCall(CUDADevice.myFunction(arg1,arg2));
56cf3a2253SJacob Faibussowitsch   } break;
57cf3a2253SJacob Faibussowitsch   #endif
58cf3a2253SJacob Faibussowitsch   }
59cf3a2253SJacob Faibussowitsch 
60cf3a2253SJacob Faibussowitsch   then calling this macro:
61cf3a2253SJacob Faibussowitsch 
62cf3a2253SJacob Faibussowitsch   PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA,myFunction,arg1,arg2)
63cf3a2253SJacob Faibussowitsch 
64cf3a2253SJacob Faibussowitsch   will expand to the following case statement:
65cf3a2253SJacob Faibussowitsch 
66cf3a2253SJacob Faibussowitsch   case PETSC_DEVICE_CUDA: {
679566063dSJacob Faibussowitsch     PetscCall(CUDADevice.myFunction(arg1,arg2));
68cf3a2253SJacob Faibussowitsch   } break
69cf3a2253SJacob Faibussowitsch 
70cf3a2253SJacob Faibussowitsch   if PetscDefined(HAVE_CUDA) evaluates to 1, and expand to nothing otherwise
7117f48955SJacob Faibussowitsch */
72394bf645SJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED(IMPLS, func, ...) PetscIfPetscDefined(PetscConcat_(HAVE_, IMPLS), PETSC_DEVICE_CASE, PETSC_VOID_0)(IMPLS, func, __VA_ARGS__)
73030f984aSJacob Faibussowitsch 
74030f984aSJacob Faibussowitsch /*@C
75811af0c4SBarry Smith   PetscDeviceCreate - Get a new handle for a particular device (often a GPU) type
76030f984aSJacob Faibussowitsch 
770e6b6b59SJacob Faibussowitsch   Not Collective
78030f984aSJacob Faibussowitsch 
79f1a722f8SMatthew G. Knepley   Input Parameters:
80811af0c4SBarry Smith + type  - The type of `PetscDevice`
81811af0c4SBarry Smith - devid - The numeric ID# of the device (pass `PETSC_DECIDE` to assign automatically)
82030f984aSJacob Faibussowitsch 
83030f984aSJacob Faibussowitsch   Output Parameter:
84811af0c4SBarry Smith . device - The `PetscDevice`
85030f984aSJacob Faibussowitsch 
862fe279fdSBarry Smith   Level: beginner
872fe279fdSBarry Smith 
88030f984aSJacob Faibussowitsch   Notes:
890e6b6b59SJacob Faibussowitsch   This routine may initialize `PetscDevice`. If this is the case, it may cause some sort of
900e6b6b59SJacob Faibussowitsch   device synchronization.
91a4af0ceeSJacob Faibussowitsch 
92811af0c4SBarry Smith   `devid` is what you might pass to `cudaSetDevice()` for example.
93030f984aSJacob Faibussowitsch 
940e6b6b59SJacob Faibussowitsch .seealso: `PetscDevice`, `PetscDeviceInitType`,
950e6b6b59SJacob Faibussowitsch `PetscDeviceInitialize()`, `PetscDeviceInitialized()`, `PetscDeviceConfigure()`,
960e6b6b59SJacob Faibussowitsch `PetscDeviceView()`, `PetscDeviceDestroy()`
97030f984aSJacob Faibussowitsch @*/
98d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceCreate(PetscDeviceType type, PetscInt devid, PetscDevice *device)
99d71ae5a4SJacob Faibussowitsch {
100030f984aSJacob Faibussowitsch   static PetscInt PetscDeviceCounter = 0;
101030f984aSJacob Faibussowitsch 
102030f984aSJacob Faibussowitsch   PetscFunctionBegin;
103a4af0ceeSJacob Faibussowitsch   PetscValidDeviceType(type, 1);
104a4af0ceeSJacob Faibussowitsch   PetscValidPointer(device, 3);
1059566063dSJacob Faibussowitsch   PetscCall(PetscDeviceInitializePackage());
1060e6b6b59SJacob Faibussowitsch   PetscCall(PetscNew(device));
1070e6b6b59SJacob Faibussowitsch   (*device)->id     = PetscDeviceCounter++;
1080e6b6b59SJacob Faibussowitsch   (*device)->type   = type;
1090e6b6b59SJacob Faibussowitsch   (*device)->refcnt = 1;
110cf3a2253SJacob Faibussowitsch   /*
11191c35059SPierre Jolivet     if you are adding a device, you also need to add its initialization in
112cf3a2253SJacob Faibussowitsch     PetscDeviceInitializeTypeFromOptions_Private() below
113cf3a2253SJacob Faibussowitsch   */
114a4af0ceeSJacob Faibussowitsch   switch (type) {
1150e6b6b59SJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HOST, getDevice, *device, devid);
1160e6b6b59SJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA, getDevice, *device, devid);
1170e6b6b59SJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP, getDevice, *device, devid);
1180e6b6b59SJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(SYCL, getDevice, *device, devid);
119030f984aSJacob Faibussowitsch   default:
12017f48955SJacob Faibussowitsch     /* in case the above macros expand to nothing this silences any unused variable warnings */
12117f48955SJacob Faibussowitsch     (void)(devid);
12298921bdaSJacob Faibussowitsch     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]);
123030f984aSJacob Faibussowitsch   }
1243ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
125030f984aSJacob Faibussowitsch }
126030f984aSJacob Faibussowitsch 
127030f984aSJacob Faibussowitsch /*@C
128811af0c4SBarry Smith   PetscDeviceDestroy - Free a `PetscDevice`
129030f984aSJacob Faibussowitsch 
1300e6b6b59SJacob Faibussowitsch   Not Collective
131030f984aSJacob Faibussowitsch 
132030f984aSJacob Faibussowitsch   Input Parameter:
1330e6b6b59SJacob Faibussowitsch . device - The `PetscDevice`
134030f984aSJacob Faibussowitsch 
135030f984aSJacob Faibussowitsch   Level: beginner
136030f984aSJacob Faibussowitsch 
1370e6b6b59SJacob Faibussowitsch .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceView()`,
1380e6b6b59SJacob Faibussowitsch `PetscDeviceGetType()`, `PetscDeviceGetDeviceId()`
139030f984aSJacob Faibussowitsch @*/
140d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceDestroy(PetscDevice *device)
141d71ae5a4SJacob Faibussowitsch {
142a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
1430e6b6b59SJacob Faibussowitsch   PetscValidPointer(device, 1);
1443ba16761SJacob Faibussowitsch   if (!*device) PetscFunctionReturn(PETSC_SUCCESS);
145a4af0ceeSJacob Faibussowitsch   PetscValidDevice(*device, 1);
1469566063dSJacob Faibussowitsch   PetscCall(PetscDeviceDereference_Internal(*device));
147a4af0ceeSJacob Faibussowitsch   if ((*device)->refcnt) {
1480e6b6b59SJacob Faibussowitsch     *device = nullptr;
1493ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
150030f984aSJacob Faibussowitsch   }
1519566063dSJacob Faibussowitsch   PetscCall(PetscFree((*device)->data));
1529566063dSJacob Faibussowitsch   PetscCall(PetscFree(*device));
1533ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
154030f984aSJacob Faibussowitsch }
155030f984aSJacob Faibussowitsch 
156a4af0ceeSJacob Faibussowitsch /*@C
157811af0c4SBarry Smith   PetscDeviceConfigure - Configure a particular `PetscDevice`
158030f984aSJacob Faibussowitsch 
1590e6b6b59SJacob Faibussowitsch   Not Collective
160a4af0ceeSJacob Faibussowitsch 
161a4af0ceeSJacob Faibussowitsch   Input Parameter:
162811af0c4SBarry Smith . device - The `PetscDevice` to configure
163a4af0ceeSJacob Faibussowitsch 
1642fe279fdSBarry Smith   Level: beginner
1652fe279fdSBarry Smith 
1660e6b6b59SJacob Faibussowitsch   Notes:
1670e6b6b59SJacob Faibussowitsch   The user should not assume that this is a cheap operation.
168a4af0ceeSJacob Faibussowitsch 
1690e6b6b59SJacob Faibussowitsch .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceView()`, `PetscDeviceDestroy()`,
1700e6b6b59SJacob Faibussowitsch `PetscDeviceGetType()`, `PetscDeviceGetDeviceId()`
171a4af0ceeSJacob Faibussowitsch @*/
172d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceConfigure(PetscDevice device)
173d71ae5a4SJacob Faibussowitsch {
174030f984aSJacob Faibussowitsch   PetscFunctionBegin;
175a4af0ceeSJacob Faibussowitsch   PetscValidDevice(device, 1);
176cf3a2253SJacob Faibussowitsch   /*
177cf3a2253SJacob Faibussowitsch     if no available configuration is available, this cascades all the way down to default
178cf3a2253SJacob Faibussowitsch     and error
179cf3a2253SJacob Faibussowitsch   */
1800e6b6b59SJacob Faibussowitsch   switch (const auto dtype = device->type) {
1810e6b6b59SJacob Faibussowitsch   case PETSC_DEVICE_HOST:
1820e6b6b59SJacob Faibussowitsch     if (PetscDefined(HAVE_HOST)) break; // always true
1839371c9d4SSatish Balay   case PETSC_DEVICE_CUDA:
1849371c9d4SSatish Balay     if (PetscDefined(HAVE_CUDA)) break;
1850e6b6b59SJacob Faibussowitsch     goto error;
1869371c9d4SSatish Balay   case PETSC_DEVICE_HIP:
1879371c9d4SSatish Balay     if (PetscDefined(HAVE_HIP)) break;
1880e6b6b59SJacob Faibussowitsch     goto error;
1899371c9d4SSatish Balay   case PETSC_DEVICE_SYCL:
1909371c9d4SSatish Balay     if (PetscDefined(HAVE_SYCL)) break;
191f4d061e9SPierre Jolivet     goto error;
1920e6b6b59SJacob Faibussowitsch   default:
1930e6b6b59SJacob Faibussowitsch   error:
1940e6b6b59SJacob Faibussowitsch     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "PETSc was not configured for PetscDeviceType %s", PetscDeviceTypes[dtype]);
195a4af0ceeSJacob Faibussowitsch   }
196dbbe0bcdSBarry Smith   PetscUseTypeMethod(device, configure);
1973ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
198a4af0ceeSJacob Faibussowitsch }
199a4af0ceeSJacob Faibussowitsch 
200a4af0ceeSJacob Faibussowitsch /*@C
201811af0c4SBarry Smith   PetscDeviceView - View a `PetscDevice`
202a4af0ceeSJacob Faibussowitsch 
2030e6b6b59SJacob Faibussowitsch   Collective on viewer
204a4af0ceeSJacob Faibussowitsch 
20591e63d38SStefano Zampini   Input Parameters:
206811af0c4SBarry Smith + device - The `PetscDevice` to view
2070e6b6b59SJacob Faibussowitsch - viewer - The `PetscViewer` to view the device with (`NULL` for `PETSC_VIEWER_STDOUT_WORLD`)
208a4af0ceeSJacob Faibussowitsch 
209a4af0ceeSJacob Faibussowitsch   Level: beginner
210a4af0ceeSJacob Faibussowitsch 
2110e6b6b59SJacob Faibussowitsch .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceConfigure()`,
2120e6b6b59SJacob Faibussowitsch `PetscDeviceDestroy()`, `PetscDeviceGetType()`, `PetscDeviceGetDeviceId()`
213a4af0ceeSJacob Faibussowitsch @*/
214d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceView(PetscDevice device, PetscViewer viewer)
215d71ae5a4SJacob Faibussowitsch {
2160e6b6b59SJacob Faibussowitsch   auto      sub = viewer;
2170e6b6b59SJacob Faibussowitsch   PetscBool iascii;
2180e6b6b59SJacob Faibussowitsch 
219a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
220a4af0ceeSJacob Faibussowitsch   PetscValidDevice(device, 1);
2210e6b6b59SJacob Faibussowitsch   if (viewer) {
222a4af0ceeSJacob Faibussowitsch     PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2230e6b6b59SJacob Faibussowitsch     PetscCall(PetscObjectTypeCompare(PetscObjectCast(viewer), PETSCVIEWERASCII, &iascii));
2240e6b6b59SJacob Faibussowitsch   } else {
2250e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &viewer));
2260e6b6b59SJacob Faibussowitsch     iascii = PETSC_TRUE;
2270e6b6b59SJacob Faibussowitsch   }
2280e6b6b59SJacob Faibussowitsch 
2290e6b6b59SJacob Faibussowitsch   if (iascii) {
2300e6b6b59SJacob Faibussowitsch     auto        dtype = PETSC_DEVICE_HOST;
2310e6b6b59SJacob Faibussowitsch     MPI_Comm    comm;
2320e6b6b59SJacob Faibussowitsch     PetscMPIInt size;
2330e6b6b59SJacob Faibussowitsch     PetscInt    id = 0;
2340e6b6b59SJacob Faibussowitsch 
2350e6b6b59SJacob Faibussowitsch     PetscCall(PetscObjectGetComm(PetscObjectCast(viewer), &comm));
2360e6b6b59SJacob Faibussowitsch     PetscCallMPI(MPI_Comm_size(comm, &size));
2370e6b6b59SJacob Faibussowitsch 
2380e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceGetDeviceId(device, &id));
2390e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceGetType(device, &dtype));
2400e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerGetSubViewer(viewer, PETSC_COMM_SELF, &sub));
2410e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(sub, "PetscDevice Object: %d MPI %s\n", size, size == 1 ? "process" : "processes"));
2420e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPushTab(sub));
2430e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(sub, "type: %s\n", PetscDeviceTypes[dtype]));
2440e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(sub, "id: %" PetscInt_FMT "\n", id));
2450e6b6b59SJacob Faibussowitsch   }
2460e6b6b59SJacob Faibussowitsch 
2470e6b6b59SJacob Faibussowitsch   // see if impls has extra viewer stuff
2480e6b6b59SJacob Faibussowitsch   PetscTryTypeMethod(device, view, sub);
2490e6b6b59SJacob Faibussowitsch 
2500e6b6b59SJacob Faibussowitsch   if (iascii) {
2510e6b6b59SJacob Faibussowitsch     // undo the ASCII specific stuff
2520e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerASCIIPopTab(sub));
2530e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerRestoreSubViewer(viewer, PETSC_COMM_SELF, &sub));
2540e6b6b59SJacob Faibussowitsch     PetscCall(PetscViewerFlush(viewer));
2550e6b6b59SJacob Faibussowitsch   }
2563ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
257a4af0ceeSJacob Faibussowitsch }
258a4af0ceeSJacob Faibussowitsch 
25991e63d38SStefano Zampini /*@C
2600e6b6b59SJacob Faibussowitsch   PetscDeviceGetType - Get the type of device
26191e63d38SStefano Zampini 
2620e6b6b59SJacob Faibussowitsch   Not Collective
26391e63d38SStefano Zampini 
26491e63d38SStefano Zampini   Input Parameter:
265811af0c4SBarry Smith . device - The `PetscDevice`
26691e63d38SStefano Zampini 
26791e63d38SStefano Zampini   Output Parameter:
2680e6b6b59SJacob Faibussowitsch . type - The `PetscDeviceType`
26991e63d38SStefano Zampini 
27091e63d38SStefano Zampini   Level: beginner
27191e63d38SStefano Zampini 
2720e6b6b59SJacob Faibussowitsch .seealso: `PetscDevice`, `PetscDeviceType`, `PetscDeviceSetDefaultDeviceType()`,
2730e6b6b59SJacob Faibussowitsch `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceDestroy()`,
2740e6b6b59SJacob Faibussowitsch `PetscDeviceGetDeviceId()`, `PETSC_DEVICE_DEFAULT()`
2750e6b6b59SJacob Faibussowitsch @*/
276d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceGetType(PetscDevice device, PetscDeviceType *type)
277d71ae5a4SJacob Faibussowitsch {
2780e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2790e6b6b59SJacob Faibussowitsch   PetscValidDevice(device, 1);
2800e6b6b59SJacob Faibussowitsch   PetscValidPointer(type, 2);
2810e6b6b59SJacob Faibussowitsch   *type = device->type;
2823ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2830e6b6b59SJacob Faibussowitsch }
2840e6b6b59SJacob Faibussowitsch 
2850e6b6b59SJacob Faibussowitsch /*@C
2860e6b6b59SJacob Faibussowitsch   PetscDeviceGetDeviceId - Get the device ID for a `PetscDevice`
2870e6b6b59SJacob Faibussowitsch 
2880e6b6b59SJacob Faibussowitsch   Not Collective
2890e6b6b59SJacob Faibussowitsch 
2900e6b6b59SJacob Faibussowitsch   Input Parameter:
2910e6b6b59SJacob Faibussowitsch . device - The `PetscDevice`
2920e6b6b59SJacob Faibussowitsch 
2930e6b6b59SJacob Faibussowitsch   Output Parameter:
2940e6b6b59SJacob Faibussowitsch . id - The id
2950e6b6b59SJacob Faibussowitsch 
2962fe279fdSBarry Smith   Level: beginner
2972fe279fdSBarry Smith 
2980e6b6b59SJacob Faibussowitsch   Notes:
2990e6b6b59SJacob Faibussowitsch   The returned ID may have been assigned by the underlying device backend. For example if the
3000e6b6b59SJacob Faibussowitsch   backend is CUDA then `id` is exactly the value returned by `cudaGetDevice()` at the time when
3010e6b6b59SJacob Faibussowitsch   this device was configured.
3020e6b6b59SJacob Faibussowitsch 
3030e6b6b59SJacob Faibussowitsch .seealso: `PetscDevice`, `PetscDeviceCreate()`, `PetscDeviceGetType()`
30491e63d38SStefano Zampini @*/
305d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceGetDeviceId(PetscDevice device, PetscInt *id)
306d71ae5a4SJacob Faibussowitsch {
30791e63d38SStefano Zampini   PetscFunctionBegin;
30891e63d38SStefano Zampini   PetscValidDevice(device, 1);
30991e63d38SStefano Zampini   PetscValidIntPointer(id, 2);
31091e63d38SStefano Zampini   *id = device->deviceId;
3113ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
31291e63d38SStefano Zampini }
31391e63d38SStefano Zampini 
314*bbfde98dSJacob Faibussowitsch namespace
315*bbfde98dSJacob Faibussowitsch {
316*bbfde98dSJacob Faibussowitsch 
3170e6b6b59SJacob Faibussowitsch struct DefaultDeviceType : public Petsc::RegisterFinalizeable<DefaultDeviceType> {
3180e6b6b59SJacob Faibussowitsch   PetscDeviceType type = PETSC_DEVICE_HARDWARE_DEFAULT_TYPE;
3190e6b6b59SJacob Faibussowitsch 
320089fb57cSJacob Faibussowitsch   PetscErrorCode finalize_() noexcept
321d71ae5a4SJacob Faibussowitsch   {
3220e6b6b59SJacob Faibussowitsch     PetscFunctionBegin;
3230e6b6b59SJacob Faibussowitsch     type = PETSC_DEVICE_HARDWARE_DEFAULT_TYPE;
3243ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
3250e6b6b59SJacob Faibussowitsch   }
3260e6b6b59SJacob Faibussowitsch };
3270e6b6b59SJacob Faibussowitsch 
328*bbfde98dSJacob Faibussowitsch auto default_device_type = DefaultDeviceType();
329*bbfde98dSJacob Faibussowitsch 
330*bbfde98dSJacob Faibussowitsch } // namespace
3310e6b6b59SJacob Faibussowitsch 
3320e6b6b59SJacob Faibussowitsch /*@C
3330e6b6b59SJacob Faibussowitsch   PETSC_DEVICE_DEFAULT - Retrieve the current default `PetscDeviceType`
3340e6b6b59SJacob Faibussowitsch 
3350e6b6b59SJacob Faibussowitsch   Not Collective
3360e6b6b59SJacob Faibussowitsch 
3372fe279fdSBarry Smith   Level: beginner
3382fe279fdSBarry Smith 
3390e6b6b59SJacob Faibussowitsch   Notes:
3400e6b6b59SJacob Faibussowitsch   Unless selected by the user, the default device is selected in the following order\:
3410e6b6b59SJacob Faibussowitsch   `PETSC_DEVICE_HIP`, `PETSC_DEVICE_CUDA`, `PETSC_DEVICE_SYCL`, `PETSC_DEVICE_HOST`.
3420e6b6b59SJacob Faibussowitsch 
3430e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceType`, `PetscDeviceSetDefaultDeviceType()`, `PetscDeviceGetType()`
3440e6b6b59SJacob Faibussowitsch @*/
345d71ae5a4SJacob Faibussowitsch PetscDeviceType PETSC_DEVICE_DEFAULT(void)
346d71ae5a4SJacob Faibussowitsch {
3470e6b6b59SJacob Faibussowitsch   return default_device_type.type;
3480e6b6b59SJacob Faibussowitsch }
3490e6b6b59SJacob Faibussowitsch 
3500e6b6b59SJacob Faibussowitsch /*@C
3510e6b6b59SJacob Faibussowitsch   PetscDeviceSetDefaultDeviceType - Set the default device type for `PetscDevice`
3520e6b6b59SJacob Faibussowitsch 
3530e6b6b59SJacob Faibussowitsch   Not Collective
3540e6b6b59SJacob Faibussowitsch 
3550e6b6b59SJacob Faibussowitsch   Input Parameter:
3560e6b6b59SJacob Faibussowitsch . type - the new default device type
3570e6b6b59SJacob Faibussowitsch 
3582fe279fdSBarry Smith   Level: beginner
3592fe279fdSBarry Smith 
3600e6b6b59SJacob Faibussowitsch   Notes:
3610e6b6b59SJacob Faibussowitsch   This sets the `PetscDeviceType` returned by `PETSC_DEVICE_DEFAULT()`.
3620e6b6b59SJacob Faibussowitsch 
3630e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceType`, `PetscDeviceGetType`,
3640e6b6b59SJacob Faibussowitsch @*/
365d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceSetDefaultDeviceType(PetscDeviceType type)
366d71ae5a4SJacob Faibussowitsch {
3670e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3680e6b6b59SJacob Faibussowitsch   PetscValidDeviceType(type, 1);
3690e6b6b59SJacob Faibussowitsch   if (default_device_type.type != type) {
3700e6b6b59SJacob Faibussowitsch     // no need to waster a PetscRegisterFinalize() slot if we don't change it
3710e6b6b59SJacob Faibussowitsch     default_device_type.type = type;
3720e6b6b59SJacob Faibussowitsch     PetscCall(default_device_type.register_finalize());
3730e6b6b59SJacob Faibussowitsch   }
3743ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
3750e6b6b59SJacob Faibussowitsch }
3760e6b6b59SJacob Faibussowitsch 
377*bbfde98dSJacob Faibussowitsch namespace
378*bbfde98dSJacob Faibussowitsch {
379*bbfde98dSJacob Faibussowitsch 
380*bbfde98dSJacob Faibussowitsch std::array<std::pair<PetscDevice, bool>, PETSC_DEVICE_MAX> defaultDevices = {};
3810e6b6b59SJacob Faibussowitsch 
3820e6b6b59SJacob Faibussowitsch /*
383da81f932SPierre Jolivet   Actual initialization function; any functions claiming to initialize PetscDevice or
3840e6b6b59SJacob Faibussowitsch   PetscDeviceContext will have to run through this one
3850e6b6b59SJacob Faibussowitsch */
386*bbfde98dSJacob Faibussowitsch PetscErrorCode PetscDeviceInitializeDefaultDevice_Internal(PetscDeviceType type, PetscInt defaultDeviceId)
387d71ae5a4SJacob Faibussowitsch {
3880e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3890e6b6b59SJacob Faibussowitsch   PetscValidDeviceType(type, 1);
3900e6b6b59SJacob Faibussowitsch   if (PetscUnlikely(!PetscDeviceInitialized(type))) {
3910e6b6b59SJacob Faibussowitsch     auto &dev  = defaultDevices[type].first;
3920e6b6b59SJacob Faibussowitsch     auto &init = defaultDevices[type].second;
3930e6b6b59SJacob Faibussowitsch 
3940e6b6b59SJacob Faibussowitsch     PetscAssert(!dev, PETSC_COMM_SELF, PETSC_ERR_MEM, "Trying to overwrite existing default device of type %s", PetscDeviceTypes[type]);
3950e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceCreate(type, defaultDeviceId, &dev));
3960e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceConfigure(dev));
3970e6b6b59SJacob Faibussowitsch     init = true;
3980e6b6b59SJacob Faibussowitsch   }
3993ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
4000e6b6b59SJacob Faibussowitsch }
401a4af0ceeSJacob Faibussowitsch 
402*bbfde98dSJacob Faibussowitsch } // namespace
403*bbfde98dSJacob Faibussowitsch 
404a4af0ceeSJacob Faibussowitsch /*@C
405811af0c4SBarry Smith   PetscDeviceInitialize - Initialize `PetscDevice`
406a4af0ceeSJacob Faibussowitsch 
4070e6b6b59SJacob Faibussowitsch   Not Collective
408a4af0ceeSJacob Faibussowitsch 
409a4af0ceeSJacob Faibussowitsch   Input Parameter:
410811af0c4SBarry Smith . type - The `PetscDeviceType` to initialize
411a4af0ceeSJacob Faibussowitsch 
4122fe279fdSBarry Smith   Level: beginner
4132fe279fdSBarry Smith 
4140e6b6b59SJacob Faibussowitsch   Notes:
4150e6b6b59SJacob Faibussowitsch   Eagerly initializes the corresponding `PetscDeviceType` if needed. If this is the case it may
4160e6b6b59SJacob Faibussowitsch   result in device synchronization.
417a4af0ceeSJacob Faibussowitsch 
4180e6b6b59SJacob Faibussowitsch .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialized()`,
4190e6b6b59SJacob Faibussowitsch `PetscDeviceCreate()`, `PetscDeviceDestroy()`
420a4af0ceeSJacob Faibussowitsch @*/
421d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceInitialize(PetscDeviceType type)
422d71ae5a4SJacob Faibussowitsch {
423a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
424a4af0ceeSJacob Faibussowitsch   PetscValidDeviceType(type, 1);
4259566063dSJacob Faibussowitsch   PetscCall(PetscDeviceInitializeDefaultDevice_Internal(type, PETSC_DECIDE));
4263ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
427a4af0ceeSJacob Faibussowitsch }
428a4af0ceeSJacob Faibussowitsch 
429a4af0ceeSJacob Faibussowitsch /*@C
430811af0c4SBarry Smith   PetscDeviceInitialized - Determines whether `PetscDevice` is initialized for a particular
431811af0c4SBarry Smith   `PetscDeviceType`
432a4af0ceeSJacob Faibussowitsch 
4330e6b6b59SJacob Faibussowitsch   Not Collective
434a4af0ceeSJacob Faibussowitsch 
435a4af0ceeSJacob Faibussowitsch   Input Parameter:
436811af0c4SBarry Smith . type - The `PetscDeviceType` to check
437a4af0ceeSJacob Faibussowitsch 
4382fe279fdSBarry Smith   Level: beginner
4392fe279fdSBarry Smith 
4400e6b6b59SJacob Faibussowitsch   Notes:
4410e6b6b59SJacob Faibussowitsch   Returns `PETSC_TRUE` if `type` is initialized, `PETSC_FALSE` otherwise.
442a4af0ceeSJacob Faibussowitsch 
443811af0c4SBarry Smith   If one has not configured PETSc for a particular `PetscDeviceType` then this routine will
444811af0c4SBarry Smith   return `PETSC_FALSE` for that `PetscDeviceType`.
445a4af0ceeSJacob Faibussowitsch 
4460e6b6b59SJacob Faibussowitsch .seealso: `PetscDevice`, `PetscDeviceInitType`, `PetscDeviceInitialize()`,
4470e6b6b59SJacob Faibussowitsch `PetscDeviceCreate()`, `PetscDeviceDestroy()`
448a4af0ceeSJacob Faibussowitsch @*/
449d71ae5a4SJacob Faibussowitsch PetscBool PetscDeviceInitialized(PetscDeviceType type)
450d71ae5a4SJacob Faibussowitsch {
4510e6b6b59SJacob Faibussowitsch   return static_cast<PetscBool>(PetscDeviceConfiguredFor_Internal(type) && defaultDevices[type].second);
4520e6b6b59SJacob Faibussowitsch }
4530e6b6b59SJacob Faibussowitsch 
4540e6b6b59SJacob Faibussowitsch /* Get the default PetscDevice for a particular type and constructs them if lazily initialized. */
455d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceGetDefaultForType_Internal(PetscDeviceType type, PetscDevice *device)
456d71ae5a4SJacob Faibussowitsch {
4570e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4580e6b6b59SJacob Faibussowitsch   PetscValidPointer(device, 2);
4590e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceInitialize(type));
4600e6b6b59SJacob Faibussowitsch   *device = defaultDevices[type].first;
4613ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
462a4af0ceeSJacob Faibussowitsch }
463a4af0ceeSJacob Faibussowitsch 
464a16fd2c9SJacob Faibussowitsch /*@C
465a16fd2c9SJacob Faibussowitsch   PetscDeviceGetAttribute - Query a particular attribute of a `PetscDevice`
466a16fd2c9SJacob Faibussowitsch 
4670e6b6b59SJacob Faibussowitsch   Not Collective
468a16fd2c9SJacob Faibussowitsch 
469a16fd2c9SJacob Faibussowitsch   Input Parameters:
470a16fd2c9SJacob Faibussowitsch + device - The `PetscDevice`
471a16fd2c9SJacob Faibussowitsch - attr   - The attribute
472a16fd2c9SJacob Faibussowitsch 
473a16fd2c9SJacob Faibussowitsch   Output Parameter:
474a16fd2c9SJacob Faibussowitsch . value - The value of the attribute
475a16fd2c9SJacob Faibussowitsch 
4762fe279fdSBarry Smith   Level: intermediate
4772fe279fdSBarry Smith 
478a16fd2c9SJacob Faibussowitsch   Notes:
479a16fd2c9SJacob Faibussowitsch   Since different attributes are often different types `value` is a `void *` to accommodate
480a16fd2c9SJacob Faibussowitsch   them all. The underlying type of the attribute is therefore included in the name of the
481da81f932SPierre Jolivet   `PetscDeviceAttribute` responsible for querying it. For example,
482a16fd2c9SJacob Faibussowitsch   `PETSC_DEVICE_ATTR_SIZE_T_SHARED_MEM_PER_BLOCK` is of type `size_t`.
483a16fd2c9SJacob Faibussowitsch 
484a16fd2c9SJacob Faibussowitsch .seealso: `PetscDeviceAtrtibute`, `PetscDeviceConfigure()`, `PetscDevice`
485a16fd2c9SJacob Faibussowitsch @*/
486d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceGetAttribute(PetscDevice device, PetscDeviceAttribute attr, void *value)
487d71ae5a4SJacob Faibussowitsch {
488a16fd2c9SJacob Faibussowitsch   PetscFunctionBegin;
489a16fd2c9SJacob Faibussowitsch   PetscValidDevice(device, 1);
490a16fd2c9SJacob Faibussowitsch   PetscValidDeviceAttribute(attr, 2);
491a16fd2c9SJacob Faibussowitsch   PetscValidPointer(value, 3);
492a16fd2c9SJacob Faibussowitsch   PetscUseTypeMethod(device, getattribute, attr, value);
4933ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
494a16fd2c9SJacob Faibussowitsch }
495a16fd2c9SJacob Faibussowitsch 
496*bbfde98dSJacob Faibussowitsch namespace
497*bbfde98dSJacob Faibussowitsch {
498*bbfde98dSJacob Faibussowitsch 
499*bbfde98dSJacob Faibussowitsch PetscErrorCode PetscDeviceInitializeTypeFromOptions_Private(MPI_Comm comm, PetscDeviceType type, PetscInt defaultDeviceId, PetscBool defaultView, PetscDeviceInitType *defaultInitType)
500d71ae5a4SJacob Faibussowitsch {
501a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
502a4af0ceeSJacob Faibussowitsch   if (!PetscDeviceConfiguredFor_Internal(type)) {
5030e6b6b59SJacob Faibussowitsch     PetscCall(PetscInfo(nullptr, "PetscDeviceType %s not available\n", PetscDeviceTypes[type]));
5040e6b6b59SJacob Faibussowitsch     defaultDevices[type].first = nullptr;
5053ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
506a4af0ceeSJacob Faibussowitsch   }
5070e6b6b59SJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "PetscDeviceType %s available, initializing\n", PetscDeviceTypes[type]));
508a4af0ceeSJacob Faibussowitsch   /* ugly switch needed to pick the right global variable... could maybe do this as a union? */
509a4af0ceeSJacob Faibussowitsch   switch (type) {
5100e6b6b59SJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HOST, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType);
5110e6b6b59SJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType);
5120e6b6b59SJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType);
5130e6b6b59SJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(SYCL, initialize, comm, &defaultDeviceId, &defaultView, defaultInitType);
514d71ae5a4SJacob Faibussowitsch   default:
515d71ae5a4SJacob Faibussowitsch     SETERRQ(comm, PETSC_ERR_PLIB, "PETSc was seemingly configured for PetscDeviceType %s but we've fallen through all cases in a switch", PetscDeviceTypes[type]);
516a4af0ceeSJacob Faibussowitsch   }
517bd2fcf0cSJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "PetscDevice %s initialized, default device id %" PetscInt_FMT ", view %s, init type %s\n", PetscDeviceTypes[type], defaultDeviceId, PetscBools[defaultView], PetscDeviceInitTypes[Petsc::util::to_underlying(*defaultInitType)]));
518cf3a2253SJacob Faibussowitsch   /*
5190e6b6b59SJacob Faibussowitsch     defaultInitType, defaultView  and defaultDeviceId now represent what the individual TYPES
5200e6b6b59SJacob Faibussowitsch     have decided to initialize as
521cf3a2253SJacob Faibussowitsch   */
5220e6b6b59SJacob Faibussowitsch   if ((*defaultInitType == PETSC_DEVICE_INIT_EAGER) || defaultView) {
5230e6b6b59SJacob Faibussowitsch     PetscCall(PetscInfo(nullptr, "Eagerly initializing %s PetscDevice\n", PetscDeviceTypes[type]));
5249566063dSJacob Faibussowitsch     PetscCall(PetscDeviceInitializeDefaultDevice_Internal(type, defaultDeviceId));
5250e6b6b59SJacob Faibussowitsch     if (defaultView) PetscCall(PetscDeviceView(defaultDevices[type].first, nullptr));
5260e6b6b59SJacob Faibussowitsch   }
5273ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5280e6b6b59SJacob Faibussowitsch }
529a4af0ceeSJacob Faibussowitsch 
530*bbfde98dSJacob Faibussowitsch PetscErrorCode PetscDeviceInitializeQueryOptions_Private(MPI_Comm comm, PetscDeviceType *deviceContextInitDevice, PetscDeviceInitType *defaultInitType, PetscInt *defaultDevice, PetscBool *defaultDeviceSet, PetscBool *defaultView)
531d71ae5a4SJacob Faibussowitsch {
5320e6b6b59SJacob Faibussowitsch   PetscInt initIdx       = PETSC_DEVICE_INIT_LAZY;
5330e6b6b59SJacob Faibussowitsch   auto     initDeviceIdx = static_cast<PetscInt>(*deviceContextInitDevice);
5340e6b6b59SJacob Faibussowitsch   auto     flg           = PETSC_FALSE;
5350e6b6b59SJacob Faibussowitsch 
5360e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
5370e6b6b59SJacob Faibussowitsch   PetscCall(PetscOptionsHasName(nullptr, nullptr, "-log_view_gpu_time", &flg));
5380e6b6b59SJacob Faibussowitsch   if (flg) PetscCall(PetscLogGpuTime());
5390e6b6b59SJacob Faibussowitsch 
5400e6b6b59SJacob Faibussowitsch   PetscOptionsBegin(comm, nullptr, "PetscDevice Options", "Sys");
5410e6b6b59SJacob Faibussowitsch   PetscCall(PetscOptionsEList("-device_enable", "How (or whether) to initialize PetscDevices", "PetscDeviceInitialize()", PetscDeviceInitTypes, 3, PetscDeviceInitTypes[initIdx], &initIdx, nullptr));
5420e6b6b59SJacob Faibussowitsch   PetscCall(PetscOptionsEList("-default_device_type", "Set the PetscDeviceType returned by PETSC_DEVICE_DEFAULT()", "PetscDeviceSetDefaultDeviceType()", PetscDeviceTypes, PETSC_DEVICE_MAX, PetscDeviceTypes[initDeviceIdx], &initDeviceIdx, defaultDeviceSet));
5430e6b6b59SJacob Faibussowitsch   PetscCall(PetscOptionsRangeInt("-device_select", "Which device to use. Pass " PetscStringize(PETSC_DECIDE) " to have PETSc decide or (given they exist) [0-" PetscStringize(PETSC_DEVICE_MAX_DEVICES) ") for a specific device", "PetscDeviceCreate()", *defaultDevice, defaultDevice, nullptr, PETSC_DECIDE, PETSC_DEVICE_MAX_DEVICES));
5440e6b6b59SJacob Faibussowitsch   PetscCall(PetscOptionsBool("-device_view", "Display device information and assignments (forces eager initialization)", "PetscDeviceView()", *defaultView, defaultView, &flg));
5450e6b6b59SJacob Faibussowitsch   PetscOptionsEnd();
5460e6b6b59SJacob Faibussowitsch 
5470e6b6b59SJacob Faibussowitsch   if (initIdx == PETSC_DEVICE_INIT_NONE) {
5480e6b6b59SJacob Faibussowitsch     /* disabled all device initialization if devices are globally disabled */
549da81f932SPierre Jolivet     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 exclusive");
5500e6b6b59SJacob Faibussowitsch     *defaultView  = PETSC_FALSE;
5510e6b6b59SJacob Faibussowitsch     initDeviceIdx = PETSC_DEVICE_HOST;
5520e6b6b59SJacob Faibussowitsch   } else {
5530e6b6b59SJacob Faibussowitsch     *defaultView = static_cast<PetscBool>(*defaultView && flg);
5540e6b6b59SJacob Faibussowitsch     if (*defaultView) initIdx = PETSC_DEVICE_INIT_EAGER;
555a4af0ceeSJacob Faibussowitsch   }
5560e6b6b59SJacob Faibussowitsch   *defaultInitType         = PetscDeviceInitTypeCast(initIdx);
5570e6b6b59SJacob Faibussowitsch   *deviceContextInitDevice = PetscDeviceTypeCast(initDeviceIdx);
5583ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
559030f984aSJacob Faibussowitsch }
560030f984aSJacob Faibussowitsch 
561030f984aSJacob Faibussowitsch /* called from PetscFinalize() do not call yourself! */
562*bbfde98dSJacob Faibussowitsch PetscErrorCode PetscDeviceFinalize_Private()
563d71ae5a4SJacob Faibussowitsch {
564030f984aSJacob Faibussowitsch   PetscFunctionBegin;
565a4af0ceeSJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
566bf025ffbSJacob Faibussowitsch     /*
567bf025ffbSJacob Faibussowitsch       you might be thinking, why on earth are you registered yet another finalizer in a
568bf025ffbSJacob Faibussowitsch       function already called during PetscRegisterFinalizeAll()? If this seems stupid it's
569bf025ffbSJacob Faibussowitsch       because it is.
570bf025ffbSJacob Faibussowitsch 
571bf025ffbSJacob Faibussowitsch       The crux of the problem is that the initializer (and therefore the ~finalizer~) of
572bf025ffbSJacob Faibussowitsch       PetscDeviceContext is guaranteed to run after PetscDevice's. So if the global context had
573bf025ffbSJacob Faibussowitsch       a default PetscDevice attached, that PetscDevice will have a reference count >0 and hence
574bf025ffbSJacob Faibussowitsch       won't be destroyed yet. So we need to repeat the check that all devices have been
575bf025ffbSJacob Faibussowitsch       destroyed again ~after~ the global context is destroyed. In summary:
576bf025ffbSJacob Faibussowitsch 
577bf025ffbSJacob Faibussowitsch       1. This finalizer runs and destroys all devices, except it may not because the global
578bf025ffbSJacob Faibussowitsch          context may still hold a reference!
579bf025ffbSJacob Faibussowitsch       2. The global context finalizer runs and does the final reference count decrement
580bf025ffbSJacob Faibussowitsch          required, which actually destroys the held device.
581bf025ffbSJacob Faibussowitsch       3. Our newly added finalizer runs and checks that all is well.
582a4af0ceeSJacob Faibussowitsch     */
583*bbfde98dSJacob Faibussowitsch     PetscCall(PetscRegisterFinalize([] {
584*bbfde98dSJacob Faibussowitsch       PetscFunctionBegin;
585*bbfde98dSJacob Faibussowitsch       for (auto &&device : defaultDevices) {
586*bbfde98dSJacob Faibussowitsch         const auto dev = device.first;
587*bbfde98dSJacob Faibussowitsch 
588*bbfde98dSJacob Faibussowitsch         PetscCheck(!dev, PETSC_COMM_WORLD, PETSC_ERR_COR, "Device of type '%s' had reference count %" PetscInt_FMT " and was not fully destroyed during PetscFinalize()", PetscDeviceTypes[dev->type], dev->refcnt);
589*bbfde98dSJacob Faibussowitsch       }
590*bbfde98dSJacob Faibussowitsch       PetscFunctionReturn(PETSC_SUCCESS);
591*bbfde98dSJacob Faibussowitsch     }));
592a4af0ceeSJacob Faibussowitsch   }
5930e6b6b59SJacob Faibussowitsch   for (auto &&device : defaultDevices) {
5940e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceDestroy(&device.first));
5950e6b6b59SJacob Faibussowitsch     device.second = false;
5960e6b6b59SJacob Faibussowitsch   }
5973ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
598030f984aSJacob Faibussowitsch }
599030f984aSJacob Faibussowitsch 
600*bbfde98dSJacob Faibussowitsch } // namespace
601*bbfde98dSJacob Faibussowitsch 
602cf3a2253SJacob Faibussowitsch /*
603cf3a2253SJacob Faibussowitsch   Begins the init proceeedings for the entire PetscDevice stack. there are 3 stages of
604cf3a2253SJacob Faibussowitsch   initialization types:
605cf3a2253SJacob Faibussowitsch 
606a4af0ceeSJacob Faibussowitsch   1. defaultInitType - how does PetscDevice as a whole expect to initialize?
607a4af0ceeSJacob Faibussowitsch   2. subTypeDefaultInitType - how does each PetscDevice implementation expect to initialize?
608a4af0ceeSJacob Faibussowitsch      e.g. you may want to blanket disable PetscDevice init (and disable say Kokkos init), but
609a4af0ceeSJacob Faibussowitsch      have all CUDA devices still initialize.
610a4af0ceeSJacob Faibussowitsch 
611a4af0ceeSJacob Faibussowitsch   All told the following happens:
612cf3a2253SJacob Faibussowitsch 
613a4af0ceeSJacob Faibussowitsch   0. defaultInitType -> LAZY
614a4af0ceeSJacob Faibussowitsch   1. Check for log_view/log_summary, if yes defaultInitType -> EAGER
615a4af0ceeSJacob Faibussowitsch   2. PetscDevice initializes each sub type with deviceDefaultInitType.
616a4af0ceeSJacob Faibussowitsch   2.1 Each enabled PetscDevice sub-type then does the above disable or view check in addition
617a4af0ceeSJacob Faibussowitsch       to checking for specific device init. if view or specific device init
618a4af0ceeSJacob Faibussowitsch       subTypeDefaultInitType -> EAGER. disabled once again overrides all.
619a4af0ceeSJacob Faibussowitsch */
6200e6b6b59SJacob Faibussowitsch 
621d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceInitializeFromOptions_Internal(MPI_Comm comm)
622d71ae5a4SJacob Faibussowitsch {
6237a101e5eSJacob Faibussowitsch   auto defaultView                    = PETSC_FALSE;
6247a101e5eSJacob Faibussowitsch   auto initializeDeviceContextEagerly = PETSC_FALSE;
6250e6b6b59SJacob Faibussowitsch   auto defaultDeviceSet               = PETSC_FALSE;
6267a101e5eSJacob Faibussowitsch   auto defaultDevice                  = PetscInt{PETSC_DECIDE};
6270e6b6b59SJacob Faibussowitsch   auto deviceContextInitDevice        = PETSC_DEVICE_DEFAULT();
6280e6b6b59SJacob Faibussowitsch   auto defaultInitType                = PETSC_DEVICE_INIT_LAZY;
629a4af0ceeSJacob Faibussowitsch 
630a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
631a4af0ceeSJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
632a4af0ceeSJacob Faibussowitsch     int result;
633a4af0ceeSJacob Faibussowitsch 
6349566063dSJacob Faibussowitsch     PetscCallMPI(MPI_Comm_compare(comm, PETSC_COMM_WORLD, &result));
635a4af0ceeSJacob Faibussowitsch     /* in order to accurately assign ranks to gpus we need to get the MPI_Comm_rank of the
636a4af0ceeSJacob Faibussowitsch      * global space */
637a4af0ceeSJacob Faibussowitsch     if (PetscUnlikely(result != MPI_IDENT)) {
638a4af0ceeSJacob Faibussowitsch       char name[MPI_MAX_OBJECT_NAME] = {};
639a4af0ceeSJacob Faibussowitsch       int  len; /* unused */
640a4af0ceeSJacob Faibussowitsch 
6419566063dSJacob Faibussowitsch       PetscCallMPI(MPI_Comm_get_name(comm, name, &len));
64298921bdaSJacob Faibussowitsch       SETERRQ(comm, PETSC_ERR_MPI, "Default devices being initialized on MPI_Comm '%s' not PETSC_COMM_WORLD", name);
643a4af0ceeSJacob Faibussowitsch     }
644a4af0ceeSJacob Faibussowitsch   }
645a4af0ceeSJacob Faibussowitsch   comm = PETSC_COMM_WORLD; /* from this point on we assume we're on PETSC_COMM_WORLD */
6469566063dSJacob Faibussowitsch   PetscCall(PetscRegisterFinalize(PetscDeviceFinalize_Private));
647a4af0ceeSJacob Faibussowitsch 
6480e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceInitializeQueryOptions_Private(comm, &deviceContextInitDevice, &defaultInitType, &defaultDevice, &defaultDeviceSet, &defaultView));
6497a101e5eSJacob Faibussowitsch 
6500e6b6b59SJacob Faibussowitsch   // the precise values don't matter here, so long as they are sequential
651bd2fcf0cSJacob Faibussowitsch   static_assert(Petsc::util::to_underlying(PETSC_DEVICE_HOST) == 0, "");
652bd2fcf0cSJacob Faibussowitsch   static_assert(Petsc::util::to_underlying(PETSC_DEVICE_CUDA) == 1, "");
653bd2fcf0cSJacob Faibussowitsch   static_assert(Petsc::util::to_underlying(PETSC_DEVICE_HIP) == 2, "");
654bd2fcf0cSJacob Faibussowitsch   static_assert(Petsc::util::to_underlying(PETSC_DEVICE_SYCL) == 3, "");
655bd2fcf0cSJacob Faibussowitsch   static_assert(Petsc::util::to_underlying(PETSC_DEVICE_MAX) == 4, "");
6560e6b6b59SJacob Faibussowitsch   for (int i = PETSC_DEVICE_HOST; i < PETSC_DEVICE_MAX; ++i) {
6570e6b6b59SJacob Faibussowitsch     const auto deviceType = PetscDeviceTypeCast(i);
658a4af0ceeSJacob Faibussowitsch     auto       initType   = defaultInitType;
659a4af0ceeSJacob Faibussowitsch 
6609566063dSJacob Faibussowitsch     PetscCall(PetscDeviceInitializeTypeFromOptions_Private(comm, deviceType, defaultDevice, defaultView, &initType));
6610e6b6b59SJacob Faibussowitsch     if (PetscDeviceConfiguredFor_Internal(deviceType)) {
6620e6b6b59SJacob Faibussowitsch       if (initType == PETSC_DEVICE_INIT_EAGER) {
663a4af0ceeSJacob Faibussowitsch         initializeDeviceContextEagerly = PETSC_TRUE;
6640e6b6b59SJacob Faibussowitsch         // only update the default device if the user hasn't set it previously
6650e6b6b59SJacob Faibussowitsch         if (!defaultDeviceSet) {
666a4af0ceeSJacob Faibussowitsch           deviceContextInitDevice = deviceType;
6670e6b6b59SJacob Faibussowitsch           PetscCall(PetscInfo(nullptr, "PetscDevice %s set as default device type due to eager initialization\n", PetscDeviceTypes[deviceType]));
6680e6b6b59SJacob Faibussowitsch         }
6690e6b6b59SJacob Faibussowitsch       } else if (initType == PETSC_DEVICE_INIT_NONE) {
6701015a2a4SJacob Faibussowitsch         if (deviceType != PETSC_DEVICE_HOST) PetscCheck(!defaultDeviceSet || (deviceType != deviceContextInitDevice), comm, PETSC_ERR_USER_INPUT, "Cannot explicitly disable the device set as default device type (%s)", PetscDeviceTypes[deviceType]);
671a4af0ceeSJacob Faibussowitsch       }
672a4af0ceeSJacob Faibussowitsch     }
6730e6b6b59SJacob Faibussowitsch   }
6740e6b6b59SJacob Faibussowitsch 
6750e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceSetDefaultDeviceType(deviceContextInitDevice));
6760e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextSetRootDeviceType_Internal(PETSC_DEVICE_DEFAULT()));
6770e6b6b59SJacob Faibussowitsch   /* ----------------------------------------------------------------------------------- */
6780e6b6b59SJacob Faibussowitsch   /*                       PetscDevice is now fully initialized                          */
6790e6b6b59SJacob Faibussowitsch   /* ----------------------------------------------------------------------------------- */
6800e6b6b59SJacob Faibussowitsch   {
6810e6b6b59SJacob Faibussowitsch     /*
6820e6b6b59SJacob Faibussowitsch       query the options db to get the root settings from the user (if any).
6830e6b6b59SJacob Faibussowitsch 
6840e6b6b59SJacob Faibussowitsch       This section is a bit of a hack. We have to reach across to dcontext.cxx to all but call
6850e6b6b59SJacob Faibussowitsch       PetscDeviceContextSetFromOptions() before we even have one, then set a few static
6860e6b6b59SJacob Faibussowitsch       variables in that file with the results.
6870e6b6b59SJacob Faibussowitsch     */
6880e6b6b59SJacob Faibussowitsch     auto dtype = std::make_pair(PETSC_DEVICE_DEFAULT(), PETSC_FALSE);
6890e6b6b59SJacob Faibussowitsch     auto stype = std::make_pair(PETSC_DEVICE_CONTEXT_DEFAULT_STREAM_TYPE, PETSC_FALSE);
6900e6b6b59SJacob Faibussowitsch 
6910e6b6b59SJacob Faibussowitsch     PetscOptionsBegin(comm, "root_", "Root PetscDeviceContext Options", "Sys");
6920e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextQueryOptions_Internal(PetscOptionsObject, dtype, stype));
6930e6b6b59SJacob Faibussowitsch     PetscOptionsEnd();
6940e6b6b59SJacob Faibussowitsch 
6950e6b6b59SJacob Faibussowitsch     if (dtype.second) PetscCall(PetscDeviceContextSetRootDeviceType_Internal(dtype.first));
6960e6b6b59SJacob Faibussowitsch     if (stype.second) PetscCall(PetscDeviceContextSetRootStreamType_Internal(stype.first));
6970e6b6b59SJacob Faibussowitsch   }
6980e6b6b59SJacob Faibussowitsch 
699a4af0ceeSJacob Faibussowitsch   if (initializeDeviceContextEagerly) {
700a4af0ceeSJacob Faibussowitsch     PetscDeviceContext dctx;
701a4af0ceeSJacob Faibussowitsch 
7020e6b6b59SJacob Faibussowitsch     PetscCall(PetscInfo(nullptr, "Eagerly initializing PetscDeviceContext with %s device\n", PetscDeviceTypes[deviceContextInitDevice]));
7030e6b6b59SJacob Faibussowitsch     /* instantiates the device context */
7049566063dSJacob Faibussowitsch     PetscCall(PetscDeviceContextGetCurrentContext(&dctx));
7059566063dSJacob Faibussowitsch     PetscCall(PetscDeviceContextSetUp(dctx));
706a4af0ceeSJacob Faibussowitsch   }
7073ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
708a4af0ceeSJacob Faibussowitsch }
709